/* OSS driver for Jack Copyright (C) 2003-2007 Jussi Laako This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #ifdef USE_BARRIER /* * POSIX conformance level should be globally defined somewhere, possibly * in config.h? Otherwise it's pre 1993/09 level, which leaves out significant * parts of threading and realtime stuff. Note: most of the parts are still * defined as optional by the standard, so OS conformance to this level * doesn't necessarily mean everything exists. */ #define _XOPEN_SOURCE 600 #endif #ifndef _REENTRANT #define _REENTRANT #endif #ifndef _THREAD_SAFE #define _THREAD_SAFE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "internal.h" #include "engine.h" #include #include "oss_driver.h" #ifndef SNDCTL_DSP_COOKEDMODE #ifdef _SIOWR #define SNDCTL_DSP_COOKEDMODE _SIOWR ('P', 30, int) #else /* _SIOWR */ #warning "Unable to define cooked mode!" #define OSS_NO_COOKED_MODE #endif /* _SIOWR */ #endif /* SNDCTL_DSP_COOKEDMODE */ #define OSS_DRIVER_N_PARAMS 11 const static jack_driver_param_desc_t oss_params[OSS_DRIVER_N_PARAMS] = { { "rate", 'r', JackDriverParamUInt, { .ui = OSS_DRIVER_DEF_FS }, NULL, "sample rate", "sample rate" }, { "period", 'p', JackDriverParamUInt, { .ui = OSS_DRIVER_DEF_BLKSIZE }, NULL, "period size", "period size" }, { "nperiods", 'n', JackDriverParamUInt, { .ui = OSS_DRIVER_DEF_NPERIODS }, NULL, "number of periods in buffer", "number of periods in buffer" }, { "wordlength", 'w', JackDriverParamInt, { .i = OSS_DRIVER_DEF_BITS }, NULL, "word length", "word length" }, { "inchannels", 'i', JackDriverParamUInt, { .ui = OSS_DRIVER_DEF_INS }, NULL, "capture channels", "capture channels" }, { "outchannels", 'o', JackDriverParamUInt, { .ui = OSS_DRIVER_DEF_OUTS }, NULL, "playback channels", "playback channels" }, { "capture", 'C', JackDriverParamString, { .str = OSS_DRIVER_DEF_DEV }, NULL, "input device", "input device" }, { "playback", 'P', JackDriverParamString, { .str = OSS_DRIVER_DEF_DEV }, NULL, "output device", "output device" }, { "ignorehwbuf", 'b', JackDriverParamBool, { }, NULL, "ignore hardware period size", "ignore hardware period size" }, { "input latency", 'I', JackDriverParamUInt, { .ui = 0 }, NULL, "system input latency", "system input latency" }, { "output latency", 'O', JackDriverParamUInt, { .ui = 0 }, NULL, "system output latency", "system output latency" } }; /* internal functions */ static void set_period_size (oss_driver_t *driver, jack_nframes_t new_period_size) { driver->period_size = new_period_size; driver->period_usecs = ((double)driver->period_size / (double)driver->sample_rate) * 1e6; driver->last_wait_ust = 0; driver->last_periodtime = driver->engine->get_microseconds (); driver->next_periodtime = 0; driver->iodelay = 0.0F; } static inline void update_times (oss_driver_t *driver) { driver->last_periodtime = driver->engine->get_microseconds (); if (driver->next_periodtime > 0) { driver->iodelay = (float) ((long double)driver->last_periodtime - (long double)driver->next_periodtime); } else { driver->iodelay = 0.0F; } driver->next_periodtime = driver->last_periodtime + driver->period_usecs; } static inline void driver_cycle (oss_driver_t *driver) { update_times (driver); driver->engine->transport_cycle_start (driver->engine, driver->last_periodtime); driver->last_wait_ust = driver->last_periodtime; driver->engine->run_cycle (driver->engine, driver->period_size, driver->iodelay); } static void copy_and_convert_in (jack_sample_t *dst, void *src, size_t nframes, int channel, int chcount, int bits) { int srcidx; int dstidx; signed short *s16src = (signed short*)src; signed int *s32src = (signed int*)src; double *f64src = (double*)src; jack_sample_t scale; srcidx = channel; switch (bits) { case 16: scale = 1.0f / 0x7fff; for (dstidx = 0; dstidx < nframes; dstidx++) { dst[dstidx] = (jack_sample_t) s16src[srcidx] * scale; srcidx += chcount; } break; case 24: scale = 1.0f / 0x7fffff; for (dstidx = 0; dstidx < nframes; dstidx++) { dst[dstidx] = (jack_sample_t) s32src[srcidx] * scale; srcidx += chcount; } break; case 32: scale = 1.0f / 0x7fffffff; for (dstidx = 0; dstidx < nframes; dstidx++) { dst[dstidx] = (jack_sample_t) s32src[srcidx] * scale; srcidx += chcount; } break; case 64: for (dstidx = 0; dstidx < nframes; dstidx++) { dst[dstidx] = (jack_sample_t)f64src[srcidx]; srcidx += chcount; } break; } } static void copy_and_convert_out (void *dst, jack_sample_t *src, size_t nframes, int channel, int chcount, int bits) { int srcidx; int dstidx; signed short *s16dst = (signed short*)dst; signed int *s32dst = (signed int*)dst; double *f64dst = (double*)dst; jack_sample_t scale; dstidx = channel; switch (bits) { case 16: scale = 0x7fff; for (srcidx = 0; srcidx < nframes; srcidx++) { s16dst[dstidx] = (signed short) (src[srcidx] >= 0.0f) ? (src[srcidx] * scale + 0.5f) : (src[srcidx] * scale - 0.5f); dstidx += chcount; } break; case 24: scale = 0x7fffff; for (srcidx = 0; srcidx < nframes; srcidx++) { s32dst[dstidx] = (signed int) (src[srcidx] >= 0.0f) ? (src[srcidx] * scale + 0.5f) : (src[srcidx] * scale - 0.5f); dstidx += chcount; } break; case 32: scale = 0x7fffffff; for (srcidx = 0; srcidx < nframes; srcidx++) { s32dst[dstidx] = (signed int) (src[srcidx] >= 0.0f) ? (src[srcidx] * scale + 0.5f) : (src[srcidx] * scale - 0.5f); dstidx += chcount; } break; case 64: for (srcidx = 0; srcidx < nframes; srcidx++) { f64dst[dstidx] = (double)src[srcidx]; dstidx += chcount; } break; } } static void set_fragment (int fd, size_t fragsize, unsigned int fragcount) { int fragsize_2p; int fragments; fragsize_2p = (int)(log (fragsize) / log (2.0) + 0.5); fragments = ((fragcount << 16) | (fragsize_2p & 0xffff)); if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &fragments) < 0) { jack_error ("OSS: failed to set fragment size: %s@%i, errno=%d", __FILE__, __LINE__, errno); } } static int get_fragment (int fd) { int fragsize; if (ioctl (fd, SNDCTL_DSP_GETBLKSIZE, &fragsize) < 0) { jack_error ("OSS: failed to get fragment size: %s@%i, errno=%d", __FILE__, __LINE__, errno); return 0; } return fragsize; } static void *io_thread(void *); /* jack driver interface */ static int oss_driver_attach (oss_driver_t *driver, jack_engine_t *engine) { int port_flags; unsigned int channel; char channel_name[64]; jack_port_t *port; jack_latency_range_t range; driver->engine = engine; set_period_size (driver, driver->period_size); if (engine->set_buffer_size (engine, driver->period_size)) { jack_error ("OSS: cannot set engine buffer size to %d (check MIDI)", driver->period_size); return -1; } engine->set_sample_rate (engine, driver->sample_rate); port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; for (channel = 0; channel < driver->capture_channels; channel++) { snprintf (channel_name, sizeof(channel_name), "capture_%u", channel + 1); port = jack_port_register (driver->client, channel_name, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); if (port == NULL) { jack_error ("OSS: cannot register port for %s: %s@%i", channel_name, __FILE__, __LINE__); break; } range.min = range.max = driver->period_size + driver->sys_in_latency; jack_port_set_latency_range (port, JackCaptureLatency, &range); driver->capture_ports = jack_slist_append (driver->capture_ports, port); } port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; for (channel = 0; channel < driver->playback_channels; channel++) { snprintf (channel_name, sizeof(channel_name), "playback_%u", channel + 1); port = jack_port_register (driver->client, channel_name, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); if (port == NULL) { jack_error ("OSS: cannot register port for %s: %s@%i", channel_name, __FILE__, __LINE__); break; } range.min = range.max = driver->period_size + driver->sys_out_latency; jack_port_set_latency_range (port, JackPlaybackLatency, &range); driver->playback_ports = jack_slist_append (driver->playback_ports, port); } jack_activate (driver->client); return 0; } static int oss_driver_detach (oss_driver_t *driver, jack_engine_t *engine) { JSList *node; if (driver->engine == NULL) { return -1; } /*jack_deactivate(driver->client);*/ /* ? */ node = driver->capture_ports; while (node != NULL) { jack_port_unregister (driver->client, ((jack_port_t*)node->data)); node = jack_slist_next (node); } jack_slist_free (driver->capture_ports); driver->capture_ports = NULL; node = driver->playback_ports; while (node != NULL) { jack_port_unregister (driver->client, ((jack_port_t*)node->data)); node = jack_slist_next (node); } jack_slist_free (driver->playback_ports); driver->playback_ports = NULL; driver->engine = NULL; return 0; } static int oss_driver_start (oss_driver_t *driver) { int flags = 0; int format; int channels; int samplerate; int infd = driver->infd; int outfd = driver->outfd; unsigned int period_size; size_t samplesize; size_t fragsize; const char *indev = driver->indev; const char *outdev = driver->outdev; switch (driver->bits) { case 24: case 32: samplesize = sizeof(int); break; case 64: samplesize = sizeof(double); break; case 16: default: samplesize = sizeof(short); break; } driver->trigger = 0; if (strcmp (indev, outdev) != 0) { if (driver->capture_channels > 0) { infd = open (indev, O_RDONLY | O_EXCL); if (infd < 0) { jack_error ( "OSS: failed to open input device %s: %s@%i, errno=%d", indev, __FILE__, __LINE__, errno); } #ifndef OSS_NO_COOKED_MODE ioctl (infd, SNDCTL_DSP_COOKEDMODE, &flags); #endif fragsize = driver->period_size * driver->capture_channels * samplesize; set_fragment (infd, fragsize, driver->nperiods); } else { infd = -1; } if (driver->playback_channels > 0) { outfd = open (outdev, O_WRONLY | O_EXCL); if (outfd < 0) { jack_error ( "OSS: failed to open output device %s: %s@%i, errno=%d", outdev, __FILE__, __LINE__, errno); } #ifndef OSS_NO_COOKED_MODE ioctl (outfd, SNDCTL_DSP_COOKEDMODE, &flags); #endif fragsize = driver->period_size * driver->playback_channels * samplesize; set_fragment (outfd, fragsize, driver->nperiods); } else { outfd = -1; } } else { if (driver->capture_channels != 0 && driver->playback_channels == 0) { infd = open (indev, O_RDWR | O_EXCL); outfd = -1; if (infd < 0) { jack_error ( "OSS: failed to open device %s: %s@%i, errno=%d", indev, __FILE__, __LINE__, errno); return -1; } #ifndef OSS_NO_COOKED_MODE ioctl (infd, SNDCTL_DSP_COOKEDMODE, &flags); #endif } else if (driver->capture_channels == 0 && driver->playback_channels != 0) { infd = -1; outfd = open (outdev, O_RDWR | O_EXCL); if (outfd < 0) { jack_error ( "OSS: failed to open device %s: %s@%i, errno=%d", outdev, __FILE__, __LINE__, errno); return -1; } #ifndef OSS_NO_COOKED_MODE ioctl (outfd, SNDCTL_DSP_COOKEDMODE, &flags); #endif } else { infd = outfd = open (indev, O_RDWR | O_EXCL); if (infd < 0) { jack_error ( "OSS: failed to open device %s: %s@%i, errno=%d", indev, __FILE__, __LINE__, errno); return -1; } #ifndef OSS_NO_COOKED_MODE ioctl (infd, SNDCTL_DSP_COOKEDMODE, &flags); #endif } if (infd >= 0 && outfd >= 0) { ioctl (outfd, SNDCTL_DSP_SETTRIGGER, &driver->trigger); driver->trigger = (PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT); if (ioctl (infd, SNDCTL_DSP_SETDUPLEX, 0) < 0) { if (errno != EINVAL) { /* Dont care */ jack_error ( "OSS: failed to enable full duplex for %s: %s@%i, errno=%d", indev, __FILE__, __LINE__, errno); } } } if (infd >= 0) { fragsize = driver->period_size * driver->capture_channels * samplesize; set_fragment (infd, fragsize, driver->nperiods); } if (outfd >= 0 && infd < 0) { fragsize = driver->period_size * driver->playback_channels * samplesize; set_fragment (outfd, fragsize, driver->nperiods); } } driver->infd = infd; driver->outfd = outfd; if (infd >= 0) { format = driver->format; if (ioctl (infd, SNDCTL_DSP_SETFMT, &format) < 0) { jack_error ( "OSS: failed to set format for %s: %s@%i, errno=%d", indev, __FILE__, __LINE__, errno); } channels = driver->capture_channels; if (ioctl (infd, SNDCTL_DSP_CHANNELS, &channels) < 0) { jack_error ( "OSS: failed to set channels for %s: %s@%i, errno=%d", indev, __FILE__, __LINE__, errno); } samplerate = driver->sample_rate; if (ioctl (infd, SNDCTL_DSP_SPEED, &samplerate) < 0) { jack_error ( "OSS: failed to set samplerate for %s: %s@%i, errno=%d", indev, __FILE__, __LINE__, errno); } jack_info ("oss_driver: %s : 0x%x/%i/%i (%i)", indev, format, channels, samplerate, get_fragment (infd)); period_size = get_fragment (infd) / samplesize / channels; if (period_size != driver->period_size && !driver->ignorehwbuf) { jack_info ("oss_driver: period size update: %u", period_size); driver->period_size = period_size; driver->period_usecs = ((double)driver->period_size / (double)driver->sample_rate) * 1e6; if (driver->engine->set_buffer_size (driver->engine, driver->period_size)) { jack_error ("OSS: cannot set engine buffer size to %d (check MIDI)", driver->period_size); return -1; } } } if (outfd >= 0 && infd != outfd) { format = driver->format; if (ioctl (outfd, SNDCTL_DSP_SETFMT, &format) < 0) { jack_error ( "OSS: failed to set format for %s: %s@%i, errno=%d", outdev, __FILE__, __LINE__, errno); } channels = driver->playback_channels; if (ioctl (outfd, SNDCTL_DSP_CHANNELS, &channels) < 0) { jack_error ( "OSS: failed to set channels for %s: %s@%i, errno=%d", outdev, __FILE__, __LINE__, errno); } samplerate = driver->sample_rate; if (ioctl (outfd, SNDCTL_DSP_SPEED, &samplerate) < 0) { jack_error ( "OSS: failed to set samplerate for %s: %s@%i, errno=%d", outdev, __FILE__, __LINE__, errno); } jack_info ("oss_driver: %s : 0x%x/%i/%i (%i)", outdev, format, channels, samplerate, get_fragment (outfd)); period_size = get_fragment (outfd) / samplesize / channels; if (period_size != driver->period_size && !driver->ignorehwbuf) { jack_info ("oss_driver: period size update: %u", period_size); driver->period_size = period_size; driver->period_usecs = ((double)driver->period_size / (double)driver->sample_rate) * 1e6; if (driver->engine->set_buffer_size (driver->engine, driver->period_size)) { jack_error ("OSS: cannot set engine buffer size to %d (check MIDI)", driver->period_size); return -1; } } } if (driver->capture_channels > 0) { driver->indevbufsize = driver->period_size * driver->capture_channels * samplesize; driver->indevbuf = malloc (driver->indevbufsize); if (driver->indevbuf == NULL) { jack_error ( "OSS: malloc() failed: %s@%i", __FILE__, __LINE__); return -1; } memset (driver->indevbuf, 0x00, driver->indevbufsize); } else { driver->indevbufsize = 0; driver->indevbuf = NULL; } if (driver->playback_channels > 0) { driver->outdevbufsize = driver->period_size * driver->playback_channels * samplesize; driver->outdevbuf = malloc (driver->outdevbufsize); if (driver->outdevbuf == NULL) { jack_error ("OSS: malloc() failed: %s@%i", __FILE__, __LINE__); return -1; } memset (driver->outdevbuf, 0x00, driver->outdevbufsize); } else { driver->outdevbufsize = 0; driver->outdevbuf = NULL; } jack_info ("oss_driver: indevbuf %zd B, outdevbuf %zd B", driver->indevbufsize, driver->outdevbufsize); pthread_mutex_init (&driver->mutex_in, NULL); pthread_mutex_init (&driver->mutex_out, NULL); # ifdef USE_BARRIER puts ("oss_driver: using barrier mode, (dual thread)"); pthread_barrier_init (&driver->barrier, NULL, 2); # else puts ("oss_driver: not using barrier mode, (single thread)"); # endif sem_init (&driver->sem_start, 0, 0); driver->run = 1; driver->threads = 0; if (infd >= 0) { if (jack_client_create_thread (NULL, &driver->thread_in, driver->engine->rtpriority, driver->engine->control->real_time, io_thread, driver) < 0) { jack_error ("OSS: jack_client_create_thread() failed: %s@%i", __FILE__, __LINE__); return -1; } driver->threads |= 1; } # ifdef USE_BARRIER if (outfd >= 0) { if (jack_client_create_thread (NULL, &driver->thread_out, driver->engine->rtpriority, driver->engine->control->real_time, io_thread, driver) < 0) { jack_error ("OSS: jack_client_create_thread() failed: %s@%i", __FILE__, __LINE__); return -1; } driver->threads |= 2; } # endif if (driver->threads & 1) { sem_post (&driver->sem_start); } if (driver->threads & 2) { sem_post (&driver->sem_start); } driver->last_periodtime = driver->engine->get_microseconds (); driver->next_periodtime = 0; driver->iodelay = 0.0F; return 0; } static int oss_driver_stop (oss_driver_t *driver) { void *retval; driver->run = 0; if (driver->threads & 1) { if (pthread_join (driver->thread_in, &retval) < 0) { jack_error ("OSS: pthread_join() failed: %s@%i", __FILE__, __LINE__); return -1; } } if (driver->threads & 2) { if (pthread_join (driver->thread_out, &retval) < 0) { jack_error ("OSS: pthread_join() failed: %s@%i", __FILE__, __LINE__); return -1; } } sem_destroy (&driver->sem_start); # ifdef USE_BARRIER pthread_barrier_destroy (&driver->barrier); # endif pthread_mutex_destroy (&driver->mutex_in); pthread_mutex_destroy (&driver->mutex_out); if (driver->outfd >= 0 && driver->outfd != driver->infd) { close (driver->outfd); driver->outfd = -1; } if (driver->infd >= 0) { close (driver->infd); driver->infd = -1; } if (driver->indevbuf != NULL) { free (driver->indevbuf); driver->indevbuf = NULL; } if (driver->outdevbuf != NULL) { free (driver->outdevbuf); driver->outdevbuf = NULL; } return 0; } static int oss_driver_read (oss_driver_t *driver, jack_nframes_t nframes) { int channel; jack_sample_t *portbuf; JSList *node; jack_port_t *port; if (!driver->run) { return 0; } if (nframes != driver->period_size) { jack_error ( "OSS: read failed nframes != period_size (%u/%u): %s@%i", nframes, driver->period_size, __FILE__, __LINE__); return -1; } pthread_mutex_lock (&driver->mutex_in); node = driver->capture_ports; channel = 0; while (node != NULL) { port = (jack_port_t*)node->data; if (jack_port_connected (port)) { portbuf = jack_port_get_buffer (port, nframes); copy_and_convert_in (portbuf, driver->indevbuf, nframes, channel, driver->capture_channels, driver->bits); } node = jack_slist_next (node); channel++; } pthread_mutex_unlock (&driver->mutex_in); return 0; } static int oss_driver_write (oss_driver_t *driver, jack_nframes_t nframes) { int channel; jack_sample_t *portbuf; JSList *node; jack_port_t *port; if (!driver->run) { return 0; } if (nframes != driver->period_size) { jack_error ( "OSS: write failed nframes != period_size (%u/%u): %s@%i", nframes, driver->period_size, __FILE__, __LINE__); return -1; } pthread_mutex_lock (&driver->mutex_out); node = driver->playback_ports; channel = 0; while (node != NULL) { port = (jack_port_t*)node->data; if (jack_port_connected (port)) { portbuf = jack_port_get_buffer (port, nframes); copy_and_convert_out (driver->outdevbuf, portbuf, nframes, channel, driver->playback_channels, driver->bits); } node = jack_slist_next (node); channel++; } pthread_mutex_unlock (&driver->mutex_out); return 0; } static int oss_driver_null_cycle (oss_driver_t *driver, jack_nframes_t nframes) { pthread_mutex_lock (&driver->mutex_in); memset (driver->indevbuf, 0x00, driver->indevbufsize); pthread_mutex_unlock (&driver->mutex_in); pthread_mutex_lock (&driver->mutex_out); memset (driver->outdevbuf, 0x00, driver->outdevbufsize); pthread_mutex_unlock (&driver->mutex_out); return 0; } static int oss_driver_bufsize (oss_driver_t *driver, jack_nframes_t nframes) { oss_driver_stop (driver); set_period_size (driver, nframes); if (driver->engine->set_buffer_size (driver->engine, driver->period_size)) { jack_error ("OSS: cannot set engine buffer size to %d (check MIDI)", driver->period_size); return -1; } jack_info ("oss_driver: period size update: %u", nframes); oss_driver_start (driver); return 0; } /* internal driver thread */ #ifdef USE_BARRIER static inline void synchronize (oss_driver_t *driver) { if (driver->threads == 3) { if (pthread_barrier_wait (&driver->barrier) == PTHREAD_BARRIER_SERIAL_THREAD) { driver_cycle (driver); } } else { driver_cycle (driver); } } #endif static void *io_thread (void *param) { size_t localsize; ssize_t io_res; void *localbuf; oss_driver_t *driver = (oss_driver_t*)param; sem_wait (&driver->sem_start); # ifdef USE_BARRIER if (pthread_self () == driver->thread_in) { localsize = driver->indevbufsize; localbuf = malloc (localsize); if (localbuf == NULL) { jack_error ("OSS: malloc() failed: %s@%i", __FILE__, __LINE__); return NULL; } while (driver->run) { io_res = read (driver->infd, localbuf, localsize); if (io_res < (ssize_t)localsize) { jack_error ( "OSS: read() failed: %s@%i, count=%d/%d, errno=%d", __FILE__, __LINE__, io_res, localsize, errno); break; } pthread_mutex_lock (&driver->mutex_in); memcpy (driver->indevbuf, localbuf, localsize); pthread_mutex_unlock (&driver->mutex_in); synchronize (driver); } free (localbuf); } else if (pthread_self () == driver->thread_out) { localsize = driver->outdevbufsize; localbuf = malloc (localsize); if (localbuf == NULL) { jack_error ("OSS: malloc() failed: %s@%i", __FILE__, __LINE__); return NULL; } if (driver->trigger) { /* don't care too much if this fails */ memset (localbuf, 0x00, localsize); write (driver->outfd, localbuf, localsize); ioctl (driver->outfd, SNDCTL_DSP_SETTRIGGER, &driver->trigger); } while (driver->run) { pthread_mutex_lock (&driver->mutex_out); memcpy (localbuf, driver->outdevbuf, localsize); pthread_mutex_unlock (&driver->mutex_out); io_res = write (driver->outfd, localbuf, localsize); if (io_res < (ssize_t)localsize) { jack_error ( "OSS: write() failed: %s@%i, count=%d/%d, errno=%d", __FILE__, __LINE__, io_res, localsize, errno); break; } synchronize (driver); } free (localbuf); } # else localsize = (driver->indevbufsize >= driver->outdevbufsize) ? driver->indevbufsize : driver->outdevbufsize; localbuf = malloc (localsize); if (localbuf == NULL) { jack_error ("OSS: malloc() failed: %s@%i", __FILE__, __LINE__); return NULL; } if (driver->trigger) { /* don't care too much if this fails */ memset (localbuf, 0x00, localsize); write (driver->outfd, localbuf, driver->outdevbufsize); ioctl (driver->outfd, SNDCTL_DSP_SETTRIGGER, &driver->trigger); } while (driver->run) { if (driver->playback_channels > 0) { pthread_mutex_lock (&driver->mutex_out); memcpy (localbuf, driver->outdevbuf, driver->outdevbufsize); pthread_mutex_unlock (&driver->mutex_out); io_res = write (driver->outfd, localbuf, driver->outdevbufsize); if (io_res < (ssize_t)driver->outdevbufsize) { jack_error ( "OSS: write() failed: %s@%i, count=%d/%d, errno=%d", __FILE__, __LINE__, io_res, driver->outdevbufsize, errno); break; } } if (driver->capture_channels > 0) { io_res = read (driver->infd, localbuf, driver->indevbufsize); if (io_res < (ssize_t)driver->indevbufsize) { jack_error ( "OSS: read() failed: %s@%i, count=%d/%d, errno=%d", __FILE__, __LINE__, io_res, driver->indevbufsize, errno); break; } pthread_mutex_lock (&driver->mutex_in); memcpy (driver->indevbuf, localbuf, driver->indevbufsize); pthread_mutex_unlock (&driver->mutex_in); } driver_cycle (driver); } free (localbuf); # endif return NULL; } /* jack driver published interface */ const char driver_client_name[] = "oss"; void driver_finish(jack_driver_t *); jack_driver_desc_t * driver_get_descriptor () { jack_driver_desc_t *desc; jack_driver_param_desc_t *params; desc = (jack_driver_desc_t*)calloc (1, sizeof(jack_driver_desc_t)); if (desc == NULL) { jack_error ("oss_driver: calloc() failed: %s@%i, errno=%d", __FILE__, __LINE__, errno); return NULL; } strcpy (desc->name, driver_client_name); desc->nparams = OSS_DRIVER_N_PARAMS; params = calloc (desc->nparams, sizeof(jack_driver_param_desc_t)); if (params == NULL) { jack_error ("oss_driver: calloc() failed: %s@%i, errno=%d", __FILE__, __LINE__, errno); return NULL; } memcpy (params, oss_params, desc->nparams * sizeof(jack_driver_param_desc_t)); desc->params = params; return desc; } jack_driver_t * driver_initialize (jack_client_t *client, JSList * params) { int bits = OSS_DRIVER_DEF_BITS; jack_nframes_t sample_rate = OSS_DRIVER_DEF_FS; jack_nframes_t period_size = OSS_DRIVER_DEF_BLKSIZE; jack_nframes_t in_latency = 0; jack_nframes_t out_latency = 0; unsigned int nperiods = OSS_DRIVER_DEF_NPERIODS; unsigned int capture_channels = OSS_DRIVER_DEF_INS; unsigned int playback_channels = OSS_DRIVER_DEF_OUTS; const JSList *pnode; const jack_driver_param_t *param; oss_driver_t *driver; driver = (oss_driver_t*)malloc (sizeof(oss_driver_t)); if (driver == NULL) { jack_error ("OSS: malloc() failed: %s@%i, errno=%d", __FILE__, __LINE__, errno); return NULL; } memset (driver, 0x00, sizeof(oss_driver_t)); jack_driver_init ((jack_driver_t*)driver); driver->attach = (JackDriverAttachFunction)oss_driver_attach; driver->detach = (JackDriverDetachFunction)oss_driver_detach; driver->start = (JackDriverStartFunction)oss_driver_start; driver->stop = (JackDriverStopFunction)oss_driver_stop; driver->read = (JackDriverReadFunction)oss_driver_read; driver->write = (JackDriverWriteFunction)oss_driver_write; driver->null_cycle = (JackDriverNullCycleFunction) oss_driver_null_cycle; driver->bufsize = (JackDriverBufSizeFunction)oss_driver_bufsize; driver->indev = NULL; driver->outdev = NULL; driver->ignorehwbuf = 0; driver->trigger = 0; pnode = params; while (pnode != NULL) { param = (const jack_driver_param_t*)pnode->data; switch (param->character) { case 'r': sample_rate = param->value.ui; break; case 'p': period_size = param->value.ui; break; case 'n': nperiods = param->value.ui; break; case 'w': bits = param->value.i; break; case 'i': capture_channels = param->value.ui; break; case 'o': playback_channels = param->value.ui; break; case 'C': driver->indev = strdup (param->value.str); break; case 'P': driver->outdev = strdup (param->value.str); break; case 'b': driver->ignorehwbuf = 1; break; case 'I': in_latency = param->value.ui; break; case 'O': out_latency = param->value.ui; break; } pnode = jack_slist_next (pnode); } driver->sample_rate = sample_rate; driver->period_size = period_size; driver->nperiods = nperiods; driver->bits = bits; driver->capture_channels = capture_channels; driver->playback_channels = playback_channels; driver->sys_in_latency = in_latency; driver->sys_out_latency = out_latency; /* setting driver->period_usecs & co is delayed until attach */ driver->finish = driver_finish; if (driver->indev == NULL) { driver->indev = strdup (OSS_DRIVER_DEF_DEV); } if (driver->outdev == NULL) { driver->outdev = strdup (OSS_DRIVER_DEF_DEV); } driver->infd = -1; driver->outfd = -1; switch (driver->bits) { # ifndef OSS_ENDIAN # ifdef __GNUC__ # if (defined(__i386__) || defined(__alpha__) || defined(__arm__) || defined(__x86_64__) || (defined(__sh__) && !defined(__LITTLE_ENDIAN__))) # define OSS_LITTLE_ENDIAN 1234 # define OSS_ENDIAN OSS_LITTLE_ENDIAN # else # define OSS_BIG_ENDIAN 4321 # define OSS_ENDIAN OSS_BIG_ENDIAN # endif # else /* __GNUC__ */ # if (defined(_AIX) || defined(AIX) || defined(sparc) || defined(__hppa) || defined(PPC) || defined(__powerpc__) && !defined(i386) && !defined(__i386) && !defined(__i386__)) # define OSS_BIG_ENDIAN 4321 # define OSS_ENDIAN OSS_BIG_ENDIAN # else # define OSS_LITTLE_ENDIAN 1234 # define OSS_ENDIAN OSS_LITTLE_ENDIAN # endif # endif /* __GNUC__ */ # endif /* OSS_ENDIAN */ # if (OSS_ENDIAN == 1234) /* little-endian architectures */ case 24: /* little-endian LSB aligned 24-bits in 32-bits integer */ driver->format = 0x00008000; break; case 32: /* little-endian 32-bit integer */ driver->format = 0x00001000; break; case 64: /* native-endian 64-bit float */ driver->format = 0x00004000; break; case 16: /* little-endian 16-bit integer */ default: driver->format = 0x00000010; break; /* big-endian architectures */ # else case 24: /* big-endian LSB aligned 24-bits in 32-bits integer */ break; driver->format = 0x00010000; case 32: /* big-endian 32-bit integer */ driver->format = 0x00002000; break; case 64: /* native-endian 64-bit float */ driver->format = 0x00004000; break; case 16: /* big-endian 16-bit integer */ default: driver->format = 0x00000020; # endif } driver->indevbuf = driver->outdevbuf = NULL; driver->capture_ports = NULL; driver->playback_ports = NULL; driver->engine = NULL; driver->client = client; return (jack_driver_t*)driver; } void driver_finish (jack_driver_t *driver) { oss_driver_t *oss_driver = (oss_driver_t*)driver; oss_driver = (oss_driver_t*)driver; if (oss_driver->indev != NULL) { free (oss_driver->indev); } if (oss_driver->outdev != NULL) { free (oss_driver->outdev); } free (driver); }