Update of /cvsroot/alsa/alsa-oss/alsa
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15895

Added Files:
        alsa-oss-emul.h mixer.c pcm.c 
Log Message:
Added missing files

--- NEW FILE: alsa-oss-emul.h ---
#ifndef __ALSA_OSS_EMUL_H
/*
 *  OSS Redirector
 *  Copyright (c) by Jaroslav Kysela <[EMAIL PROTECTED]>
 *
 *
 *   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
 *
 */

#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define NEW_MACRO_VARARGS
#endif

#if 1
#define DEBUG_POLL
#define DEBUG_SELECT
#ifdef NEW_MACRO_VARARGS
#define DEBUG(...) do { if (alsa_oss_debug) fprintf(stderr, __VA_ARGS__); } while (0)
#else /* !NEW_MACRO_VARARGS */
#define DEBUG(args...) do { if (alsa_oss_debug) fprintf(stderr, ##args); } while (0)
#endif
#else
#ifdef NEW_MACRO_VARARGS
#define DEBUG(...)
#else /* !NEW_MACRO_VARARGS */
#define DEBUG(args...)
#endif
#endif

#define OSS_MAJOR               14
#define OSS_DEVICE_MIXER        0
#define OSS_DEVICE_SEQUENCER    1
#define OSS_DEVICE_MIDI         2
#define OSS_DEVICE_DSP          3
#define OSS_DEVICE_AUDIO        4
#define OSS_DEVICE_DSPW         5
#define OSS_DEVICE_SNDSTAT      6
#define OSS_DEVICE_MUSIC        8
#define OSS_DEVICE_DMMIDI       9
#define OSS_DEVICE_DMFM         10
#define OSS_DEVICE_AMIXER       11
#define OSS_DEVICE_ADSP         12
#define OSS_DEVICE_AMIDI        13
#define OSS_DEVICE_ADMMIDI      14

#define OSS_WAIT_EVENT_READ     (1<<0)
#define OSS_WAIT_EVENT_WRITE    (1<<1)
#define OSS_WAIT_EVENT_ERROR    (1<<2)

extern int lib_oss_pcm_open(const char *pathname, int flags, ...);
extern int lib_oss_pcm_close(int fd);
extern int lib_oss_pcm_nonblock(int fd, int nonblock);
extern ssize_t lib_oss_pcm_read(int fd, void *buf, size_t count);
extern ssize_t lib_oss_pcm_write(int fd, const void *buf, size_t count);
extern void * lib_oss_pcm_mmap(void *start, size_t length, int prot, int flags, int 
fd, off_t offset);
extern int lib_oss_pcm_munmap(void *start, size_t length);
extern int lib_oss_pcm_ioctl(int fd, unsigned long int request, ...);
extern int lib_oss_pcm_select_prepare(int fd, fd_set *readfds, fd_set *writefds, 
fd_set *exceptfds);
extern int lib_oss_pcm_select_result(int fd, fd_set *readfds, fd_set *writefds, fd_set 
*exceptfds);
extern int lib_oss_pcm_poll_fds(int fd);
extern int lib_oss_pcm_poll_prepare(int fd, struct pollfd *ufds);
extern int lib_oss_pcm_poll_result(int fd, struct pollfd *ufds);

extern int lib_oss_mixer_open(const char *pathname, int flags, ...);
extern int lib_oss_mixer_close(int fd);
extern int lib_oss_mixer_ioctl(int fd, unsigned long int request, ...);

extern int alsa_oss_debug;
extern snd_output_t *alsa_oss_debug_out;

#endif /* __ALSA_OSS_EMUL_H */

--- NEW FILE: mixer.c ---
/*
 *  OSS -> ALSA compatibility layer
 *  Copyright (c) by Abramo Bagnara <[EMAIL PROTECTED]>
 *
 *
 *   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
 *
 */

#define _GNU_SOURCE

#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <sys/select.h>
#include <sys/mman.h>
#include <stdarg.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <assert.h>
#include <linux/soundcard.h>
#include <alsa/asoundlib.h>

#include "alsa-oss-emul.h"

typedef struct _oss_mixer {
        int fileno;
        snd_mixer_t *mix;
        unsigned int modify_counter;
        snd_mixer_elem_t *elems[SOUND_MIXER_NRDEVICES];
        struct _oss_mixer *next;
} oss_mixer_t;

static oss_mixer_t *mixer_fds = NULL;

static oss_mixer_t *look_for_fd(int fd)
{
        oss_mixer_t *result = mixer_fds;
        while (result) {
                if (result->fileno == fd)
                        return result;
                result = result->next;
        }
        return NULL;
}

static void insert_fd(oss_mixer_t *xfd)
{
        xfd->next = mixer_fds;
        mixer_fds = xfd;
}

static void remove_fd(oss_mixer_t *xfd)
{
        oss_mixer_t *result = mixer_fds, *prev = NULL;
        while (result) {
                if (result == xfd) {
                        if (prev == NULL)
                                mixer_fds = xfd->next;
                        else
                                prev->next = xfd->next;
                        return;
                }
                prev = result;
                result = result->next;
        }
        assert(0);
}

static int oss_mixer_dev(const char *name, unsigned int index)
{
        static struct {
                char *name;
                unsigned int index;
        } id[SOUND_MIXER_NRDEVICES] = {
                [SOUND_MIXER_VOLUME] = { "Master", 0 },
                [SOUND_MIXER_BASS] = { "Tone Control - Bass", 0 },
                [SOUND_MIXER_TREBLE] = { "Tone Control - Treble", 0 },
                [SOUND_MIXER_SYNTH] = { "Synth", 0 },
                [SOUND_MIXER_PCM] = { "PCM", 0 },
                [SOUND_MIXER_SPEAKER] = { "PC Speaker", 0 },
                [SOUND_MIXER_LINE] = { "Line", 0 },
                [SOUND_MIXER_MIC] = { "Mic", 0 },
                [SOUND_MIXER_CD] = { "CD", 0 },
                [SOUND_MIXER_IMIX] = { "Monitor Mix", 0 },
                [SOUND_MIXER_ALTPCM] = { "PCM", 1 },
                [SOUND_MIXER_RECLEV] = { "-- nothing --", 0 },
                [SOUND_MIXER_IGAIN] = { "Capture", 0 },
                [SOUND_MIXER_OGAIN] = { "Playback", 0 },
                [SOUND_MIXER_LINE1] = { "Aux", 0 },
                [SOUND_MIXER_LINE2] = { "Aux", 1 },
                [SOUND_MIXER_LINE3] = { "Aux", 2 },
                [SOUND_MIXER_DIGITAL1] = { "Digital", 0 },
                [SOUND_MIXER_DIGITAL2] = { "Digital", 1 },
                [SOUND_MIXER_DIGITAL3] = { "Digital", 2 },
                [SOUND_MIXER_PHONEIN] = { "Phone", 0 },
                [SOUND_MIXER_PHONEOUT] = { "Phone", 1 },
                [SOUND_MIXER_VIDEO] = { "Video", 0 },
                [SOUND_MIXER_RADIO] = { "Radio", 0 },
                [SOUND_MIXER_MONITOR] = { "Monitor", 0 },
        };
        unsigned int k;
        for (k = 0; k < SOUND_MIXER_NRDEVICES; ++k) {
                if (index == id[k].index &&
                    strcmp(name, id[k].name) == 0)
                        return k;
        }
        return -1;
}

int lib_oss_mixer_close(int fd)
{
        int err, result = 0;
        oss_mixer_t *mixer = look_for_fd(fd);
        err = snd_mixer_close(mixer->mix);
        if (err < 0)
                result = err;
        remove_fd(mixer);
        free(mixer);
        if (result < 0) {
                errno = -result;
                result = -1;
        }
        close(fd);
        DEBUG("close(%d) -> %d", fd, result);
        if (result < 0)
                DEBUG("(errno=%d)\n", errno);
        else
                DEBUG("\n");
        return 0;
}

static int oss_mixer_elem_callback(snd_mixer_elem_t *elem, unsigned int mask)
{
        oss_mixer_t *mixer = snd_mixer_elem_get_callback_private(elem);
        if (mask == SND_CTL_EVENT_MASK_REMOVE) {
                int idx = oss_mixer_dev(snd_mixer_selem_get_name(elem),
                                        snd_mixer_selem_get_index(elem));
                if (idx >= 0)
                        mixer->elems[idx] = 0;
                return 0;
        }
        if (mask & SND_CTL_EVENT_MASK_VALUE) {
                mixer->modify_counter++;
        }
        return 0;
}

