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