Home | History | Annotate | Download | only in io
      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