static int oss_mixer_callback(snd_mixer_t *mixer, unsigned int mask, 
                              snd_mixer_elem_t *elem)
{
        if (mask & SND_CTL_EVENT_MASK_ADD) {
                oss_mixer_t *mix = snd_mixer_get_callback_private(mixer);
                int idx = oss_mixer_dev(snd_mixer_selem_get_name(elem),
                                        snd_mixer_selem_get_index(elem));
                if (idx >= 0) {
                        mix->elems[idx] = elem;
                        snd_mixer_selem_set_playback_volume_range(elem, 0, 100);
                        snd_mixer_selem_set_capture_volume_range(elem, 0, 100);
                        snd_mixer_elem_set_callback(elem, oss_mixer_elem_callback);
                        snd_mixer_elem_set_callback_private(elem, mix);
                }
        }
        return 0;
}

static int oss_mixer_open(int card, int device, int oflag, mode_t mode 
ATTRIBUTE_UNUSED)
{
        oss_mixer_t *mixer;
        int fd = -1;
        int result;
        char name[64];

        char *s = getenv("ALSA_OSS_DEBUG");
        if (s) {
                alsa_oss_debug = 1;
                if (alsa_oss_debug_out == NULL) {
                        if (snd_output_stdio_attach(&alsa_oss_debug_out, stderr, 0) < 
0)
                                alsa_oss_debug_out = NULL;
                }
        }
        switch (device) {
        case OSS_DEVICE_MIXER:
                sprintf(name, "mixer%d", card);
                break;
        case OSS_DEVICE_AMIXER:
                sprintf(name, "amixer%d", card);
                break;
        default:
                errno = ENODEV;
                return -1;
        }
        switch (oflag & O_ACCMODE) {
        case O_RDONLY:
        case O_WRONLY:
        case O_RDWR:
                break;
        default:
                errno = EINVAL;
                return -1;
        }
        fd = open("/dev/null", oflag & O_ACCMODE);
        assert(fd >= 0);
        mixer = calloc(1, sizeof(oss_mixer_t));
        if (!mixer) {
                errno = -ENOMEM;
                return -1;
        }
        result = snd_mixer_open(&mixer->mix, 0);
        if (result < 0)
                goto _error;
        result = snd_mixer_attach(mixer->mix, name);
        if (result < 0) {
                /* try to open the default mixer as fallback */
                if (card == 0)
                        strcpy(name, "default");
                else
                        sprintf(name, "hw:%d", card);
                result = snd_mixer_attach(mixer->mix, name);
                if (result < 0)
                        goto _error1;
        }
        result = snd_mixer_selem_register(mixer->mix, NULL, NULL);
        if (result < 0)
                goto _error1;
        snd_mixer_set_callback(mixer->mix, oss_mixer_callback);
        snd_mixer_set_callback_private(mixer->mix, mixer);
        result = snd_mixer_load(mixer->mix);
        if (result < 0)
                goto _error1;
        mixer->fileno = result;
        insert_fd(mixer);
        return fd;
 _error1:
        snd_mixer_close(mixer->mix);
 _error:
        close(fd);
        errno = -result;
        return -1;
}

static int oss_mixer_read_recsrc(oss_mixer_t *mixer, unsigned int *ret)
{
        unsigned int mask = 0;
        unsigned int k;
        int err = 0;
        for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
                snd_mixer_elem_t *elem = mixer->elems[k];
                if (elem && 
                    snd_mixer_selem_has_capture_switch(elem)) {
                        int sw;
                        err = snd_mixer_selem_get_capture_switch(elem, 
SND_MIXER_SCHN_FRONT_LEFT, &sw);
                        if (err < 0)
                                break;
                        if (sw)
                                mask |= 1 << k;
                }
        }
        *ret = mask;
        return err;
}


int lib_oss_mixer_ioctl(int fd, unsigned long cmd, ...)
{
        int err = 0;
        va_list args;
        void *arg;
        oss_mixer_t *mixer = look_for_fd(fd);
        snd_mixer_t *mix;
        unsigned int dev;

        if (mixer == NULL) {
                errno = ENODEV;
                return -1;
        }
        mix = mixer->mix;
        va_start(args, cmd);
        arg = va_arg(args, void *);
        va_end(args);
        DEBUG("ioctl(%d, ", fd);
        switch (cmd) {
        case OSS_GETVERSION:
                *(int*)arg = SOUND_VERSION;
                DEBUG("OSS_GETVERSION, %p) -> [%d]\n", arg, *(int*)arg);
                break;
        case SOUND_MIXER_INFO:
        {
                mixer_info *info = arg;
                snd_mixer_handle_events(mix);
                strcpy(info->id, "alsa-oss");
                strcpy(info->name, "alsa-oss");
                info->modify_counter = mixer->modify_counter;
                DEBUG("SOUND_MIXER_INFO, %p) -> {%s, %s, %d}\n", info, info->id, 
info->name, info->modify_counter);
                break;
        }
        case SOUND_OLD_MIXER_INFO:
        {
                _old_mixer_info *info = arg;
                strcpy(info->id, "alsa-oss");
                strcpy(info->name, "alsa-oss");
                DEBUG("SOUND_OLD_MIXER_INFO, %p) -> {%s, %s}\n", info, info->id, 
info->name);
                break;
        }
        case SOUND_MIXER_WRITE_RECSRC:
        {
                unsigned int k, mask = *(unsigned int *) arg;
                unsigned int old;
                int excl = 0;
                DEBUG("SOUND_MIXER_WRITE_RECSRC, %p) -> [%x]", arg, mask);
                err = oss_mixer_read_recsrc(mixer, &old);
                if (err < 0)
                        break;
                for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
                        snd_mixer_elem_t *elem = mixer->elems[k];
                        if (elem && 
                            snd_mixer_selem_has_capture_switch(elem)) {
                                if (!excl &&
                                    snd_mixer_selem_has_capture_switch_exclusive(elem) 
&&
                                    mask & ~old) {
                                        mask &= ~old;
                                        excl = 1;
                                }
                                err = snd_mixer_selem_set_capture_switch_all(elem, 
!!(mask & 1 << k));
                                if (err < 0)
                                        break;
                        }
                }
                if (err < 0)
                        break;
                goto __read_recsrc;
        }
        case SOUND_MIXER_READ_RECSRC:
        {
                unsigned int mask;
                DEBUG("SOUND_MIXER_READ_RECSRC, %p) ->", arg);
        __read_recsrc:
                err = oss_mixer_read_recsrc(mixer, &mask);
                *(int *)arg = mask;
                DEBUG(" [%x]\n", mask);
                break;
        }
        case SOUND_MIXER_READ_DEVMASK:
        {
                int k, mask = 0;
                for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
                        snd_mixer_elem_t *elem = mixer->elems[k];
                        if (elem && 
                            snd_mixer_selem_has_playback_volume(elem))
                                mask |= 1 << k;
                }
                *(int *)arg = mask;
                DEBUG("SOUND_MIXER_READ_DEVMASK, %p) -> [%x]\n", arg, mask);
                break;
        }
        case SOUND_MIXER_READ_RECMASK:
        {
                int k, mask = 0;
                for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
                        snd_mixer_elem_t *elem = mixer->elems[k];
                        if (elem &&
                            snd_mixer_selem_has_capture_switch(elem))
                                mask |= 1 << k;
                }
                *(int *)arg = mask;
                DEBUG("SOUND_MIXER_READ_RECMASK, %p) -> [%x]\n", arg, mask);
                break;
        }
        case SOUND_MIXER_READ_STEREODEVS:
        {
                int k, mask = 0;
                for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
                        snd_mixer_elem_t *elem = mixer->elems[k];
                        if (elem && 
                            snd_mixer_selem_has_playback_volume(elem) &&
                            !snd_mixer_selem_is_playback_mono(elem))
                                mask |= 1 << k;
                }
                *(int *)arg = mask;
                DEBUG("SOUND_MIXER_READ_STEREODEVS, %p) -> [%x]\n", arg, mask);
                break;
        }
        case SOUND_MIXER_READ_CAPS:
        {
                int k;
                *(int *)arg = 0;
                for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
                        snd_mixer_elem_t *elem = mixer->elems[k];
                        if (elem && 
                            snd_mixer_selem_has_capture_switch_exclusive(elem)) {
                                * (int*) arg = SOUND_CAP_EXCL_INPUT;
                                break;
                        }
                }
                DEBUG("SOUND_MIXER_READ_CAPS, %p) -> [%x]\n", arg, *(int*) arg);
                break;
        }
        default:
                if (cmd >= MIXER_WRITE(0) && cmd < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) 
{
                        snd_mixer_elem_t *elem;
                        long lvol, rvol;
                        dev = cmd & 0xff;
                        lvol = *(int *)arg & 0xff;
                        if (lvol > 100)
                                lvol = 100;
                        rvol = (*(int *)arg >> 8) & 0xff;
                        if (rvol > 100)
                                rvol = 100;
                        DEBUG("SOUND_MIXER_WRITE[%d], %p) -> {%ld, %ld}", dev, arg, 
lvol, rvol);
                        elem = mixer->elems[dev];
                        if (!elem) {
                                err = -EINVAL;
                                break;
                        }
                        if (!snd_mixer_selem_has_playback_volume(elem)) {
                                err = -EINVAL;
                                break;
                        }
                        err = snd_mixer_selem_set_playback_volume(elem, 
SND_MIXER_SCHN_FRONT_LEFT, lvol);
                        if (err < 0) 
                                break;
                        if (snd_mixer_selem_is_playback_mono(elem)) {
                                if (snd_mixer_selem_has_playback_switch(elem))
                                        err = 
snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0);
                                if (err < 0) 
                                        break;
                        } else {
                                err = snd_mixer_selem_set_playback_volume(elem, 
SND_MIXER_SCHN_FRONT_RIGHT, rvol);
                                if (err < 0) 
                                        break;
                                if (snd_mixer_selem_has_playback_switch(elem)) {
                                        if 
(snd_mixer_selem_has_playback_switch_joined(elem))
                                                err = 
snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0 || rvol 
!= 0);
                                        else {
                                                err = 
snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0);
                                                if (err < 0) 
                                                        break;
                                                err = 
snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, rvol != 0);
                                                if (err < 0) 
                                                        break;
                                        }
                                }
                        }
                        if (!snd_mixer_selem_has_capture_volume(elem))
                                break;
                        err = snd_mixer_selem_set_capture_volume(elem, 
SND_MIXER_SCHN_FRONT_LEFT, lvol);
                        if (err < 0) 
                                break;
                        if (!snd_mixer_selem_is_capture_mono(elem)) {
                                err = snd_mixer_selem_set_capture_volume(elem, 
SND_MIXER_SCHN_FRONT_RIGHT, rvol);
                                if (err < 0) 
                                        break;
                        }
                        goto __read;
                }
                if (cmd >= MIXER_READ(0) && cmd < MIXER_READ(SOUND_MIXER_NRDEVICES)) {
                        snd_mixer_elem_t *elem;
                        long lvol, rvol;
                        int sw;
                        dev = cmd & 0xff;
                        DEBUG("SOUND_MIXER_READ[%d], %p) ->", dev, arg);
                __read:
                        elem = mixer->elems[dev];
                        if (!elem) {
                                err = -EINVAL;
                                break;
                        }
                        if (!snd_mixer_selem_has_playback_volume(elem)) {
                                err = -EINVAL;
                                break;
                        }
                        err = snd_mixer_selem_get_playback_switch(elem, 
SND_MIXER_SCHN_FRONT_LEFT, &sw);
                        if (err < 0) 
                                break;
                        if (sw) {
                                err = snd_mixer_selem_get_playback_volume(elem, 
SND_MIXER_SCHN_FRONT_LEFT, &lvol);
                                if (err < 0) 
                                        break;
                        } else
                                lvol = 0;
                        if (snd_mixer_selem_is_playback_mono(elem)) {
                                rvol = lvol;
                        } else {
                                err = snd_mixer_selem_get_playback_switch(elem, 
SND_MIXER_SCHN_FRONT_RIGHT, &sw);
                                if (err < 0) 
                                        break;
                                if (sw) {
                                        err = 
snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &rvol);
                                        if (err < 0) 
                                                break;
                                } else
                                        rvol = 0;
                        }
                        * (int*) arg = lvol | (rvol << 8);
                        DEBUG("{%ld, %ld}\n", lvol, rvol);
                        break;
                }
                DEBUG("%lx, %p)\n", cmd, arg);
                err = -ENXIO;
                break;
        }
        if (err >= 0)
                return 0;
        errno = -err;
        return -1;
}

