usbtimeout
Colin (478) 2433 posts |
The usbtimeout special field doesn’t work if the device is opened in blocking mode – which is the default mode. The problem is that gbpb_get in DeviceFS.FSystem.s waits in a loop until data appears in the buffer. This stops callbacks so the timeout doesn’t work. Putting
after label 10 in gbpb_get (which is the main loop label) fixes the problem by triggering callbacks. fs_get, fs_put and gbpb_put need similar fixes. note if the ‘sleep’ special field is used (which sleeps in a taskwindow) timeouts work because sleep_on_rx/sleep_on_tx already do a XOS_LeaveOS, XOS_EnterOS |
Jeffrey Lee (213) 6048 posts |
In the past Ben’s expressed concern about the idea of triggering callbacks from within DeviceFS operations (or, perhaps more correctly, some unnamed engineers at Pace had concerns – see here). So although this looks like a good change to you and me, maybe there’ll be some side-effects that we don’t fully understand. Unfortunately I can’t remember what problems I was solving by making the timeouts trigger in callbacks instead of directly from interrupts/RTSupport threads. Perhaps there’s another solution that can be used to trigger them, although I’m tempted to say that we should stop wasting our time with hacked-up code and instead focus on finding a way we can implement threads properly. Then we can “simply” start with a clean port of the BSD USB drivers (+ dwc_otg) and have the code rely on thread synchronisation primitives for its operation instead of unreliable callbacks and weak assumptions. |
Jeffrey Lee (213) 6048 posts |
Actually I guess the big problem is that callbacks are meant to occur when the OS is in a state where it’s safe to do filesystem operations (citation needed). But if callbacks are being triggered from within filesystem operations then that could cause all kinds of problems. |
Colin (478) 2433 posts |
Whilst I agree that half remembered problems are a good reason not to change things, DeviceFS does already trigger callbacks if the ‘sleep’ option is used for multitasking in a taskwindow. If its ok for the sleep option why isn’t it ok for the non sleep mode? If it’s wrong then it should be taken out of sleep_on_rx/sleep_on_tx. Not triggering callbacks in DeviceFS isn’t guaranteed either anyone can trigger them during an upcall.
I don’t see how threads help. The BSD drivers just queue transfers and call you back when they are complete with an optional timeout. I’m no expert on threading – I’ve only programmed RISCOS – but it seems to me that threading needs to be from the application level not the drivers themselves – the BSD drivers don’t do any blocking except during the connect device phase (hardly a problem compared to DeviceFS blocking). Using rtsupport to implement timeouts seems like a sledghammer to crack a nut to me. For me the biggest mistake was to use DeviceFS. It doesn’t fit with the USB model but we are stuck with it now so have to try and work around its problems without breaking existing programs. It should have been implemented as a library of BSD functions like the socket library. Using DeviceFS to implement SCSISoftUSB is silly, no class driver should have to use it. |
Jeffrey Lee (213) 6048 posts |
AIUI the problem we have is that when a HCD signals a timeout to USBDriver, it must be done by calling usb_transfer_complete() from a thread context (unlike regular calls to usb_transfer_complete(), which are allowed to come from interrupt contexts – I’m not quite sure why there’s the distinction between the two). For BSD this is easy, as all that needs to happen is for the HCD to push the timeout event onto a queue which is then processed by a worker thread within the driver. Standard mutexes and semaphores, etc. can be used to make sure the code will only run once it’s safe to do so. For RISC OS it’s trickier because we don’t really have any threading, so we resort to using callbacks, as they’re the only method we have/had for scheduling something for running when it’s safe to call into USBDriver. Potentially we could use an RTSupport thread to process the timeout, but that might also require us to go through USBDriver and work out why there’s the distinction between thread and non-thread contexts in the first place (USBDriver doesn’t know about RTSupport, so to it the RTSupport thread will look like an interrupt).
Indeed! |
Dave Higton (1515) 3526 posts |
I, for one, would be most grateful to see a document that states how modules and applications should do USB transfers. A USB driver for something like Bluetooth, a serial device, or a graphics tablet needs to be performing transfers with priority above the applications. Right now my Bluetooth module is doing transfers on the timer tick. I understand from recent correspondence that I shouldn’t be doing that. But what should I do instead? |
Colin (478) 2433 posts |
Its embarrassing to put it down in writing. Forget about OS_GBPB its a waste of time. Use buffer calls to read/write examine the contents of the buffer. As you know a bulk USB transfer is a sequence zero or more full packets followed by a short packet. For writing only put data into an empty buffer, only in a single block transfer and follow that with a DeviceCall WakeUpTx with the size of the transfer. The WakeUpTX size must be the size of the data you put in the buffer. This ensures that the transfer is queued as a single USB transfer and not mixed with subsequent transfers. When the buffer empties the transfer is complete. For reading use WakeUpRX when the buffer is empty, wait for UpCall_DeviceRxDataPresent and empty the buffer. If the number of bytes in the buffer is less than the bytes requested in WakeUpRX then there has been a short transfer. This is useful if you are streaming as you can request 1 buffer full and the device will give you what it has. When streaming you need to queue the next transfer with WakeUpRx from the upcall handler after you have emptied the receive buffer. Even as I write this I see problems that I can’t solve with the current interface. |
Dave Higton (1515) 3526 posts |
Thanks for the notes, Colin. Clearly I still have a lot to learn. As for DeviceFS versus BSD functions: two of you dislike the way it is. Peter Naulls suggested some years back that it would be good to see a libusb interface. That’s one I have used in Linux programming some years back. So those of us who have worked most closely on USB would like to see a change away from DeviceFS. My first question is whether DeviceFS and any replacement interface could coexist, either just by adding the new interface and letting drivers choose the interface, or by setting a system variable to get DeviceFS to leave specific devices or device classes alone. If so, a gradual transition would be possible. |
Colin (478) 2433 posts |
Yes they could. I’ve had 2 versions of a library working but scrapped them as being too complex – C callback functions were a pain to program with – but I now think I can work around that using cmhg generic veneers instead and abstracting the netbsd xfer structure. It doesn’t make usb programming easy but at least you get flexibility. Devicefs doesn’t claim any classes it’s an interface for writing classes. Yes the keyboard/mouse/ hub classes are in the usbdriver module but tellingly they don’t use the devicefs interface. |
Jeffrey Lee (213) 6048 posts |
Yes, CMHG generic veneers sounds like the way to go. That’s basically how I handled it for the VCHIQ module (which exposes a bunch of SWIs which are just wrappers around the C functions exposed by the original Linux library). When calling VCHIQ_Initialise (which registers clients with the module, corresponding to the vchi_initialise() C function) you’d pass in your private word pointer. Assembler veneers within VCHIQ would then be used to set R12 to that value before calling any of the callback functions, allowing the module to call standard CMHG generic veneers. |
Colin (478) 2433 posts |
Jeffrey. I had a look at your code for calling the callback in VCHIQ. I would never have thought of dropping into SVC mode. One puzzling point, shouldn’t the Push/Pull “lr” be “v2”? Your code is below.
|
Jeffrey Lee (213) 6048 posts |
Dropping into SVC mode is only relevant if you’re running code in SYS mode (see here). USBDriver doesn’t use SYS mode, so you should be safe from that particular nasty.
No, it’s saving and restoring lr_svc around the call to the SVC mode callback. v2 shouldn’t need preserving because the callback should preserve it for us. Looking at that code now, I can see that it could be optimised slightly if I was to swap the order of the arguments around (move private_word into the last argument, then load it directly into r12/ip. r3 then becomes the function pointer, and there’s no need to save v1 on entry. Plus of course I could have used BLX to invoke the callback, since the module isn’t intended for use on <ARMv5) |
Colin (478) 2433 posts |
Thanks. I see now you have a different set of registers after the mode change. |
Dave Higton (1515) 3526 posts |
I’ve just been reading up on UpCalls. Most odd. The idea is that they can be used so that the OS can call up to an application. But it’s not clear to me whether application memory is mapped in when the UpCall happens. Maybe it doesn’t matter… a handler has to be installed by OS_ChangeEnvironment, and that handler can be in RMA or DA. The fundamental thing that worries me is that I can’t see any way to remove the handler, short of re-installing the handler that was there before and whose address was returned to me on installing mine. But if the app crashes, my handler is still there. It doesn’t look very secure. Or have I missed something? It doesn’t really look like calling up to an application at all. It looks like calling a handler, which is separate from the app, and leaving it up to the handler to find some way of notifying the app. Presumably a pollword non-zero. OTOH an UpCall is just one way of finding out whether a transfer has happened. Polling when a transfer is expected is another. |
Dave Higton (1515) 3526 posts |
So have I understood correctly what UpCalls do, and what they don’t? Is there any tutorial stuff anywhere about handling UpCalls? If (if, again) I’ve understood correctly, an UpCall can’t call into an application; there has to be a free standing handler. What’s more, the handler can’t be in BASIC or C – it has to be in assembly language. OK, the handler is probably going to be very simple, but assembly language is the least portable language we have. |
Colin (478) 2433 posts |
Upcalls are just functions that get called by the OS when it is doing something like in EtherUSB.c.usb. It won’t page in your application. |
Rick Murray (539) 13840 posts |
Is the USB hooking into UpCall or using UpCallV ? As for upcalls or events, Bad Things can happen if the OS tries to call a routine in a module that isn’t paged in, so this stuff is better left to modules. Receiving events in C in a module is a bit faffy, but once you learn the method, it is quite simple. |
Colin (478) 2433 posts |
The buffers module – which USB uses via devicefs – and devicefs module both call OS_UpCall when certain ‘events’ happen. Third party programs can claim the UpCallV to be informed of these events. |
Dave Higton (1515) 3526 posts |
So when I want to transfer, say, 6 megabytes, does that mean I have to put all 6 meg into a buffer and make a single call? Or can it be chunked in, say, 32 kiB chunks? How would the receiving end know when a complete photograph had been sent? |
Colin (478) 2433 posts |
Assuming the protocol you are using for transfer doesn’t contain a transfer size, USB transfers are 0 or more full packets followed by a short packet. So to send 6MB you need to ensure that only whole packets are sent until the last one – it’s very similar to the wimp ram transfer protocol. For sending you can request a transfer of 6MB and keep filling the buffer with full packets. To make it simpler make the buffer a multiple of maxpacket size and keep filling it. Avoid putting a short packet into the buffer as it may get sent before you enter more data and be interpreted as an end of transfer. |