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