static void error_handler(const char *file ATTRIBUTE_UNUSED,
                          int line ATTRIBUTE_UNUSED,
                          const char *func ATTRIBUTE_UNUSED,
                          int err ATTRIBUTE_UNUSED,
                          const char *fmt ATTRIBUTE_UNUSED,
                          ...)
{
        /* suppress the error message from alsa-lib */
}

int lib_oss_mixer_open(const char *file, int oflag, ...)
{
        int result;
        int minor, card, device;
        struct stat s;
        mode_t mode;
        va_list args;
        va_start(args, oflag);
        mode = va_arg(args, mode_t);
        va_end(args);
        result = stat(file, &s);
        if (result < 0) {
                if (!strncmp(file, "/dev/mixer", 10))
                        minor = (atoi(file + 10) << 4) | OSS_DEVICE_MIXER;
                else if (!strncmp(file, "/dev/amixer", 11))
                        minor = (atoi(file + 11) << 4) | OSS_DEVICE_AMIXER;
                else {
                        errno = ENOENT;
                        return -1;
                }
        } else {
                if (!S_ISCHR(s.st_mode) || ((s.st_rdev >> 8) & 0xff) != OSS_MAJOR) {
                        errno = ENOENT;
                        return -1;
                }
                minor = s.st_rdev & 0xff;
        }
        if (! alsa_oss_debug)
                snd_lib_error_set_handler(error_handler);
        card = minor >> 4;
        device = minor & 0x0f;
        switch (device) {
        case OSS_DEVICE_MIXER:
        case OSS_DEVICE_AMIXER:
                result = oss_mixer_open(card, device, oflag, mode);
                DEBUG("open(\"%s\", %d, %d) -> %d\n", file, oflag, mode, result);
                return result;
        default:
                errno = ENOENT;
                return -1;
        }
}

--- NEW FILE: pcm.c ---
/*
 *  OSS -> ALSA compatibility layer
 *  Copyright (c) by Abramo Bagnara <[EMAIL PROTECTED]>
 *
 *
 *   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
 *
 */

#define _GNU_SOURCE

#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <sys/select.h>
#include <sys/mman.h>
#include <stdarg.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#include <assert.h>
#include <linux/soundcard.h>
#include <alsa/asoundlib.h>

#include "alsa-oss-emul.h"

snd_pcm_uframes_t _snd_pcm_boundary(snd_pcm_t *pcm);
snd_pcm_uframes_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm);

int alsa_oss_debug = 0;
snd_output_t *alsa_oss_debug_out = NULL;

typedef struct {
        snd_pcm_t *pcm;
        size_t frame_bytes;
        struct {
                snd_pcm_uframes_t period_size;
                snd_pcm_uframes_t buffer_size;
                snd_pcm_uframes_t boundary;
                snd_pcm_uframes_t old_hw_ptr;
                size_t mmap_buffer_bytes;
                size_t mmap_period_bytes;
        } alsa;
        struct {
                snd_pcm_uframes_t period_size;
                unsigned int periods;
                snd_pcm_uframes_t buffer_size;
                size_t bytes;
        } oss;
        unsigned int stopped:1;
        void *mmap_buffer;
        size_t mmap_bytes;
        snd_pcm_channel_area_t *mmap_areas;
        snd_pcm_uframes_t mmap_advance;
} oss_dsp_stream_t;

typedef struct {
        unsigned int channels;
        unsigned int rate;
        unsigned int oss_format;
        snd_pcm_format_t format;
        unsigned int fragshift;
        unsigned int maxfrags;
        unsigned int subdivision;
        oss_dsp_stream_t streams[2];
} oss_dsp_t;

typedef struct fd {
        int fileno;
        oss_dsp_t *dsp;
        void *mmap_area;
        struct fd *next;
} fd_t;

static fd_t *pcm_fds = NULL;


static fd_t *look_for_fd(int fd)
{
        fd_t *result = pcm_fds;
        while (result) {
                if (result->fileno == fd)
                        return result;
                result = result->next;
        }
        return NULL;
}

static inline oss_dsp_t *look_for_dsp(int fd)
{
        fd_t *xfd = look_for_fd(fd);
        return xfd ? xfd->dsp : NULL;
}

static inline oss_dsp_t *look_for_mmap_addr(void * addr)
{
        fd_t *result = pcm_fds;
        while (result) {
                if (result->mmap_area == addr)
                        return result->dsp ? result->dsp : NULL;
                result = result->next;
        }
        return NULL;
}

static void insert_fd(fd_t *xfd)
{
        xfd->next = pcm_fds;
        pcm_fds = xfd;
}

static void remove_fd(fd_t *xfd)
{
        fd_t *result = pcm_fds, *prev = NULL;
        while (result) {
                if (result == xfd) {
                        if (prev == NULL)
                                pcm_fds = xfd->next;
                        else
                                prev->next = xfd->next;
                        return;
                }
                prev = result;
                result = result->next;
        }
        assert(0);
}

