audio_support.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Audio Support Module
*
* This module is use by Audio Drivers that use the new audio driver
* architecture. It provides common services to get and set data
* structures used by Audio Drivers and Audio Personality Modules.
* It provides the home for the audio_state_t structures, one per
* audio device.
*
* Audio Drivers set their qinit structures to the open, close, info, put
* and service routines in this module. Then this module determines
* which Audio Personality Module to call to implement the read, write,
* and ioctl semantics.
*
* and reloads. The space_*() routines are used to save an main anchor
* which points to a linked list of instance data structures. Each instance
* data structure points to APM data structures for its instance. These
* instance and APM persistent data structures are available only via the
* main anchor.
*/
/*
* Solaris external defines.
*/
extern pri_t minclsyspri;
/*
* External functions not declared in header files.
*/
extern void space_free(char *key);
/* streams stuff */
#ifdef DEBUG
/*
* Global audio tracing variables
*/
int audio_tb_pos = 0;
uint_t audio_tb_seq = 0;
#endif
/*
* Global hidden variables.
*/
/* for persistent memory */
static char *audio_key_class = AUDIO_KEY_CLASS;
static audio_inst_info_t *audio_drv_list_head;
/* list of all registered drivers */
/* locks */
/* console message logging */
static kmutex_t audio_sup_log_lock;
static char audio_sup_log_buf[256];
static audio_device_t audio_device_info = {
};
/*
* Local Support Routine Prototypes For Audio Support Module
*/
static void audio_sup_free_drv_entry(dev_info_t *);
static void audio_sup_free_inst_persist(audio_state_t *,
static int audio_sup_persist(audio_state_t *, char *);
/*
* Module Linkage Structures
*/
/* Linkage structure for loadable drivers */
static struct modlmisc audio_modlmisc = {
&mod_miscops, /* drv_modops */
};
static struct modlinkage audio_modlinkage =
{
MODREV_1, /* ml_rev */
(void*)&audio_modlmisc, /* ml_linkage */
NULL /* NULL terminates the list */
};
/*
* Standard loadable Module Configuration Entry Points
*/
/*
* _init()
*
* Description:
* Driver initialization, called when module is first loaded.
* Global module locks are initialized here.
* Arguments:
* None
*
* Returns:
* mod_install() status, see mod_install(9f)
*/
int
_init(void)
{
int error;
#ifdef DEBUG
/* initialize the trace lock */
#endif
/* standard linkage call */
#ifdef DEBUG
#endif
return (error);
}
/* init mutexes after we can't have any failures */
/* initialize the instance list lock */
/* log buffer mutex */
/* persistent data mutex */
ATRACE("audiosup _init() successful", 0);
return (error);
} /* _init() */
/*
* _fini()
*
* Description
* Module de-initialization, called when driver is to be unloaded.
* Free resources that were allocated in _init().
*
* Arguments:
* None
*
* Returns:
* mod_remove() status, see mod_remove(9f)
*/
int
_fini(void)
{
int error;
ATRACE("in audiosup _fini()", 0);
/* all drivers must have unregistered */
return (error);
}
/* free instance list lock */
/* free log lock */
/* free persistent memory lock */
#ifdef DEBUG
#endif
return (0);
} /* _fini() */
/*
* _info()
*
* Description:
* Module information, returns information about the driver.
*
* Arguments:
* modinfo *modinfop Pointer to an opaque modinfo structure
*
* Returns:
* mod_info() status, see mod_info(9f)
*/
int
{
int rc;
return (rc);
} /* _info() */
/*
* Public Audio Device Independent Driver Entry Points
*
* Standard Driver Entry Points
*/
/*
* audio_sup_attach() and audio_sup_detach() are being replaced in the
* next minor release of Solaris. Audio drivers must be modified to use
* the new interfaces.
*/
/*ARGSUSED*/
{
} /* audio_sup_attach() */
/*ARGSUSED*/
int
{
return (audio_sup_unregister(handle));
}
/*
* audio_sup_register()
*
* Description:
* This routine initializes structures used for an instance of an
* audio driver. Persistent state storage is allocated and used to
* store state when drivers are unloaded. This routine always allocates
* a very small chunk of memory for each instance which we never free.
*
* Arguments:
* dev_info_t *dip Ptr to the device's dev_info structure
* audio_sup_reg_data_t *data Registration data
*
* Returns:
* audiohdl_t Handle to the audio device is successful
* NULL Attach failed
*/
{
int i;
int sup_chs;
/* make sure we have a supported version */
data->asrd_version);
return (NULL);
}
/* minors per inst - reserved audio channels */
ATRACE_32("audio_sup_register() supported channels per device",
sup_chs);
/* register and get device instance number */
ATRACE("audio_sup_register() "
"audio_sup_create_drv_entry() failed", 0);
return (NULL);
}
/*
* WARNING: From here on all error returns must worry about the
* driver list.
*
* Initialize the instance mutex and condition variables. Used to
* allocate channels.
*/
/* initialize other state information */
/* sanity check that we can use the device */
(sup_chs <= AUDIO_CLONE_CHANLIM));
/* setup persistent memory */
ATRACE("audio_sup_register() couldn't set up persist mem", 0);
goto error;
}
/*
* WARNING: From here on we cannot fail. Otherwise we would have
* to loop through the channel structures and clear those CVs
* and locks.
*
* Initialize the channel structures.
*/
/* most everything is zero, do it quickly */
/* now do the non-zero members */
}
done:
return (AUDIO_STATE2HDL(statep));
return (NULL);
} /* audio_sup_register() */
/*
* audio_sup_unregister()
*
* Description:
* This routine de-initializes structures used for an instance of the
* of an audio driver. It doesn't really do too much.
*
* Arguments:
* audiohdl_t handle Handle to the device
*
* Returns:
* AUDIO_SUCCESS Detach successful
* AUDIO_FAILURE Detach failed
*/
int
{
/*
* WARNING: From here on all error returns must worry about the
* instance state structures.
*/
/* get the state pointer for this instance */
ATRACE("audio_sup_unregister() "
"audio_sup_devinfo_to_state() failed", 0);
return (AUDIO_FAILURE);
}
/*
* Free the instance persistent data struct if it doesn't point to
* any saved data.
*/
}
/* remove the dip from the instance list */
ATRACE("audio_sup_unregister() returning", 0);
return (AUDIO_SUCCESS);
} /* audio_sup_unregister() */
/*
* audio_sup_open()
*
* Description:
* This routine is called when the kernel wants to open an Audio Driver
* channel. It figures out what kind of device it is and calls the
* appropriate Audio Personality Module.
*
* Arguments:
* queue_t *q Pointer to the read queue
* dev_t *devp Pointer to the device
* int flag Open flags
* int sflag STREAMS flags
* cred_t *credp Pointer to the user's credential struct.
*
* Returns:
* 0 Successfully opened the device
* errno Error number for failed open
*/
int
{
int rc;
ATRACE("in audio_sup_open()", q);
/* get the state structure */
"audio_sup_open() audio_sup_devt_to_state() failed", 0);
return (ENXIO);
}
/* get the device type */
/* get the APM info structure */
ATRACE_32("audio_sup_open() audio_sup_get_apm_info() failed",
type);
return (ENXIO);
}
if (rc == AUDIO_SUCCESS) {
/* open was successful, make sure we've got routines */
/* close the device */
}
}
return (rc);
} /* audio_sup_open() */
/*
* audio_sup_close()
*
* Description:
* This routine is called when the kernel wants to close an Audio Driver
* channel. It figures out what kind of device it is and calls the
* appropriate Audio Personality Module.
*
* Arguments:
* queue_t *q Pointer to the read queue
* int flag Open flags
* cred_t *credp Pointer to the user's credential struct.
*
* Returns:
* 0 Successfully closed the device
* errno Error number for failed close
*/
int
{
ATRACE("in audio_sup_close()", q);
ATRACE("audio_sup_close() bad chptr", 0);
return (EIO);
}
} /* audio_sup_open() */
/*
* audio_sup_restore_state()
*
* Description:
* Restore the state of the hardware that the specified APM controls.
* The specified APM is called to do the restore. It is up to that
* APM's restore state function to restart the hardware or not. The
* APM also deals with the direction. If anything keeps the state from
* being restored then AUDIO_FAILURE is returned. If AUDIO_ALL_DEVICES
* is sent then if any one APM fails the whole call fails. It is also
* a failure to restore an APM that doesn't have an apm_restore_state()
* function, which is optional.
*
* Arguments:
* audiohdl_t handle Device handle
* audio_device_type_e device The device to restore
* int dir The direction to restore
*
* Returns:
* AUDIO_SUCCESS State restored
* AUDIO_FAILURE State not restored
*/
int
{
int found;
return (AUDIO_FAILURE);
}
if ((dir & AUDIO_BOTH) == 0) {
return (AUDIO_FAILURE);
}
/* nothing to restore, so this is an error */
return (AUDIO_FAILURE);
}
/* go through the list looking for APMs that match this device */
/* does this APM match? */
if (device != AUDIO_ALL_DEVICES &&
/* nope, it doesn't, so get the next one */
ATRACE_32("audio_sup_restore_state() not this one",
continue;
}
/* does this APM have a restore function? */
/* if AUDIO_ALL_DEVICES then it's okay to be NULL */
if (device == AUDIO_ALL_DEVICES) {
ATRACE("audio_sup_restore_state() NULL", 0);
continue;
} else {
ATRACE("audio_sup_restore_state() fail NULL",
0);
return (AUDIO_FAILURE);
}
}
/* this is ours, so call it */
ATRACE("audio_sup_restore_state() calling restore",
ATRACE("audio_sup_restore_state() restore failed",
return (AUDIO_FAILURE);
}
ATRACE_32("audio_sup_restore_state() restore succeeded",
/* are we done? */
if (device != AUDIO_ALL_DEVICES) {
/* yes, just a single device to restore */
"audio_sup_restore_state() single dev success",
device);
return (AUDIO_SUCCESS);
}
}
/* if we specify the device then it's possible it wasn't there, so ck */
ATRACE_32("audio_sup_restore_state() single device not found",
device);
return (AUDIO_FAILURE);
}
/*
* Either we found the single device or we looked at all the devices
* and they succeeded. Even if there wasn't a single restore function
* to call when AUDIO_ALL_DEVICES was specified we still succeeded.
*/
return (AUDIO_SUCCESS);
} /* audio_sup_restore_state() */
/*
* audio_sup_save_state()
*
* Description:
* Save the state of the hardware that the specified APM controls.
* The specified APM is called to do the save. It is up to that
* APM's save state function to define any interaction with the
* hardware. If anything keeps the state from being saved then
* AUDIO_FAILURE is returned. If AUDIO_ALL_DEVICES is sent then if
* any one APM fails the whole call fails. It is also a failure to
* save an APM that doesn't have an apm_save_state() function, which
* is optional.
*
* Arguments:
* audiohdl_t handle Device handle
* audio_device_type_e device The device to save
* int dir The direction to save
*
* Returns:
* AUDIO_SUCCESS State save
* AUDIO_FAILURE State not save
*/
int
{
int found;
return (AUDIO_FAILURE);
}
if ((dir & AUDIO_BOTH) == 0) {
return (AUDIO_FAILURE);
}
/* nothing to save, so this is an error */
return (AUDIO_FAILURE);
}
/* go through the list looking for APMs that match this device */
/* does this APM match? */
if (device != AUDIO_ALL_DEVICES &&
/* nope, it doesn't, so get the next one */
ATRACE_32("audio_sup_save_state() not this one",
continue;
}
/* does this APM have a save function? */
/* if AUDIO_ALL_DEVICES then it's okay to be NULL */
if (device == AUDIO_ALL_DEVICES) {
ATRACE("audio_sup_save_state() NULL", 0);
continue;
} else {
ATRACE("audio_sup_save_state() fail NULL",
0);
return (AUDIO_FAILURE);
}
}
/* this is ours, so call it */
ATRACE("audio_sup_save_state() calling save",
ATRACE("audio_sup_save_state() save failed",
return (AUDIO_FAILURE);
}
ATRACE_32("audio_sup_save_state() save succeeded",
/* are we done? */
if (device != AUDIO_ALL_DEVICES) {
/* yes, just a single device to save */
"audio_sup_save_state() single dev success",
device);
return (AUDIO_SUCCESS);
}
}
/* if we specify the device then it's possible it wasn't there, so ck */
ATRACE_32("audio_sup_save_state() single device not found",
device);
return (AUDIO_FAILURE);
}
/*
* Either we found the single device or we looked at all the devices
* and they succeeded. Even if there wasn't a single save function
* to call when AUDIO_ALL_DEVICES was specified we still succeeded.
*/
return (AUDIO_SUCCESS);
} /* audio_sup_save_state() */
/*
* audio_sup_getinfo()
*
* XXX this function is incorrect and should be obsoleted
*
* Description:
* Get driver information.
*
* Arguments:
* def_info_t *dip Pointer to the device's dev_info structure
* WARNING: Don't use this dev_info structure
* ddi_info_cmd_t infocmd Getinfo command
* void *arg Command specific argument
* void **result Pointer to the requested information
*
* Returns:
* DDI_SUCCESS The information could be returned
* DDI_FAILURE The information couldn't be returned
*/
/*ARGSUSED*/
int
void **result)
{
int error;
int instance = 0;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_FAILURE;
} else {
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
} else {
error = DDI_FAILURE;
}
break;
default:
error = DDI_FAILURE;
break;
}
return (error);
} /* audio_sup_getinfo() */
/*
* audio_sup_rput()
*
* Description:
* Make sure we have a valid function pointer. If we do we make the call.
*
*
* Arguments:
* queue_t *q Pointer to a queue
* mblk_t *mp Ptr to the msg block being passed to the queue
*
* Returns:
* 0 Always returns 0
*/
int
{
int rc;
ATRACE("in audio_sup_rput()", q);
ATRACE("audio_sup_rput() bad chptr", 0);
return (0);
}
return (rc);
} /* audio_sup_rput() */
/*
* audio_sup_rsvc()
*
* Description:
* Make sure we have a valid function pointer. If we do we make the call.
*
* Arguments:
* queue_t *q Pointer to a queue
*
* Returns:
* 0 Always returns 0
*/
int
{
int rc;
ATRACE("in audio_sup_rsvc()", q);
ATRACE("audio_sup_rsvc() null chptr", 0);
return (0);
}
return (rc);
} /* audio_sup_rsvc() */
/*
* audio_sup_wput()
*
* Description:
* Make sure we have a valid function pointer. If we do we make the call.
*
*
* Arguments:
* queue_t *q Pointer to a queue
* mblk_t *mp Ptr to the msg block being passed to the queue
*
* Returns:
* 0 Always returns 0
*/
int
{
int rc = 0;
ATRACE("in audio_sup_wput()", q);
ATRACE("audio_sup_close() bad chptr", 0);
return (0);
}
/* pick off the audio support ioctls, there aren't very many */
case M_IOCTL:
case AUDIO_GET_CH_NUMBER:
case AUDIO_GET_CH_TYPE:
case AUDIO_GET_NUM_CHS:
case AUDIO_GET_AD_DEV:
case AUDIO_GET_APM_DEV:
case AUDIO_GET_AS_DEV:
ATRACE_32("audio_sup_wput() "
break;
default:
ATRACE("audio_sup_wput() IOCTL calling ch_wput()",
chptr);
}
break;
case M_IOCDATA:
ATRACE("audio_sup_wput() M_IOCDATA NULL cmd calling "
"ch_wput()", chptr);
break;
}
switch (cmd->ais_command) {
case AUDIO_COPY_OUT_CH_NUMBER:
case AUDIO_COPY_OUT_CH_TYPE:
case AUDIO_COPY_OUT_NUM_CHS:
case AUDIO_COPY_OUT_AD_DEV:
case AUDIO_COPY_OUT_APM_DEV:
case AUDIO_COPY_OUT_AS_DEV:
ATRACE_32("audio_sup_wput() "
"IOCDATA calling audio_sup_wiocdata()",
cmd->ais_command);
break;
default:
ATRACE("audio_sup_wput() IOCDATA calling ch_wput()",
chptr);
}
break;
default:
}
return (rc);
} /* audio_sup_wput() */
/*
* audio_sup_wsvc()
*
* Description:
* Make sure we have a valid function pointer. If we do we make the call.
*
* Arguments:
* queue_t *q Pointer to a queue
*
* Returns:
* 0 Always returns 0
*/
int
{
int rc;
ATRACE("in audio_sup_wsvc()", q);
ATRACE("audio_sup_wsvc() bad chptr", 0);
return (EIO);
}
return (rc);
} /* audio_sup_wsvc() */
/*
* Public Audio Personality Module Support Routines
*
* Channel Routines
*/
/*
* audio_sup_alloc_ch()
*
* Description:
* Go through the list of channels. Find the first one that isn't
* assigned and take it. If there aren't any channels then depending
* on the flags either wait for a channel to become free or return
* NULL
*
* CAUTION: Make sure *error is always set before returning.
*
* NOTE: This routine expects the Audio Personality Module to fill in
* all the members of the audio_ch_t structure.
*
* Arguments:
* audio_state_t *statep The device state structure
* int *error Error code
* audio_device_type_e type The device type
* int flags AUDIO_NO_SLEEP or AUDIO_SLEEP
*
* Returns:
* valid pointer to ch Channel allocated
* NULL Channel not allocated
*/
int flags)
{
int i;
int rc;
/* make sure there's an apm_infop for the type */
ATRACE("audio_sup_alloc_ch() audio_sup_get_apm_info() failed",
statep);
return (NULL);
}
/* find the first unused channel */
/* cv_broadcast() means we may need to try many times */
/* no channels available right now, do we wait? */
if (flags & AUDIO_NO_SLEEP) {
/* don't wait for a channel to become free */
ATRACE("audio_sup_alloc_ch() no ch return", 0);
return (NULL);
}
/* wait for a channel to become free */
if (rc <= 0) {
ATRACE("audio_sup_alloc_ch() max chs signal wakeup",
statep);
/*
* This channel may have had a signal, but that doesn't
* mean any of the other channels may proceed. So make
* sure every channel gets another go.
*/
ATRACE("audio_sup_alloc_ch() no ch sig return", 0);
return (NULL);
}
}
/* we've got a channel, so find it */
/* found it! */
break;
}
}
/* just to be sure */
chptr->ch_adata_cnt = 0;
*error = 0; /* no errors */
return (chptr);
} /* audio_sup_alloc_ch() */
/*
* audio_sup_free_ch()
*
* Description:
* This routine returns a channel structure to the device instance's
* pool of channel structures. All the various pointers must be freed
* and NULLed before this routine is called. It then does a cv_broadcast()
* to wake up any cv_wait()/cv_wait_sig() calls which might be waiting
* for a channel to be freed.
*
* Arguments:
* audio_ch_t *chptr The channel structure to free
*
* Returns:
* AUDIO_SUCCESS No error
* AUDIO_FAILURE One of the pointers is not set to NULL or
* chptr is not valid
*/
int
{
return (AUDIO_FAILURE);
}
/* finally, clear the channel structure and make available for reuse */
#ifdef DEBUG
#endif
if (chptr->ch_private) {
ATRACE("audio_sup_free_ch() chptr->ch_private != NULL",
chptr->ch_private);
return (AUDIO_FAILURE);
}
ATRACE("audio_sup_free_ch() chptr->ch_info.info != NULL",
return (AUDIO_FAILURE);
}
/* free the message list */
statep->as_ch_inuse--;
chptr->ch_adata_cnt = 0;
/* the channel is freed, so send the broadcast */
return (AUDIO_SUCCESS);
} /* audio_sup_free_ch() */
/*
* Persistent Memory Routines
*/
/*
* audio_sup_get_persist_state()
*
* Description:
* Search through the linked list of audio_apm_persist data structures
* for this driver instance's persistent data for the specifed audio
* personality module.
*
* Arguments:
* audio_state_t *statep Device state structure
* audio_device_type_e dev_type APM to set data for
*
* Returns:
* valid pointer Pointer to the saved data
* NULL Couldn't find the data
*/
void *
{
void *ret_ptr;
ATRACE("audio_sup_get_persist_state() found state",
ret_ptr);
return (ret_ptr);
}
}
return (NULL);
} /* audio_sup_get_persist_state() */
/*
* audio_sup_free_persist_state()
*
* Description:
* Search through the linked list of audio_apm_persist data structures
* for this driver instance's persistent data for the specifed audio
* personality module. When found remove it from the linked list and
* free it.
*
* If the dev_type is set to AUDIO_ALL_DEVICES then we free all of
* the entries.
*
* Arguments:
* audio_state_t *statep Device state structure
* audio_device_type_e dev_type APM to free data for
*
* Returns:
* AUDIO_SUCCESS Memory freed
* AUDIO_FAILURE Memory not found to free
*/
int
{
/* no items on the list */
return (AUDIO_FAILURE);
}
/* see if we clear all, or just one that matches dev_type */
if (dev_type == AUDIO_ALL_DEVICES) {
/* clear all of them */
while (list) {
}
return (AUDIO_SUCCESS);
} else {
/* clear just the match */
while (list) {
/* remove from the linked list */
ATRACE("audio_sup_free_persist_state() "
"freeing state", list);
return (AUDIO_SUCCESS);
}
}
}
ATRACE("audio_sup_free_persist_state() not found, failed", 0);
return (AUDIO_FAILURE);
} /* audio_sup_free_persist_state() */
/*
* audio_sup_set_persist_state()
*
* Description:
* Search through the list to see if we already have data for this
* APM type. If so then free it, then update with the new data. If
* the APM type isn't found then allocate an audio_apm_persist_t
* structure and add to the list.
*
* Arguments:
* audio_state_t *statep Device state structure
* audio_device_type_e dev_type APM to set data for
* void *state_data The data to save
* size_t state_size Size of the persistent data
*
* Returns:
* AUDIO_SUCCESS Successfully set persistent state info
* AUDIO_FAILURE Couldn't set persistent state info
*/
int
{
/* get the anchor */
/* look for a matching device type */
break;
}
}
/*
* If not found or first on list then allocate a new structure and
* place on the list.
*/
/* allocate an audio_apm_persist struct */
/* and place at the beginning of the list */
} else {
/* we found on the list, so free old data */
}
/* update the rest of the new data structure */
return (AUDIO_SUCCESS);
} /* audio_sup_set_persist_state() */
/*
* Device Independent Driver Registration Routines
*/
/*
* audio_sup_register_apm()
*
* Description:
* Register the Audio Personality Module with this instance of the
* Audio Driver. This provides a place to store state information
* for the APM.
*
* We only allow one instance of an APM present for each instance of
* an Audio Driver.
*
* NOTE: Instance and type are mandatory.
*
* NOTE: It is okay for memory allocation to sleep.
*
* Arguments:
* audio_state_t *statep Device state structure
* audio_device_type_e type APM type
* audio_apm_reg_t *reg_info Ptr to APM registration information
*
* Returns:
* valid pointer The audio_apm_info structure registered
* NULL Couldn't register the APM
*/
{
/* make sure the registration information data structure is okay */
ATRACE("audio_sup_register_apm() bad version",
return (NULL);
}
/* we must have an open() and close() routine */
ATRACE("audio_sup_register_apm() aar_apm_open()",
ATRACE("audio_sup_register_apm() apm_close()",
return (NULL);
}
/* allocate the structure now so we won't sleep with as_lock held */
/* first make sure we haven't already registered this type before */
ATRACE("audio_sup_register_apm() "
"duplicate diaudio type", 0);
return (NULL);
}
}
ATRACE("audio_sup_register_apm() not a duplicate, ok to continue", 0);
/* put at the head of the list */
return (apm_infop);
} /* audio_sup_register_apm() */
/*
*
* Description:
* Unregister the Audio Personality Module from this instance of the
* Audio Driver. If the APM wasn't registered we silently and
* successfully fail and return. Otherwise we free the structure which
* represents the APM. However, if the private data wasn't freed we
* fail.
*
* Arguments:
* audio_state_t *statep device state structure
* audio_device_type_e type APM type
*
* Returns:
* AUDIO_SUCCESS APM unregistered
* AUDIO_FAILURE APM private data not freed
*/
int
{
/* protect the personality module list */
apm_infop = *papm_infop;
while (apm_infop) {
break;
}
apm_infop = *papm_infop;
}
/* type not found on the list or the list is empty */
return (AUDIO_SUCCESS);
}
/* make sure the private data has been freed */
if (apm_infop->apm_private) {
ATRACE("audio_sup_unregister_apm() private data not cleared",
return (AUDIO_FAILURE);
}
/* remove the item by bypassing it */
/* APM off the list, so okay to release the lock */
ATRACE("audio_sup_unregister_apm() done", 0);
return (AUDIO_SUCCESS);
} /* audio_sup_unregister_apm() */
/*
* Audio task queue routines
*/
/*
* audio_sup_taskq_create()
*
* Description:
* Wrapper to abstract the creation of a kernel taskq. We pick some
* defaults that make sense for audio, such as single threaded.
*
* Arguments:
* const char *q_name Name of the task queue to create
*
* Returns:
* handle Queue successfully created
* NULL Couldn't allocate structs, failed
*/
audio_sup_taskq_create(const char *q_name)
{
return (AUDIO_TQHDL2AUDIOTQHDL(tq));
} /* audio_sup_taskq_create() */
/*
* audio_sup_taskq_destroy()
*
* Description:
* Destroy the taskq.
*
* CAUTION: If the taskq is used after destroying then the system
* will probably panic
*
* Arguments:
* audio_taskq_t tq_handle Handle to the taskq to destroy
*
* Returns:
* void
*/
void
{
} /* audio_sup_taskq_destroy() */
/*
* audio_sup_taskq_dispatch()
*
* Description:
* Dispatch a task. The task_function pointer must not be NULL.
*
* Arguments:
* audio_taskq_t tq_handle Handle to the taskq to destroy
* void (*)() task_function Ptr to the function to execute
* void *arg Ptr to argument for task_function
* int sleep KM_SLEEP or KM_NOSLEEP
*
* Returns:
* AUDIO_SUCCESS Task scheduled
* AUDIO_FAILURE Task not scheduled or bad task_function
* or bad sleep flags
*/
int
{
if (task_function == NULL) {
return (AUDIO_FAILURE);
}
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} else {
return (AUDIO_FAILURE);
}
} /* audio_sup_taskq_dispatch() */
/*
* audio_sup_taskq_resume()
*
* Description:
* Resume task execution.
*
* Arguments:
* audio_taskq_t tq_handle Handle to the taskq to wait on
*
* Returns:
* void
*/
void
{
} /* audio_sup_taskq_resume() */
/*
* audio_sup_taskq_suspended()
*
* Description:
* Determine if the taskq is running or suspended.
*
* Arguments:
* audio_taskq_t tq_handle Handle to the taskq to wait on
*
* Returns:
* AUDIO_TASKQ_RUNNING The taskq is running
* AUDIO_TASKQ_SUSPENDED The taskq is not running (suspended)
*/
int
{
ATRACE("audio_sup_taskq_suspend() returning suspended",
return (AUDIO_TASKQ_SUSPENDED);
} else {
ATRACE("audio_sup_taskq_suspend() returning running",
return (AUDIO_TASKQ_RUNNING);
}
} /* audio_sup_taskq_suspended() */
/*
* audio_sup_taskq_suspend()
*
* Description:
* Tasks on the taskq are suspended when this routine returns.
* Running tasks will continue to execute, but all new tasks will
* be suspended.
*
* Arguments:
* audio_taskq_t tq_handle Handle to the taskq to wait on
*
* Returns:
* void
*/
void
{
} /* audio_sup_taskq_suspend() */
/*
* audio_sup_taskq_wait()
*
* Description:
* Wait for all pending tasks to complete
*
* Arguments:
* audio_taskq_t tq_handle Handle to the taskq to wait on
*
* Returns:
* void
*/
void
{
} /* audio_sup_taskq_wait() */
/*
* Audio Data Handling Routines
*/
/*
* audio_sup_flush_audio_data()
*
* Description:
* Flush all the data queued up for a channel. We remain locked at
* all times so no one else can sneak in and grab a data structure.
*
* Arguments:
* audio_ch_t *chptr Pointer to the channel structure
*
* Returns:
* void
*/
void
{
/* set up for next loop */
chptr->ch_adata_cnt--;
}
} /* audio_sup_flush_audio_data() */
/*
* audio_sup_free_audio_data()
*
* Description:
* Free the audio data.
*
* NOTE: The audio data structure must already be off the list, so there
* isn't a need to lock the data list.
*
* Arguments:
* audio_data_t *adata The audio data structure to free
*
* Returns:
* void
*/
void
{
return;
}
ATRACE("audio_sup_free_audio_data() adata differ, orig",
adata->adata_orig);
ATRACE("audio_sup_free_audio_data() adata differ, proc",
adata->adata_proc);
if (adata->adata_orig) {
ATRACE("audio_sup_free_audio_data() freeing original data",
adata->adata_orig);
}
if (adata->adata_proc) {
ATRACE("audio_sup_free_audio_data() freeing processed data",
adata->adata_proc);
}
ATRACE("audio_sup_free_audio_data() done", 0);
} /* audio_sup_free_audio_data() */
/*
* audio_sup_get_audio_data()
*
* Description:
* Get the oldest audio data structure off the channel's data list, which
* would be the first message.
*
* Arguments:
* audio_ch_t *chptr Pointer to the channel structure
*
* Returns:
* Valid audio_data_t pointer The audio data structure
* NULL No audio data available
*/
{
if (tmp) {
/* set up for next time */
chptr->ch_adata_cnt--;
return (tmp);
}
return (NULL);
} /* audio_sup_get_audio_data() */
/*
* audio_sup_get_audio_data_cnt()
*
* Description:
* Get the number of data structures currently queued on the data list.
*
* Arguments:
* audio_ch_t *chptr Pointer to the channel structure
*
* Returns:
* >= 0 The number of queued data structures
*/
int
{
int tmp;
return (tmp);
} /* audio_sup_get_audio_data_cnt() */
/*
* audio_sup_get_audio_data_size()
*
* Description:
* Get the number of bytes stored in data structures that are currently
* queued on the data list. Look at the proc_size if it's there and
* otherwise look at the orig_size.
*
* Arguments:
* audio_ch_t *chptr Pointer to the channel structure
*
* Returns:
* >= 0 The number of bytes queued
*/
int
{
int tmp = 0;
/* lock the structure */
while (adata != 0) {
if (adata->adata_proc) {
} else {
}
}
return (tmp);
} /* audio_sup_get_audio_data_size() */
/*
* audio_sup_putback_audio_data()
*
* Description:
* Put the audio data structure back onto the list. It will be the first
* to be removed the next time audio_sup_get_audio_data() is called.
*
* Arguments:
* audio_ch_t *chptr Pointer to the channel structure
* audio_data_t *adata The message to put back on the list
*
* Returns:
* void
*/
void
{
if (adata == 0) {
ATRACE("audio_sup_putback_audio_data() bad message pointer",
adata);
return;
}
/* lock the data list */
chptr->ch_adata_cnt++;
} /* audio_sup_putback_audio_data() */
/*
* audio_sup_save_audio_data()
*
* Description:
* Save audio data on the channel's data list. New data is placed at
* the end of the list.
*
* CAUTION: This routine may be called from interrupt context, so memory
* allocation cannot sleep.
*
* Arguments:
* audio_ch_t *chptr Pointer to the channel structure
* void *adata_orig Pointer to the original data to save
* size_t adata_osize Size of the original data
* void *adata_proc Pointer to the processed data to save
* size_t adata_psize Size of the processed data
*
* Returns:
* AUDIO_SUCCESS The message was successfully saved
* AUDIO_FAILURE The message was not successfully saved
*/
int
{
/* first we allocate an audio_data_t structure (zeros out next field) */
ATRACE("audio_sup_save_audio_data() kmem_zalloc() failed", 0);
return (AUDIO_FAILURE);
}
if (adata_orig) {
}
if (adata_proc) {
}
/* now we save the message */
/* see if this is the first message */
return (AUDIO_SUCCESS);
}
/* append new message to list */
chptr->ch_adata_cnt++;
return (AUDIO_SUCCESS);
} /* audio_sup_save_audio_data() */
/*
* Minor <--> Channel Routines:
*
* audio_sup_ch_to_minor()
*
* Description:
* Return the minor number of the channel.
*
* Arguments:
* audio_state_t *statep The device state structure
* int channel The device channel
*
* Returns:
* >= 0 The minor number of the channel
*/
int
{
ATRACE_32("audio_sup_ch_to_minor() returning",
} /* audio_sup_ch_to_minor() */
/*
* audio_sup_get_max_chs()
*
* Description:
* Get the maximum number of supported channels per instance.
*
* Arguments:
* audiohdl_t handle Handle to the device
*
* Returns:
* > 0 The number of minor numbers per instance
*/
int
{
ATRACE_32("in audio_sup_get_max_chs() returning",
statep->as_max_chs);
return (statep->as_max_chs);
} /* audio_sup_get_max_chs() */
/*
* audio_sup_get_minors_per_inst()
*
* Description:
* Get the number of minor numbers allowed per instance.
*
* Arguments:
* audiohdl_t handle Handle to the device
*
* Returns:
* > 0 The number of minor numbers per instance
*/
int
{
ATRACE_32("in audio_sup_get_minors_per_inst() returning",
return (statep->as_minors_per_inst);
} /* audio_sup_get_minors_per_inst() */
/*
* audio_sup_construct_minor()
*
* Description:
* construct a minor number for this dip and device type
*
* Arguments:
* audiohdl_t handle Handle to the device
* device_type_e device_type type of audio device the channel is
* associated with
*
* Returns:
* >= 0 minor node number
*/
int
{
int minor;
return (minor);
} /* audio_sup_construct_minor() */
/*
* audio_sup_minor_to_ch()
*
* Description:
* Convert a minor number to a channel number.
*
* Arguments:
* audio_state_t *statep The device state structure
* minor_t minor The minor number to convert
*
* Returns:
* >= 0 The channel number
*/
int
{
ATRACE_32("audio_sup_minor_to_ch() returning",
} /* audio_sup_minor_to_ch() */
/*
* audio_sup_type_to_minor()
*
* Description:
* Normally a macro would be used to figure out the minor number. But
* we don't want the Audio Driver using the Audio Support Module's
* macros which might change. So we provide a call that will let us
* change what we are doing later on if we wish.
*
* Arguments:
* audio_device_type_e type The device type we want the minor # of
*
* Returns:
* The device type
* AUDIO_FAILURE Unrecognized audio device
*/
int
{
int minor;
switch (type) {
case AUDIO:
break;
case AUDIOCTL:
break;
case WTABLE:
break;
case MIDI:
break;
case ATIME:
break;
case USER1:
break;
case USER2:
break;
case USER3:
break;
case UNDEFINED:
/*FALLTHROUGH*/
default:
break;
}
return (minor);
} /* audio_sup_type_to_minor() */
/*
* audio_sup_devt_to_instance()
*
* Description:
* Convert a dev_t to instance
*
* Arguments:
* dev_t dev The device we are getting the instance for
*
* Returns:
* >= 0 The instance number
*/
int
{
return (AUDIO_MINOR_TO_INST(devt));
} /* audio_sup_devt_to_instance() */
/*
* Miscellaneous Routines
*/
/*
* audio_sup_devt_to_ch_type()
*
* Description:
* Given a channel's minor number figure out what kind of channel
* it is. This works for both the reserved minor nodes as well as
* the clone channels.
*
* Arguments:
* audio_state_t *statep The device state structure
* dev_t dev The device we are getting the type of
*
* Returns:
* The device type
* AUDIO_FAILURE Couldn't get the state structure, so failed
*/
{
/* figure out the minor number given an instance */
if (minor < audio_reserved) {
ATRACE_32("audio_sup_devt_to_ch_type() reserved minor",
minor);
switch (minor) {
case AUDIO_MINOR_AUDIO:
break;
case AUDIO_MINOR_AUDIOCTL:
break;
case AUDIO_MINOR_WAVE_TABLE:
break;
case AUDIO_MINOR_MIDI_PORT:
break;
case AUDIO_MINOR_TIME:
break;
case AUDIO_MINOR_USER1:
break;
case AUDIO_MINOR_USER2:
break;
case AUDIO_MINOR_USER3:
break;
default:
break;
}
ATRACE_32("audio_sup_devt_to_ch_type() reserved, returning",
type);
return (type);
} else {
ATRACE_32("audio_sup_devt_to_ch_type() allocated channel",
minor);
ATRACE("audio_sup_devt_to_ch_type() "
"audio_sup_devt_to_ch_type() failed", 0);
return ((audio_device_type_e)AUDIO_FAILURE);
}
ATRACE_32("audio_sup_devt_to_ch_type() returning type",
}
} /* audio_sup_devt_to_ch_type() */
/*
* audio_sup_get_channel_number()
*
* Description:
* Get the channel number for the audio queue.
*
* Arguments:
* queue_t *q Pointer to a queue structure
*
* Returns:
* channel number The channel number for the audio queue.
* AUDIO_FAILURE Bad q_ptr
*/
int
{
ATRACE("in audio_sup_get_channel_number()", q);
ATRACE("audio_sup_get_channel_number() bad chptr", 0);
return (AUDIO_FAILURE);
}
ATRACE("audio_sup_get_channel_number() returning",
} /* audio_sup_get_channel_number() */
/*
* audio_sup_get_apm_info()
*
* Description:
* Get the audio_apm_info structure for the audio instance and
* type passed in.
*
* NOTE: Since the apm_info list is created when the driver is
* attached it should never change during normal operation
* of the audio device. Therefore we don't need to lock
* the list while we traverse it.
*
* Arguments:
* audio_state_t *statep device state structure
* audio_device_type_e type APM type
*
* Returns:
* valid pointer Ptr to the returned audio_apm_info struct
* NULL audio_apm_info struct not found
*/
{
/* sanity check */
ATRACE("audio_sup_get_apm_info() returning NULL (fail)", 0);
return (NULL);
}
break;
}
}
/* make sure we got a structure */
return (NULL);
}
return (apm_infop);
} /* audio_sup_get_apm_info() */
/*
* audio_sup_get_dip()
*
* Description:
* Get the dev_info_t pointer for the audio handle.
*
* Arguments:
* audiohdl_t handle Handle to the device
*
* Returns:
* dev_info_t * The dip for the handle, always returned
*/
{
} /* audio_sup_get_dip() */
/*
* audio_sup_get_info()
*
* Description:
* Get the info structure for the audio queue.
*
* Arguments:
* queue_t *q Pointer to a queue structure
*
* Returns:
* valid pointer Ptr to the returned audio_apm_info struct
*/
void *
{
ATRACE("in audio_sup_get_info()", q);
ATRACE("audio_sup_close() bad chptr", 0);
return (NULL);
}
} /* audio_sup_get_info() */
/*
* audio_sup_mblk_alloc()
*
* Description:
* Allocate a STREAMS message block if the current block isn't there or
* is too small. This is placed into the continuation pointer for the
* passed in message block.
*
* When we return the b_wptr is set to b_rptr + size;
*
* Arguments:
* mblk_t *mp STREAMS message block to add to
* size_t size Size of the message to allocate
*
* Returns:
* AUDIO_SUCCESS Message block allocated
* AUDIO_FAILURE Message block not allocated
*/
int
{
/* first the easy case, the buffer is already big enough */
return (AUDIO_SUCCESS);
}
/* the old message either isn't there or it's too small */
/* here, but too small */
}
/* no memory leak, time to allocate the new message */
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio_sup_mblk_alloc() */
/*
* audio_sup_get_private()
*
* Description:
* Return the pointer to the audio driver's private state information.
*
* Arguments:
* audiohdl_t handle Handle to the device
*
* Returns:
* Pointer to private data, even if it's set to NULL
*/
void *
{
} /* audio_sup_get_private() */
/*
* audio_sup_set_private()
*
* Description:
* Set the audio driver's private state in the audio handle. It may
* be reset at any time.
*
* Arguments:
* audiohdl_t handle Handle to the device
* void *private Audio driver's private data
*
* Returns:
* void
*/
void
{
} /* audio_sup_set_private() */
/*
* audio_sup_log()
*
* Description:
* similar to cmn_err but prefixes the message with
* <drivername><instance>:
*
* Arguments:
* audiohdl_t handle May be NULL in which case it behaves as
* cmn_err()
* uint_t level A constant indicating the severity
* char *format The message to be displayed
*
* NOTE: ? ! ^ should work as expected
*/
void
{
int n = 0;
int skip = 0;
if (statep) {
switch (fmt[0]) {
case '?':
case '!':
case '^':
audio_sup_log_buf[0] = fmt[n++];
skip++;
break;
default:
break;
}
n = strlen(audio_sup_log_buf);
}
ATRACE(audio_sup_log_buf, 0);
} /* audio_sup_log() */
/*
* audio_sup_update_persist_key()
*
* Description:
* Update the persistent key to a new value. Memory is freed from the old
* key and allocated for the new key.
*
* Arguments:
* dev_info_t *dip dev_info_t pointer for the device
* char *key Pointer to the new key string
* int sleep_okay Non-zero if okay to sleep on mem. alloc.
*
* Returns:
* AUDIO_SUCCESS Key successfully updated to new key
* AUDIO_FAILURE Memory allocation failed, dip not found
*/
int
{
char *new_key;
/* see audio_sup_persist() for an explaination on keys */
return (AUDIO_FAILURE);
}
key_length++; /* make room for NULL at end of string */
if (sleep_okay) {
} else {
return (AUDIO_FAILURE);
}
}
/*
* Find our dip, we do this after getting the key so we hold the
* locks for as short a time as possible.
*/
break;
}
}
return (AUDIO_FAILURE);
}
return (AUDIO_SUCCESS);
} /* audio_sup_update_persist_key() */
/*
* STREAMS Private Data Routines
*/
/*
* audio_sup_set_qptr()
*
* Description:
* Allocate an audio_qptr structure to hold the private STREAM data
* structure. Then set the values and point both the RD() and WR()
* STREAMS queues to this data structure. That way it'll be available
* regardless of which direction the queue may be pointed.
*
* NOTE: open() and close() use the read queue, which is why we put it on
* both the read and write side.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* dev_t dev Device name
* void *data Pointer to the private data
*
* Returns:
* void
*/
void
{
ATRACE("in audio_sup_set_qptr()", q);
} /* audio_sup_set_qptr() */
/*
* audio_sup_free_qptr()
*
* Description:
* Free the private STREAMS data structure.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
*
* Returns:
* void
*/
void
{
ATRACE("in audio_sup_free_qptr()", q);
ATRACE("audio_sup_free_qptr() done", q);
} /* audio_sup_free_qptr() */
/*
* audio_sup_get_qptr_dev()
*
* Description:
* Get the device info from the private data.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
*
* Returns:
* The dev_t that was saved
*/
{
ATRACE("in audio_sup_get_qptr_dev()", q);
/* make sure the q_ptr is valid */
return (NULL);
}
} /* audio_sup_get_qptr_dev() */
/*
* audio_sup_get_qptr_data()
*
* Description:
* Get the data info from the private data.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
*
* Returns:
* Pointer to the private data.
*/
void *
{
ATRACE("in audio_sup_get_qptr_data()", q);
/* make sure the q_ptr is valid */
return (NULL);
}
} /* audio_sup_get_qptr_data() */
/*
* audio_sup_get_qptr_instance()
*
* Description:
* Get the instance number from the private data's dev_t.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
*
* Returns:
* >= 0 The instance number
*/
int
{
ATRACE("in audio_sup_get_qptr_instance()", q);
return (instance);
} /* audio_sup_get_qptr_instance() */
/*
* State structure handling:
*/
/*
* audio_sup_devt_to_state()
*
* Description:
* This routine is used to associate the device number with the
* dev_info_t pointer. It returns the device's state structure when
* it is done.
*
* Arguments:
* dev_t dev Device name
*
* Returns:
* Valid pointer Pointer to the state
* NULL State pointer not found
*/
{
int instance;
/* protect the driver list */
while (list) {
return (statep);
}
}
ATRACE("audio_sup_devt_to_state() returning NULL", 0);
return (NULL);
} /* audio_sup_devt_to_state() */
/*
* audio_sup_devinfo_to_state()
*
* Description:
* Get the state pointer for the audio device given a devinfo_t *.
*
* Arguments:
* dev_info_t *dip dev_info_t pointer for the device
*
* Returns:
* Valid pointer Pointer to the state
* NULL State pointer not found
*/
{
/* verify, if given */
ATRACE("audio_sup_devinfo_to_state() "
} else {
return (NULL);
}
} /* audio_sup_devinfo_to_state() */
/*
* audio_sup_create_drv_entry()
*
* Description:
* Create & add an entry in the instance list (audio_drv_list_head).
* The whole linked list is scanned
* to make sure we don't ever get a duplicate entry.
*
* Arguments:
* dev_info_t *dip dev_info_t pointer for the device, what we use
* to find the instance
*
* Returns:
* Valid pointer Valid instance
* NULL Instance already registered
*/
static audio_inst_info_t *
{
/* protect the driver list */
while (list) {
ATRACE("audio_sup_create_drv_entry() "
"instance already registered", dip);
"%s%d already registered",
return (NULL);
}
}
/* "dip" is not registered, create new one and add to list */
return (entry);
} /* audio_sup_create_drv_entry() */
/*
* audio_sup_free_drv_entry()
*
* Description:
* This routine is used to clear entries in the driver list that
* were added in by audio_sup_create_drv_entry().
*
* Arguments:
* dev_info_t *dip dev_info_t pointer for the device.
* if NULL, delete all entries
*
* Returns:
* void
*/
static void
{
int i;
int num_chs;
/* protect the driver list */
while (list) {
/* found it */
/* de-initialize the channel structures */
/*
* All we have to worry about is locks and
* condition variables.
*/
}
/* destroy mutex, cv */
/* free the structure */
/* see if we need to go again */
if (dip) {
/* nope, done */
break;
}
}
}
ATRACE("audio_sup_free_drv_entry() done", 0);
} /* audio_sup_free_drv_entry() */
/*
* audio_sup_free_apm_persist()
*
* Description:
* Free the audio_apm_persist list that is associated with the instance.
*
* Arguments:
* audio_state_t *statep Pointer to device state info
* audio_apm_persist_t *instp Pointer to instance data
*
* Returns:
* void Silently fails if key useless
*/
static void
{
while (tmp) {
}
} /* audio_sup_free_apm_persist() */
/*
* audio_sup_free_inst_persist()
*
* Description:
* Free the audio_inst_persist_t structure for this dip.
*
* Arguments:
* audio_state_t *statep Pointer to device state info
* audio_inst_persist_t *persistp Pointer to the struct to remove
*
* Returns:
* void
*/
static void
{
while (list) {
/* found it, free the APM list and key */
}
}
/* remove from the list */
ATRACE("audio_sup_free_inst_persist() "
"freeing dip & return", list);
return;
}
}
} /* audio_sup_free_inst_persist() */
/*
* audio_sup_lookup_drv_entry()
*
* Description:
* Lookup audio_inst_info_t pointer corresponding to dip
*
* Arguments:
* dev_info_t *dip dev_info_t pointer for the device
*
* Returns:
* audio_inst_info_t pointer or NULL
*/
static audio_inst_info_t *
{
while (list) {
break;
}
}
return (list);
} /* audio_sup_lookup_drv_entry() */
/*
* audio_sup_persist()
*
* Description:
* Routine to initialize and find persistent state and its unique key.
*
* There are two types of persistent state, per instance and per APM
* state. Per instance saves the key and dip. Thus the correct instance
* may be found based on the key or the dip. It is also possible to
* detect, given a driver provided unique key, when a device moves
* from one port to another. Right now a linear search is used to find
* the matching dip. If we get to the point of supporting 100s or 1000s
* of instances then this will need to be changed to something more
* sophisticated.
*
* The second type is used to save the state for each APM for the
* instance. It is not a requirement for any APM to save persistent
* state, thus we could be allocating the per instance state structure
* for nothing. The APM is responsible for allocating the memory and
* providing a pointer to that memory and the size.
* audio_sup_free_persist_state() will free this memory for the APM.
*
* The per instance state structures are found by retreiving the
* main anchor using the AUDIO_SUP_KEY unique key.
*
* If the reset-configuration property is set to non-zero then any
* saved state is cleared and the APMs rebuild the persistent data
* from scratch. This effectively resets them to their defaults.
*
* NOTE: All allocated memory is owned by the main anchor.
*
* Arguments:
* audio_state_t *statep The device state structure
* char *key Unique key to for finding memory
*
* Returns:
*/
static int
{
char *anchor_key = AUDIO_SUP_KEY;
char *new_key;
char pathbuf[MAXPATHLEN];
int do_reset = 0;
/*
* The 1st step is to make sure we have the anchor. It could have
* been allocated on a previous audio framework load, or it could be
* unallocated.
*/
if (audio_main_anchor == NULL) {
/* anchor not loaded, so find it */
ATRACE("audio_sup_persist() space_fetch() main_anchor",
if (main_anchor == NULL) {
/* 1st load, need to allocate the anchor memory */
KM_SLEEP);
if (space_store(anchor_key,
(uintptr_t)main_anchor) != 0) {
"!cannot create persistent anchor");
return (AUDIO_FAILURE);
}
ATRACE("audio_sup_persist() space_store() main_anchor",
}
/* set this after the check above! */
ATRACE("audio_sup_persist() new audio_main_anchor",
}
/*
* Set up the instance key. Following the comments in space.c this
* key is in one of two formats:
*
* AUDIO:key_string_from_driver
*
* or
*
* AUDIO:pathname
*
* The former is when the driver passes a string for the key. This
* string MUST be unique. The later is when the driver passes a
* NULL key string.
*/
} else {
}
key_length++; /* make room for NULL at end of string */
/*
* In order to determine if this device has moved to a different port
* or if the key has changed we need to search through all of the dev
* structures. Once this loop ends we'll know if the current key and
* dev are present. This is one of the simple search algorithms that
* would need to be changed if we need to support a huge number of
* instances.
*/
ATRACE("audio_sup_persist() dev: found key",
break;
}
}
if (key_persistp) {
/* free new_key so we don't have a memory leak */
} else {
/* don't free new_key because we need it */
/* put on list */
}
/*
* CAUTION: From here new_key must have been freed or placed in an
* audio_inst_persist_t structure. Otherwise we'll have a memory
* leak.
*
* We are done!
*/
/* have statep point to instance persist info */
/*
* The final piece of information we need is whether we need to
* do a reset or not.
*/
"reset-configuration", 0)) {
do_reset++;
}
/*
* Now that we have good persistence data see if we need to reset it.
* We don't block the attach() in this case because the driver is still
* useful.
*/
AUDIO_ALL_DEVICES) == AUDIO_FAILURE) {
}
return (AUDIO_SUCCESS);
} /* audio_sup_persist() */
/*
* audio_sup_wiocdata()
*
* Description:
* This routine is called by audio_sup_wput() to process the IOCDATA
* messages that belong to the Audio Support Module's routines.
*
* We only support transparent ioctls.
*
* WARNING: Don't forget to free the mblk_t struct used to hold private
* data. The done: jump point takes care of this.
*
* WARNING: Don't free the private mblk_t structure if the command is
* going to call qreply(). This frees the private date that will
* be needed for the next M_IOCDATA message.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* audio_ch_t *chptr Pointer to this channel's state information
*
* Returns:
* 0 Always returns a 0, becomes on return for
* audio_sup_wput()
*/
static int
{
int error = 0;
/* make sure we've got a good return value */
goto done;
}
/* find the command */
switch (cmd->ais_command) {
case AUDIO_COPY_OUT_CH_NUMBER: /* AUDIO_GET_CH_NUMBER */
}
break;
case AUDIO_COPY_OUT_CH_TYPE: /* AUDIO_GET_CH_TYPE */
}
break;
case AUDIO_COPY_OUT_NUM_CHS: /* AUDIO_GET_NUM_CHS */
}
break;
case AUDIO_COPY_OUT_AD_DEV: /* AUDIO_GET_AD_DEV */
}
break;
case AUDIO_COPY_OUT_APM_DEV: /* AUDIO_GET_APM_DEV */
}
break;
case AUDIO_COPY_OUT_AS_DEV: /* AUDIO_GET_AS_DEV */
}
break;
default:
break;
}
/* we always either ack or nack depending on if error is set or not */
done:
if (csp->cp_private) {
ATRACE("audio_sup_wiocdata() freeing csp->cp_private",
csp->cp_private);
}
if (cqp->cq_private) {
ATRACE("audio_sup_wiocdata() freeing cqp->cq_private",
cqp->cq_private);
}
if (error) {
} else {
}
return (0);
} /* audio_sup_wiocdata() */
/*
* audio_sup_wioctl()
*
* Description:
* This routine is called by audio_sup_wput() to process all M_IOCTL
* messages that the Audio Support Module provides.
*
* We only support transparent ioctls. Since this is a driver we
* nack unrecognized ioctls.
*
* The following ioctls are supported:
* AUDIO_GET_CH_NUMBER
* AUDIO_GET_CH_TYPE
* AUDIO_GET_NUM_CHS
* AUDIO_GET_AD_DEV
* AUDIO_GET_APM_DEV
* AUDIO_GET_AS_DEV
* unknown nack back up the queue
*
* CAUTION: This routine is called from interrupt context, so memory
* allocation cannot sleep.
*
* WARNING: There cannot be any locks owned by calling routines.
*
* Arguments:
* queue_t *q Pointer to the STREAMS queue
* mblk_t *mp Pointer to the message block
* audio_ch_t *chptr Pointer to this channel's state information
*
* Returns:
* 0 Always returns a 0, becomes a return for
* audio_sup_wput()
*/
static int
{
int error;
/* make sure this is a transparent ioctl */
ATRACE_32("audio_sup_wioctl() not TRANSPARENT",
goto nack;
}
/* get a buffer for private data */
ATRACE("audio_sup_wioctl() state kmem_alloc() failed", 0);
goto nack;
}
case AUDIO_GET_CH_NUMBER:
/* save state for M_IOCDATA processing */
/* user space addr */
/* only an int, so reuse the data block without checks */
/* Setup for copyout */
NULL);
/* send the copy out request */
ATRACE("audio_sup_wioctl() AUDIO_GET_CH_NUMBER returning",
chptr);
return (0);
/* end AUDIO_GET_CH_NUMBER */
case AUDIO_GET_CH_TYPE:
/* save state for M_IOCDATA processing */
/* user space addr */
/* only an int, so reuse the data block without checks */
/* Setup for copyout */
NULL);
/* send the copy out request */
return (0);
/* end AUDIO_GET_CH_TYPE */
case AUDIO_GET_NUM_CHS:
/* save state for M_IOCDATA processing */
/* user space addr */
/* only an int, so reuse the data block without checks */
/* Setup for copyout */
NULL);
/* send the copy out request */
ATRACE("audio_sup_wioctl() AUDIO_GET_NUM_CHS returning",
chptr);
return (0);
/* end AUDIO_GET_NUM_CHS */
case AUDIO_GET_AD_DEV:
/* save state for M_IOCDATA processing */
/* user space addr */
/* Setup for copyout */
/* put the data in the buffer, but try to reuse it first */
goto nack;
}
}
/*
* We don't bother to lock the state structure because this
* is static data.
*/
sizeof (*chptr->ch_dev_info);
/* send the copy out request */
return (0);
case AUDIO_GET_APM_DEV:
/* save state for M_IOCDATA processing */
/* user space addr */
/* Setup for copyout */
/* put the data in the buffer, but try to reuse it first */
sizeof (*chptr->ch_dev_info)) {
goto nack;
}
}
/*
* We don't bother to lock the state structure because this
* is static data.
*/
sizeof (*chptr->ch_dev_info));
sizeof (*chptr->ch_dev_info);
/* send the copy out request */
return (0);
case AUDIO_GET_AS_DEV:
/* save state for M_IOCDATA processing */
/* user space addr */
/* Setup for copyout */
/* put the data in the buffer, but try to reuse it first */
sizeof (*chptr->ch_dev_info)) {
goto nack;
}
}
/*
* We don't bother to lock the state structure because this
* is static data.
*/
sizeof (*chptr->ch_dev_info);
/* send the copy out request */
return (0);
default: /* this should never happen */
break;
}
/* we always nack */
nack:
/* we always nack if we break out of the switch() */
if (state) { /* free allocated state memory */
}
return (0);
} /* audio_sup_wioctl() */