Re: Line6 Toneport UX1 driver again

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



At Tue, 11 Jul 2006 14:59:40 +0200,
Stefano D'Angelo wrote:
> 
> 2006/7/11, Takashi Iwai <tiwai@xxxxxxx>:
> 
>     At Sat, 8 Jul 2006 18:14:15 +0200,
>     Stefano D'Angelo wrote:
>     >
>     > Excuse me if I'm repeatedly asking the same things, but I still can't understand
>     how
>     > some stuff works probably.
>     > I have these lines of code:
>     >
>     > static snd_pcm_hardware_t snd_line6_tp_ux1_playback_hw = {
>     >     .info             = SNDRV_PCM_INFO_INTERLEAVED |
>     >                         SNDRV_PCM_INFO_BLOCK_TRANSFER,
>     >     .formats          = SNDRV_PCM_FMTBIT_S16_LE,
>     >     .rates            = SNDRV_PCM_RATE_48000,
>     >     .rate_min         = 48000,
>     >     .rate_max         = 48000,
>     >     .channels_min     = 2,
>     >     .channels_max     = 2,
>     >     /* double buffering */
>     >     .buffer_bytes_max = LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2 * 2,
>     >     .period_bytes_min = LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2,
>     >     .period_bytes_max = LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2,
>     >     .periods_min      = 1,
>     >     .periods_max      = 2
>     > };
>     >
>     > and then also:
>     >
>     > static snd_pcm_uframes_t snd_line6_tp_ux1_pcm_pointer (
>     >                                                snd_pcm_substream_t* substream)
>     > {
>     >     struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
>     >
>     >     printk ("pos: %d\n", chip->playback_pos);
>     >     return chip->playback_pos;
>     > }
>     >
>     > static int snd_line6_tp_ux1_playback_copy (snd_pcm_substream_t *substream,
>     >                                            int channel, snd_pcm_uframes_t pos,
>     >                                            void *src, snd_pcm_uframes_t count)
>     > {
>     >     struct snd_line6_tp_ux1 *chip = snd_pcm_substream_chip (substream);
>     >     snd_pcm_runtime_t *runtime = substream->runtime;
>     >     static struct urb* urb;
>     >
>     >     int i = 0;
>     >     static int j = 0;
>     >
>     >     urb = usb_alloc_urb (2, GFP_ATOMIC);
>     >     urb->dev = chip->dev;
>     >     urb->pipe = usb_sndisocpipe (urb->dev, 1);
>     >     urb->complete = snd_line6_tp_ux1_complete_playback_transfer;
>     >     urb->context = substream;
>     >     urb->transfer_buffer = kmalloc (LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2,
>     > GFP_ATOMIC);
>     >     urb->transfer_buffer_length = LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT * 2;
>     >     urb->transfer_flags = URB_ISO_ASAP;
>     >
>     >     memcpy (urb->transfer_buffer, src, count * 2 * 2);
>     >
>     >     for (; i < LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT; i++) {
>     >         printk ("%d\n", ((s16*)urb->transfer_buffer)[i]);
>     >     }
>     >     printk ("---------\n");
>     >
>     >     urb->interval = 1;
>     >     urb->number_of_packets = 2;
>     >     urb->iso_frame_desc[0].offset = 0;
>     >     urb->iso_frame_desc[0].length = urb->iso_frame_desc[1].offset =
>     >     urb->iso_frame_desc[1].length = LINE6_TP_UX1_48000_16_PACKET_SIZE_OUT;
>     >     if (chip->playback_pos)
>     >         chip->playback_pos = 0;
>     >     else
>     >         chip->playback_pos = count;
>     >     j++;
>     >     printk (":: %d %d %d\n", j, chip->playback_pos, runtime->buffer_size);
>     >     snd_pcm_period_elapsed (substream);
>     >     usb_submit_urb (urb, GFP_KERNEL);
>     >
>     >     return count;
>     > }
>     >
>     > Don't care about printk()s and codying style, count in the copy callback is
>     always 96,
>     > which is the # of frames of a period, so everytime the copy callback is called,
>     a
>     > period is elapsed. Since the buffer is long 2 periods, the playback position
>     pointer
>     > should be either 0 or 96, I think (beginning or half of the buffer).
>     > Now, instead of working as expected, the copy callback just works twice, for the
>     first
>     > two periods (one buffer length). Then the pointer becomes 0 and I can't
>     understand why
>     > the playback stops.
>     > Please help! I have some people who keep on asking me about the driver, and it's
>     a
>     > pity that I can't overcome such simple problems and let them use their sound
>     card!
>    
>     Calling snd_pcm_period_elapsed() in copy callback doesn't make sense.
>     The copy callback is called when the PCM core requires something to
>     copy.
>    
>     In general, all PCM driver callbacks are passive.  They need to do
>     only what they are requested.   The role of copy callback is to copy
>     the given user-space data to the hardware buffer.  The role of pointer
>     callback is to return the current (DMA) transfer position in the ring
>     buffer.  The role of trigger callback is to start or stop the PCM
>     stream...
>    
>     In the ALSA PCM model, there are some assumptions:
>    
>     1. You have a ring buffer containing PCM data.
>     2. DMA is running at constant rate, transferring the data in
>        background and updating the position.
>    
>     Usually, the DMA controller issues an interrupt when a given size of
>     data ("period" in ALSA term) is processed.  The driver calls
>     snd_pcm_period_elapsed() in the interrupt handler, then.
>    
>     However, in the case of USB devices, it's a bit tricky, because the
>     transfer is done via non-continuous packets.  To overcome this,
>     snd-usb-audio allocates a ring-buffer via vmalloc() in hw_params
>     callback.  The PCM core copies the user-space data to the ring-buffer,
>     and the driver copies the data from the ring-buffer to each urb
>     appropriately.
>    
>     The first trigger-start callback prepares the urbs (with data copy
>     from ring-buffer) and submit them.  When urb-complete callback is
>     called, the driver updates the current position, then *here* calls
>     snd_pcm_period_elapsed() if the pre-given period size was already
>     processed.  Then, copy the data to urb, and re-submit it.
> 
>     Takashi
> 
> Thanks a lot, however I realized that just yesterday and I think I fixed the whole
> thing, 48kHz 16-bits stereo output works properly now (just a little problem occours,
> some overruns at the beginning of the output, but I think I know how to fix it).
> Since I've got a person who owns a Line6 TonePort UX2 sound card and its' likely that
> these sound cards share the same protocol, I sent him the driver code and he will test
> it in the next days. So I renamed it as snd_toneport.
> However I did this way (tell me if it is correct - just 48kHz 16-bits stereo support
> now):
> 
> [...]
> 
> static snd_pcm_hardware_t snd_toneport_playback_hw = {
> 
>     .info             = SNDRV_PCM_INFO_INTERLEAVED |
>                         SNDRV_PCM_INFO_BLOCK_TRANSFER,
>     .formats          = SNDRV_PCM_FMTBIT_S16_LE,
>     .rates            = SNDRV_PCM_RATE_48000,
>     .rate_min         = 48000,
>     .rate_max         = 48000,
>     .channels_min     = 2,
>     .channels_max     = 2,
>     .buffer_bytes_max = TONEPORT_48000_16_PACKET_SIZE_OUT * 2 * 10,
>     .period_bytes_min = TONEPORT_48000_16_PACKET_SIZE_OUT * 2,
>     .period_bytes_max = TONEPORT_48000_16_PACKET_SIZE_OUT * 2,
>     .periods_min      = 1,
>     .periods_max      = 10
> };
> 
> [...]
> 
> /*
>  * playback transfers' completition callback
>  *
>  * TODO: error handling and full duplex
>  */
> static void snd_toneport_complete_playback_transfer (struct urb* urb,
>     struct pt_regs* regs)
> {
>     snd_pcm_substream_t *substream = (snd_pcm_substream_t *)(urb->context);
>     struct snd_toneport *chip = snd_pcm_substream_chip (substream);
> 
>     chip->playback_pos += TONEPORT_48000_16_PACKET_SIZE_OUT / 2;
>     if (chip->playback_pos >= TONEPORT_48000_16_PACKET_SIZE_OUT / 2 * 10)
>         chip->playback_pos -= TONEPORT_48000_16_PACKET_SIZE_OUT / 2 *
>             10;
> 
>     snd_pcm_period_elapsed (substream);
> 
>     kfree (urb->transfer_buffer);
>     usb_free_urb (urb);
> }
> 
> [...]
> 
> /*
>  * copy callback
>  */
> static int snd_toneport_playback_copy (snd_pcm_substream_t *substream,
> 
>     int channel, snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count)
> {
>     struct snd_toneport *chip = snd_pcm_substream_chip (substream);
>     struct urb* urb;
> 
>     urb = usb_alloc_urb (2, GFP_ATOMIC);
>     urb->dev = chip->dev;
>     urb->pipe = usb_sndisocpipe (urb->dev, 1);
>     urb->complete = snd_toneport_complete_playback_transfer;
>     urb->context = substream;
>     urb->transfer_buffer = kmalloc (TONEPORT_48000_16_PACKET_SIZE_OUT * 2,
>         GFP_ATOMIC);
>     urb->transfer_buffer_length = TONEPORT_48000_16_PACKET_SIZE_OUT * 2;
> 
>     urb->transfer_flags = URB_ISO_ASAP;
>     memcpy (urb->transfer_buffer, src, count * 2 * 2);
>     urb->interval = 1;
>     urb->number_of_packets = 2;
>     urb->iso_frame_desc[0].offset = 0;
>     urb->iso_frame_desc[0].length = urb->iso_frame_desc[1].offset =
>     urb->iso_frame_desc[1].length = TONEPORT_48000_16_PACKET_SIZE_OUT;
>     usb_submit_urb (urb, GFP_ATOMIC);
> 
>     return count;
> }
> 
> [...]
> 
> I tried without a copy callback via DMA transfers (if I'm not wrong also snd-usb-audio
> does this way) but it just didn't work (maybe the card does not support DMA?)
> However thanks a lot again!

Did you allocate the ring-buffer in hw_params callback, not only the
urbs?

You could implement using copy callback, but in that case, the driver
won't work with mmap mode.  So, a ring-buffer as the primary buffer
and manual copy to urbs would be a more flexible option.

Anyway, the copy callback shouldn't submit urbs by itself.  The copy
callback is to copy the data.  That's all.  The copy can be operated
even before starting the stream (filling the buffer first, then
start). Hence, the urbs should be submitted in the trigger-start
callback.

Also, kmalloc/kfree at each time is inefficient.  You can reuse the
urbs and packet data buffers.


Takashi


-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux