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>