Re: Line6 Toneport UX1 driver again

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

 



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!

Stefano
-------------------------------------------------------------------------
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