|
Packit |
675970 |
/*
|
|
Packit |
675970 |
* PCM - USB_STREAM plugin
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* Copyright (c) 2008, 2010 by Karsten Wiese <fzu@wemgehoertderstaat.de>
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* This library is free software; you can redistribute it and/or modify
|
|
Packit |
675970 |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit |
675970 |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
675970 |
* the License, or (at your option) any later version.
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
675970 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
675970 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
675970 |
* GNU Lesser General Public License for more details.
|
|
Packit |
675970 |
*
|
|
Packit |
675970 |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
675970 |
* License along with this library; if not, write to the Free Software
|
|
Packit |
675970 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
675970 |
*/
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#define _GNU_SOURCE
|
|
Packit |
675970 |
#include <byteswap.h>
|
|
Packit |
675970 |
#include <sys/mman.h>
|
|
Packit |
675970 |
#include <sys/shm.h>
|
|
Packit |
675970 |
#include <sys/ioctl.h>
|
|
Packit |
675970 |
#include <sys/types.h>
|
|
Packit |
675970 |
#include <sys/socket.h>
|
|
Packit |
675970 |
#include <pthread.h>
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#include <alsa/asoundlib.h>
|
|
Packit |
675970 |
#include <alsa/pcm_external.h>
|
|
Packit |
675970 |
#include <alsa/hwdep.h>
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#include "usb_stream.h"
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#define DEBUG
|
|
Packit |
675970 |
#ifdef DEBUG
|
|
Packit |
675970 |
#define DBG(f, ...) \
|
|
Packit |
675970 |
fprintf(stderr, "%s:%i %i "f"\n", __FUNCTION__, __LINE__, getpid(), ## __VA_ARGS__);
|
|
Packit |
675970 |
#else
|
|
Packit |
675970 |
#define DBG(f, ...)
|
|
Packit |
675970 |
#endif
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#ifdef VDEBUG
|
|
Packit |
675970 |
#define VDBG(f, ...) \
|
|
Packit |
675970 |
fprintf(stderr, "%s:%i %i "f"\n", __FUNCTION__, __LINE__, getpid(), ## __VA_ARGS__);
|
|
Packit |
675970 |
#else
|
|
Packit |
675970 |
#define VDBG(f, ...)
|
|
Packit |
675970 |
#endif
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#define FRAME_SIZE 6
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#define LCARD 32
|
|
Packit |
675970 |
struct user_usb_stream {
|
|
Packit |
675970 |
char card[LCARD];
|
|
Packit |
675970 |
unsigned use;
|
|
Packit |
675970 |
struct usb_stream *s;
|
|
Packit |
675970 |
void *write_area;
|
|
Packit |
675970 |
struct user_usb_stream *next;
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
|
|
Packit |
675970 |
typedef struct {
|
|
Packit |
675970 |
snd_pcm_ioplug_t io;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_hwdep_t *hwdep;
|
|
Packit |
675970 |
struct user_usb_stream *uus;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
struct pollfd pfd;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
unsigned int num_ports;
|
|
Packit |
675970 |
unsigned periods_start;
|
|
Packit |
675970 |
unsigned periods_done;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
unsigned channels;
|
|
Packit |
675970 |
snd_pcm_uframes_t period_size;
|
|
Packit |
675970 |
unsigned int rate;
|
|
Packit |
675970 |
} snd_pcm_us_t;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static struct user_usb_stream *uus;
|
|
Packit |
675970 |
static pthread_mutex_t uus_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static struct user_usb_stream *get_uus(const char *card)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
pthread_mutex_lock(&uus_mutex);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
struct user_usb_stream **l_uus = &uus,
|
|
Packit |
675970 |
*r_uus = NULL;
|
|
Packit |
675970 |
while (*l_uus) {
|
|
Packit |
675970 |
if (strcmp((*l_uus)->card, card) == 0) {
|
|
Packit |
675970 |
r_uus = *l_uus;
|
|
Packit |
675970 |
r_uus->use++;
|
|
Packit |
675970 |
goto unlock;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
l_uus = &(*l_uus)->next;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
r_uus = calloc(1, sizeof(*r_uus));
|
|
Packit |
675970 |
if (r_uus) {
|
|
Packit |
675970 |
r_uus->use = 1;
|
|
Packit |
675970 |
strcpy(r_uus->card, card);
|
|
Packit |
675970 |
*l_uus = r_uus;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
unlock:
|
|
Packit |
675970 |
pthread_mutex_unlock(&uus_mutex);
|
|
Packit |
675970 |
return r_uus;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void uus_free(snd_pcm_us_t *us)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
if (!us->uus)
|
|
Packit |
675970 |
return;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
pthread_mutex_lock(&uus_mutex);
|
|
Packit |
675970 |
us->uus->use--;
|
|
Packit |
675970 |
if (!us->uus->use) {
|
|
Packit |
675970 |
struct user_usb_stream **n_uus = &uus,
|
|
Packit |
675970 |
*p_uus;
|
|
Packit |
675970 |
while (us->uus != *n_uus) {
|
|
Packit |
675970 |
p_uus = *n_uus;
|
|
Packit |
675970 |
n_uus = &p_uus->next;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
*n_uus = us->uus->next;
|
|
Packit |
675970 |
if (us->uus->s) {
|
|
Packit |
675970 |
munmap(us->uus->write_area, us->uus->s->write_size);
|
|
Packit |
675970 |
munmap(us->uus->s, us->uus->s->read_size);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
free(us->uus);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
pthread_mutex_unlock(&uus_mutex);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static void us_free(snd_pcm_us_t *us)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
uus_free(us);
|
|
Packit |
675970 |
free(us);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_us_close(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_us_t *us = io->private_data;
|
|
Packit |
675970 |
snd_hwdep_close(us->hwdep);
|
|
Packit |
675970 |
us_free(us);
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static snd_pcm_sframes_t snd_pcm_us_pointer(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_us_t *us = io->private_data;
|
|
Packit |
675970 |
struct usb_stream *s = us->uus->s;
|
|
Packit |
675970 |
snd_pcm_sframes_t hw_pointer;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
switch (io->state) {
|
|
Packit |
675970 |
case SND_PCM_STATE_RUNNING:
|
|
Packit |
675970 |
VDBG("%u %u", s->periods_done, us->periods_done);
|
|
Packit |
675970 |
if (s->periods_done - us->periods_done <= 1)
|
|
Packit |
675970 |
hw_pointer =
|
|
Packit |
675970 |
(s->periods_done - us->periods_start) & 1 ?
|
|
Packit |
675970 |
io->period_size : 0;
|
|
Packit |
675970 |
else
|
|
Packit |
675970 |
hw_pointer = -EPIPE;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
break;
|
|
Packit |
675970 |
case SND_PCM_STATE_XRUN:
|
|
Packit |
675970 |
hw_pointer = -EPIPE;
|
|
Packit |
675970 |
break;
|
|
Packit |
675970 |
default:
|
|
Packit |
675970 |
hw_pointer = 0;
|
|
Packit |
675970 |
break;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
VDBG("%li", hw_pointer);
|
|
Packit |
675970 |
return hw_pointer;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_us_prepare(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
struct usb_stream_config us_cfg;
|
|
Packit |
675970 |
snd_pcm_us_t *us = io->private_data;
|
|
Packit |
675970 |
struct user_usb_stream *uus = us->uus;
|
|
Packit |
675970 |
int ioctl_result, err;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
VDBG("");
|
|
Packit |
675970 |
|
|
Packit |
675970 |
us_cfg.version = USB_STREAM_INTERFACE_VERSION;
|
|
Packit |
675970 |
us_cfg.frame_size = FRAME_SIZE;
|
|
Packit |
675970 |
us_cfg.sample_rate = io->rate;
|
|
Packit |
675970 |
us_cfg.period_frames = io->period_size;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
ioctl_result = snd_hwdep_ioctl(us->hwdep, SNDRV_USB_STREAM_IOCTL_SET_PARAMS, &us_cfg);
|
|
Packit |
675970 |
if (ioctl_result < 0) {
|
|
Packit |
675970 |
perror("Couldn't configure usb_stream\n");
|
|
Packit |
675970 |
return ioctl_result;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (ioctl_result && uus && uus->s) {
|
|
Packit |
675970 |
err = munmap(uus->write_area, uus->s->write_size);
|
|
Packit |
675970 |
if (err < 0)
|
|
Packit |
675970 |
return -errno;
|
|
Packit |
675970 |
err = munmap(uus->s, uus->s->read_size);
|
|
Packit |
675970 |
if (err < 0)
|
|
Packit |
675970 |
return -errno;
|
|
Packit |
675970 |
uus->s = NULL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (!uus->s) {
|
|
Packit |
675970 |
uus->s = mmap(NULL, sizeof(struct usb_stream),
|
|
Packit |
675970 |
PROT_READ,
|
|
Packit |
675970 |
MAP_SHARED, us->pfd.fd,
|
|
Packit |
675970 |
0);
|
|
Packit |
675970 |
if (MAP_FAILED == uus->s) {
|
|
Packit |
675970 |
perror("ALSA/USX2Y: mmap");
|
|
Packit |
675970 |
return -errno;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
VDBG("%p %lx %i", uus->s, uus->s, uus->s->read_size);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (memcmp(&uus->s->cfg, &us_cfg, sizeof(us_cfg))) {
|
|
Packit |
675970 |
perror("usb_stream Configuration error usb_stream\n");
|
|
Packit |
675970 |
return -EIO;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
|
|
Packit |
675970 |
uus->s = mremap(uus->s, sizeof(struct usb_stream), uus->s->read_size, MREMAP_MAYMOVE);
|
|
Packit |
675970 |
if (MAP_FAILED == uus->s) {
|
|
Packit |
675970 |
perror("ALSA/USX2Y: mmap");
|
|
Packit |
675970 |
return -EPERM;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
VDBG("%p %lx %i", uus->s, uus->s, uus->s->read_size);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
uus->write_area = mmap(NULL, uus->s->write_size,
|
|
Packit |
675970 |
PROT_READ|PROT_WRITE,
|
|
Packit |
675970 |
MAP_SHARED, us->pfd.fd,
|
|
Packit |
675970 |
(uus->s->read_size + 4095) & ~4095);
|
|
Packit |
675970 |
if (MAP_FAILED == uus->write_area) {
|
|
Packit |
675970 |
perror("ALSA/USX2Y: mmap");
|
|
Packit |
675970 |
return -1;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
VDBG("%p %i", uus->write_area, uus->s->write_size);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (uus->s->state != usb_stream_ready)
|
|
Packit |
675970 |
return -EIO;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (poll(&us->pfd, 1, 500000) < 0)
|
|
Packit |
675970 |
return -errno;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_us_start(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_us_t *us = io->private_data;
|
|
Packit |
675970 |
VDBG("%u", us->uus->s->periods_done);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
us->periods_start = us->periods_done = us->uus->s->periods_done;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_us_stop(snd_pcm_ioplug_t *io)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_us_t *us = io->private_data;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (!us->uus->s)
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
VDBG("%u", us->uus->s->periods_done);
|
|
Packit |
675970 |
if (io->stream == SND_PCM_STREAM_PLAYBACK)
|
|
Packit |
675970 |
memset(us->uus->write_area, 0, us->uus->s->write_size);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static snd_pcm_sframes_t snd_pcm_us_write(snd_pcm_ioplug_t *io,
|
|
Packit |
675970 |
const snd_pcm_channel_area_t *areas,
|
|
Packit |
675970 |
snd_pcm_uframes_t offset,
|
|
Packit |
675970 |
snd_pcm_uframes_t size)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
void *playback_addr;
|
|
Packit |
675970 |
snd_pcm_us_t *us = io->private_data;
|
|
Packit |
675970 |
struct user_usb_stream *uus = us->uus;
|
|
Packit |
675970 |
struct usb_stream *s = uus->s;
|
|
Packit |
675970 |
unsigned bytes;
|
|
Packit |
675970 |
void *src = areas->addr + offset * s->cfg.frame_size;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
VDBG("%li %li %i %i", offset, size, areas->first, areas->step);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
playback_addr = uus->write_area + s->outpacket[0].offset;
|
|
Packit |
675970 |
memcpy(playback_addr, src, s->outpacket[0].length);
|
|
Packit |
675970 |
bytes = size * s->cfg.frame_size;
|
|
Packit |
675970 |
if (bytes > s->outpacket[0].length) {
|
|
Packit |
675970 |
playback_addr = uus->write_area + s->outpacket[1].offset;
|
|
Packit |
675970 |
memcpy(playback_addr, src + s->outpacket[0].length,
|
|
Packit |
675970 |
bytes - s->outpacket[0].length);
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
us->periods_done++;
|
|
Packit |
675970 |
return size;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int usb_stream_read(struct user_usb_stream *uus, void *to)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
struct usb_stream *s = uus->s;
|
|
Packit |
675970 |
unsigned p = s->inpacket_split, l = 0;
|
|
Packit |
675970 |
void *i = (void *)s + s->inpacket[p].offset + s->inpacket_split_at;
|
|
Packit |
675970 |
unsigned il = s->inpacket[p].length - s->inpacket_split_at;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
do {
|
|
Packit |
675970 |
if (l + il > s->period_size)
|
|
Packit |
675970 |
il = s->period_size - l;
|
|
Packit |
675970 |
memcpy(to + l, i, il);
|
|
Packit |
675970 |
l += il;
|
|
Packit |
675970 |
if (l >= s->period_size)
|
|
Packit |
675970 |
break;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
p = (p + 1) % s->inpackets;
|
|
Packit |
675970 |
i = (void *)s + s->inpacket[p].offset;
|
|
Packit |
675970 |
il = s->inpacket[p].length;
|
|
Packit |
675970 |
} while (p != s->inpacket_split);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return l;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static snd_pcm_sframes_t snd_pcm_us_read(snd_pcm_ioplug_t *io,
|
|
Packit |
675970 |
const snd_pcm_channel_area_t *areas,
|
|
Packit |
675970 |
snd_pcm_uframes_t offset,
|
|
Packit |
675970 |
snd_pcm_uframes_t size)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_us_t *us = io->private_data;
|
|
Packit |
675970 |
unsigned frame_size = us->uus->s->cfg.frame_size;
|
|
Packit |
675970 |
void *to = areas->addr + offset * frame_size;
|
|
Packit |
675970 |
snd_pcm_uframes_t red;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (size) {
|
|
Packit |
675970 |
if (size != us->uus->s->cfg.period_frames) {
|
|
Packit |
675970 |
SNDERR("usb_stream plugin only supports period_size"
|
|
Packit |
675970 |
" long reads, sorry");
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
if (us->uus->s->periods_done - us->periods_done == 1) {
|
|
Packit |
675970 |
red = usb_stream_read(us->uus, to) / frame_size;
|
|
Packit |
675970 |
us->periods_done++;
|
|
Packit |
675970 |
return red;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
} else
|
|
Packit |
675970 |
if (io->state == SND_PCM_STATE_XRUN)
|
|
Packit |
675970 |
return -EPIPE;
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static snd_pcm_ioplug_callback_t us_playback_callback = {
|
|
Packit |
675970 |
.close = snd_pcm_us_close,
|
|
Packit |
675970 |
.start = snd_pcm_us_start,
|
|
Packit |
675970 |
.stop = snd_pcm_us_stop,
|
|
Packit |
675970 |
.transfer = snd_pcm_us_write,
|
|
Packit |
675970 |
.pointer = snd_pcm_us_pointer,
|
|
Packit |
675970 |
.prepare = snd_pcm_us_prepare,
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
static snd_pcm_ioplug_callback_t us_capture_callback = {
|
|
Packit |
675970 |
.close = snd_pcm_us_close,
|
|
Packit |
675970 |
.start = snd_pcm_us_start,
|
|
Packit |
675970 |
.stop = snd_pcm_us_stop,
|
|
Packit |
675970 |
.transfer = snd_pcm_us_read,
|
|
Packit |
675970 |
.pointer = snd_pcm_us_pointer,
|
|
Packit |
675970 |
.prepare = snd_pcm_us_prepare,
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
|
|
Packit |
675970 |
#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0]))
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int us_set_hw_constraint(snd_pcm_us_t *us)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
unsigned access_list[] = {
|
|
Packit |
675970 |
SND_PCM_ACCESS_MMAP_INTERLEAVED,
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
unsigned format_list[] = {
|
|
Packit |
675970 |
SND_PCM_FORMAT_S24_3LE,
|
|
Packit |
675970 |
};
|
|
Packit |
675970 |
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
unsigned int rate_min = us->rate ? us->rate : 44100,
|
|
Packit |
675970 |
rate_max = us->rate ? us->rate : 96000,
|
|
Packit |
675970 |
period_bytes_min = us->period_size ? FRAME_SIZE * us->period_size : 128,
|
|
Packit |
675970 |
period_bytes_max = us->period_size ? FRAME_SIZE * us->period_size : 64*4096;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if ((err = snd_pcm_ioplug_set_param_list(&us->io, SND_PCM_IOPLUG_HW_ACCESS,
|
|
Packit |
675970 |
ARRAY_SIZE(access_list), access_list)) < 0 ||
|
|
Packit |
675970 |
(err = snd_pcm_ioplug_set_param_list(&us->io, SND_PCM_IOPLUG_HW_FORMAT,
|
|
Packit |
675970 |
ARRAY_SIZE(format_list), format_list)) < 0 ||
|
|
Packit |
675970 |
(err = snd_pcm_ioplug_set_param_minmax(&us->io, SND_PCM_IOPLUG_HW_CHANNELS,
|
|
Packit |
675970 |
us->channels, us->channels)) < 0 ||
|
|
Packit |
675970 |
(err = snd_pcm_ioplug_set_param_minmax(&us->io, SND_PCM_IOPLUG_HW_RATE,
|
|
Packit |
675970 |
rate_min, rate_max)) < 0 ||
|
|
Packit |
675970 |
(err = snd_pcm_ioplug_set_param_minmax(&us->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
|
|
Packit |
675970 |
period_bytes_min, period_bytes_max)) < 0 ||
|
|
Packit |
675970 |
(err = snd_pcm_ioplug_set_param_minmax(&us->io, SND_PCM_IOPLUG_HW_PERIODS,
|
|
Packit |
675970 |
2, 2)) < 0)
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
static int snd_pcm_us_open(snd_pcm_t **pcmp, const char *name,
|
|
Packit |
675970 |
const char *card,
|
|
Packit |
675970 |
snd_pcm_stream_t stream, int mode,
|
|
Packit |
675970 |
snd_pcm_uframes_t period_size,
|
|
Packit |
675970 |
unsigned int rate)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_pcm_us_t *us;
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
char us_name[32];
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (strlen(card) >= LCARD)
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
assert(pcmp);
|
|
Packit |
675970 |
us = calloc(1, sizeof(*us));
|
|
Packit |
675970 |
if (!us)
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (snprintf(us_name, sizeof(us_name), "hw:%s", card)
|
|
Packit |
675970 |
>= (int)sizeof(us_name)) {
|
|
Packit |
675970 |
fprintf(stderr, "%s: WARNING: USB_STREAM client name '%s' truncated to %d characters, might not be unique\n",
|
|
Packit |
675970 |
__func__, us_name, (int)strlen(us_name));
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
VDBG("%i %s", stream, us_name);
|
|
Packit |
675970 |
us->uus = get_uus(card);
|
|
Packit |
675970 |
if (!us->uus) {
|
|
Packit |
675970 |
free(us);
|
|
Packit |
675970 |
return -ENOMEM;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
err = snd_hwdep_open(&us->hwdep, us_name, O_RDWR);
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
us_free(us);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
snd_hwdep_poll_descriptors(us->hwdep, &us->pfd, 1);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
us->channels = 2;
|
|
Packit |
675970 |
us->period_size = period_size;
|
|
Packit |
675970 |
us->rate = rate;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
us->io.version = SND_PCM_IOPLUG_VERSION;
|
|
Packit |
675970 |
us->io.name = "ALSA <-> USB_STREAM PCM I/O Plugin";
|
|
Packit |
675970 |
us->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?
|
|
Packit |
675970 |
&us_playback_callback : &us_capture_callback;
|
|
Packit |
675970 |
us->io.private_data = us;
|
|
Packit |
675970 |
us->io.mmap_rw = 0;
|
|
Packit |
675970 |
us->io.poll_fd = us->pfd.fd;
|
|
Packit |
675970 |
us->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
err = snd_pcm_ioplug_create(&us->io, name, stream, mode);
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
us_free(us);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
err = us_set_hw_constraint(us);
|
|
Packit |
675970 |
if (err < 0) {
|
|
Packit |
675970 |
snd_pcm_ioplug_delete(&us->io);
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
VDBG("");
|
|
Packit |
675970 |
*pcmp = us->io.pcm;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return 0;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
|
|
Packit |
675970 |
SND_PCM_PLUGIN_DEFINE_FUNC(usb_stream)
|
|
Packit |
675970 |
{
|
|
Packit |
675970 |
snd_config_iterator_t i, next;
|
|
Packit |
675970 |
const char *card;
|
|
Packit |
675970 |
int err;
|
|
Packit |
675970 |
long period_size = 0, rate = 0;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
snd_config_for_each(i, next, conf) {
|
|
Packit |
675970 |
snd_config_t *n = snd_config_iterator_entry(i);
|
|
Packit |
675970 |
const char *id;
|
|
Packit |
675970 |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
|
|
Packit |
675970 |
if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
if (strcmp(id, "card") == 0) {
|
|
Packit |
675970 |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
|
|
Packit |
675970 |
SNDERR("Invalid type for %s", id);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
snd_config_get_string(n, &card;;
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
if (strcmp(id, "period_size") == 0) {
|
|
Packit |
675970 |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
|
|
Packit |
675970 |
SNDERR("Invalid type for %s", id);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
snd_config_get_integer(n, &period_size);
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
if (strcmp(id, "rate") == 0) {
|
|
Packit |
675970 |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
|
|
Packit |
675970 |
SNDERR("Invalid type for %s", id);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
snd_config_get_integer(n, &rate;;
|
|
Packit |
675970 |
continue;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
SNDERR("Unknown field %s", id);
|
|
Packit |
675970 |
return -EINVAL;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
err = snd_pcm_us_open(pcmp, name, card, stream, mode, period_size, rate);
|
|
Packit |
675970 |
|
|
Packit |
675970 |
return err;
|
|
Packit |
675970 |
}
|
|
Packit |
675970 |
|
|
Packit |
675970 |
SND_PCM_PLUGIN_SYMBOL(usb_stream);
|