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 if (queue == NULL) { 115 BEEP_DEBUG((CE_WARN, 116 "beep_init : kmem_zalloc of beep_queue failed.")); 117 return (DDI_FAILURE); 118 } 119 120 beep_state.arg = arg; 121 beep_state.mode = BEEP_OFF; 122 beep_state.beep_freq = beep_freq_func; 123 beep_state.beep_on = beep_on_func; 124 beep_state.beep_off = beep_off_func; 125 beep_state.timeout_id = 0; 126 127 beep_state.queue_head = 0; 128 beep_state.queue_tail = 0; 129 beep_state.queue_size = beep_queue_size; 130 beep_state.queue = queue; 131 132 mutex_exit(&beep_state.mutex); 133 134 BEEP_DEBUG1((CE_CONT, "beep_init : done.")); 135 return (DDI_SUCCESS); 136 } 137 138 139 int 140 beep_fini(void) 141 { 142 BEEP_DEBUG1((CE_CONT, "beep_fini() : start.")); 143 144 (void) beeper_off(); 145 146 mutex_enter(&beep_state.mutex); 147 148 if (beep_state.mode == BEEP_UNINIT) { 149 mutex_exit(&beep_state.mutex); 150 BEEP_DEBUG((CE_WARN, 151 "beep_fini : beep_state already uninitialized.")); 152 return (0); 153 } 154 155 if (beep_state.queue != NULL) 156 kmem_free(beep_state.queue, 157 sizeof (beep_entry_t) * beep_state.queue_size); 158 159 beep_state.arg = (void *)NULL; 160 beep_state.mode = BEEP_UNINIT; 161 beep_state.beep_freq = (beep_freq_func_t)NULL; 162 beep_state.beep_on = (beep_on_func_t)NULL; 163 beep_state.beep_off = (beep_off_func_t)NULL; 164 beep_state.timeout_id = 0; 165 166 beep_state.queue_head = 0; 167 beep_state.queue_tail = 0; 168 beep_state.queue_size = 0; 169 beep_state.queue = (beep_entry_t *)NULL; 170 171 mutex_exit(&beep_state.mutex); 172 173 BEEP_DEBUG1((CE_CONT, "beep_fini() : done.")); 174 175 return (0); 176 } 177 178 179 int 180 beeper_off(void) 181 { 182 BEEP_DEBUG1((CE_CONT, "beeper_off : start.")); 183 184 mutex_enter(&beep_state.mutex); 185 186 if (beep_state.mode == BEEP_UNINIT) { 187 mutex_exit(&beep_state.mutex); 188 return (ENXIO); 189 } 190 191 if (beep_state.mode == BEEP_TIMED) { 192 (void) untimeout(beep_state.timeout_id); 193 beep_state.timeout_id = 0; 194 } 195 196 if (beep_state.mode != BEEP_OFF) { 197 beep_state.mode = BEEP_OFF; 198 199 if (beep_state.beep_off != NULL) 200 (*beep_state.beep_off)(beep_state.arg); 201 } 202 203 beep_state.queue_head = 0; 204 beep_state.queue_tail = 0; 205 206 mutex_exit(&beep_state.mutex); 207 208 BEEP_DEBUG1((CE_CONT, "beeper_off : done.")); 209 210 return (0); 211 } 212 213 int 214 beeper_freq(enum beep_type type, int freq) 215 { 216 beep_params_t *bp; 217 218 BEEP_DEBUG1((CE_CONT, "beeper_freq(%d, %d) : start", type, freq)); 219 220 /* 221 * The frequency value is limited to the range of [0 - 32767] 222 */ 223 if (freq < 0 || freq > INT16_MAX) 224 return (EINVAL); 225 226 for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) { 227 if (bp->type == type) 228 break; 229 } 230 231 if (bp->type != type) { 232 BEEP_DEBUG((CE_WARN, "beeper_freq : invalid type.")); 233 234 return (EINVAL); 235 } 236 237 bp->frequency = freq; 238 239 BEEP_DEBUG1((CE_CONT, "beeper_freq : done.")); 240 return (0); 241 } 242 243 /* 244 * beep : 245 * Start beeping for period specified by the type value, 246 * from the value in the beep_param structure in milliseconds. 247 */ 248 int 249 beep(enum beep_type type) 250 { 251 252 beep_params_t *bp; 253 254 BEEP_DEBUG1((CE_CONT, "beep(%d) : start.", type)); 255 256 for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) { 257 if (bp->type == type) 258 break; 259 } 260 261 if (bp->type != type) { 262 263 BEEP_DEBUG((CE_WARN, "beep : invalid type.")); 264 265 /* If type doesn't match, return silently without beeping */ 266 return (EINVAL); 267 } 268 269 return (beep_mktone(bp->frequency, bp->duration)); 270 } 271 272 273 /*ARGSUSED*/ 274 int 275 beep_polled(enum beep_type type) 276 { 277 /* 278 * No-op at this time. 279 * 280 * Don't think we can make this work in general, as tem_safe 281 * has a requirement of no mutexes, but kbd sends messages 282 * through streams. 283 */ 284 285 BEEP_DEBUG1((CE_CONT, "beep_polled(%d)", type)); 286 287 return (0); 288 } 289 290 /* 291 * beeper_on : 292 * Turn the beeper on 293 */ 294 int 295 beeper_on(enum beep_type type) 296 { 297 beep_params_t *bp; 298 int status = 0; 299 300 BEEP_DEBUG1((CE_CONT, "beeper_on(%d) : start.", type)); 301 302 for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) { 303 if (bp->type == type) 304 break; 305 } 306 307 if (bp->type != type) { 308 309 BEEP_DEBUG((CE_WARN, "beeper_on : invalid type.")); 310 311 /* If type doesn't match, return silently without beeping */ 312 return (EINVAL); 313 } 314 315 mutex_enter(&beep_state.mutex); 316 317 if (beep_state.mode == BEEP_UNINIT) { 318 status = ENXIO; 319 320 /* Start another beep only if the previous one is over */ 321 } else if (beep_state.mode == BEEP_OFF) { 322 if (bp->frequency != 0) { 323 beep_state.mode = BEEP_ON; 324 325 if (beep_state.beep_freq != NULL) 326 (*beep_state.beep_freq)(beep_state.arg, 327 bp->frequency); 328 329 if (beep_state.beep_on != NULL) 330 (*beep_state.beep_on)(beep_state.arg); 331 } 332 } else { 333 status = EBUSY; 334 } 335 336 mutex_exit(&beep_state.mutex); 337 338 BEEP_DEBUG1((CE_CONT, "beeper_on : done, status %d.", status)); 339 340 return (status); 341 } 342 343 344 int 345 beep_mktone(int frequency, int duration) 346 { 347 int next; 348 int status = 0; 349 350 BEEP_DEBUG1((CE_CONT, "beep_mktone(%d, %d) : start.", frequency, 351 duration)); 352 353 /* 354 * The frequency value is limited to the range of [0 - 32767] 355 */ 356 if (frequency < 0 || frequency > INT16_MAX) 357 return (EINVAL); 358 359 mutex_enter(&beep_state.mutex); 360 361 if (beep_state.mode == BEEP_UNINIT) { 362 status = ENXIO; 363 364 } else if (beep_state.mode == BEEP_TIMED) { 365 366 /* If already processing a beep, queue this one */ 367 368 if (frequency != 0) { 369 next = beep_state.queue_tail + 1; 370 if (next == beep_state.queue_size) 371 next = 0; 372 373 if (next != beep_state.queue_head) { 374 /* 375 * If there is room in the queue, 376 * add this entry 377 */ 378 379 beep_state.queue[beep_state.queue_tail]. 380 frequency = (unsigned short)frequency; 381 382 beep_state.queue[beep_state.queue_tail]. 383 duration = (unsigned short)duration; 384 385 beep_state.queue_tail = next; 386 } else { 387 status = EAGAIN; 388 } 389 } 390 391 } else if (beep_state.mode == BEEP_OFF) { 392 393 /* Start another beep only if the previous one is over */ 394 395 if (frequency != 0) { 396 beep_state.mode = BEEP_TIMED; 397 398 if (beep_state.beep_freq != NULL) 399 (*beep_state.beep_freq)(beep_state.arg, 400 frequency); 401 402 if (beep_state.beep_on != NULL) 403 (*beep_state.beep_on)(beep_state.arg); 404 405 /* 406 * Set timeout for ending the beep after the 407 * specified time 408 */ 409 410 beep_state.timeout_id = timeout(beep_timeout, NULL, 411 drv_usectohz(duration * 1000)); 412 } 413 } else { 414 status = EBUSY; 415 } 416 417 mutex_exit(&beep_state.mutex); 418 419 BEEP_DEBUG1((CE_CONT, "beep_mktone : done, status %d.", status)); 420 421 return (status); 422 } 423 424 425 /* 426 * Turn the beeper off which had been turned on from beep() 427 * for a specified period of time 428 */ 429 /*ARGSUSED*/ 430 void 431 beep_timeout(void *arg) 432 { 433 int frequency; 434 int duration; 435 int next; 436 437 BEEP_DEBUG1((CE_CONT, "beeper_timeout : start.")); 438 439 mutex_enter(&beep_state.mutex); 440 441 beep_state.timeout_id = 0; 442 443 if (beep_state.mode == BEEP_UNINIT) { 444 mutex_exit(&beep_state.mutex); 445 BEEP_DEBUG1((CE_CONT, "beep_timeout : uninitialized.")); 446 return; 447 } 448 449 if ((beep_state.mode == BEEP_ON) || 450 (beep_state.mode == BEEP_TIMED)) { 451 452 beep_state.mode = BEEP_OFF; 453 454 if (beep_state.beep_off != NULL) 455 (*beep_state.beep_off)(beep_state.arg); 456 } 457 458 if (beep_state.queue_head != beep_state.queue_tail) { 459 460 next = beep_state.queue_head; 461 462 frequency = beep_state.queue[next].frequency; 463 464 duration = beep_state.queue[next].duration; 465 466 next++; 467 if (next == beep_state.queue_size) 468 next = 0; 469 470 beep_state.queue_head = next; 471 472 beep_state.mode = BEEP_TIMED; 473 474 if (frequency != 0) { 475 if (beep_state.beep_freq != NULL) 476 (*beep_state.beep_freq)(beep_state.arg, 477 frequency); 478 479 if (beep_state.beep_on != NULL) 480 (*beep_state.beep_on)(beep_state.arg); 481 } 482 483 /* Set timeout for ending the beep after the specified time */ 484 485 beep_state.timeout_id = timeout(beep_timeout, NULL, 486 drv_usectohz(duration * 1000)); 487 } 488 489 mutex_exit(&beep_state.mutex); 490 491 BEEP_DEBUG1((CE_CONT, "beep_timeout : done.")); 492 } 493 494 495 /* 496 * Return true (1) if we are sounding a tone. 497 */ 498 int 499 beep_busy(void) 500 { 501 int status; 502 503 BEEP_DEBUG1((CE_CONT, "beep_busy : start.")); 504 505 mutex_enter(&beep_state.mutex); 506 507 status = beep_state.mode != BEEP_UNINIT && 508 beep_state.mode != BEEP_OFF; 509 510 mutex_exit(&beep_state.mutex); 511 512 BEEP_DEBUG1((CE_CONT, "beep_busy : status %d.", status)); 513 514 return (status); 515 } 516