/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This is the Beep module for supporting keyboard beep for keyboards
* that do not have the beeping feature within themselves
*
*/
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/ddi_impldefs.h>
#include <sys/kmem.h>
#include <sys/beep.h>
#include <sys/inttypes.h>
/*
* 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);
}