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
beep_init(void * arg,beep_on_func_t beep_on_func,beep_off_func_t beep_off_func,beep_freq_func_t beep_freq_func)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
beep_fini(void)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
beeper_off(void)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
beeper_freq(enum beep_type type,int freq)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
beep(enum beep_type type)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
beep_polled(enum beep_type type)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
beeper_on(enum beep_type type)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
beep_mktone(int frequency,int duration)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
beep_timeout(void * arg)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
beep_busy(void)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