linux/sound/usb/usx2y/usx2yhwdeppcm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 */
   4
   5/* USX2Y "rawusb" aka hwdep_pcm implementation
   6
   7 Its usb's unableness to atomically handle power of 2 period sized data chuncs
   8 at standard samplerates,
   9 what led to this part of the usx2y module: 
  10 It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
  11 The pair uses a hardware dependent alsa-device for mmaped pcm transport.
  12 Advantage achieved:
  13         The usb_hc moves pcm data from/into memory via DMA.
  14         That memory is mmaped by jack's usx2y driver.
  15         Jack's usx2y driver is the first/last to read/write pcm data.
  16         Read/write is a combination of power of 2 period shaping and
  17         float/int conversation.
  18         Compared to mainline alsa/jack we leave out power of 2 period shaping inside
  19         snd-usb-usx2y which needs memcpy() and additional buffers.
  20         As a side effect possible unwanted pcm-data coruption resulting of
  21         standard alsa's snd-usb-usx2y period shaping scheme falls away.
  22         Result is sane jack operation at buffering schemes down to 128frames,
  23         2 periods.
  24         plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
  25         cost of easier triggered i.e. aeolus xruns (128 or 256frames,
  26         2periods works but is useless cause of crackling).
  27
  28 This is a first "proof of concept" implementation.
  29 Later, functionalities should migrate to more appropriate places:
  30 Userland:
  31 - The jackd could mmap its float-pcm buffers directly from alsa-lib.
  32 - alsa-lib could provide power of 2 period sized shaping combined with int/float
  33   conversation.
  34   Currently the usx2y jack driver provides above 2 services.
  35 Kernel:
  36 - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio
  37   devices can use it.
  38   Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y. 
  39*/
  40
  41#include <linux/delay.h>
  42#include <linux/gfp.h>
  43#include "usbusx2yaudio.c"
  44
  45#if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1
  46
  47#include <sound/hwdep.h>
  48
  49
  50static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
  51{
  52        struct urb      *urb = subs->completed_urb;
  53        struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
  54        int             i, lens = 0, hwptr_done = subs->hwptr_done;
  55        struct usX2Ydev *usX2Y = subs->usX2Y;
  56        if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) { //FIXME
  57                int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
  58                if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
  59                        head = 0;
  60                usX2Y->hwdep_pcm_shm->capture_iso_start = head;
  61                snd_printdd("cap start %i\n", head);
  62        }
  63        for (i = 0; i < nr_of_packs(); i++) {
  64                if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
  65                        snd_printk(KERN_ERR "active frame status %i. Most probably some hardware problem.\n", urb->iso_frame_desc[i].status);
  66                        return urb->iso_frame_desc[i].status;
  67                }
  68                lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
  69        }
  70        if ((hwptr_done += lens) >= runtime->buffer_size)
  71                hwptr_done -= runtime->buffer_size;
  72        subs->hwptr_done = hwptr_done;
  73        subs->transfer_done += lens;
  74        /* update the pointer, call callback if necessary */
  75        if (subs->transfer_done >= runtime->period_size) {
  76                subs->transfer_done -= runtime->period_size;
  77                snd_pcm_period_elapsed(subs->pcm_substream);
  78        }
  79        return 0;
  80}
  81
  82static inline int usX2Y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
  83                                              struct usX2Ydev * usX2Y)
  84{
  85        return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
  86}
  87
  88/*
  89 * prepare urb for playback data pipe
  90 *
  91 * we copy the data directly from the pcm buffer.
  92 * the current position to be copied is held in hwptr field.
  93 * since a urb can handle only a single linear buffer, if the total
  94 * transferred area overflows the buffer boundary, we cannot send
  95 * it directly from the buffer.  thus the data is once copied to
  96 * a temporary buffer and urb points to that.
  97 */
  98static int usX2Y_hwdep_urb_play_prepare(struct snd_usX2Y_substream *subs,
  99                                        struct urb *urb)
 100{
 101        int count, counts, pack;
 102        struct usX2Ydev *usX2Y = subs->usX2Y;
 103        struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
 104        struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
 105
 106        if (0 > shm->playback_iso_start) {
 107                shm->playback_iso_start = shm->captured_iso_head -
 108                        usX2Y_iso_frames_per_buffer(runtime, usX2Y);
 109                if (0 > shm->playback_iso_start)
 110                        shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
 111                shm->playback_iso_head = shm->playback_iso_start;
 112        }
 113
 114        count = 0;
 115        for (pack = 0; pack < nr_of_packs(); pack++) {
 116                /* calculate the size of a packet */
 117                counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
 118                if (counts < 43 || counts > 50) {
 119                        snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
 120                        return -EPIPE;
 121                }
 122                /* set up descriptor */
 123                urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
 124                urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
 125                if (atomic_read(&subs->state) != state_RUNNING)
 126                        memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
 127                               urb->iso_frame_desc[pack].length);
 128                if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
 129                        shm->playback_iso_head = 0;
 130                count += counts;
 131        }
 132        urb->transfer_buffer_length = count * usX2Y->stride;
 133        return 0;
 134}
 135
 136
 137static inline void usX2Y_usbpcm_urb_capt_iso_advance(struct snd_usX2Y_substream *subs,
 138                                                     struct urb *urb)
 139{
 140        int pack;
 141        for (pack = 0; pack < nr_of_packs(); ++pack) {
 142                struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
 143                if (NULL != subs) {
 144                        struct snd_usX2Y_hwdep_pcm_shm *shm = subs->usX2Y->hwdep_pcm_shm;
 145                        int head = shm->captured_iso_head + 1;
 146                        if (head >= ARRAY_SIZE(shm->captured_iso))
 147                                head = 0;
 148                        shm->captured_iso[head].frame = urb->start_frame + pack;
 149                        shm->captured_iso[head].offset = desc->offset;
 150                        shm->captured_iso[head].length = desc->actual_length;
 151                        shm->captured_iso_head = head;
 152                        shm->captured_iso_frames++;
 153                }
 154                if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
 155                    desc->length >= SSS)
 156                        desc->offset -= (SSS - desc->length);
 157        }
 158}
 159
 160static inline int usX2Y_usbpcm_usbframe_complete(struct snd_usX2Y_substream *capsubs,
 161                                                 struct snd_usX2Y_substream *capsubs2,
 162                                                 struct snd_usX2Y_substream *playbacksubs,
 163                                                 int frame)
 164{
 165        int err, state;
 166        struct urb *urb = playbacksubs->completed_urb;
 167
 168        state = atomic_read(&playbacksubs->state);
 169        if (NULL != urb) {
 170                if (state == state_RUNNING)
 171                        usX2Y_urb_play_retire(playbacksubs, urb);
 172                else if (state >= state_PRERUNNING)
 173                        atomic_inc(&playbacksubs->state);
 174        } else {
 175                switch (state) {
 176                case state_STARTING1:
 177                        urb = playbacksubs->urb[0];
 178                        atomic_inc(&playbacksubs->state);
 179                        break;
 180                case state_STARTING2:
 181                        urb = playbacksubs->urb[1];
 182                        atomic_inc(&playbacksubs->state);
 183                        break;
 184                }
 185        }
 186        if (urb) {
 187                if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
 188                    (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
 189                        return err;
 190                }
 191        }
 192        
 193        playbacksubs->completed_urb = NULL;
 194
 195        state = atomic_read(&capsubs->state);
 196        if (state >= state_PREPARED) {
 197                if (state == state_RUNNING) {
 198                        if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
 199                                return err;
 200                } else if (state >= state_PRERUNNING)
 201                        atomic_inc(&capsubs->state);
 202                usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
 203                if (NULL != capsubs2)
 204                        usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
 205                if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
 206                        return err;
 207                if (NULL != capsubs2)
 208                        if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
 209                                return err;
 210        }
 211        capsubs->completed_urb = NULL;
 212        if (NULL != capsubs2)
 213                capsubs2->completed_urb = NULL;
 214        return 0;
 215}
 216
 217
 218static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
 219{
 220        struct snd_usX2Y_substream *subs = urb->context;
 221        struct usX2Ydev *usX2Y = subs->usX2Y;
 222        struct snd_usX2Y_substream *capsubs, *capsubs2, *playbacksubs;
 223
 224        if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
 225                snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
 226                            usb_get_current_frame_number(usX2Y->dev),
 227                            subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
 228                            urb->status, urb->start_frame);
 229                return;
 230        }
 231        if (unlikely(urb->status)) {
 232                usX2Y_error_urb_status(usX2Y, subs, urb);
 233                return;
 234        }
 235
 236        subs->completed_urb = urb;
 237        capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
 238        capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
 239        playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
 240        if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
 241            (NULL == capsubs2 || capsubs2->completed_urb) &&
 242            (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
 243                if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame))
 244                        usX2Y->wait_iso_frame += nr_of_packs();
 245                else {
 246                        snd_printdd("\n");
 247                        usX2Y_clients_stop(usX2Y);
 248                }
 249        }
 250}
 251
 252
 253static void usX2Y_hwdep_urb_release(struct urb **urb)
 254{
 255        usb_kill_urb(*urb);
 256        usb_free_urb(*urb);
 257        *urb = NULL;
 258}
 259
 260/*
 261 * release a substream
 262 */
 263static void usX2Y_usbpcm_urbs_release(struct snd_usX2Y_substream *subs)
 264{
 265        int i;
 266        snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
 267        for (i = 0; i < NRURBS; i++)
 268                usX2Y_hwdep_urb_release(subs->urb + i);
 269}
 270
 271static void usX2Y_usbpcm_subs_startup_finish(struct usX2Ydev * usX2Y)
 272{
 273        usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
 274        usX2Y->prepare_subs = NULL;
 275}
 276
 277static void i_usX2Y_usbpcm_subs_startup(struct urb *urb)
 278{
 279        struct snd_usX2Y_substream *subs = urb->context;
 280        struct usX2Ydev *usX2Y = subs->usX2Y;
 281        struct snd_usX2Y_substream *prepare_subs = usX2Y->prepare_subs;
 282        if (NULL != prepare_subs &&
 283            urb->start_frame == prepare_subs->urb[0]->start_frame) {
 284                atomic_inc(&prepare_subs->state);
 285                if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
 286                        struct snd_usX2Y_substream *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
 287                        if (cap_subs2 != NULL)
 288                                atomic_inc(&cap_subs2->state);
 289                }
 290                usX2Y_usbpcm_subs_startup_finish(usX2Y);
 291                wake_up(&usX2Y->prepare_wait_queue);
 292        }
 293
 294        i_usX2Y_usbpcm_urb_complete(urb);
 295}
 296
 297/*
 298 * initialize a substream's urbs
 299 */
 300static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
 301{
 302        int i;
 303        unsigned int pipe;
 304        int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
 305        struct usb_device *dev = subs->usX2Y->dev;
 306
 307        pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
 308                        usb_rcvisocpipe(dev, subs->endpoint);
 309        subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
 310        if (!subs->maxpacksize)
 311                return -EINVAL;
 312
 313        /* allocate and initialize data urbs */
 314        for (i = 0; i < NRURBS; i++) {
 315                struct urb **purb = subs->urb + i;
 316                if (*purb) {
 317                        usb_kill_urb(*purb);
 318                        continue;
 319                }
 320                *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
 321                if (NULL == *purb) {
 322                        usX2Y_usbpcm_urbs_release(subs);
 323                        return -ENOMEM;
 324                }
 325                (*purb)->transfer_buffer = is_playback ?
 326                        subs->usX2Y->hwdep_pcm_shm->playback : (
 327                                subs->endpoint == 0x8 ?
 328                                subs->usX2Y->hwdep_pcm_shm->capture0x8 :
 329                                subs->usX2Y->hwdep_pcm_shm->capture0xA);
 330
 331                (*purb)->dev = dev;
 332                (*purb)->pipe = pipe;
 333                (*purb)->number_of_packets = nr_of_packs();
 334                (*purb)->context = subs;
 335                (*purb)->interval = 1;
 336                (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
 337        }
 338        return 0;
 339}
 340
 341/*
 342 * free the buffer
 343 */
 344static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
 345{
 346        struct snd_pcm_runtime *runtime = substream->runtime;
 347        struct snd_usX2Y_substream *subs = runtime->private_data,
 348                *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
 349        mutex_lock(&subs->usX2Y->pcm_mutex);
 350        snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
 351
 352        if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
 353                struct snd_usX2Y_substream *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
 354                atomic_set(&subs->state, state_STOPPED);
 355                usX2Y_usbpcm_urbs_release(subs);
 356                if (!cap_subs->pcm_substream ||
 357                    !cap_subs->pcm_substream->runtime ||
 358                    !cap_subs->pcm_substream->runtime->status ||
 359                    cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
 360                        atomic_set(&cap_subs->state, state_STOPPED);
 361                        if (NULL != cap_subs2)
 362                                atomic_set(&cap_subs2->state, state_STOPPED);
 363                        usX2Y_usbpcm_urbs_release(cap_subs);
 364                        if (NULL != cap_subs2)
 365                                usX2Y_usbpcm_urbs_release(cap_subs2);
 366                }
 367        } else {
 368                struct snd_usX2Y_substream *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
 369                if (atomic_read(&playback_subs->state) < state_PREPARED) {
 370                        atomic_set(&subs->state, state_STOPPED);
 371                        if (NULL != cap_subs2)
 372                                atomic_set(&cap_subs2->state, state_STOPPED);
 373                        usX2Y_usbpcm_urbs_release(subs);
 374                        if (NULL != cap_subs2)
 375                                usX2Y_usbpcm_urbs_release(cap_subs2);
 376                }
 377        }
 378        mutex_unlock(&subs->usX2Y->pcm_mutex);
 379        return snd_pcm_lib_free_pages(substream);
 380}
 381
 382static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
 383{
 384        struct usX2Ydev * usX2Y = subs->usX2Y;
 385        usX2Y->prepare_subs = subs;
 386        subs->urb[0]->start_frame = -1;
 387        smp_wmb();      // Make sure above modifications are seen by i_usX2Y_subs_startup()
 388        usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
 389}
 390
 391static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
 392{
 393        int     p, u, err,
 394                stream = subs->pcm_substream->stream;
 395        struct usX2Ydev *usX2Y = subs->usX2Y;
 396
 397        if (SNDRV_PCM_STREAM_CAPTURE == stream) {
 398                usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
 399                usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
 400        }
 401
 402        for (p = 0; 3 >= (stream + p); p += 2) {
 403                struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
 404                if (subs != NULL) {
 405                        if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
 406                                return err;
 407                        subs->completed_urb = NULL;
 408                }
 409        }
 410
 411        for (p = 0; p < 4; p++) {
 412                struct snd_usX2Y_substream *subs = usX2Y->subs[p];
 413                if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
 414                        goto start;
 415        }
 416
 417 start:
 418        usX2Y_usbpcm_subs_startup(subs);
 419        for (u = 0; u < NRURBS; u++) {
 420                for (p = 0; 3 >= (stream + p); p += 2) {
 421                        struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
 422                        if (subs != NULL) {
 423                                struct urb *urb = subs->urb[u];
 424                                if (usb_pipein(urb->pipe)) {
 425                                        unsigned long pack;
 426                                        if (0 == u)
 427                                                atomic_set(&subs->state, state_STARTING3);
 428                                        urb->dev = usX2Y->dev;
 429                                        for (pack = 0; pack < nr_of_packs(); pack++) {
 430                                                urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
 431                                                urb->iso_frame_desc[pack].length = subs->maxpacksize;
 432                                        }
 433                                        urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs(); 
 434                                        if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
 435                                                snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
 436                                                err = -EPIPE;
 437                                                goto cleanup;
 438                                        }  else {
 439                                                snd_printdd("%i\n", urb->start_frame);
 440                                                if (u == 0)
 441                                                        usX2Y->wait_iso_frame = urb->start_frame;
 442                                        }
 443                                        urb->transfer_flags = 0;
 444                                } else {
 445                                        atomic_set(&subs->state, state_STARTING1);
 446                                        break;
 447                                }                       
 448                        }
 449                }
 450        }
 451        err = 0;
 452        wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
 453        if (atomic_read(&subs->state) != state_PREPARED)
 454                err = -EPIPE;
 455                
 456 cleanup:
 457        if (err) {
 458                usX2Y_subs_startup_finish(usX2Y);       // Call it now
 459                usX2Y_clients_stop(usX2Y);              // something is completely wroong > stop evrything                      
 460        }
 461        return err;
 462}
 463
 464/*
 465 * prepare callback
 466 *
 467 * set format and initialize urbs
 468 */
 469static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
 470{
 471        struct snd_pcm_runtime *runtime = substream->runtime;
 472        struct snd_usX2Y_substream *subs = runtime->private_data;
 473        struct usX2Ydev *usX2Y = subs->usX2Y;
 474        struct snd_usX2Y_substream *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
 475        int err = 0;
 476        snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
 477
 478        if (NULL == usX2Y->hwdep_pcm_shm) {
 479                usX2Y->hwdep_pcm_shm = alloc_pages_exact(sizeof(struct snd_usX2Y_hwdep_pcm_shm),
 480                                                         GFP_KERNEL);
 481                if (!usX2Y->hwdep_pcm_shm)
 482                        return -ENOMEM;
 483                memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
 484        }
 485
 486        mutex_lock(&usX2Y->pcm_mutex);
 487        usX2Y_subs_prepare(subs);
 488// Start hardware streams
 489// SyncStream first....
 490        if (atomic_read(&capsubs->state) < state_PREPARED) {
 491                if (usX2Y->format != runtime->format)
 492                        if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
 493                                goto up_prepare_mutex;
 494                if (usX2Y->rate != runtime->rate)
 495                        if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
 496                                goto up_prepare_mutex;
 497                snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
 498                            "self" : "playpipe");
 499                if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
 500                        goto up_prepare_mutex;
 501        }
 502
 503        if (subs != capsubs) {
 504                usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
 505                if (atomic_read(&subs->state) < state_PREPARED) {
 506                        while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) >
 507                               usX2Y->hwdep_pcm_shm->captured_iso_frames) {
 508                                snd_printdd("Wait: iso_frames_per_buffer=%i,"
 509                                            "captured_iso_frames=%i\n",
 510                                            usX2Y_iso_frames_per_buffer(runtime, usX2Y),
 511                                            usX2Y->hwdep_pcm_shm->captured_iso_frames);
 512                                if (msleep_interruptible(10)) {
 513                                        err = -ERESTARTSYS;
 514                                        goto up_prepare_mutex;
 515                                }
 516                        } 
 517                        if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
 518                                goto up_prepare_mutex;
 519                }
 520                snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
 521                            usX2Y_iso_frames_per_buffer(runtime, usX2Y),
 522                            usX2Y->hwdep_pcm_shm->captured_iso_frames);
 523        } else
 524                usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
 525
 526 up_prepare_mutex:
 527        mutex_unlock(&usX2Y->pcm_mutex);
 528        return err;
 529}
 530
 531static struct snd_pcm_hardware snd_usX2Y_4c =
 532{
 533        .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 534                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 535                                 SNDRV_PCM_INFO_MMAP_VALID),
 536        .formats =                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
 537        .rates =                   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
 538        .rate_min =                44100,
 539        .rate_max =                48000,
 540        .channels_min =            2,
 541        .channels_max =            4,
 542        .buffer_bytes_max =     (2*128*1024),
 543        .period_bytes_min =     64,
 544        .period_bytes_max =     (128*1024),
 545        .periods_min =          2,
 546        .periods_max =          1024,
 547        .fifo_size =              0
 548};
 549
 550
 551
 552static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
 553{
 554        struct snd_usX2Y_substream      *subs = ((struct snd_usX2Y_substream **)
 555                                         snd_pcm_substream_chip(substream))[substream->stream];
 556        struct snd_pcm_runtime  *runtime = substream->runtime;
 557
 558        if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
 559                return -EBUSY;
 560
 561        runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
 562                (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
 563        runtime->private_data = subs;
 564        subs->pcm_substream = substream;
 565        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
 566        return 0;
 567}
 568
 569
 570static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
 571{
 572        struct snd_pcm_runtime *runtime = substream->runtime;
 573        struct snd_usX2Y_substream *subs = runtime->private_data;
 574
 575        subs->pcm_substream = NULL;
 576        return 0;
 577}
 578
 579
 580static const struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
 581{
 582        .open =         snd_usX2Y_usbpcm_open,
 583        .close =        snd_usX2Y_usbpcm_close,
 584        .ioctl =        snd_pcm_lib_ioctl,
 585        .hw_params =    snd_usX2Y_pcm_hw_params,
 586        .hw_free =      snd_usX2Y_usbpcm_hw_free,
 587        .prepare =      snd_usX2Y_usbpcm_prepare,
 588        .trigger =      snd_usX2Y_pcm_trigger,
 589        .pointer =      snd_usX2Y_pcm_pointer,
 590};
 591
 592
 593static int usX2Y_pcms_busy_check(struct snd_card *card)
 594{
 595        struct usX2Ydev *dev = usX2Y(card);
 596        int i;
 597
 598        for (i = 0; i < dev->pcm_devs * 2; i++) {
 599                struct snd_usX2Y_substream *subs = dev->subs[i];
 600                if (subs && subs->pcm_substream &&
 601                    SUBSTREAM_BUSY(subs->pcm_substream))
 602                        return -EBUSY;
 603        }
 604        return 0;
 605}
 606
 607static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
 608{
 609        struct snd_card *card = hw->card;
 610        int err;
 611
 612        mutex_lock(&usX2Y(card)->pcm_mutex);
 613        err = usX2Y_pcms_busy_check(card);
 614        if (!err)
 615                usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
 616        mutex_unlock(&usX2Y(card)->pcm_mutex);
 617        return err;
 618}
 619
 620
 621static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
 622{
 623        struct snd_card *card = hw->card;
 624        int err;
 625
 626        mutex_lock(&usX2Y(card)->pcm_mutex);
 627        err = usX2Y_pcms_busy_check(card);
 628        if (!err)
 629                usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
 630        mutex_unlock(&usX2Y(card)->pcm_mutex);
 631        return err;
 632}
 633
 634
 635static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
 636{
 637}
 638
 639
 640static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
 641{
 642}
 643
 644
 645static vm_fault_t snd_usX2Y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
 646{
 647        unsigned long offset;
 648        void *vaddr;
 649
 650        offset = vmf->pgoff << PAGE_SHIFT;
 651        vaddr = (char *)((struct usX2Ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
 652        vmf->page = virt_to_page(vaddr);
 653        get_page(vmf->page);
 654        return 0;
 655}
 656
 657
 658static const struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
 659        .open = snd_usX2Y_hwdep_pcm_vm_open,
 660        .close = snd_usX2Y_hwdep_pcm_vm_close,
 661        .fault = snd_usX2Y_hwdep_pcm_vm_fault,
 662};
 663
 664
 665static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area)
 666{
 667        unsigned long   size = (unsigned long)(area->vm_end - area->vm_start);
 668        struct usX2Ydev *usX2Y = hw->private_data;
 669
 670        if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
 671                return -EBUSY;
 672
 673        /* if userspace tries to mmap beyond end of our buffer, fail */ 
 674        if (size > PAGE_ALIGN(sizeof(struct snd_usX2Y_hwdep_pcm_shm))) {
 675                snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm)); 
 676                return -EINVAL;
 677        }
 678
 679        if (!usX2Y->hwdep_pcm_shm) {
 680                return -ENODEV;
 681        }
 682        area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
 683        area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
 684        area->vm_private_data = hw->private_data;
 685        return 0;
 686}
 687
 688
 689static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
 690{
 691        struct usX2Ydev *usX2Y = hwdep->private_data;
 692        if (NULL != usX2Y->hwdep_pcm_shm)
 693                free_pages_exact(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
 694}
 695
 696
 697int usX2Y_hwdep_pcm_new(struct snd_card *card)
 698{
 699        int err;
 700        struct snd_hwdep *hw;
 701        struct snd_pcm *pcm;
 702        struct usb_device *dev = usX2Y(card)->dev;
 703        if (1 != nr_of_packs())
 704                return 0;
 705
 706        if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
 707                return err;
 708
 709        hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
 710        hw->private_data = usX2Y(card);
 711        hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
 712        hw->ops.open = snd_usX2Y_hwdep_pcm_open;
 713        hw->ops.release = snd_usX2Y_hwdep_pcm_release;
 714        hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
 715        hw->exclusive = 1;
 716        sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
 717
 718        err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
 719        if (err < 0) {
 720                return err;
 721        }
 722        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
 723        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
 724
 725        pcm->private_data = usX2Y(card)->subs;
 726        pcm->info_flags = 0;
 727
 728        sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
 729        snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
 730                                      SNDRV_DMA_TYPE_CONTINUOUS,
 731                                      snd_dma_continuous_data(GFP_KERNEL),
 732                                      64*1024, 128*1024);
 733        snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
 734                                      SNDRV_DMA_TYPE_CONTINUOUS,
 735                                      snd_dma_continuous_data(GFP_KERNEL),
 736                                      64*1024, 128*1024);
 737
 738        return 0;
 739}
 740
 741#else
 742
 743int usX2Y_hwdep_pcm_new(struct snd_card *card)
 744{
 745        return 0;
 746}
 747
 748#endif
 749