[haiku-development] Re: [USB] What is the right way to feed queue_isochronous with audio data?

  • From: "Michael Lotz" <mmlr@xxxxxxxx>
  • To: haiku-development@xxxxxxxxxxxxx
  • Date: Fri, 22 Jan 2010 18:45:50 +0000

Hi There

> I have some questions about using isochronous USB transfers with 
> Haiku USB
> interface version 3. There are some problems and I have seen no 
> samples of
> using such transfers under Haiku. :-( Looks like my usb_audio driver 
> will
> be the first one. ;-) 

I think the old BeOS usb_audio was the only reference that ever used 
it, but for lack of hardware I have never checked if that one actually 
did anything.

> First of all some words about allocating buffers used simultaneously 
> both
> to multi-audio buffers exchange and submitting with queue_isochronous.
> Every audio stream logically connected to isochronous interface 
> allocate
> the memory area. This area is big enought to contains both audio data
> buffers and array of usb_iso_packet_descriptor's:
> http://dev.haiku-os.org/browser/haiku/branches/developer/siarzhuk/usb_audio/Stream.cpp#L125
> cpp#L125> 

Looks reasonable, except for the codingstyle. Indent levels by exactly 
one tab, put operators on the new line and don't put spaces after the 
opening or before the closing parenthesis (at the risk of this being 
scrambled by email):

fArea = create_area((fIsInput) ? DRIVER_NAME "_record_area"
        : DRIVER_NAME "_playback_area", (void**)&fDescriptors,
        B_ANY_KERNEL_ADDRESS, bufferSize, B_CONTIGUOUS, B_READ_AREA
        | B_WRITE_AREA);

That way it also gives you reasonable amounts of space so you don't 
have to do quite as many lines.

> The layout of this area begins with descriptors array and continues 
> with
> audio data buffers. There are typically 2 audio data buffers for 
> every usb
> audio stream. Note that I have also initialized those descriptors in 
> some
> way (see line #154). May be it is significant.

Filling out the request_length with the packet size is the right thing 
to do, the other fields are initialized by the stack.

> On first call of multi-buffer-exchange ioctl I'm perform the "start 
> of
> stream operation" - I submit first buffer of audio data into
> queue_isochronous call. Please look on function Stream::
> _QueueNextTransfer
> at line 234 for details. The "data" argument points to start of audio 
> data
> buffer, "dataLength" is the size of this buffer, "packetDesc" points 
> to
> start of descriptors array, "packetCount" is the count of descriptors 
> used
> in this transfer, "callback" points to static Stream::
> _TransferCallback
> function, "callbackCookie" is a "this" pointer for the current Stream
> object. Note that "MaxFrameSize requirement" for this endpoint is 
> satisfied
> by adjusted count of descriptors. But the meaning of "flags" and
> "startingFrameNumber" is still unclear for me. :-( 

The starting frame number tells the stack at what frame number to 
schedule the transfer. There's a certain amount of frames you can 
address in the host controller and by specifying a frame number you 
could pick where in that range you'd want to schedule. Since the frame 
number concept is host controller specific and there is no way for you 
to retrieve amount and current frame number you can't use it as an 
input paramter at all. That's where the flags argument comes into play: 
It supports exactly one value USB_ISO_ASAP, which tells the stack to 
schedule the transfer as soon as possible (i.e. in the next frame that 
can safely be accessed and has enough bandwidth for the transfer). The 
starting frame number contains the picked one as an output parameter 
once the queue call has returned. Sice again you can't do anything 
useful with that number, you can just as well supply NULL as the 
startingFrameNumber which then implies USB_ISO_ASAP.

> After submitting this buffer I receive "_TransferCallback" 
> notification
> with the "status" B_OK and "actualLength" equal to 0. Looks like the 
> length
> should be not zero in case of successful transfer. I think that 
> indicate
> some problems either with my call or isochronous handling in the
> corresponding USB bus manager. I have activated extra tracing in USB
> modules and found that my transfer was submitted correctly - both 
> messages
> at line 1047
> http://dev.haiku-os.org/browser/haiku/trunk/src/add-ons/kernel/busses/usb/uhci.cpp#L1074
> > and line 1127 were displayed with correct data I have submitted. 
> Looks
> like the FinishIsochronousTransfers thread was waked up too. But there 
> are
> no more traces about this transfer. :-(

The actual length and status field you get directly from the callback 
are irrelevant in the isochronous case as you do not schedule a single 
packet but multiple ones at the same time. The actual status code as 
well as the proper actual length are stored into the 
usb_iso_packet_descriptors you supplied in the queue call. Each one of 
these will contain info on one of the scheduled packets.

> Note that I have tried this on the system with UHCI USB HW controller 
> and
> both with "ehci" and "uhci" modules. The device I have tested is USB 
> 1.1
> audio. 

Note that isochronous transfers are only implemented in UHCI. OHCI as 
well as EHCI don't support them. Also note that for the lack of 
software using isochronous transfers the UHCI implementation isn't 
heavily tested. Salvatore Benedetto (emitrax) implemented isochronous 
support for UHCI in one of the past GSoCs, so he might know some more 
details or could help you out in case of problems.

> The Questions:
>  #1: Is it correct to provide descriptors array and audio data buffer 
> from
> the same memory area in the way I have described above? Note that 
> they are
> not overlapped.

It doesn't really matter since the stack (or actually the host 
controller driver) will make copies of the data to physical memory. The 
descriptors are just filled with the status data, so nothing to worry 
about.

>  #2: What is the meaning of "startingFrameNumber" parameter in the
> queue_isochronous call? 

See above. In short just use NULL and USB_ISO_ASAP.

>  #3: Should _TransferCallback provide me a real length of transferred 
> data
> in "actualLength" parameter?

It can't provide you with meaningful data for these. The isochronous 
transmission is, unlike all other transfer types, not guaranteed to 
transfer all data. Each packet can be tranferred in full, partially or 
not at all. So you don't get a clear "this many bytes have been 
transferred" result, but you get the result for each packet 
individually. Isochronous transfers are the UDP of USB while all others 
are TCP.

>  #4: Are there any suggestions to trace anything else? :-)

If the above doesn't help you then it is possible that the 
implementation has yet unrevealed issues due to the lack of testing due 
to the lack of users of that transfer type. You could file a bug report 
if that seems likely.

Regards
Michael

Other related posts: