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 beep_state.arg = arg;
115 beep_state.mode = BEEP_OFF;
116 beep_state.beep_freq = beep_freq_func;
117 beep_state.beep_on = beep_on_func;
118 beep_state.beep_off = beep_off_func;
119 beep_state.timeout_id = 0;
120
121 beep_state.queue_head = 0;
122 beep_state.queue_tail = 0;
123 beep_state.queue_size = beep_queue_size;
124 beep_state.queue = queue;
125
126 mutex_exit(&beep_state.mutex);
127
128 BEEP_DEBUG1((CE_CONT, "beep_init : done."));
129 return (DDI_SUCCESS);
130 }
131
132
133 int
134 beep_fini(void)
135 {
136 BEEP_DEBUG1((CE_CONT, "beep_fini() : start."));
137
138 (void) beeper_off();
139
140 mutex_enter(&beep_state.mutex);
141
142 if (beep_state.mode == BEEP_UNINIT) {
143 mutex_exit(&beep_state.mutex);
144 BEEP_DEBUG((CE_WARN,
145 "beep_fini : beep_state already uninitialized."));
146 return (0);
147 }
148
149 if (beep_state.queue != NULL)
150 kmem_free(beep_state.queue,
151 sizeof (beep_entry_t) * beep_state.queue_size);
152
153 beep_state.arg = (void *)NULL;
154 beep_state.mode = BEEP_UNINIT;
155 beep_state.beep_freq = (beep_freq_func_t)NULL;
156 beep_state.beep_on = (beep_on_func_t)NULL;
157 beep_state.beep_off = (beep_off_func_t)NULL;
158 beep_state.timeout_id = 0;
159
160 beep_state.queue_head = 0;
161 beep_state.queue_tail = 0;
162 beep_state.queue_size = 0;
163 beep_state.queue = (beep_entry_t *)NULL;
164
165 mutex_exit(&beep_state.mutex);
166
167 BEEP_DEBUG1((CE_CONT, "beep_fini() : done."));
168
169 return (0);
170 }
171
172
173 int
174 beeper_off(void)
175 {
176 BEEP_DEBUG1((CE_CONT, "beeper_off : start."));
177
178 mutex_enter(&beep_state.mutex);
179
180 if (beep_state.mode == BEEP_UNINIT) {
181 mutex_exit(&beep_state.mutex);
182 return (ENXIO);
183 }
184
185 if (beep_state.mode == BEEP_TIMED) {
186 (void) untimeout(beep_state.timeout_id);
187 beep_state.timeout_id = 0;
188 }
189
190 if (beep_state.mode != BEEP_OFF) {
191 beep_state.mode = BEEP_OFF;
192
193 if (beep_state.beep_off != NULL)
194 (*beep_state.beep_off)(beep_state.arg);
195 }
196
197 beep_state.queue_head = 0;
198 beep_state.queue_tail = 0;
199
200 mutex_exit(&beep_state.mutex);
201
202 BEEP_DEBUG1((CE_CONT, "beeper_off : done."));
203
204 return (0);
205 }
206
207 int
208 beeper_freq(enum beep_type type, int freq)
209 {
210 beep_params_t *bp;
211
212 BEEP_DEBUG1((CE_CONT, "beeper_freq(%d, %d) : start", type, freq));
213
214 /*
215 * The frequency value is limited to the range of [0 - 32767]
216 */
217 if (freq < 0 || freq > INT16_MAX)
218 return (EINVAL);
219
220 for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) {
221 if (bp->type == type)
222 break;
223 }
224
225 if (bp->type != type) {
226 BEEP_DEBUG((CE_WARN, "beeper_freq : invalid type."));
227
228 return (EINVAL);
229 }
230
231 bp->frequency = freq;
232
233 BEEP_DEBUG1((CE_CONT, "beeper_freq : done."));
234 return (0);
235 }
236
237 /*
238 * beep :
239 * Start beeping for period specified by the type value,
240 * from the value in the beep_param structure in milliseconds.
241 */
242 int
243 beep(enum beep_type type)
244 {
245
246 beep_params_t *bp;
247
248 BEEP_DEBUG1((CE_CONT, "beep(%d) : start.", type));
249
250 for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) {
251 if (bp->type == type)
252 break;
253 }
254
255 if (bp->type != type) {
256
257 BEEP_DEBUG((CE_WARN, "beep : invalid type."));
258
259 /* If type doesn't match, return silently without beeping */
260 return (EINVAL);
261 }
262
263 return (beep_mktone(bp->frequency, bp->duration));
264 }
265
266
267 /*ARGSUSED*/
268 int
269 beep_polled(enum beep_type type)
270 {
271 /*
272 * No-op at this time.
273 *
274 * Don't think we can make this work in general, as tem_safe
275 * has a requirement of no mutexes, but kbd sends messages
276 * through streams.
277 */
278
279 BEEP_DEBUG1((CE_CONT, "beep_polled(%d)", type));
280
281 return (0);
282 }
283
284 /*
285 * beeper_on :
286 * Turn the beeper on
287 */
288 int
289 beeper_on(enum beep_type type)
290 {
291 beep_params_t *bp;
292 int status = 0;
293
294 BEEP_DEBUG1((CE_CONT, "beeper_on(%d) : start.", type));
295
296 for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) {
297 if (bp->type == type)
298 break;
299 }
300
301 if (bp->type != type) {
302
303 BEEP_DEBUG((CE_WARN, "beeper_on : invalid type."));
304
305 /* If type doesn't match, return silently without beeping */
306 return (EINVAL);
307 }
308
309 mutex_enter(&beep_state.mutex);
310
311 if (beep_state.mode == BEEP_UNINIT) {
312 status = ENXIO;
313
314 /* Start another beep only if the previous one is over */
315 } else if (beep_state.mode == BEEP_OFF) {
316 if (bp->frequency != 0) {
317 beep_state.mode = BEEP_ON;
318
319 if (beep_state.beep_freq != NULL)
320 (*beep_state.beep_freq)(beep_state.arg,
321 bp->frequency);
322
323 if (beep_state.beep_on != NULL)
324 (*beep_state.beep_on)(beep_state.arg);
325 }
326 } else {
327 status = EBUSY;
328 }
329
330 mutex_exit(&beep_state.mutex);
331
332 BEEP_DEBUG1((CE_CONT, "beeper_on : done, status %d.", status));
333
334 return (status);
335 }
336
337
338 int
339 beep_mktone(int frequency, int duration)
340 {
341 int next;
342 int status = 0;
343
344 BEEP_DEBUG1((CE_CONT, "beep_mktone(%d, %d) : start.", frequency,
345 duration));
346
347 /*
348 * The frequency value is limited to the range of [0 - 32767]
349 */
350 if (frequency < 0 || frequency > INT16_MAX)
351 return (EINVAL);
352
353 mutex_enter(&beep_state.mutex);
354
355 if (beep_state.mode == BEEP_UNINIT) {
356 status = ENXIO;
357
358 } else if (beep_state.mode == BEEP_TIMED) {
359
360 /* If already processing a beep, queue this one */
361
362 if (frequency != 0) {
363 next = beep_state.queue_tail + 1;
364 if (next == beep_state.queue_size)
365 next = 0;
366
367 if (next != beep_state.queue_head) {
368 /*
369 * If there is room in the queue,
370 * add this entry
371 */
372
373 beep_state.queue[beep_state.queue_tail].
374 frequency = (unsigned short)frequency;
375
376 beep_state.queue[beep_state.queue_tail].
377 duration = (unsigned short)duration;
378
379 beep_state.queue_tail = next;
380 } else {
381 status = EAGAIN;
382 }
383 }
384
385 } else if (beep_state.mode == BEEP_OFF) {
386
387 /* Start another beep only if the previous one is over */
388
389 if (frequency != 0) {
390 beep_state.mode = BEEP_TIMED;
391
392 if (beep_state.beep_freq != NULL)
393 (*beep_state.beep_freq)(beep_state.arg,
394 frequency);
395
396 if (beep_state.beep_on != NULL)
397 (*beep_state.beep_on)(beep_state.arg);
398
399 /*
400 * Set timeout for ending the beep after the
401 * specified time
402 */
403
404 beep_state.timeout_id = timeout(beep_timeout, NULL,
405 drv_usectohz(duration * 1000));
406 }
407 } else {
408 status = EBUSY;
409 }
410
411 mutex_exit(&beep_state.mutex);
412
413 BEEP_DEBUG1((CE_CONT, "beep_mktone : done, status %d.", status));
414
415 return (status);
416 }
417
418
419 /*
420 * Turn the beeper off which had been turned on from beep()
421 * for a specified period of time
422 */
423 /*ARGSUSED*/
424 void
425 beep_timeout(void *arg)
426 {
427 int frequency;
428 int duration;
429 int next;
430
431 BEEP_DEBUG1((CE_CONT, "beeper_timeout : start."));
432
433 mutex_enter(&beep_state.mutex);
434
435 beep_state.timeout_id = 0;
436
437 if (beep_state.mode == BEEP_UNINIT) {
438 mutex_exit(&beep_state.mutex);
439 BEEP_DEBUG1((CE_CONT, "beep_timeout : uninitialized."));
440 return;
441 }
442
443 if ((beep_state.mode == BEEP_ON) ||
444 (beep_state.mode == BEEP_TIMED)) {
445
446 beep_state.mode = BEEP_OFF;
447
448 if (beep_state.beep_off != NULL)
449 (*beep_state.beep_off)(beep_state.arg);
450 }
451
452 if (beep_state.queue_head != beep_state.queue_tail) {
453
454 next = beep_state.queue_head;
455
456 frequency = beep_state.queue[next].frequency;
457
458 duration = beep_state.queue[next].duration;
459
460 next++;
461 if (next == beep_state.queue_size)
462 next = 0;
463
464 beep_state.queue_head = next;
465
466 beep_state.mode = BEEP_TIMED;
467
468 if (frequency != 0) {
469 if (beep_state.beep_freq != NULL)
470 (*beep_state.beep_freq)(beep_state.arg,
471 frequency);
472
473 if (beep_state.beep_on != NULL)
474 (*beep_state.beep_on)(beep_state.arg);
475 }
476
477 /* Set timeout for ending the beep after the specified time */
478
479 beep_state.timeout_id = timeout(beep_timeout, NULL,
480 drv_usectohz(duration * 1000));
481 }
482
483 mutex_exit(&beep_state.mutex);
484
485 BEEP_DEBUG1((CE_CONT, "beep_timeout : done."));
486 }
487
488
489 /*
490 * Return true (1) if we are sounding a tone.
491 */
492 int
493 beep_busy(void)
494 {
495 int status;
496
497 BEEP_DEBUG1((CE_CONT, "beep_busy : start."));
498
499 mutex_enter(&beep_state.mutex);
500
501 status = beep_state.mode != BEEP_UNINIT &&
502 beep_state.mode != BEEP_OFF;
503
504 mutex_exit(&beep_state.mutex);
505
506 BEEP_DEBUG1((CE_CONT, "beep_busy : status %d.", status));
507
508 return (status);
509 }