static unsigned int ld2(u_int32_t v)
{
        unsigned r = 0;

        if (v >= 0x10000) {
                v >>= 16;
                r += 16;
        }
        if (v >= 0x100) {
                v >>= 8;
                r += 8;
        }
        if (v >= 0x10) {
                v >>= 4;
                r += 4;
        }
        if (v >= 4) {
                v >>= 2;
                r += 2;
        }
        if (v >= 2)
                r++;
        return r;
}

static snd_pcm_format_t oss_format_to_alsa(int format)
{
        switch (format) {
        case AFMT_MU_LAW:       return SND_PCM_FORMAT_MU_LAW;
        case AFMT_A_LAW:        return SND_PCM_FORMAT_A_LAW;
        case AFMT_IMA_ADPCM:    return SND_PCM_FORMAT_IMA_ADPCM;
        case AFMT_U8:           return SND_PCM_FORMAT_U8;
        case AFMT_S16_LE:       return SND_PCM_FORMAT_S16_LE;
        case AFMT_S16_BE:       return SND_PCM_FORMAT_S16_BE;
        case AFMT_S8:           return SND_PCM_FORMAT_S8;
        case AFMT_U16_LE:       return SND_PCM_FORMAT_U16_LE;
        case AFMT_U16_BE:       return SND_PCM_FORMAT_U16_BE;
        case AFMT_MPEG:         return SND_PCM_FORMAT_MPEG;
        default:                return SND_PCM_FORMAT_U8;
        }
}

static int alsa_format_to_oss(snd_pcm_format_t format)
{
        switch (format) {
        case SND_PCM_FORMAT_MU_LAW:     return AFMT_MU_LAW;
        case SND_PCM_FORMAT_A_LAW:      return AFMT_A_LAW;
        case SND_PCM_FORMAT_IMA_ADPCM:  return AFMT_IMA_ADPCM;
        case SND_PCM_FORMAT_U8:         return AFMT_U8;
        case SND_PCM_FORMAT_S16_LE:     return AFMT_S16_LE;
        case SND_PCM_FORMAT_S16_BE:     return AFMT_S16_BE;
        case SND_PCM_FORMAT_S8:         return AFMT_S8;
        case SND_PCM_FORMAT_U16_LE:     return AFMT_U16_LE;
        case SND_PCM_FORMAT_U16_BE:     return AFMT_U16_BE;
        case SND_PCM_FORMAT_MPEG:       return AFMT_MPEG;
        default:                        return -EINVAL;
        }
}

