Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
This file implements the actual client/server device pair. There will be separate oss_userdev instance for each process that has opened the client side.
This file is part of Open Sound System.
Copyright (C) 4Front Technologies 1996-2008.
This this source file is released under GPL v2 license (no other versions). See the COPYING file included in the main directory of this source distribution for the license terms and conditions.
#include "oss_userdev_cfg.h" #include <oss_userdev_exports.h> #include "userdev.h" static void userdev_free_device_pair (userdev_devc_t *devc); extern int userdev_visible_clientnodes; static void transfer_audio (userdev_portc_t * server_portc, dmap_t * dmap_from, dmap_t * dmap_to) { int l = dmap_from->fragment_size; unsigned char *fromp, *top; if (dmap_to->fragment_size != l) { cmn_err (CE_WARN, "Fragment size mismatch (%d != %d)\n", dmap_to->fragment_size, l); /* Perform emergency stop */ server_portc->input_triggered = 0; server_portc->output_triggered = 0; server_portc->peer->input_triggered = 0; server_portc->peer->output_triggered = 0; return; } fromp = dmap_from->dmabuf + (dmap_from->byte_counter % dmap_from->bytes_in_use); top = dmap_to->dmabuf + (dmap_to->byte_counter % dmap_to->bytes_in_use); memcpy (top, fromp, l); } static void handle_input (userdev_portc_t * server_portc) { userdev_portc_t *client_portc = server_portc->peer; if (client_portc->output_triggered) { transfer_audio (server_portc, audio_engines[client_portc->audio_dev]->dmap_out, audio_engines[server_portc->audio_dev]->dmap_in); oss_audio_outputintr (client_portc->audio_dev, 0); } oss_audio_inputintr (server_portc->audio_dev, 0); } static void handle_output (userdev_portc_t * server_portc) { userdev_portc_t *client_portc = server_portc->peer; if (client_portc->input_triggered) { transfer_audio (server_portc, audio_engines[server_portc->audio_dev]->dmap_out, audio_engines[client_portc->audio_dev]->dmap_in); oss_audio_inputintr (client_portc->audio_dev, 0); } oss_audio_outputintr (server_portc->audio_dev, 0); } static void userdev_cb (void *pc) {
This timer callback routine will get called 100 times/second. It handles movement of audio data between the client and server sides.
userdev_portc_t *server_portc = pc; userdev_devc_t *devc = server_portc->devc; int tmout = devc->poll_ticks; if (tmout < 1) tmout = 1; devc->timeout_id = 0; /* No longer valid */ if (server_portc->input_triggered) handle_input (server_portc); if (server_portc->output_triggered) handle_output (server_portc); /* Retrigger timer callback */ if (server_portc->input_triggered || server_portc->output_triggered) devc->timeout_id = timeout (userdev_cb, server_portc, tmout); } static int userdev_check_input (int dev) { userdev_portc_t *portc = audio_engines[dev]->portc; if (!portc->peer->output_triggered) { return OSS_ECONNRESET; } return 0; } static int userdev_check_output (int dev) { userdev_portc_t *portc = audio_engines[dev]->portc; if (!portc->peer->input_triggered) { return OSS_ECONNRESET; } if (portc->peer->open_mode == 0) return OSS_EIO; return 0; } static void setup_sample_format (userdev_portc_t * portc) { adev_t *adev; userdev_devc_t *devc = portc->devc; int fragsize, frame_size; frame_size = devc->channels * devc->fmt_bytes; if (frame_size == 0) frame_size = 4; fragsize = (devc->rate * frame_size * devc->poll_ticks) / OSS_HZ; /* Number of bytes/fragment */ devc->rate = fragsize * 100 / frame_size; /* Setup the server side */ adev = audio_engines[portc->audio_dev]; adev->min_block = adev->max_block = fragsize; /* Setup the client side */ adev = audio_engines[portc->peer->audio_dev]; adev->min_block = adev->max_block = fragsize; adev->max_rate = adev->min_rate = devc->rate; adev->iformat_mask = devc->fmt; adev->oformat_mask = devc->fmt; adev->xformat_mask = devc->fmt; adev->min_channels = adev->max_channels = devc->channels; } static int userdev_server_set_rate (int dev, int arg) { userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; adev_t *client_adev = audio_engines[portc->peer->audio_dev]; if (arg == 0) return devc->rate; if (portc->peer->input_triggered || portc->peer->output_triggered) return devc->rate; if (arg < 5000) arg = 5000; if (arg > MAX_RATE) arg = MAX_RATE; /* Force the sample rate to be multiple of 100 */ arg = (arg / 100) * 100; devc->rate = arg; client_adev->min_rate = arg; client_adev->max_rate = arg; setup_sample_format (portc); return devc->rate = arg; } /*ARGSUSED*/ static int userdev_client_set_rate (int dev, int arg) { userdev_devc_t *devc = audio_engines[dev]->devc; return devc->rate; } static short userdev_server_set_channels (int dev, short arg) { userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; adev_t *client_adev = audio_engines[portc->peer->audio_dev]; if (arg == 0) return devc->channels; if (portc->peer->input_triggered || portc->peer->output_triggered) return devc->channels; if (arg < 1) arg = 1; if (arg > MAX_CHANNELS) arg = MAX_CHANNELS; devc->channels = arg; client_adev->min_channels=client_adev->max_channels=arg; setup_sample_format (portc); return devc->channels; } /*ARGSUSED*/ static short userdev_client_set_channels (int dev, short arg) { userdev_devc_t *devc = audio_engines[dev]->devc; return devc->channels; /* Server side channels */ } static unsigned int userdev_server_set_format (int dev, unsigned int arg) { userdev_devc_t *devc = audio_engines[dev]->devc; userdev_portc_t *portc = audio_engines[dev]->portc; adev_t *client_adev = audio_engines[portc->peer->audio_dev]; if (arg == 0) return devc->fmt; if (portc->peer->input_triggered || portc->peer->output_triggered) return devc->fmt; switch (arg) { case AFMT_S16_NE: devc->fmt_bytes = 2; break; case AFMT_S32_NE: devc->fmt_bytes = 4; break; default: /* Unsupported format */ arg = AFMT_S16_NE; devc->fmt_bytes = 2; } devc->fmt = arg; client_adev->oformat_mask = arg; client_adev->iformat_mask = arg; setup_sample_format (portc); return devc->fmt; } /*ARGSUSED*/ static unsigned int userdev_client_set_format (int dev, unsigned int arg) { userdev_devc_t *devc = audio_engines[dev]->devc; return devc->fmt; /* Server side sample format */ } static void userdev_trigger (int dev, int state); static void userdev_reset (int dev) { userdev_trigger (dev, 0); } /*ARGSUSED*/ static int userdev_server_open (int dev, int mode, int open_flags) { userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; oss_native_word flags; if (portc == NULL || portc->peer == NULL) return OSS_ENXIO; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (portc->open_mode) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EBUSY; } portc->open_mode = mode; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); devc->open_count++; return 0; } /*ARGSUSED*/ static int userdev_client_open (int dev, int mode, int open_flags) { userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; oss_native_word flags; if (portc == NULL || portc->peer == NULL) return OSS_ENXIO; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (portc->open_mode) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EBUSY; } portc->open_mode = mode; devc->open_count++; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } static void wipe_audio_buffers(userdev_devc_t *devc) {
Write silence to the audio buffers when only one of the sides is open. This prevents the device from looping the last fragments written to the device.
dmap_t *dmap; dmap = audio_engines[devc->client_portc.audio_dev]->dmap_out; if (dmap != NULL && dmap->dmabuf != NULL) memset(dmap->dmabuf, 0, dmap->buffsize); dmap = audio_engines[devc->client_portc.audio_dev]->dmap_in; if (dmap != NULL && dmap->dmabuf != NULL) memset(dmap->dmabuf, 0, dmap->buffsize); dmap = audio_engines[devc->server_portc.audio_dev]->dmap_out; if (dmap != NULL && dmap->dmabuf != NULL) memset(dmap->dmabuf, 0, dmap->buffsize); dmap = audio_engines[devc->server_portc.audio_dev]->dmap_in; if (dmap != NULL && dmap->dmabuf != NULL) memset(dmap->dmabuf, 0, dmap->buffsize); } /*ARGSUSED*/ static void userdev_server_close (int dev, int mode) { userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; oss_native_word flags; int open_count; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); portc->open_mode = 0; /* Stop the client side because there is no server */ portc->peer->input_triggered = 0; portc->peer->output_triggered = 0; open_count = --devc->open_count; if (open_count == 0) userdev_free_device_pair (devc); else wipe_audio_buffers(devc); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } /*ARGSUSED*/ static void userdev_client_close (int dev, int mode) { userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; oss_native_word flags; int open_count; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); portc->open_mode = 0; open_count = --devc->open_count; if (open_count == 0) userdev_free_device_pair (devc); else wipe_audio_buffers(devc); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } /*ARGSUSED*/ static int userdev_ioctl (int dev, unsigned int cmd, ioctl_arg arg) { switch (cmd) { case SNDCTL_GETLABEL: {
Return an empty string so that this feature can be tested. Complete functionality is to be implemented later.
oss_label_t *s = (oss_label_t *) arg; memset (s, 0, sizeof (oss_label_t)); return 0; } break; case SNDCTL_GETSONG: {
Return an empty string so that this feature can be tested. Complete functionality is to be implemented later.
oss_longname_t *s = (oss_longname_t *) arg; memset (s, 0, sizeof (oss_longname_t)); return 0; } break; } return OSS_EINVAL; } static void set_adev_name(int dev, const char *name) { adev_t *adev = audio_engines[dev]; strcpy(adev->name, name); #ifdef CONFIG_OSS_VMIX if (adev->vmix_mixer != NULL) vmix_change_devnames(adev->vmix_mixer, name); #endif } static int create_instance(int dev, userdev_create_t *crea) { userdev_devc_t *devc = audio_engines[dev]->devc; char tmp_name[64]; devc->match_method = crea->match_method; devc->match_key = crea->match_key; devc->create_flags = crea->flags; devc->poll_ticks = (crea->poll_interval * OSS_HZ) / 1000; if (devc->poll_ticks < 1) devc->poll_ticks = 1; crea->poll_interval = (1000*devc->poll_ticks) / OSS_HZ; if (crea->poll_interval<1) crea->poll_interval = 1; crea->name[sizeof(crea->name)-1]=0; /* Overflow protectgion */ sprintf(tmp_name, "%s (server)", crea->name); tmp_name[49]=0; set_adev_name (devc->client_portc.audio_dev, crea->name); set_adev_name (devc->server_portc.audio_dev, tmp_name); strcpy(crea->devnode, audio_engines[devc->client_portc.audio_dev]->devnode); return 0; } static int userdev_set_control (int dev, int ctl, unsigned int cmd, int value) { userdev_devc_t *devc = mixer_devs[dev]->devc; if (ctl < 0 || ctl >= USERDEV_MAX_MIXERS) return OSS_EINVAL; if (cmd == SNDCTL_MIX_READ) { return devc->mixer_values[ctl]; } devc->mixer_values[ctl] = value; devc->modify_counter++; return 0; } /*ARGSUSED*/ static int userdev_server_ioctl (int dev, unsigned int cmd, ioctl_arg arg) { switch (cmd) { case USERDEV_CREATE_INSTANCE: { userdev_create_t *crea = (userdev_create_t *)arg; return create_instance(dev, crea); } break; case USERDEV_GET_CLIENTCOUNT: { userdev_devc_t *devc = audio_engines[dev]->devc; return *arg = (devc->client_portc.open_mode != 0); } break;
Mixer related ioctl calls
case USERDEV_CREATE_MIXGROUP: { userdev_devc_t *devc = audio_engines[dev]->devc; userdev_mixgroup_t *grp=(userdev_mixgroup_t*)arg; int group; grp->name[sizeof(grp->name)-1]=0; /* Buffer overflow protection */ if ((group=mixer_ext_create_group(devc->mixer_dev, grp->parent, grp->name))<0) return group; grp->num = group; return 0; } break; case USERDEV_CREATE_MIXCTL: { userdev_devc_t *devc = audio_engines[dev]->devc; userdev_mixctl_t *c=(userdev_mixctl_t*)arg; oss_mixext *ext; int ctl; c->name[sizeof(c->name)-1]=0; /* Buffer overflow protection */ if (c->index < 0 || c->index >= USERDEV_MAX_MIXERS) return OSS_EINVAL; if ((ctl = mixer_ext_create_control (devc->mixer_dev, c->parent, c->index, userdev_set_control, c->type, c->name, c->maxvalue, c->flags)) < 0) return ctl; c->num = ctl; ext = mixer_find_ext (devc->mixer_dev, ctl); ext->minvalue = c->offset; ext->control_no= c->control_no; ext->rgbcolor = c->rgbcolor; if (c->type == MIXT_ENUM) { memcpy(ext->enum_present, c->enum_present, sizeof(ext->enum_present)); mixer_ext_set_strings (devc->mixer_dev, ctl, c->enum_choises, 0); } return 0; } break; case USERDEV_GET_MIX_CHANGECOUNT: { userdev_devc_t *devc = audio_engines[dev]->devc; return *arg = devc->modify_counter; } break; case USERDEV_SET_MIXERS: { userdev_devc_t *devc = audio_engines[dev]->devc; memcpy(devc->mixer_values, arg, sizeof(devc->mixer_values)); mixer_devs[devc->mixer_dev]->modify_counter++; //cmn_err(CE_CONT, "Set %08x %08x\n", devc->mixer_values[0], devc->mixer_values[1]); return 0; } break; case USERDEV_GET_MIXERS: { userdev_devc_t *devc = audio_engines[dev]->devc; memcpy(arg, devc->mixer_values, sizeof(devc->mixer_values)); return 0; } break; } return userdev_ioctl(dev, cmd, arg); } /*ARGSUSED*/ static void userdev_output_block (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { } /*ARGSUSED*/ static void userdev_start_input (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { } static void userdev_trigger (int dev, int state) { userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; if (portc->open_mode & OPEN_READ) /* Handle input */ { portc->input_triggered = !!(state & OPEN_READ); } if (portc->open_mode & OPEN_WRITE) /* Handle output */ { portc->output_triggered = !!(state & OPEN_WRITE); } if (portc->output_triggered || portc->input_triggered) /* Something is going on */ { int tmout = devc->poll_ticks; if (tmout < 1) tmout = 1; if (portc->port_type != PT_SERVER) portc = portc->peer; /* Switch to the server side */ if (portc->output_triggered || portc->input_triggered) /* Something is going on */ if (devc->timeout_id == 0) { devc->timeout_id = timeout (userdev_cb, portc, tmout); } } else { if (portc->port_type == PT_SERVER) if (devc->timeout_id != 0) { untimeout (devc->timeout_id); devc->timeout_id = 0; } } } /*ARGSUSED*/ static int userdev_server_prepare_for_input (int dev, int bsize, int bcount) { oss_native_word flags; userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); portc->input_triggered = 0; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int userdev_server_prepare_for_output (int dev, int bsize, int bcount) { oss_native_word flags; userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); portc->output_triggered = 0; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int userdev_client_prepare_for_input (int dev, int bsize, int bcount) { oss_native_word flags; userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); portc->input_triggered = 0; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int userdev_client_prepare_for_output (int dev, int bsize, int bcount) { oss_native_word flags; userdev_portc_t *portc = audio_engines[dev]->portc; userdev_devc_t *devc = audio_engines[dev]->devc; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); portc->output_triggered = 0; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int userdev_alloc_buffer (int dev, dmap_t * dmap, int direction) { #define MY_BUFFSIZE (64*1024) if (dmap->dmabuf != NULL) return 0; dmap->dmabuf_phys = 0; /* Not mmap() capable */ dmap->dmabuf = KERNEL_MALLOC (MY_BUFFSIZE); if (dmap->dmabuf == NULL) return OSS_ENOSPC; dmap->buffsize = MY_BUFFSIZE; return 0; } /*ARGSUSED*/ static int userdev_free_buffer (int dev, dmap_t * dmap, int direction) { if (dmap->dmabuf == NULL) return 0; KERNEL_FREE (dmap->dmabuf); dmap->dmabuf = NULL; return 0; } #if 0 static int userdev_get_buffer_pointer (int dev, dmap_t * dmap, int direction) { } #endif /*ARGSUSED*/ static int userdev_ioctl_override (int dev, unsigned int cmd, ioctl_arg arg) {
Purpose of this ioctl override function is to intercept mixer ioctl calls made on the client side and to hide everything outside the userdev instance from the application.
Note that this ioctl is related with the client side audio device. However if /dev/mixer points to this (audio) device then all mixer acess will be redirected too. Also the vmix driver will redirect mixer/system ioctl calls to this function.
int err; userdev_devc_t *devc = audio_engines[dev]->devc; adev_t *adev = audio_engines[devc->client_portc.audio_dev]; switch (cmd) { case SNDCTL_MIX_NRMIX: return *arg=1; break; case SNDCTL_MIX_NREXT: *arg = devc->mixer_dev; return OSS_EAGAIN; /* Continue with the default handler */ break; case SNDCTL_SYSINFO: {
Fake SNDCTL_SYSINFO to report just one mixer device which is the one associated with the client.
oss_sysinfo *info = (oss_sysinfo *) arg; int i; if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0) return err;
Hide all non-oss_userdev devices
strcpy (info->product, "OSS (userdev)"); info->nummixers = 1; info->numcards = 1; info->numaudios = 1; for (i = 0; i < 8; i++) info->openedaudio[i] = 0; return 0; } break; case SNDCTL_MIXERINFO: { oss_mixerinfo *info = (oss_mixerinfo *) arg; info->dev = devc->mixer_dev; /* Redirect to oss_userdev mixer */ if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0) return err; strcpy(info->name, adev->name); info->card_number = 0; return 0; } case SNDCTL_AUDIOINFO: case SNDCTL_AUDIOINFO_EX: { oss_audioinfo *info = (oss_audioinfo *) arg; info->dev = devc->client_portc.audio_dev; cmd = SNDCTL_ENGINEINFO; if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0) return err; info->card_number = 0; return 0; } case SNDCTL_CARDINFO: { oss_card_info *info = (oss_card_info *) arg; info->card = adev->card_number; /* Redirect to oss_userdev0 */ if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0) return err; info->card = 0; return 0; } case SNDCTL_MIX_EXTINFO: { oss_mixext *ext = (oss_mixext*)arg; ext->dev = devc->mixer_dev; if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0) return err; if (ext->type == MIXT_DEVROOT) { oss_mixext_root *root = (oss_mixext_root *) ext->data; strncpy(root->name, adev->name, 48); root->name[47]=0; } return 0; } break; case SNDCTL_MIX_READ: case SNDCTL_MIX_WRITE: { oss_mixer_value *ent = (oss_mixer_value*)arg; ent->dev = devc->mixer_dev; return OSS_EAGAIN; /* Redirect */ } break; default: return OSS_EAGAIN; } } static audiodrv_t userdev_server_driver = { userdev_server_open, userdev_server_close, userdev_output_block, userdev_start_input, userdev_server_ioctl, userdev_server_prepare_for_input, userdev_server_prepare_for_output, userdev_reset, NULL, NULL, NULL, NULL, userdev_trigger, userdev_server_set_rate, userdev_server_set_format, userdev_server_set_channels, NULL, NULL, userdev_check_input, userdev_check_output, userdev_alloc_buffer, userdev_free_buffer, NULL, NULL, NULL /* userdev_get_buffer_pointer */ }; static audiodrv_t userdev_client_driver = { userdev_client_open, userdev_client_close, userdev_output_block, userdev_start_input, userdev_ioctl, userdev_client_prepare_for_input, userdev_client_prepare_for_output, userdev_reset, NULL, NULL, NULL, NULL, userdev_trigger, userdev_client_set_rate, userdev_client_set_format, userdev_client_set_channels, NULL, NULL, userdev_check_input, userdev_check_output, userdev_alloc_buffer, userdev_free_buffer, NULL, NULL, NULL, // userdev_get_buffer_pointer NULL, // userdev_calibrate_speed NULL, // userdev_sync_control NULL, // userdev_prepare_to_stop NULL, // userdev_get_input_pointer NULL, // userdev_get_output_pointer NULL, // userdev_bind NULL, // userdev_setup_fragments NULL, // userdev_redirect userdev_ioctl_override }; static int userdev_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg) { if (cmd == SOUND_MIXER_READ_CAPS) return *arg = SOUND_CAP_NOLEGACY; #if 0 if (cmd == SOUND_MIXER_READ_DEVMASK || cmd == SOUND_MIXER_READ_RECMASK || cmd == SOUND_MIXER_READ_RECSRC) return *arg = 0; if (cmd == SOUND_MIXER_READ_VOLUME || cmd == SOUND_MIXER_READ_PCM) return *arg = 100 | (100 << 8); if (cmd == SOUND_MIXER_WRITE_VOLUME || cmd == SOUND_MIXER_WRITE_PCM) return *arg = 100 | (100 << 8); #endif return OSS_EINVAL; } static mixer_driver_t userdev_mixer_driver = { userdev_mixer_ioctl }; static int install_server (userdev_devc_t * devc) { userdev_portc_t *portc = &devc->server_portc; int adev; int opts = ADEV_STEREOONLY | ADEV_16BITONLY | ADEV_VIRTUAL | ADEV_FIXEDRATE | ADEV_SPECIAL | ADEV_HIDDEN | ADEV_DUPLEX; memset (portc, 0, sizeof (*portc)); portc->devc = devc; portc->port_type = PT_SERVER; if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, devc->osdev, devc->osdev, "User space audio device server side", &userdev_server_driver, sizeof (audiodrv_t), opts, SUPPORTED_FORMATS, devc, -1)) < 0) { return adev; } audio_engines[adev]->portc = portc; audio_engines[adev]->min_rate = 5000; audio_engines[adev]->max_rate = MAX_RATE; audio_engines[adev]->min_channels = 1; audio_engines[adev]->max_channels = MAX_CHANNELS; audio_engines[adev]->vmix_mixer=NULL; strcpy(audio_engines[adev]->devnode, userdev_server_devnode); portc->audio_dev = adev; return adev; } static int null_mixer_init(int de) { return 0; } static void userdev_create_mixer(userdev_devc_t * devc) { if ((devc->mixer_dev = oss_install_mixer (OSS_MIXER_DRIVER_VERSION, devc->osdev, devc->osdev, "OSS userdev mixer", &userdev_mixer_driver, sizeof (mixer_driver_t), devc)) < 0) { devc->mixer_dev = -1; return; } mixer_ext_set_init_fn (devc->mixer_dev, null_mixer_init, USERDEV_MAX_MIXERS*2); } static int install_client (userdev_devc_t * devc) { userdev_portc_t *portc = &devc->client_portc; int adev; int opts = ADEV_STEREOONLY | ADEV_16BITONLY | ADEV_VIRTUAL | ADEV_DUPLEX | ADEV_FIXEDRATE | ADEV_SPECIAL | ADEV_LOOP; memset (portc, 0, sizeof (*portc)); userdev_create_mixer(devc); portc->devc = devc; portc->port_type = PT_CLIENT; if (!userdev_visible_clientnodes && !(devc->create_flags & USERDEV_F_VMIX_PRIVATENODE)) { opts |= ADEV_HIDDEN; } if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, devc->osdev, devc->osdev, "User space audio device", &userdev_client_driver, sizeof (audiodrv_t), opts, SUPPORTED_FORMATS, devc, -1)) < 0) { return adev; } if (!userdev_visible_clientnodes) /* Invisible client device nodes */ strcpy(audio_engines[adev]->devnode, userdev_client_devnode); audio_engines[adev]->portc = portc; audio_engines[adev]->mixer_dev = devc->mixer_dev; audio_engines[adev]->min_rate = 5000; audio_engines[adev]->max_rate = MAX_RATE; audio_engines[adev]->min_channels = 1; audio_engines[adev]->max_channels = MAX_CHANNELS; portc->audio_dev = adev; #ifdef CONFIG_OSS_VMIX vmix_attach_audiodev(devc->osdev, adev, -1, 0); #endif return adev; } int userdev_create_device_pair(void) { int client_engine, server_engine; userdev_devc_t *devc; oss_native_word flags; if ((devc=PMALLOC(userdev_osdev, sizeof (*devc))) == NULL) return OSS_ENOMEM; memset(devc, 0, sizeof(*devc)); devc->osdev = userdev_osdev; MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV); devc->active=1; devc->rate = 48000; devc->fmt = AFMT_S16_NE; devc->fmt_bytes = 2; devc->channels = 2; devc->poll_ticks = 10; if ((server_engine=install_server (devc)) < 0) return server_engine; if ((client_engine=install_client (devc)) < 0) return client_engine; devc->client_portc.peer = &devc->server_portc; devc->server_portc.peer = &devc->client_portc;
Insert the device to the list of available devices
MUTEX_ENTER_IRQDISABLE(userdev_global_mutex, flags); devc->next_instance = userdev_active_device_list; userdev_active_device_list = devc; MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags); return server_engine; } static void userdev_free_device_pair (userdev_devc_t *devc) { oss_native_word flags; set_adev_name(devc->client_portc.audio_dev, "User space audio device"); set_adev_name(devc->server_portc.audio_dev, "User space audio device server side"); MUTEX_ENTER_IRQDISABLE(userdev_global_mutex, flags); devc->match_method = 0; devc->match_key = 0;
Add to the free device pair list.
devc->next_instance = userdev_free_device_list; userdev_free_device_list = devc;
Remove the device pair from the active device list.
if (userdev_active_device_list == devc) /* First device in the list */ { userdev_active_device_list = userdev_active_device_list->next_instance; } else { userdev_devc_t *this = userdev_active_device_list, *prev = NULL; while (this != NULL) { if (this == devc) { prev->next_instance = this->next_instance; /* Remove */ break; } prev = this; this = this->next_instance; } } MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags); } void userdev_reinit_instance(userdev_devc_t *devc) { if (devc->mixer_dev < 0) return; mixer_ext_rebuild_all (devc->mixer_dev, null_mixer_init, USERDEV_MAX_MIXERS*2); } void userdev_delete_device_pair(userdev_devc_t *devc) { if (!devc->active) return; devc->active = 0; MUTEX_CLEANUP(devc->mutex); } int usrdev_find_free_device_pair(void) { oss_native_word flags; userdev_devc_t *devc; MUTEX_ENTER_IRQDISABLE(userdev_global_mutex, flags); if (userdev_free_device_list != NULL) { devc = userdev_free_device_list; userdev_free_device_list = userdev_free_device_list->next_instance; devc->next_instance = userdev_active_device_list; userdev_active_device_list = devc; MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags); return devc->server_portc.audio_dev; } MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags); return OSS_ENXIO; }