/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This is the Beep module for supporting keyboard beep for keyboards * that do not have the beeping feature within themselves * */ #include #include #include #include #include #include #include #include #include /* * Debug stuff * BEEP_DEBUG used for errors * BEEP_DEBUG1 prints when beep_debug > 1 and used for normal messages */ #ifdef DEBUG int beep_debug = 0; #define BEEP_DEBUG(args) if (beep_debug) cmn_err args #define BEEP_DEBUG1(args) if (beep_debug > 1) cmn_err args #else #define BEEP_DEBUG(args) #define BEEP_DEBUG1(args) #endif int beep_queue_size = BEEP_QUEUE_SIZE; /* * Note that mutex_init is not called on the mutex in beep_state, * But assumes that zeroed memory does not need to call mutex_init, * as documented in mutex.c */ beep_state_t beep_state; beep_params_t beep_params[] = { {BEEP_CONSOLE, 900, 200}, {BEEP_TYPE4, 2000, 0}, {BEEP_DEFAULT, 1000, 200}, /* Must be last */ }; /* * beep_init: * Allocate the beep_queue structure * Initialize beep_state structure * Called from beep driver attach routine */ 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) { beep_entry_t *queue; BEEP_DEBUG1((CE_CONT, "beep_init(0x%lx, 0x%lx, 0x%lx, 0x%lx) : start.", (unsigned long) arg, (unsigned long) beep_on_func, (unsigned long) beep_off_func, (unsigned long) beep_freq_func)); mutex_enter(&beep_state.mutex); if (beep_state.mode != BEEP_UNINIT) { mutex_exit(&beep_state.mutex); BEEP_DEBUG((CE_WARN, "beep_init : beep_state already initialized.")); return (DDI_SUCCESS); } queue = kmem_zalloc(sizeof (beep_entry_t) * beep_queue_size, KM_SLEEP); BEEP_DEBUG1((CE_CONT, "beep_init : beep_queue kmem_zalloc(%d) = 0x%lx.", (int)sizeof (beep_entry_t) * beep_queue_size, (unsigned long)queue)); if (queue == NULL) { BEEP_DEBUG((CE_WARN, "beep_init : kmem_zalloc of beep_queue failed.")); return (DDI_FAILURE); } beep_state.arg = arg; beep_state.mode = BEEP_OFF; beep_state.beep_freq = beep_freq_func; beep_state.beep_on = beep_on_func; beep_state.beep_off = beep_off_func; beep_state.timeout_id = 0; beep_state.queue_head = 0; beep_state.queue_tail = 0; beep_state.queue_size = beep_queue_size; beep_state.queue = queue; mutex_exit(&beep_state.mutex); BEEP_DEBUG1((CE_CONT, "beep_init : done.")); return (DDI_SUCCESS); } int beep_fini(void) { BEEP_DEBUG1((CE_CONT, "beep_fini() : start.")); (void) beeper_off(); mutex_enter(&beep_state.mutex); if (beep_state.mode == BEEP_UNINIT) { mutex_exit(&beep_state.mutex); BEEP_DEBUG((CE_WARN, "beep_fini : beep_state already uninitialized.")); return (0); } if (beep_state.queue != NULL) kmem_free(beep_state.queue, sizeof (beep_entry_t) * beep_state.queue_size); beep_state.arg = (void *)NULL; beep_state.mode = BEEP_UNINIT; beep_state.beep_freq = (beep_freq_func_t)NULL; beep_state.beep_on = (beep_on_func_t)NULL; beep_state.beep_off = (beep_off_func_t)NULL; beep_state.timeout_id = 0; beep_state.queue_head = 0; beep_state.queue_tail = 0; beep_state.queue_size = 0; beep_state.queue = (beep_entry_t *)NULL; mutex_exit(&beep_state.mutex); BEEP_DEBUG1((CE_CONT, "beep_fini() : done.")); return (0); } int beeper_off(void) { BEEP_DEBUG1((CE_CONT, "beeper_off : start.")); mutex_enter(&beep_state.mutex); if (beep_state.mode == BEEP_UNINIT) { mutex_exit(&beep_state.mutex); return (ENXIO); } if (beep_state.mode == BEEP_TIMED) { (void) untimeout(beep_state.timeout_id); beep_state.timeout_id = 0; } if (beep_state.mode != BEEP_OFF) { beep_state.mode = BEEP_OFF; if (beep_state.beep_off != NULL) (*beep_state.beep_off)(beep_state.arg); } beep_state.queue_head = 0; beep_state.queue_tail = 0; mutex_exit(&beep_state.mutex); BEEP_DEBUG1((CE_CONT, "beeper_off : done.")); return (0); } int beeper_freq(enum beep_type type, int freq) { beep_params_t *bp; BEEP_DEBUG1((CE_CONT, "beeper_freq(%d, %d) : start", type, freq)); /* * The frequency value is limited to the range of [0 - 32767] */ if (freq < 0 || freq > INT16_MAX) return (EINVAL); for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) { if (bp->type == type) break; } if (bp->type != type) { BEEP_DEBUG((CE_WARN, "beeper_freq : invalid type.")); return (EINVAL); } bp->frequency = freq; BEEP_DEBUG1((CE_CONT, "beeper_freq : done.")); return (0); } /* * beep : * Start beeping for period specified by the type value, * from the value in the beep_param structure in milliseconds. */ int beep(enum beep_type type) { beep_params_t *bp; BEEP_DEBUG1((CE_CONT, "beep(%d) : start.", type)); for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) { if (bp->type == type) break; } if (bp->type != type) { BEEP_DEBUG((CE_WARN, "beep : invalid type.")); /* If type doesn't match, return silently without beeping */ return (EINVAL); } return (beep_mktone(bp->frequency, bp->duration)); } /*ARGSUSED*/ int beep_polled(enum beep_type type) { /* * No-op at this time. * * Don't think we can make this work in general, as tem_safe * has a requirement of no mutexes, but kbd sends messages * through streams. */ BEEP_DEBUG1((CE_CONT, "beep_polled(%d)", type)); return (0); } /* * beeper_on : * Turn the beeper on */ int beeper_on(enum beep_type type) { beep_params_t *bp; int status = 0; BEEP_DEBUG1((CE_CONT, "beeper_on(%d) : start.", type)); for (bp = beep_params; bp->type != BEEP_DEFAULT; bp++) { if (bp->type == type) break; } if (bp->type != type) { BEEP_DEBUG((CE_WARN, "beeper_on : invalid type.")); /* If type doesn't match, return silently without beeping */ return (EINVAL); } mutex_enter(&beep_state.mutex); if (beep_state.mode == BEEP_UNINIT) { status = ENXIO; /* Start another beep only if the previous one is over */ } else if (beep_state.mode == BEEP_OFF) { if (bp->frequency != 0) { beep_state.mode = BEEP_ON; if (beep_state.beep_freq != NULL) (*beep_state.beep_freq)(beep_state.arg, bp->frequency); if (beep_state.beep_on != NULL) (*beep_state.beep_on)(beep_state.arg); } } else { status = EBUSY; } mutex_exit(&beep_state.mutex); BEEP_DEBUG1((CE_CONT, "beeper_on : done, status %d.", status)); return (status); } int beep_mktone(int frequency, int duration) { int next; int status = 0; BEEP_DEBUG1((CE_CONT, "beep_mktone(%d, %d) : start.", frequency, duration)); /* * The frequency value is limited to the range of [0 - 32767] */ if (frequency < 0 || frequency > INT16_MAX) return (EINVAL); mutex_enter(&beep_state.mutex); if (beep_state.mode == BEEP_UNINIT) { status = ENXIO; } else if (beep_state.mode == BEEP_TIMED) { /* If already processing a beep, queue this one */ if (frequency != 0) { next = beep_state.queue_tail + 1; if (next == beep_state.queue_size) next = 0; if (next != beep_state.queue_head) { /* * If there is room in the queue, * add this entry */ beep_state.queue[beep_state.queue_tail]. frequency = (unsigned short)frequency; beep_state.queue[beep_state.queue_tail]. duration = (unsigned short)duration; beep_state.queue_tail = next; } else { status = EAGAIN; } } } else if (beep_state.mode == BEEP_OFF) { /* Start another beep only if the previous one is over */ if (frequency != 0) { beep_state.mode = BEEP_TIMED; if (beep_state.beep_freq != NULL) (*beep_state.beep_freq)(beep_state.arg, frequency); if (beep_state.beep_on != NULL) (*beep_state.beep_on)(beep_state.arg); /* * Set timeout for ending the beep after the * specified time */ beep_state.timeout_id = timeout(beep_timeout, NULL, drv_usectohz(duration * 1000)); } } else { status = EBUSY; } mutex_exit(&beep_state.mutex); BEEP_DEBUG1((CE_CONT, "beep_mktone : done, status %d.", status)); return (status); } /* * Turn the beeper off which had been turned on from beep() * for a specified period of time */ /*ARGSUSED*/ void beep_timeout(void *arg) { int frequency; int duration; int next; BEEP_DEBUG1((CE_CONT, "beeper_timeout : start.")); mutex_enter(&beep_state.mutex); beep_state.timeout_id = 0; if (beep_state.mode == BEEP_UNINIT) { mutex_exit(&beep_state.mutex); BEEP_DEBUG1((CE_CONT, "beep_timeout : uninitialized.")); return; } if ((beep_state.mode == BEEP_ON) || (beep_state.mode == BEEP_TIMED)) { beep_state.mode = BEEP_OFF; if (beep_state.beep_off != NULL) (*beep_state.beep_off)(beep_state.arg); } if (beep_state.queue_head != beep_state.queue_tail) { next = beep_state.queue_head; frequency = beep_state.queue[next].frequency; duration = beep_state.queue[next].duration; next++; if (next == beep_state.queue_size) next = 0; beep_state.queue_head = next; beep_state.mode = BEEP_TIMED; if (frequency != 0) { if (beep_state.beep_freq != NULL) (*beep_state.beep_freq)(beep_state.arg, frequency); if (beep_state.beep_on != NULL) (*beep_state.beep_on)(beep_state.arg); } /* Set timeout for ending the beep after the specified time */ beep_state.timeout_id = timeout(beep_timeout, NULL, drv_usectohz(duration * 1000)); } mutex_exit(&beep_state.mutex); BEEP_DEBUG1((CE_CONT, "beep_timeout : done.")); } /* * Return true (1) if we are sounding a tone. */ int beep_busy(void) { int status; BEEP_DEBUG1((CE_CONT, "beep_busy : start.")); mutex_enter(&beep_state.mutex); status = beep_state.mode != BEEP_UNINIT && beep_state.mode != BEEP_OFF; mutex_exit(&beep_state.mutex); BEEP_DEBUG1((CE_CONT, "beep_busy : status %d.", status)); return (status); }