static int oss_dsp_hw_params(oss_dsp_t *dsp)
{
        int k;
        for (k = 1; k >= 0; --k) {
                oss_dsp_stream_t *str = &dsp->streams[k];
                snd_pcm_t *pcm = str->pcm;
                snd_pcm_hw_params_t *hw;
                int err;
                unsigned int rate, periods_min;
                if (!pcm)
                        continue;
                str->frame_bytes = snd_pcm_format_physical_width(dsp->format) * 
dsp->channels / 8;
                snd_pcm_hw_params_alloca(&hw);
                snd_pcm_hw_params_any(pcm, hw);
                dsp->format = oss_format_to_alsa(dsp->oss_format);

                err = snd_pcm_hw_params_set_format(pcm, hw, dsp->format);
                if (err < 0)
                        return err;
                err = snd_pcm_hw_params_set_channels(pcm, hw, dsp->channels);
                if (err < 0)
                        return err;
                rate = dsp->rate;
                err = snd_pcm_hw_params_set_rate_near(pcm, hw, &rate, 0);
                if (err < 0)
                        return err;
#if 0
                err = snd_pcm_hw_params_set_periods_integer(pcm, hw);
                if (err < 0)
                        return err;
#endif

                if (str->mmap_buffer) {
                        snd_pcm_access_mask_t *mask;
                        snd_pcm_access_mask_alloca(&mask);
                        snd_pcm_access_mask_any(mask);
                        snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
                        snd_pcm_access_mask_set(mask, 
SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
                        snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
                        err = snd_pcm_hw_params_set_access_mask(pcm, hw, mask);
                        if (err < 0)
                                return err;
                        err = snd_pcm_hw_params_set_period_size(pcm, hw, 
str->alsa.mmap_period_bytes / str->frame_bytes, 0);
                        if (err < 0)
                                return err;
                        err = snd_pcm_hw_params_set_buffer_size(pcm, hw, 
str->alsa.mmap_buffer_bytes / str->frame_bytes);
                        if (err < 0)
                                return err;
                        err = snd_pcm_hw_params_set_access(pcm, hw, 
SND_PCM_ACCESS_MMAP_INTERLEAVED);
                        if (err < 0)
                                return err;
                } else {
                        err = snd_pcm_hw_params_set_access(pcm, hw, 
SND_PCM_ACCESS_RW_INTERLEAVED);
                        if (err < 0)
                                return err;
                        periods_min = 2;
                        err = snd_pcm_hw_params_set_periods_min(pcm, hw, &periods_min, 
0);
                        if (err < 0)
                                return err;
                        if (dsp->maxfrags > 0) {
                                unsigned int periods_max = dsp->maxfrags;
                                err = snd_pcm_hw_params_set_periods_max(pcm, hw,
                                                                        &periods_max, 
0);
                                if (err < 0)
                                        return err;
                        }
                        if (dsp->fragshift > 0) {
                                snd_pcm_uframes_t s = (1 << dsp->fragshift) / 
str->frame_bytes;
                                s *= 16;
                                while (s >= 1024 && (err = 
snd_pcm_hw_params_set_buffer_size(pcm, hw, s)) < 0)
                                        s /= 2;
                                s = (1 << dsp->fragshift) / str->frame_bytes;
                                while (s >= 256 && (err = 
snd_pcm_hw_params_set_period_size(pcm, hw, s, 0)) < 0)
                                        s /= 2;
                                if (err < 0) {
                                        s = (1 << dsp->fragshift) / str->frame_bytes;
                                        err = 
snd_pcm_hw_params_set_period_size_near(pcm, hw, &s, 0);
                                }
                        } else {
                                snd_pcm_uframes_t s = 16, old_s;
                                while (s * 2 < dsp->rate / 2) 
                                        s *= 2;
                                old_s = s = s / 2;
                                while (s >= 1024 && (err = 
snd_pcm_hw_params_set_buffer_size(pcm, hw, s)) < 0)
                                        s /= 2;
                                s = old_s;
                                while (s >= 256 && (err = 
snd_pcm_hw_params_set_period_size(pcm, hw, s, 0)) < 0)
                                        s /= 2;
                                if (err < 0) {
                                        s = old_s;
                                        err = 
snd_pcm_hw_params_set_period_size_near(pcm, hw, &s, 0);
                                }
                        }
                        if (err < 0)
                                return err;
                }
                err = snd_pcm_hw_params(pcm, hw);
                if (err < 0)
                        return err;
#if 0
                if (alsa_oss_debug)
                        snd_pcm_dump_setup(pcm, stderr);
#endif
                if (err < 0)
                        return err;
                dsp->oss_format = alsa_format_to_oss(dsp->format);
                err = snd_pcm_hw_params_get_period_size(hw, &str->alsa.period_size, 0);
                if (err < 0)
                        return err;
                err = snd_pcm_hw_params_get_buffer_size(hw, &str->alsa.buffer_size);
                if (err < 0)
                        return err;
                str->oss.buffer_size = 1 << ld2(str->alsa.buffer_size);
                if (str->oss.buffer_size < str->alsa.buffer_size)
                        str->oss.buffer_size *= 2;
                str->oss.period_size = 1 << ld2(str->alsa.period_size);
                if (str->oss.period_size < str->alsa.period_size)
                        str->oss.period_size *= 2;
                str->oss.periods = str->oss.buffer_size / str->oss.period_size;
                if (str->mmap_areas)
                        free(str->mmap_areas);
                str->mmap_areas = NULL;
                if (str->mmap_buffer) {
                        unsigned int c;
                        snd_pcm_channel_area_t *a;
                        unsigned int bits_per_sample, bits_per_frame;
                        str->mmap_areas = calloc(dsp->channels, 
sizeof(*str->mmap_areas));
                        if (!str->mmap_areas)
                                return -ENOMEM;
                        bits_per_sample = snd_pcm_format_physical_width(dsp->format);
                        bits_per_frame = bits_per_sample * dsp->channels;
                        a = str->mmap_areas;
                        for (c = 0; c < dsp->channels; c++, a++) {
                                a->addr = str->mmap_buffer;
                                a->first = bits_per_sample * c;
                                a->step = bits_per_frame;
                        }
                }
        }
        return 0;
}

static int oss_dsp_sw_params(oss_dsp_t *dsp)
{
        int k;
        for (k = 1; k >= 0; --k) {
                oss_dsp_stream_t *str = &dsp->streams[k];
                snd_pcm_t *pcm = str->pcm;
                snd_pcm_sw_params_t *sw;
                int err;
                if (!pcm)
                        continue;
                snd_pcm_sw_params_alloca(&sw);
                snd_pcm_sw_params_current(pcm, sw);
                snd_pcm_sw_params_set_xfer_align(pcm, sw, 1);
                snd_pcm_sw_params_set_start_threshold(pcm, sw, 
                                                      str->stopped ? 
str->alsa.buffer_size + 1 :
                                                      str->alsa.period_size);
#if 1
                snd_pcm_sw_params_set_stop_threshold(pcm, sw,
                                                     str->mmap_buffer ? LONG_MAX :
                                                     str->alsa.buffer_size);
#else
                snd_pcm_sw_params_set_stop_threshold(pcm, sw,
                                                     LONG_MAX);
                snd_pcm_sw_params_set_silence_threshold(pcm, sw,
                                                       str->alsa.period_size);
                snd_pcm_sw_params_set_silence_size(pcm, sw,
                                                   str->alsa.period_size);
#endif
                err = snd_pcm_sw_params(pcm, sw);
                if (err < 0)
                        return err;
                str->alsa.boundary = _snd_pcm_boundary(pcm);
        }
        return 0;
}

static int oss_dsp_params(oss_dsp_t *dsp)
{
        int err;
        err = oss_dsp_hw_params(dsp);
        if (err < 0) 
                return err;
        err = oss_dsp_sw_params(dsp);
        if (err < 0) 
                return err;
#if 0
        if (alsa_oss_debug && alsa_oss_debug_out) {
                int k;
                for (k = 1; k >= 0; --k) {
                        oss_dsp_stream_t *str = &dsp->streams[k];
                        if (str->pcm)
                                snd_pcm_dump(str->pcm, alsa_oss_debug_out);
                }
        }
#endif
        return 0;
}

int lib_oss_pcm_close(int fd)
{
        int result = 0;
        int k;
        fd_t *xfd = look_for_fd(fd);
        oss_dsp_t *dsp;
        
        if (xfd == NULL) {
                errno = ENOENT;
                return -1;
        }
        dsp = xfd->dsp;
        for (k = 0; k < 2; ++k) {
                int err;
                oss_dsp_stream_t *str = &dsp->streams[k];
                if (!str->pcm)
                        continue;
                if (k == SND_PCM_STREAM_PLAYBACK) {
                        if (snd_pcm_state(str->pcm) != SND_PCM_STATE_OPEN)
                                snd_pcm_drain(str->pcm);
                }
                err = snd_pcm_close(str->pcm);
                if (err < 0)
                        result = err;
        }
        remove_fd(xfd);
        free(dsp);
        free(xfd);
        if (result < 0) {
                errno = -result;
                result = -1;
        }
        close(fd);
        DEBUG("close(%d) -> %d", fd, result);
        if (result < 0)
                DEBUG("(errno=%d)\n", errno);
        else
                DEBUG("\n");
        return 0;
}

static int oss_dsp_open(int card, int device, int oflag, mode_t mode)
{
        oss_dsp_t *dsp;
        unsigned int pcm_mode = 0;
        unsigned int streams, k;
        int format = AFMT_MU_LAW;
        int fd = -1;
        fd_t *xfd;
        int result;
        char name[64];

        char *s = getenv("ALSA_OSS_DEBUG");
        if (s) {
                alsa_oss_debug = 1;
                if (alsa_oss_debug_out == NULL) {
                        if (snd_output_stdio_attach(&alsa_oss_debug_out, stderr, 0) < 
0)
                                alsa_oss_debug_out = NULL;
                }
        }
        switch (device) {
        case OSS_DEVICE_DSP:
                format = AFMT_U8;
                sprintf(name, "dsp%d", card);
                break;
        case OSS_DEVICE_DSPW:
                format = AFMT_S16_LE;
                sprintf(name, "dspW%d", card);
                break;
        case OSS_DEVICE_AUDIO:
                sprintf(name, "audio%d", card);
                break;
        case OSS_DEVICE_ADSP:
                sprintf(name, "adsp%d", card);
                break;
        default:
                errno = ENOENT;
                return -1;
        }
        if (mode & O_NONBLOCK)
                pcm_mode = SND_PCM_NONBLOCK;
        switch (oflag & O_ACCMODE) {
        case O_RDONLY:
                streams = 1 << SND_PCM_STREAM_CAPTURE;
                break;
        case O_WRONLY:
                streams = 1 << SND_PCM_STREAM_PLAYBACK;
                break;
        case O_RDWR:
                streams = ((1 << SND_PCM_STREAM_PLAYBACK) | 
                           (1 << SND_PCM_STREAM_CAPTURE));
                break;
        default:
                errno = EINVAL;
                return -1;
        }
        fd = open("/dev/null", oflag & O_ACCMODE);
        if (fd < 0)
                return -1;
        xfd = calloc(1, sizeof(fd_t));
        if (!xfd) {
                close(fd);
                errno = ENOMEM;
                return -1;
        }
        dsp = calloc(1, sizeof(oss_dsp_t));
        if (!dsp) {
                close(fd);
                free(xfd);
                errno = ENOMEM;
                return -1;
        }
        xfd->dsp = dsp;
        dsp->channels = 1;
        dsp->rate = 8000;
        dsp->oss_format = format;
        result = -EINVAL;
        for (k = 0; k < 2; ++k) {
                if (!(streams & (1 << k)))
                        continue;
                result = snd_pcm_open(&dsp->streams[k].pcm, name, k, pcm_mode);
                if (result < 0)
                        break;
        }
        if (result < 0) {
                result = 0;
                for (k = 0; k < 2; ++k) {
                        if (dsp->streams[k].pcm) {
                                snd_pcm_close(dsp->streams[k].pcm);
                                dsp->streams[k].pcm = NULL;
                        }
                }
                /* try to open the default pcm as fallback */
                if (card == 0 && (device == OSS_DEVICE_DSP || device == 
OSS_DEVICE_AUDIO))
                        strcpy(name, "default");
                else
                        sprintf(name, "plughw:%d", card);
                for (k = 0; k < 2; ++k) {
                        if (!(streams & (1 << k)))
                                continue;
                        result = snd_pcm_open(&dsp->streams[k].pcm, name, k, pcm_mode);
                        if (result < 0)
                                goto _error;
                }
        }
        result = oss_dsp_params(dsp);
        if (result < 0)
                goto _error;
        xfd->fileno = result;
        insert_fd(xfd);
        return fd;

 _error:
        close(fd);
        errno = -result;
        return -1;
}

ssize_t lib_oss_pcm_write(int fd, const void *buf, size_t n)
{
        ssize_t result;
        oss_dsp_t *dsp = look_for_dsp(fd);
        oss_dsp_stream_t *str;
        snd_pcm_t *pcm;
        snd_pcm_uframes_t frames;

        if (dsp == NULL) {
                errno = EBADFD;
                result = -1;
                goto _end;
        }
        str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
        pcm = str->pcm;
        if (!pcm) {
                errno = EBADFD;
                result = -1;
                goto _end;
        }
        frames = n / str->frame_bytes;
 _again:
        result = snd_pcm_writei(pcm, buf, frames);
        if (result == -EPIPE && 
            snd_pcm_state(pcm) == SND_PCM_STATE_XRUN &&
            (result = snd_pcm_prepare(pcm)) == 0)
                goto _again;
        if (result == -EPIPE && 
            snd_pcm_state(pcm) == SND_PCM_STATE_SUSPENDED) {
                while ((result = snd_pcm_resume(pcm)) == -EAGAIN)
                        sleep(1);
                if (result < 0 && (result = snd_pcm_prepare(pcm)) == 0)
                        goto _again;
        }
        if (result < 0) {
                errno = -result;
                result = -1;
                goto _end;
        }
        result *= str->frame_bytes;
        str->oss.bytes += result;
 _end:
        DEBUG("write(%d, %p, %ld) -> %ld", fd, buf, (long)n, (long)result);
        if (result < 0)
                DEBUG("(errno=%d)\n", errno);
        else
                DEBUG("\n");
        return result;
}

ssize_t lib_oss_pcm_read(int fd, void *buf, size_t n)
{
        ssize_t result;
        oss_dsp_t *dsp = look_for_dsp(fd);
        oss_dsp_stream_t *str;
        snd_pcm_t *pcm;
        snd_pcm_uframes_t frames;

        if (dsp == NULL) {
                errno = EBADFD;
                result = -1;
                goto _end;
        }
        str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
        pcm = str->pcm;
        if (!pcm) {
                errno = EBADFD;
                result = -1;
                goto _end;
        }
        frames = n / str->frame_bytes;
 _again:
        result = snd_pcm_readi(pcm, buf, frames);
        if (result == -EPIPE && 
            snd_pcm_state(pcm) == SND_PCM_STATE_XRUN &&
            (result = snd_pcm_prepare(pcm)) == 0)
                goto _again;
        if (result == -EPIPE && 
            snd_pcm_state(pcm) == SND_PCM_STATE_SUSPENDED) {
                while ((result = snd_pcm_resume(pcm)) == -EAGAIN)
                        sleep(1);
                if (result < 0 && (result = snd_pcm_prepare(pcm)) == 0)
                        goto _again;
        }
        if (result < 0) {
                errno = -result;
                result = -1;
                goto _end;
        }
        result *= str->frame_bytes;
        str->oss.bytes += result;
 _end:
        DEBUG("read(%d, %p, %ld) -> %ld", fd, buf, (long)n, (long)result);
        if (result < 0)
                DEBUG("(errno=%d)\n", errno);
        else
                DEBUG("\n");
        return result;
}

#define USE_REWIND 1

static void oss_dsp_mmap_update(oss_dsp_t *dsp, snd_pcm_stream_t stream,
                                snd_pcm_sframes_t delay)
{
        oss_dsp_stream_t *str = &dsp->streams[stream];
        snd_pcm_t *pcm = str->pcm;
        snd_pcm_sframes_t err;
        snd_pcm_uframes_t size;
        const snd_pcm_channel_area_t *areas;
        switch (stream) {
        case SND_PCM_STREAM_PLAYBACK:
                if (delay < 0) {
                        snd_pcm_reset(pcm);
                        str->mmap_advance -= delay;
                        if (str->mmap_advance > dsp->rate / 10)
                                str->mmap_advance = dsp->rate / 10;
//                      fprintf(stderr, "mmap_advance=%ld\n", str->mmap_advance);
                }
#if USE_REWIND
                err = snd_pcm_rewind(pcm, str->alsa.buffer_size);
                if (err < 0)
                        return;
                size = str->mmap_advance;
//              fprintf(stderr, "delay=%ld rewind=%ld forward=%ld offset=%ld\n",
//                      delay, err, size, snd_pcm_mmap_offset(pcm));
#else
                size = str->mmap_advance - delay;
#endif
                while (size > 0) {
                        snd_pcm_uframes_t ofs;
                        snd_pcm_uframes_t frames = size;
                        snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames);
//                      fprintf(stderr, "copy %ld %ld %d\n", ofs, frames, dsp->format);
                        snd_pcm_areas_copy(areas, ofs, str->mmap_areas, ofs, 
                                           dsp->channels, frames,
                                           dsp->format);
                        err = snd_pcm_mmap_commit(pcm, ofs, frames);
                        assert(err == (snd_pcm_sframes_t) frames);
                        size -= frames;
                }
                break;
        case SND_PCM_STREAM_CAPTURE:
                break;
        }
}

