1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * This is the Beep module for supporting keyboard beep for keyboards 28 * that do not have the beeping feature within themselves 29 * 30 */ 31 32 #include <sys/types.h> 33 #include <sys/conf.h> 34 35 #include <sys/ddi.h> 36 #include <sys/sunddi.h> 37 #include <sys/modctl.h> 38 #include <sys/ddi_impldefs.h> 39 #include <sys/kmem.h> 40 41 #include <sys/beep.h> 42 #include <sys/inttypes.h> 43 44 /* 45 * Debug stuff 46 * BEEP_DEBUG used for errors 47 * BEEP_DEBUG1 prints when beep_debug > 1 and used for normal messages 48 */ 49 #ifdef DEBUG 50 int beep_debug = 0; 51 #define BEEP_DEBUG(args) if (beep_debug) cmn_err args 52 #define BEEP_DEBUG1(args) if (beep_debug > 1) cmn_err args 53 #else 54 #define BEEP_DEBUG(args) 55 #define BEEP_DEBUG1(args) 56 #endif 57 58 int beep_queue_size = BEEP_QUEUE_SIZE; 59 60 /* 61 * Note that mutex_init is not called on the mutex in beep_state, 62 * But assumes that zeroed memory does not need to call mutex_init, 63 * as documented in mutex.c 64 */ 65 66 beep_state_t beep_state; 67 68 beep_params_t beep_params[] = { 69 {BEEP_CONSOLE, 900, 200}, 70 {BEEP_TYPE4, 2000, 0}, 71 {BEEP_DEFAULT, 1000, 200}, /* Must be last */ 72 }; 73 74 75 /* 76 * beep_init: 77 * Allocate the beep_queue structure 78 * Initialize beep_state structure 79 * Called from beep driver attach routine 80 */ 81 82 int 83 beep_init(void *arg, 84 beep_on_func_t beep_on_func, 85 beep_off_func_t beep_off_func, 86 beep_freq_func_t beep_freq_func) 87 { 88 beep_entry_t *queue; 89 90 BEEP_DEBUG1((CE_CONT, 91 "beep_init(0x%lx, 0x%lx, 0x%lx, 0x%lx) : start.", 92 (unsigned long) arg, 93 (unsigned long) beep_on_func, 94 (unsigned long) beep_off_func, 95 (unsigned long) beep_freq_func)); 96 97 mutex_enter(&beep_state.mutex); 98 99 if (beep_state.mode != BEEP_UNINIT) { 100 mutex_exit(&beep_state.mutex); 101 BEEP_DEBUG((CE_WARN, 102 "beep_init : beep_state already initialized.")); 103 return (DDI_SUCCESS); 104 } 105 106 queue = kmem_zalloc(sizeof (beep_entry_t) * beep_queue_size, 107 KM_SLEEP); 108 109 BEEP_DEBUG1((CE_CONT, 110 "beep_init : beep_queue kmem_zalloc(%d) = 0x%lx.", 111 (int)sizeof (beep_entry_t) * beep_queue_size, 112 (unsigned long)queue)); 113 114 beep_state.arg = arg; 115 beep_state.mode = BEEP_OFF; 116 beep_state.beep_freq = beep_freq_func; 117 beep_state.beep_on = beep_on_func; 118 beep_state.beep_off = beep_off_func; 119 beep_state.timeout_id = 0; 120 121 beep_state.queue_head = 0; 122 beep_state.queue_tail = 0; 123 beep_state.queue_size = beep_queue_size; 124 beep_state.queue = queue; 125 126 mutex_exit(&beep_state.mutex); 127 128 BEEP_DEBUG1((CE_CONT, "beep_init : done.")); 129 return (DDI_SUCCESS); 130 } 131 132 133 int 134 beep_fini(void) 135 { 136 BEEP_DEBUG1((CE_CONT, "beep_fini() : start.")); 137 138 (void) beeper_off(); 139 140 mutex_enter(&beep_state.mutex); 141 142 if (beep_state.mode == BEEP_UNINIT) { 143 mutex_exit(&beep_state.mutex); 144 BEEP_DEBUG((CE_WARN, 145 "beep_fini : beep_state already uninitialized.")); 146 return (0); 147 } 148 149 if (beep_state.queue != NULL) 150 kmem_free(beep_state.queue, 151 sizeof (beep_entry_t) * beep_state.queue_size); 152 153 beep_state.arg = (void *)NULL; 154 beep_state.mode = BEEP_UNINIT; 155 beep_state.beep_freq = (beep_freq_func_t)NULL; 156 beep_state.beep_on = (beep_on_func_t)NULL; 157 beep_state.beep_off = (beep_off_func_t)NULL; 158 beep_state.timeout_id = 0; 159 160 beep_state.queue_head = 0; 161 beep_state.queue_tail = 0; 162 beep_state.queue_size = 0; 163 beep_state.queue = (beep_entry_t *)NULL; 164 165 mutex_exit(&beep_state.mutex); 166 167 BEEP_DEBUG1((CE_CONT, "beep_fini() : done.")); 168 169 return (0); 170 } 171 172 173 int 174 beeper_off(void) 175 { 176 BEEP_DEBUG1((CE_CONT, "beeper_off : start.")); 177 178 mutex_enter(&beep_state.mutex); 179 180 if (beep_state.mode == BEEP_UNINIT) { 181 mutex_exit(&beep_state.mutex); 182 return (ENXIO); 183 } 184 185 if (beep_state.mode == BEEP_TIMED) { 186 (void) untimeout(beep_state.timeout_id); 187 beep_state.timeout_id = 0; 188 } 189 190 if (beep_state.mode != BEEP_OFF) { 191 beep_state.mode = BEEP_OFF; 192 193 if (beep_state.beep_off != NULL) 194 (*beep_state.beep_off)(beep_state.arg); 195 } 196 197 beep_state.queue_head = 0; 198 beep_state.queue_tail = 0; 199 200 mutex_exit(&beep_state.mutex); 201 202 BEEP_DEBUG1((CE_CONT, "beeper_off : done.")); 203 204 return (0); 205 } 206 207 int 208 beeper_freq(enum beep_type type, int freq) 209 { 210 beep_params_t *bp; 211 212 BEEP_DEBUG1((CE_CONT, "beeper_freq(%d, %d) : start", type, freq)); 213 214 /* 215 * The frequency value is limited to the range of [0 - 32767] 216 */ 217 if (freq < 0 || freq > INT16_MAX) 218 return (EINVAL); 219 220 for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) { 221 if (bp->type == type) 222 break; 223 } 224 225 if (bp->type != type) { 226 BEEP_DEBUG((CE_WARN, "beeper_freq : invalid type.")); 227 228 return (EINVAL); 229 } 230 231 bp->frequency = freq; 232 233 BEEP_DEBUG1((CE_CONT, "beeper_freq : done.")); 234 return (0); 235 } 236 237 /* 238 * beep : 239 * Start beeping for period specified by the type value, 240 * from the value in the beep_param structure in milliseconds. 241 */ 242 int 243 beep(enum beep_type type) 244 { 245 246 beep_params_t *bp; 247 248 BEEP_DEBUG1((CE_CONT, "beep(%d) : start.", type)); 249 250 for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) { 251 if (bp->type == type) 252 break; 253 } 254 255 if (bp->type != type) { 256 257 BEEP_DEBUG((CE_WARN, "beep : invalid type.")); 258 259 /* If type doesn't match, return silently without beeping */ 260 return (EINVAL); 261 } 262 263 return (beep_mktone(bp->frequency, bp->duration)); 264 } 265 266 267 /*ARGSUSED*/ 268 int 269 beep_polled(enum beep_type type) 270 { 271 /* 272 * No-op at this time. 273 * 274 * Don't think we can make this work in general, as tem_safe 275 * has a requirement of no mutexes, but kbd sends messages 276 * through streams. 277 */ 278 279 BEEP_DEBUG1((CE_CONT, "beep_polled(%d)", type)); 280 281 return (0); 282 } 283 284 /* 285 * beeper_on : 286 * Turn the beeper on 287 */ 288 int 289 beeper_on(enum beep_type type) 290 { 291 beep_params_t *bp; 292 int status = 0; 293 294 BEEP_DEBUG1((CE_CONT, "beeper_on(%d) : start.", type)); 295 296 for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) { 297 if (bp->type == type) 298 break; 299 } 300 301 if (bp->type != type) { 302 303 BEEP_DEBUG((CE_WARN, "beeper_on : invalid type.")); 304 305 /* If type doesn't match, return silently without beeping */ 306 return (EINVAL); 307 } 308 309 mutex_enter(&beep_state.mutex); 310 311 if (beep_state.mode == BEEP_UNINIT) { 312 status = ENXIO; 313 314 /* Start another beep only if the previous one is over */ 315 } else if (beep_state.mode == BEEP_OFF) { 316 if (bp->frequency != 0) { 317 beep_state.mode = BEEP_ON; 318 319 if (beep_state.beep_freq != NULL) 320 (*beep_state.beep_freq)(beep_state.arg, 321 bp->frequency); 322 323 if (beep_state.beep_on != NULL) 324 (*beep_state.beep_on)(beep_state.arg); 325 } 326 } else { 327 status = EBUSY; 328 } 329 330 mutex_exit(&beep_state.mutex); 331 332 BEEP_DEBUG1((CE_CONT, "beeper_on : done, status %d.", status)); 333 334 return (status); 335 } 336 337 338 int 339 beep_mktone(int frequency, int duration) 340 { 341 int next; 342 int status = 0; 343 344 BEEP_DEBUG1((CE_CONT, "beep_mktone(%d, %d) : start.", frequency, 345 duration)); 346 347 /* 348 * The frequency value is limited to the range of [0 - 32767] 349 */ 350 if (frequency < 0 || frequency > INT16_MAX) 351 return (EINVAL); 352 353 mutex_enter(&beep_state.mutex); 354 355 if (beep_state.mode == BEEP_UNINIT) { 356 status = ENXIO; 357 358 } else if (beep_state.mode == BEEP_TIMED) { 359 360 /* If already processing a beep, queue this one */ 361 362 if (frequency != 0) { 363 next = beep_state.queue_tail + 1; 364 if (next == beep_state.queue_size) 365 next = 0; 366 367 if (next != beep_state.queue_head) { 368 /* 369 * If there is room in the queue, 370 * add this entry 371 */ 372 373 beep_state.queue[beep_state.queue_tail]. 374 frequency = (unsigned short)frequency; 375 376 beep_state.queue[beep_state.queue_tail]. 377 duration = (unsigned short)duration; 378 379 beep_state.queue_tail = next; 380 } else { 381 status = EAGAIN; 382 } 383 } 384 385 } else if (beep_state.mode == BEEP_OFF) { 386 387 /* Start another beep only if the previous one is over */ 388 389 if (frequency != 0) { 390 beep_state.mode = BEEP_TIMED; 391 392 if (beep_state.beep_freq != NULL) 393 (*beep_state.beep_freq)(beep_state.arg, 394 frequency); 395 396 if (beep_state.beep_on != NULL) 397 (*beep_state.beep_on)(beep_state.arg); 398 399 /* 400 * Set timeout for ending the beep after the 401 * specified time 402 */ 403 404 beep_state.timeout_id = timeout(beep_timeout, NULL, 405 drv_usectohz(duration * 1000)); 406 } 407 } else { 408 status = EBUSY; 409 } 410 411 mutex_exit(&beep_state.mutex); 412 413 BEEP_DEBUG1((CE_CONT, "beep_mktone : done, status %d.", status)); 414 415 return (status); 416 } 417 418 419 /* 420 * Turn the beeper off which had been turned on from beep() 421 * for a specified period of time 422 */ 423 /*ARGSUSED*/ 424 void 425 beep_timeout(void *arg) 426 { 427 int frequency; 428 int duration; 429 int next; 430 431 BEEP_DEBUG1((CE_CONT, "beeper_timeout : start.")); 432 433 mutex_enter(&beep_state.mutex); 434 435 beep_state.timeout_id = 0; 436 437 if (beep_state.mode == BEEP_UNINIT) { 438 mutex_exit(&beep_state.mutex); 439 BEEP_DEBUG1((CE_CONT, "beep_timeout : uninitialized.")); 440 return; 441 } 442 443 if ((beep_state.mode == BEEP_ON) || 444 (beep_state.mode == BEEP_TIMED)) { 445 446 beep_state.mode = BEEP_OFF; 447 448 if (beep_state.beep_off != NULL) 449 (*beep_state.beep_off)(beep_state.arg); 450 } 451 452 if (beep_state.queue_head != beep_state.queue_tail) { 453 454 next = beep_state.queue_head; 455 456 frequency = beep_state.queue[next].frequency; 457 458 duration = beep_state.queue[next].duration; 459 460 next++; 461 if (next == beep_state.queue_size) 462 next = 0; 463 464 beep_state.queue_head = next; 465 466 beep_state.mode = BEEP_TIMED; 467 468 if (frequency != 0) { 469 if (beep_state.beep_freq != NULL) 470 (*beep_state.beep_freq)(beep_state.arg, 471 frequency); 472 473 if (beep_state.beep_on != NULL) 474 (*beep_state.beep_on)(beep_state.arg); 475 } 476 477 /* Set timeout for ending the beep after the specified time */ 478 479 beep_state.timeout_id = timeout(beep_timeout, NULL, 480 drv_usectohz(duration * 1000)); 481 } 482 483 mutex_exit(&beep_state.mutex); 484 485 BEEP_DEBUG1((CE_CONT, "beep_timeout : done.")); 486 } 487 488 489 /* 490 * Return true (1) if we are sounding a tone. 491 */ 492 int 493 beep_busy(void) 494 { 495 int status; 496 497 BEEP_DEBUG1((CE_CONT, "beep_busy : start.")); 498 499 mutex_enter(&beep_state.mutex); 500 501 status = beep_state.mode != BEEP_UNINIT && 502 beep_state.mode != BEEP_OFF; 503 504 mutex_exit(&beep_state.mutex); 505 506 BEEP_DEBUG1((CE_CONT, "beep_busy : status %d.", status)); 507 508 return (status); 509 }