xref: /illumos-gate/usr/src/uts/common/io/beep.c (revision afab0816ecb604f0099a09ad8ee398f0d7b77b1c)
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