int lib_oss_pcm_ioctl(int fd, unsigned long cmd, ...)
{
        int result, err = 0;
        va_list args;
        void *arg;
        oss_dsp_t *dsp = look_for_dsp(fd);
        oss_dsp_stream_t *str;
        snd_pcm_t *pcm;

        if (dsp == NULL) {
                errno = EBADFD;
                return -1;
        }
        va_start(args, cmd);
        arg = va_arg(args, void *);
        va_end(args);
        DEBUG("ioctl(%d, ", fd);
        switch (cmd) {
        case OSS_GETVERSION:
                *(int*)arg = SOUND_VERSION;
                DEBUG("OSS_GETVERSION, %p) -> [%d]\n", arg, *(int*)arg);
                break;
        case SNDCTL_DSP_RESET:
        {
                int k;
                DEBUG("SNDCTL_DSP_RESET)\n");
                result = 0;
                for (k = 0; k < 2; ++k) {
                        str = &dsp->streams[k];
                        pcm = str->pcm;
                        if (!pcm)
                                continue;
                        err = snd_pcm_drop(pcm);
                        if (err >= 0)
                                err = snd_pcm_prepare(pcm);
                        if (err < 0)
                                result = err;
                        str->oss.bytes = 0;
                }
                err = result;
                break;
        }
        case SNDCTL_DSP_SYNC:
        {
                int k;
                DEBUG("SNDCTL_DSP_SYNC)\n");
                result = 0;
                for (k = 0; k < 2; ++k) {
                        str = &dsp->streams[k];
                        pcm = str->pcm;
                        if (!pcm)
                                continue;
                        err = snd_pcm_drain(pcm);
                        if (err >= 0)
                                err = snd_pcm_prepare(pcm);
                        if (err < 0)
                                result = err;
                        
                }
                err = result;
                break;
        }
        case SNDCTL_DSP_SPEED:
                dsp->rate = *(int *)arg;
                err = oss_dsp_params(dsp);
                DEBUG("SNDCTL_DSP_SPEED, %p[%d]) -> [%d]\n", arg, *(int *)arg, 
dsp->rate);
                *(int *)arg = dsp->rate;
                break;
        case SNDCTL_DSP_STEREO:
                if (*(int *)arg)
                        dsp->channels = 2;
                else
                        dsp->channels = 1;
                err = oss_dsp_params(dsp);
                DEBUG("SNDCTL_DSP_STEREO, %p[%d]) -> [%d]\n", arg, *(int *)arg, 
dsp->channels - 1);
                *(int *)arg = dsp->channels - 1;
                break;
        case SNDCTL_DSP_CHANNELS:
                dsp->channels = (*(int *)arg);
                err = oss_dsp_params(dsp);
                if (err < 0)
                        break;
                DEBUG("SNDCTL_DSP_CHANNELS, %p[%d]) -> [%d]\n", arg, *(int *)arg, 
dsp->channels);
                *(int *)arg = dsp->channels;
                break;
        case SNDCTL_DSP_SETFMT:
                if (*(int *)arg != AFMT_QUERY) {
                        dsp->oss_format = *(int *)arg;
                        err = oss_dsp_params(dsp);
                        if (err < 0)
                                break;
                }
                DEBUG("SNDCTL_DSP_SETFMT, %p[%d]) -> [%d]\n", arg, *(int *)arg, 
dsp->oss_format);
                *(int *) arg = dsp->oss_format;
                break;
        case SNDCTL_DSP_GETBLKSIZE:
                str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
                if (!str->pcm)
                        str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
                pcm = str->pcm;
                *(int *) arg = str->oss.period_size * str->frame_bytes;
                DEBUG("SNDCTL_DSP_GETBLKSIZE, %p) -> [%d]\n", arg, *(int *)arg);
                break;
        case SNDCTL_DSP_POST:
                DEBUG("SNDCTL_DSP_POST)\n");
                break;
        case SNDCTL_DSP_SUBDIVIDE:
                DEBUG("SNDCTL_DSP_SUBDIVIDE, %p[%d])\n", arg, *(int *)arg);
                dsp->subdivision = *(int *)arg;
                if (dsp->subdivision < 1)
                        dsp->subdivision = 1;
                err = oss_dsp_params(dsp);
                break;
        case SNDCTL_DSP_SETFRAGMENT:
        {
                DEBUG("SNDCTL_DSP_SETFRAGMENT, %p[%x])\n", arg, *(int *)arg);
                dsp->fragshift = *(int *)arg & 0xffff;
                if (dsp->fragshift < 4)
                        dsp->fragshift = 4;
                dsp->maxfrags = ((*(int *)arg) >> 16) & 0xffff;
                if (dsp->maxfrags < 2)
                        dsp->maxfrags = 2;
                err = oss_dsp_params(dsp);
                break;
        }
        case SNDCTL_DSP_GETFMTS:
        {
                *(int *)arg = (AFMT_MU_LAW | AFMT_A_LAW | AFMT_IMA_ADPCM | 
                               AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | 
                               AFMT_S8 | AFMT_U16_LE | AFMT_U16_BE);
                DEBUG("SNDCTL_DSP_GETFMTS, %p) -> [%d]\n", arg, *(int *)arg);
                break;
        }
        case SNDCTL_DSP_NONBLOCK:
        {       
                DEBUG("SNDCTL_DSP_NONBLOCK)\n");
                return lib_oss_pcm_nonblock(fd, 1);
        }
        case SNDCTL_DSP_GETCAPS:
        {
                result = DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP;
                if (dsp->streams[SND_PCM_STREAM_PLAYBACK].pcm && 
                    dsp->streams[SND_PCM_STREAM_CAPTURE].pcm)
                        result |= DSP_CAP_DUPLEX;
                *(int*)arg = result;
                DEBUG("SNDCTL_DSP_GETCAPS, %p) -> [%d]\n", arg, *(int*)arg);
                break;
        }
        case SNDCTL_DSP_GETTRIGGER:
        {
                int s = 0;
                pcm = dsp->streams[SND_PCM_STREAM_PLAYBACK].pcm;
                if (pcm) {
                        if (snd_pcm_state(pcm) == SND_PCM_STATE_RUNNING)
                                s |= PCM_ENABLE_OUTPUT;
                }
                pcm = dsp->streams[SND_PCM_STREAM_CAPTURE].pcm;
                if (pcm) {
                        if (snd_pcm_state(pcm) == SND_PCM_STATE_RUNNING)
                                s |= PCM_ENABLE_INPUT;
                }
                *(int*)arg = s;
                DEBUG("SNDCTL_DSP_GETTRIGGER, %p) -> [%d]\n", arg, *(int*)arg);
                break;
        }               
        case SNDCTL_DSP_SETTRIGGER:
        {
                DEBUG("SNDCTL_DSP_SETTRIGGER, %p[%d])\n", arg, *(int*)arg);
                result = *(int*) arg;
                str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
                pcm = str->pcm;
                if (pcm) {
                        if (result & PCM_ENABLE_INPUT) {
                                if (str->stopped) {
                                        str->stopped = 0;
                                        err = oss_dsp_sw_params(dsp);
                                        if (err < 0)
                                                break;
                                        err = snd_pcm_start(pcm);
                                        if (err < 0)
                                                break;
                                }
                        } else {
                                if (!str->stopped) {
                                        str->stopped = 1;
                                        err = snd_pcm_drop(pcm);
                                        if (err < 0)
                                                break;
                                        err = oss_dsp_sw_params(dsp);
                                        if (err < 0)
                                                break;
                                        err = snd_pcm_prepare(pcm);
                                        if (err < 0)
                                                break;
                                }
                        }
                }
                str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
                pcm = str->pcm;
                if (pcm) {
                        if (result & PCM_ENABLE_OUTPUT) {
                                if (str->stopped) {
                                        str->stopped = 0;
                                        err = oss_dsp_sw_params(dsp);
                                        if (err < 0)
                                                break;
                                        if (str->mmap_buffer) {
                                                const snd_pcm_channel_area_t *areas;
                                                snd_pcm_uframes_t offset;
                                                snd_pcm_uframes_t size = 
str->alsa.buffer_size;
                                                snd_pcm_mmap_begin(pcm, &areas, 
&offset, &size);
                                                snd_pcm_areas_copy(areas, 0, 
str->mmap_areas, 0,
                                                                   dsp->channels, size,
                                                                   dsp->format);
                                                snd_pcm_mmap_commit(pcm, offset, size);
                                        }
                                        err = snd_pcm_start(pcm);
                                        if (err < 0)
                                                break;
                                }
                        } else {
                                if (!str->stopped) {
                                        str->stopped = 1;
                                        err = snd_pcm_drop(pcm);
                                        if (err < 0)
                                                break;
                                        err = oss_dsp_sw_params(dsp);
                                        if (err < 0)
                                                break;
                                        err = snd_pcm_prepare(pcm);
                                        if (err < 0)
                                                break;
                                }
                        }
                }
                break;
        }
        case SNDCTL_DSP_GETISPACE:
        {
                snd_pcm_sframes_t avail, delay;
                snd_pcm_state_t state;
                audio_buf_info *info = arg;
                str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
                pcm = str->pcm;
                if (!pcm) {
                        err = -EINVAL;
                        break;
                }
                state = snd_pcm_state(pcm);
                if (state == SND_PCM_STATE_RUNNING) {
                        snd_pcm_delay(pcm, &delay);
                        if (str->mmap_buffer)
                                oss_dsp_mmap_update(dsp, SND_PCM_STREAM_CAPTURE, 
delay);
                }
                avail = snd_pcm_avail_update(pcm);
                if (avail < 0)
                        avail = 0;
                if ((snd_pcm_uframes_t)avail > str->oss.buffer_size)
                        avail = str->oss.buffer_size;
                info->fragsize = str->oss.period_size * str->frame_bytes;
                info->fragstotal = str->oss.periods;
                info->bytes = avail * str->frame_bytes;
                info->fragments = avail / str->oss.period_size;
                DEBUG("SNDCTL_DSP_GETISPACE, %p) -> {%d, %d, %d, %d}\n", arg,
                      info->fragments,
                      info->fragstotal,
                      info->fragsize,
                      info->bytes);
                break;
        }
        case SNDCTL_DSP_GETOSPACE:
        {
                snd_pcm_sframes_t avail, delay;
                snd_pcm_state_t state;
                audio_buf_info *info = arg;
                str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
                pcm = str->pcm;
                if (!pcm) {
                        err = -EINVAL;
                        break;
                }
                state = snd_pcm_state(pcm);
                if (state == SND_PCM_STATE_RUNNING || 
                    state == SND_PCM_STATE_DRAINING) {
                        snd_pcm_delay(pcm, &delay);
                        if (str->mmap_buffer)
                                oss_dsp_mmap_update(dsp, SND_PCM_STREAM_PLAYBACK, 
delay);
                }
                avail = snd_pcm_avail_update(pcm);
                if (avail < 0 || (snd_pcm_uframes_t)avail > str->oss.buffer_size)
                        avail = str->oss.buffer_size;
                info->fragsize = str->oss.period_size * str->frame_bytes;
                info->fragstotal = str->oss.periods;
                info->bytes = avail * str->frame_bytes;
                info->fragments = avail / str->oss.period_size;
                DEBUG("SNDCTL_DSP_GETOSPACE, %p) -> {%d %d %d %d}\n", arg,
                      info->fragments,
                      info->fragstotal,
                      info->fragsize,
                      info->bytes);
                break;
        }
        case SNDCTL_DSP_GETIPTR:
        {
                snd_pcm_sframes_t delay = 0;
                snd_pcm_uframes_t hw_ptr;
                snd_pcm_state_t state;
                count_info *info = arg;
                str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
                pcm = str->pcm;
                if (!pcm) {
                        err = -EINVAL;
                        break;
                }
                state = snd_pcm_state(pcm);
                if (state == SND_PCM_STATE_RUNNING) {
                        snd_pcm_delay(pcm, &delay);
                        if (str->mmap_buffer)
                                oss_dsp_mmap_update(dsp, SND_PCM_STREAM_CAPTURE, 
delay);
                }
                /* FIXME */
                hw_ptr = _snd_pcm_mmap_hw_ptr(pcm);
                info->bytes = hw_ptr;
                info->bytes *= str->frame_bytes;
                info->ptr = hw_ptr % str->oss.buffer_size;
                info->ptr *= str->frame_bytes;
                if (str->mmap_buffer) {
                        ssize_t n = (hw_ptr / str->oss.period_size) - 
(str->alsa.old_hw_ptr / str->oss.period_size);
                        if (n < 0)
                                n += str->alsa.boundary / str->oss.period_size;
                        info->blocks = n;
                        str->alsa.old_hw_ptr = hw_ptr;
                } else
                        info->blocks = delay / str->oss.period_size;
                DEBUG("SNDCTL_DSP_GETIPTR, %p) -> {%d %d %d}\n", arg,
                      info->bytes,
                      info->blocks,
                      info->ptr);
                break;
        }
        case SNDCTL_DSP_GETOPTR:
        {
                snd_pcm_sframes_t delay = 0;
                snd_pcm_uframes_t hw_ptr;
                snd_pcm_state_t state;
                count_info *info = arg;
                str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
                pcm = str->pcm;
                if (!pcm) {
                        err = -EINVAL;
                        break;
                }
                state = snd_pcm_state(pcm);
                if (state == SND_PCM_STATE_RUNNING || 
                    state == SND_PCM_STATE_DRAINING) {
                        snd_pcm_delay(pcm, &delay);
                        if (str->mmap_buffer)
                                oss_dsp_mmap_update(dsp, SND_PCM_STREAM_PLAYBACK, 
delay);
                }
                /* FIXME */
                hw_ptr = _snd_pcm_mmap_hw_ptr(pcm);
                info->bytes = hw_ptr;
                info->bytes *= str->frame_bytes;
                info->ptr = hw_ptr % str->oss.buffer_size;
                info->ptr *= str->frame_bytes;
                if (str->mmap_buffer) {
                        ssize_t n = (hw_ptr / str->oss.period_size) - 
(str->alsa.old_hw_ptr / str->oss.period_size);
                        if (n < 0)
                                n += str->alsa.boundary / str->oss.period_size;
                        info->blocks = n;
                        str->alsa.old_hw_ptr = hw_ptr;
                } else
                        info->blocks = delay / str->oss.period_size;
                DEBUG("SNDCTL_DSP_GETOPTR, %p) -> {%d %d %d}\n", arg,
                      info->bytes,
                      info->blocks,
                      info->ptr);
                break;
        }
        case SNDCTL_DSP_GETODELAY:
        {
                snd_pcm_sframes_t delay = 0;
                snd_pcm_state_t state;
                str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
                pcm = str->pcm;
                if (!pcm) {
                        err = -EINVAL;
                        break;
                }
                state = snd_pcm_state(pcm);
                if (state == SND_PCM_STATE_RUNNING || 
                    state == SND_PCM_STATE_DRAINING) {
                        snd_pcm_delay(pcm, &delay);
                        if (str->mmap_buffer)
                                oss_dsp_mmap_update(dsp, SND_PCM_STREAM_PLAYBACK, 
delay);
                }
                *(int *)arg = delay * str->frame_bytes;
                DEBUG("SNDCTL_DSP_GETODELAY, %p) -> [%d]\n", arg, *(int*)arg); 
                break;
        }
        case SNDCTL_DSP_SETDUPLEX:
                DEBUG("SNDCTL_DSP_SETDUPLEX)\n"); 
                break;
        case SOUND_PCM_READ_RATE:
        {
                *(int *)arg = dsp->rate;
                DEBUG("SOUND_PCM_READ_RATE, %p) -> [%d]\n", arg, *(int*)arg); 
                break;
        }
        case SOUND_PCM_READ_CHANNELS:
        {
                *(int *)arg = dsp->channels;
                DEBUG("SOUND_PCM_READ_CHANNELS, %p) -> [%d]\n", arg, *(int*)arg); 
                break;
        }
        case SOUND_PCM_READ_BITS:
        {
                *(int *)arg = snd_pcm_format_width(dsp->format);
                DEBUG("SOUND_PCM_READ_BITS, %p) -> [%d]\n", arg, *(int*)arg); 
                break;
        }
        case SNDCTL_DSP_MAPINBUF:
                DEBUG("SNDCTL_DSP_MAPINBUF)\n");
                err = -EINVAL;
                break;
        case SNDCTL_DSP_MAPOUTBUF:
                DEBUG("SNDCTL_DSP_MAPOUTBUF)\n");
                err = -EINVAL;
                break;
        case SNDCTL_DSP_SETSYNCRO:
                DEBUG("SNDCTL_DSP_SETSYNCRO)\n");
                err = -EINVAL;
                break;
        case SOUND_PCM_READ_FILTER:
                DEBUG("SOUND_PCM_READ_FILTER)\n");
                err = -EINVAL;
                break;
        case SOUND_PCM_WRITE_FILTER:
                DEBUG("SOUND_PCM_WRITE_FILTER)\n");
                err = -EINVAL;
                break;
        default:
                DEBUG("%lx, %p)\n", cmd, arg);
                // return oss_mixer_ioctl(...);
                err = -ENXIO;
                break;
        }
        if (err >= 0)
                return 0;
        DEBUG("dsp ioctl error = %d\n", err);
        errno = -err;
        return -1;
}

