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