"Fossies" - the Fresh Open Source Software Archive

Member "alsa-plugins-1.2.5/usb_stream/pcm_usb_stream.c" (27 May 2021, 12585 Bytes) of package /linux/misc/alsa-plugins-1.2.5.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pcm_usb_stream.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.2.2_vs_1.2.5.

    1 /*
    2  *  PCM - USB_STREAM plugin
    3  *
    4  *  Copyright (c) 2008, 2010 by Karsten Wiese <fzu@wemgehoertderstaat.de>
    5  *
    6  * This library is free software; you can redistribute it and/or modify
    7  * it under the terms of the GNU Lesser General Public License as
    8  * published by the Free Software Foundation; either version 2.1 of
    9  * the License, or (at your option) any later version.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU Lesser General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU Lesser General Public
   17  * License along with this library; if not, write to the Free Software
   18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   19  */
   20 
   21 #define _GNU_SOURCE
   22 #include <byteswap.h>
   23 #include <sys/mman.h>
   24 #include <sys/shm.h>
   25 #include <sys/ioctl.h>
   26 #include <sys/types.h>
   27 #include <sys/socket.h>
   28 #include <pthread.h> 
   29 
   30 #include <alsa/asoundlib.h>
   31 #include <alsa/pcm_external.h>
   32 #include <alsa/hwdep.h>
   33 
   34 #include "usb_stream.h"
   35 
   36 #define DEBUG
   37 #ifdef DEBUG
   38 #define DBG(f, ...) \
   39     fprintf(stderr, "%s:%i %i "f"\n", __FUNCTION__, __LINE__, getpid(), ## __VA_ARGS__);
   40 #else
   41 #define DBG(f, ...)
   42 #endif
   43 
   44 #ifdef VDEBUG
   45 #define VDBG(f, ...)    \
   46     fprintf(stderr, "%s:%i %i "f"\n", __FUNCTION__, __LINE__, getpid(), ## __VA_ARGS__);
   47 #else
   48 #define VDBG(f, ...)
   49 #endif
   50 
   51 #define FRAME_SIZE 6
   52 
   53 struct user_usb_stream {
   54     int         card;
   55     unsigned        use;
   56     struct usb_stream   *s;
   57     void            *write_area;
   58     struct user_usb_stream  *next;
   59 };
   60 
   61 typedef struct {
   62     snd_pcm_ioplug_t    io;
   63 
   64     snd_hwdep_t     *hwdep;
   65     struct user_usb_stream  *uus;
   66 
   67     struct pollfd       pfd;
   68 
   69     unsigned int        num_ports;
   70     unsigned        periods_start;
   71     unsigned        periods_done;
   72 
   73     unsigned        channels;
   74     snd_pcm_uframes_t   period_size;
   75     unsigned int        rate;
   76 } snd_pcm_us_t;
   77 
   78 static struct user_usb_stream *uus;
   79 static pthread_mutex_t uus_mutex = PTHREAD_MUTEX_INITIALIZER;
   80 
   81 static struct user_usb_stream *get_uus(int card)
   82 {
   83     pthread_mutex_lock(&uus_mutex);
   84 
   85     struct user_usb_stream **l_uus = &uus,
   86                 *r_uus = NULL;
   87     while (*l_uus) {
   88         if ((*l_uus)->card == card) {
   89             r_uus = *l_uus;
   90             r_uus->use++;
   91             goto unlock;
   92         }
   93         l_uus = &(*l_uus)->next;
   94     }
   95     r_uus = calloc(1, sizeof(*r_uus));
   96     if (r_uus) {
   97         r_uus->use = 1;
   98         r_uus->card = card;
   99         *l_uus = r_uus;
  100     }
  101 
  102 unlock:
  103     pthread_mutex_unlock(&uus_mutex);
  104     return r_uus;
  105 }
  106 
  107 static void uus_free(snd_pcm_us_t *us)
  108 {
  109     if (!us->uus)
  110         return;
  111 
  112     pthread_mutex_lock(&uus_mutex);
  113     us->uus->use--;
  114     if (!us->uus->use) {
  115         struct user_usb_stream  **n_uus = &uus,
  116                     *p_uus;
  117         while (us->uus != *n_uus) {
  118             p_uus = *n_uus;
  119             n_uus = &p_uus->next;
  120         }
  121         *n_uus = us->uus->next;
  122         if (us->uus->s) {
  123             munmap(us->uus->write_area, us->uus->s->write_size);
  124             munmap(us->uus->s, us->uus->s->read_size);
  125         }
  126         free(us->uus);
  127     }
  128     pthread_mutex_unlock(&uus_mutex);
  129 }
  130 
  131 static void us_free(snd_pcm_us_t *us)
  132 {
  133     uus_free(us);
  134     free(us);
  135 }
  136 
  137 static int snd_pcm_us_close(snd_pcm_ioplug_t *io)
  138 {
  139     snd_pcm_us_t *us = io->private_data;
  140     snd_hwdep_close(us->hwdep);
  141     us_free(us);
  142     return 0;
  143 }
  144 
  145 static snd_pcm_sframes_t snd_pcm_us_pointer(snd_pcm_ioplug_t *io)
  146 {
  147     snd_pcm_us_t *us = io->private_data;
  148     struct usb_stream *s = us->uus->s;
  149     snd_pcm_sframes_t hw_pointer;
  150 
  151     switch (io->state) {
  152     case SND_PCM_STATE_RUNNING:
  153         VDBG("%u %u", s->periods_done, us->periods_done);
  154         if (s->periods_done - us->periods_done <= 1)
  155             hw_pointer =
  156                 (s->periods_done - us->periods_start) & 1 ?
  157                 io->period_size : 0;
  158         else
  159             hw_pointer = -EPIPE;
  160 
  161         break;
  162     case SND_PCM_STATE_XRUN:
  163         hw_pointer = -EPIPE;
  164         break;
  165     default:
  166         hw_pointer = 0;
  167         break;
  168     }
  169     VDBG("%li", hw_pointer);
  170     return hw_pointer;
  171 }
  172 
  173 static int snd_pcm_us_prepare(snd_pcm_ioplug_t *io)
  174 {
  175     struct usb_stream_config    us_cfg;
  176     snd_pcm_us_t            *us = io->private_data;
  177     struct user_usb_stream      *uus = us->uus;
  178     int             ioctl_result, err;
  179 
  180     VDBG("");
  181 
  182     us_cfg.version = USB_STREAM_INTERFACE_VERSION;
  183     us_cfg.frame_size = FRAME_SIZE;
  184     us_cfg.sample_rate = io->rate;
  185     us_cfg.period_frames = io->period_size;
  186 
  187     ioctl_result = snd_hwdep_ioctl(us->hwdep, SNDRV_USB_STREAM_IOCTL_SET_PARAMS, &us_cfg);
  188     if (ioctl_result < 0) {
  189         perror("Couldn't configure usb_stream\n");
  190         return ioctl_result;
  191     }
  192 
  193     if (ioctl_result && uus && uus->s) {
  194         err = munmap(uus->write_area, uus->s->write_size);
  195         if (err < 0)
  196             return -errno;
  197         err = munmap(uus->s, uus->s->read_size);
  198         if (err < 0)
  199             return -errno;
  200         uus->s = NULL;
  201     }
  202 
  203     if (!uus->s) {
  204         uus->s = mmap(NULL, sizeof(struct usb_stream),
  205              PROT_READ,
  206              MAP_SHARED, us->pfd.fd,
  207              0);
  208         if (MAP_FAILED == uus->s) {
  209             perror("ALSA/USX2Y: mmap");
  210             return -errno;
  211         }
  212 
  213         VDBG("%p %lx %i", uus->s, uus->s, uus->s->read_size);
  214 
  215         if (memcmp(&uus->s->cfg, &us_cfg, sizeof(us_cfg))) {
  216             perror("usb_stream Configuration error usb_stream\n");
  217             return -EIO;
  218         }
  219 
  220 
  221         uus->s = mremap(uus->s, sizeof(struct usb_stream), uus->s->read_size, MREMAP_MAYMOVE);
  222         if (MAP_FAILED == uus->s) {
  223             perror("ALSA/USX2Y: mmap");
  224             return -EPERM;
  225         }
  226 
  227         VDBG("%p %lx %i", uus->s, uus->s, uus->s->read_size);
  228 
  229         uus->write_area = mmap(NULL, uus->s->write_size,
  230                        PROT_READ|PROT_WRITE,
  231                        MAP_SHARED, us->pfd.fd,
  232                        (uus->s->read_size + 4095) & ~4095);
  233         if (MAP_FAILED == uus->write_area) {
  234             perror("ALSA/USX2Y: mmap");
  235             return -1;
  236         }
  237         VDBG("%p %i", uus->write_area, uus->s->write_size);
  238     }
  239 
  240     if (uus->s->state != usb_stream_ready)
  241         return -EIO;
  242 
  243     if (poll(&us->pfd, 1, 500000) < 0)
  244         return -errno;
  245 
  246     return 0;
  247 }
  248 
  249 static int snd_pcm_us_start(snd_pcm_ioplug_t *io)
  250 {
  251     snd_pcm_us_t *us = io->private_data;
  252     VDBG("%u", us->uus->s->periods_done);
  253 
  254     us->periods_start = us->periods_done = us->uus->s->periods_done;
  255     
  256     return 0;
  257 }
  258 
  259 static int snd_pcm_us_stop(snd_pcm_ioplug_t *io)
  260 {
  261     snd_pcm_us_t *us = io->private_data;
  262 
  263     if (!us->uus->s)
  264       return 0;
  265 
  266     VDBG("%u", us->uus->s->periods_done);
  267     if (io->stream == SND_PCM_STREAM_PLAYBACK)
  268         memset(us->uus->write_area, 0, us->uus->s->write_size);
  269 
  270     return 0;
  271 }
  272 
  273 static snd_pcm_sframes_t snd_pcm_us_write(snd_pcm_ioplug_t *io,
  274                    const snd_pcm_channel_area_t *areas,
  275                    snd_pcm_uframes_t offset,
  276                    snd_pcm_uframes_t size)
  277 {
  278     void *playback_addr;
  279     snd_pcm_us_t *us = io->private_data;
  280     struct user_usb_stream *uus = us->uus;
  281     struct usb_stream *s = uus->s;
  282     unsigned bytes;
  283     void *src = areas->addr + offset * s->cfg.frame_size;
  284 
  285     VDBG("%li %li %i %i", offset, size, areas->first, areas->step);
  286 
  287     playback_addr = uus->write_area + s->outpacket[0].offset;
  288     memcpy(playback_addr, src, s->outpacket[0].length);
  289     bytes = size * s->cfg.frame_size;
  290     if (bytes > s->outpacket[0].length) {
  291         playback_addr = uus->write_area + s->outpacket[1].offset;
  292         memcpy(playback_addr, src + s->outpacket[0].length,
  293                bytes - s->outpacket[0].length);
  294     }
  295     us->periods_done++;
  296     return size;
  297 }
  298 
  299 static int usb_stream_read(struct user_usb_stream *uus, void *to)
  300 {
  301     struct usb_stream *s = uus->s;
  302     unsigned p = s->inpacket_split, l = 0;
  303     void *i = (void *)s + s->inpacket[p].offset + s->inpacket_split_at;
  304     unsigned il = s->inpacket[p].length - s->inpacket_split_at;
  305 
  306     do {
  307         if (l + il > s->period_size)
  308             il = s->period_size - l;
  309         memcpy(to + l, i, il);
  310         l += il;
  311         if (l >= s->period_size)
  312             break;
  313 
  314         p = (p + 1) % s->inpackets;
  315         i = (void *)s + s->inpacket[p].offset;
  316         il = s->inpacket[p].length;
  317     } while (p != s->inpacket_split);
  318 
  319     return l;
  320 }
  321 
  322 static snd_pcm_sframes_t snd_pcm_us_read(snd_pcm_ioplug_t *io,
  323                   const snd_pcm_channel_area_t *areas,
  324                   snd_pcm_uframes_t offset,
  325                   snd_pcm_uframes_t size)
  326 {
  327     snd_pcm_us_t *us = io->private_data;
  328     unsigned frame_size = us->uus->s->cfg.frame_size;
  329     void *to = areas->addr + offset * frame_size;
  330     snd_pcm_uframes_t red;
  331 
  332     if (size) {
  333         if (size != us->uus->s->cfg.period_frames) {
  334             SNDERR("usb_stream plugin only supports period_size"
  335                    " long reads, sorry");
  336             return -EINVAL;
  337         }
  338         if (us->uus->s->periods_done - us->periods_done == 1) {
  339             red = usb_stream_read(us->uus, to) / frame_size;
  340             us->periods_done++;
  341             return red;
  342         }
  343     } else
  344         if (io->state == SND_PCM_STATE_XRUN)
  345             return -EPIPE;
  346     return 0;
  347 }
  348 
  349 static snd_pcm_ioplug_callback_t us_playback_callback = {
  350     .close = snd_pcm_us_close,
  351     .start = snd_pcm_us_start,
  352     .stop = snd_pcm_us_stop,
  353     .transfer = snd_pcm_us_write,
  354     .pointer = snd_pcm_us_pointer,
  355     .prepare = snd_pcm_us_prepare,
  356 };
  357 static snd_pcm_ioplug_callback_t us_capture_callback = {
  358     .close = snd_pcm_us_close,
  359     .start = snd_pcm_us_start,
  360     .stop = snd_pcm_us_stop,
  361     .transfer = snd_pcm_us_read,
  362     .pointer = snd_pcm_us_pointer,
  363     .prepare = snd_pcm_us_prepare,
  364 };
  365 
  366 #define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0]))
  367 
  368 static int us_set_hw_constraint(snd_pcm_us_t *us)
  369 {
  370     unsigned access_list[] = {
  371         SND_PCM_ACCESS_MMAP_INTERLEAVED,
  372     };
  373     unsigned format_list[] = {
  374         SND_PCM_FORMAT_S24_3LE,
  375     };
  376 
  377     int err;
  378     unsigned int rate_min = us->rate ? us->rate : 44100,
  379         rate_max = us->rate ? us->rate : 96000,
  380         period_bytes_min = us->period_size ? FRAME_SIZE * us->period_size : 128,
  381         period_bytes_max = us->period_size ? FRAME_SIZE * us->period_size : 64*4096;
  382 
  383     if ((err = snd_pcm_ioplug_set_param_list(&us->io, SND_PCM_IOPLUG_HW_ACCESS,
  384                          ARRAY_SIZE(access_list), access_list)) < 0 ||
  385         (err = snd_pcm_ioplug_set_param_list(&us->io, SND_PCM_IOPLUG_HW_FORMAT,
  386                          ARRAY_SIZE(format_list), format_list)) < 0 ||
  387         (err = snd_pcm_ioplug_set_param_minmax(&us->io, SND_PCM_IOPLUG_HW_CHANNELS,
  388                            us->channels, us->channels)) < 0 ||
  389         (err = snd_pcm_ioplug_set_param_minmax(&us->io, SND_PCM_IOPLUG_HW_RATE,
  390                            rate_min, rate_max)) < 0 ||
  391         (err = snd_pcm_ioplug_set_param_minmax(&us->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
  392                            period_bytes_min, period_bytes_max)) < 0 ||
  393         (err = snd_pcm_ioplug_set_param_minmax(&us->io, SND_PCM_IOPLUG_HW_PERIODS,
  394                            2, 2)) < 0)
  395         return err;
  396 
  397     return 0;
  398 }
  399 
  400 static int snd_pcm_us_open(snd_pcm_t **pcmp, const char *name,
  401                    int card,
  402                    snd_pcm_stream_t stream, int mode,
  403                    snd_pcm_uframes_t period_size,
  404                    unsigned int rate)
  405 {
  406     snd_pcm_us_t *us;
  407     int err;
  408     char us_name[32];
  409 
  410     assert(pcmp);
  411     us = calloc(1, sizeof(*us));
  412     if (!us)
  413         return -ENOMEM;
  414 
  415     if (snprintf(us_name, sizeof(us_name), "hw:%d", card)
  416         >= (int)sizeof(us_name)) {
  417         fprintf(stderr, "%s: WARNING: USB_STREAM client name '%s' truncated to %d characters, might not be unique\n",
  418             __func__, us_name, (int)strlen(us_name));
  419     }
  420     VDBG("%i %s", stream, us_name);
  421     us->uus = get_uus(card);
  422     if (!us->uus) {
  423         free(us);
  424         return -ENOMEM;
  425     }
  426     err = snd_hwdep_open(&us->hwdep, us_name, O_RDWR);
  427     if (err < 0) {
  428         us_free(us);
  429         return err;
  430     }
  431     snd_hwdep_poll_descriptors(us->hwdep, &us->pfd, 1);
  432 
  433     us->channels = 2;
  434     us->period_size = period_size;
  435     us->rate = rate;
  436 
  437     us->io.version = SND_PCM_IOPLUG_VERSION;
  438     us->io.name = "ALSA <-> USB_STREAM PCM I/O Plugin";
  439     us->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?
  440         &us_playback_callback : &us_capture_callback;
  441     us->io.private_data = us;
  442     us->io.mmap_rw = 0;
  443     us->io.poll_fd = us->pfd.fd;
  444     us->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
  445 
  446     err = snd_pcm_ioplug_create(&us->io, name, stream, mode);
  447     if (err < 0) {
  448         us_free(us);
  449         return err;
  450     }
  451 
  452     err = us_set_hw_constraint(us);
  453     if (err < 0) {
  454         snd_pcm_ioplug_delete(&us->io);
  455         return err;
  456     }
  457 
  458     VDBG("");
  459     *pcmp = us->io.pcm;
  460 
  461     return 0;
  462 }
  463 
  464 
  465 SND_PCM_PLUGIN_DEFINE_FUNC(usb_stream)
  466 {
  467     snd_config_iterator_t i, next;
  468     int err, card = -1;
  469     long period_size = 0, rate = 0;
  470     
  471     snd_config_for_each(i, next, conf) {
  472         snd_config_t *n = snd_config_iterator_entry(i);
  473         const char *id;
  474         if (snd_config_get_id(n, &id) < 0)
  475             continue;
  476 
  477         if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
  478             continue;
  479         if (strcmp(id, "card") == 0) {
  480             card = snd_config_get_card(n);
  481             if (card < 0) {
  482                 SNDERR("Invalid card '%s'", id);
  483                 return -EINVAL;
  484             }
  485             continue;
  486         }
  487         if (strcmp(id, "period_size") == 0) {
  488             if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
  489                 SNDERR("Invalid type for %s", id);
  490                 return -EINVAL;
  491             }
  492             snd_config_get_integer(n, &period_size);
  493             continue;
  494         }
  495         if (strcmp(id, "rate") == 0) {
  496             if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
  497                 SNDERR("Invalid type for %s", id);
  498                 return -EINVAL;
  499             }
  500             snd_config_get_integer(n, &rate);
  501             continue;
  502         }
  503         SNDERR("Unknown field %s", id);
  504         return -EINVAL;
  505     }
  506 
  507     err = snd_pcm_us_open(pcmp, name, card, stream, mode, period_size, rate);
  508 
  509     return err;
  510 }
  511 
  512 SND_PCM_PLUGIN_SYMBOL(usb_stream);