int lib_oss_pcm_nonblock(int fd, int nonblock)
{
        oss_dsp_t *dsp = look_for_dsp(fd);
        int k;

        if (dsp == NULL) {
                errno = EBADFD;
                return -1;
        }
        for (k = 0; k < 2; ++k) {
                snd_pcm_t *pcm = dsp->streams[k].pcm;
                int err;
                if (!pcm)
                        continue;
                err = snd_pcm_nonblock(pcm, nonblock);
                if (err < 0) {
                        errno = -err;
                        return -1;
                }
        }
        return 0;
}

void * lib_oss_pcm_mmap(void *addr ATTRIBUTE_UNUSED, size_t len ATTRIBUTE_UNUSED, int 
prot, int flags ATTRIBUTE_UNUSED, int fd, off_t offset ATTRIBUTE_UNUSED)
{
        int err;
        void *result;
        oss_dsp_t *dsp = look_for_dsp(fd);
        oss_dsp_stream_t *str;

        if (dsp == NULL) {
                errno = -EBADFD;
                return NULL;
        }
        switch (prot & (PROT_READ | PROT_WRITE)) {
        case PROT_READ:
                str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
                break;
        case PROT_WRITE:
                str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
                break;
        case PROT_READ | PROT_WRITE:
                str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
                if (!str->pcm)
                        str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
                break;
        default:
                errno = EINVAL;
                result = MAP_FAILED;
                goto _end;
        }
        if (!str->pcm) {
                errno = EBADFD;
                result = MAP_FAILED;
                goto _end;
        }
        assert(!str->mmap_buffer);
        result = malloc(len);
        if (!result) {
                result = MAP_FAILED;
                goto _end;
        }
        str->mmap_buffer = result;
        str->mmap_bytes = len;
        str->alsa.mmap_period_bytes = str->oss.period_size * str->frame_bytes;
        str->alsa.mmap_buffer_bytes = str->oss.buffer_size * str->frame_bytes;
        err = oss_dsp_params(dsp);
        if (err < 0) {
                free(result);
                errno = -err;
                result = MAP_FAILED;
                goto _end;
        }
 _end:
        DEBUG("mmap(%p, %lu, %d, %d, %d, %ld) -> %p\n", addr, (unsigned long)len, 
prot, flags, fd, offset, result);
        return result;
}

