Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 15 Apr 2002 00:53:42 +0100 (BST)
From:      Andrew Gordon <arg-bsd@arg1.demon.co.uk>
To:        Peter Haight <peterh@sapros.com>
Cc:        <hardware@freebsd.org>
Subject:   Re: Reading from the USB ugen device.
Message-ID:  <20020415002005.W79917-100000@server.arg.sj.co.uk>
In-Reply-To: <200204141902.g3EJ2rRg031891@wartch.sapros.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Sun, 14 Apr 2002, Peter Haight wrote:
>
> I've been working with an application called coldsync to get my Sony Clie to
> sync to my computer. I'm using FreeBSD-4.5-STABLE. I've gotten it to work,
> but I ran into an issue I don't understand and I'd like to know what's going
> on.

I don't know anything about coldsync, but here are a few notes about
receiving with the ugen driver.  Apologies if I'm telling you stuff that's
obvious or you already know.

> The software begins by sending a USB_DISCOVER on usb0 and then tries to open
> /dev/ugen0. If that succeeds it checks to make sure the device is the Clie
> (using USB_GET_DEVICEINFO) and then it sends a vendor information request
> over the control endpoint. From the information it gets back from that it
> figures out which endpoint to open and then it opens that one (Usually
> ugen0.2). Then it sends the USB_SET_SHORT_XFER on that device.

USB_SET_SHORT_XFER indicates that you are expecting the device to indicate
the end of a transaction, rather than you knowing the exact size in
advance.

The way bulk read transfers work on the bus is that the host polls the USB
device repeatedly: the device can respond with a NAK (no data currently
available; this is just flow-control and has no impact on terminating the
transaction unless it NAKs repeatedly and a timeout occurs); or it can
respond with a full-size packet, indicating that the transaction isn't
complete and it should be polled again immediately to get more; or it can
respond with a less-than-full-size packet indicating end of transaction
(zero-length is permitted to cover the case where the transaction is a
multiple of the packet size).  The definition of 'full size' is fixed in
the device's configuration, normally 64 bytes.

> The original author of the code, had a routine which reads data from the
> ugen0.2 device 1024 bytes at a time. A comment in the code indicates that it
> needs to make sure it reads any data available because the kernel isn't
> buffering anything.
>
> Well, that would work for about 8 reads (read(2)) and if I ran it under gdb,
> each of those reads would actually just return whatever amount of data we
> were expecting according to the protocol. Probably about 6-100 bytes at a
> time.  Then it would just block indefinitely when I was expecting 64 bytes.
>
> I managed to fix it by getting rid of the 1024 byte reads and just reading
> whatever I was expecting. Once I made that change, it didn't block on that
> read and then went on to read the rest of about 6MB from the device.

With the ugen driver, you are expected to either:

  a) Don't set USB_SET_SHORT_XFER, and do read() calls with exactly
     the buffer size required.

  b) Set USB_SET_SHORT_XFER and use a buffer larger than the largest
     possible transaction; the result will contain exactly one
     transaction.

I haven't analysed exactly what happens when you use a buffer shorter than
the transaction size (the devices I've been playing with don't do huge
transfers in one lump).  However, there is an obvious ambiguity when the
transaction size turns out to be an exact multiple of the buffer size you
are using: after reading N full buffers, you don't know if the last buffer
was full because there is more data to follow, or if it was full because
the transaction ended there, as for the USB_SET_SHORT_TRANSFER=0 case.
Possibly you get a 0-length read next time, but I suspect that in fact
that the information is lost and you can't tell.

> Anyway, this all has me worried about timing with an unbuffered device. I
> don't have a good understanding of what's going on, so I'm not sure what's a
> good way to deal with this issue.

Timing isn't usually an issue with bulk transfers (unless your device has
overall throughput requirements) since the flow-control is fairly
transparent.

However, there are a number of things that work really badly with the ugen
driver:

  - Full duplex: you can only achieve this by forking two processes,
    as the driver doesn't support select()/poll(), mainly because it
    doesn't allow queued transactions.

  - Streaming throughput: You have the choice of doing large transactions,
    which give good throughput (transferred as fast as the bus/device
    can go) but leave your process blocked for the duration and unable
    to work on preparing for the next transaction; or you do small
    transactions, in which case the throughput drops off disasterously
    since there will be a delay between exiting one read()/write() call
    on the driver and initiating the next, during which the USB bus
    goes idle - this will often be quite a long delay if your process
    has got de-scheduled in the meantime.

I had been thinking of adding another mode to ugen where there is a queue
of read/write transactions and you can use select() to pace the creation
of new transactions - as you do with a socket or a tty style device.  This
would be quite easy to add to ugen to suit my requirements (maintaining a
smooth stream of smallish (~1Kbyte) transactions in both directions), but
enhancing it to solve your problem of wanting to break a huge transaction
into small pieces (and presumably to start processing those pieces before
the transaction is complete) would be more tricky.


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hardware" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020415002005.W79917-100000>