int lib_oss_pcm_munmap(void *addr, size_t len)
{
        int err;
        oss_dsp_t *dsp = look_for_mmap_addr(addr);
        oss_dsp_stream_t *str;

        if (dsp == NULL) {
                errno = EBADFD;
                return -1;
        }
        DEBUG("munmap(%p, %lu)\n", addr, (unsigned long)len);
        str = &dsp->streams[SND_PCM_STREAM_PLAYBACK];
        if (!str->pcm)
                str = &dsp->streams[SND_PCM_STREAM_CAPTURE];
        assert(str->mmap_buffer);
        free(str->mmap_buffer);
        str->mmap_buffer = 0;
        str->mmap_bytes = 0;
        err = oss_dsp_params(dsp);
        if (err < 0) {
                errno = -err;
                return -1;
        }
        return 0;
}

static void error_handler(const char *file ATTRIBUTE_UNUSED,
                          int line ATTRIBUTE_UNUSED,
                          const char *func ATTRIBUTE_UNUSED,
                          int err ATTRIBUTE_UNUSED,
                          const char *fmt ATTRIBUTE_UNUSED,
                          ...)
{
        /* suppress the error message from alsa-lib */
}

int lib_oss_pcm_open(const char *file, int oflag, ...)
{
        int result;
        int minor, card, device;
        struct stat s;
        mode_t mode;
        va_list args;
        va_start(args, oflag);
        mode = va_arg(args, mode_t);
        va_end(args);
        result = stat(file, &s);
        if (result < 0) {
                if (!strncmp(file, "/dev/dsp", 8))
                        minor = (atoi(file + 8) << 4) | OSS_DEVICE_DSP;
                else if (!strncmp(file, "/dev/dspW", 9))
                        minor = (atoi(file + 9) << 4) | OSS_DEVICE_DSPW;
                else if (!strncmp(file, "/dev/adsp", 9))
                        minor = (atoi(file + 9) << 4) | OSS_DEVICE_ADSP;
                else if (!strncmp(file, "/dev/audio", 10))
                        minor = (atoi(file + 10) << 4) | OSS_DEVICE_AUDIO;
                else {
                        errno = ENOENT;
                        return -1;
                }
        } else {
                if (!S_ISCHR(s.st_mode) || ((s.st_rdev >> 8) & 0xff) != OSS_MAJOR) {
                        errno = ENOENT;
                        return -1;
                }
                minor = s.st_rdev & 0xff;
        }
        if (! alsa_oss_debug)
                snd_lib_error_set_handler(error_handler);
        card = minor >> 4;
        device = minor & 0x0f;
        switch (device) {
        case OSS_DEVICE_DSP:
        case OSS_DEVICE_DSPW:
        case OSS_DEVICE_AUDIO:
        case OSS_DEVICE_ADSP:
                result = oss_dsp_open(card, device, oflag, mode);
                DEBUG("open(\"%s\", %d, %d) -> %d\n", file, oflag, mode, result);
                return result;
        default:
                errno = ENOENT;
                return -1;
        }
}



-------------------------------------------------------
The SF.Net email is sponsored by EclipseCon 2004
Premiere Conference on Open Tools Development and Integration
See the breadth of Eclipse activity. February 3-5 in Anaheim, CA.
http://www.eclipsecon.org/osdn
_______________________________________________
Alsa-cvslog mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/alsa-cvslog

Reply via email to