David and Greg: Stepping back and trying to look at the bigger picture led to the following thoughts regarding the synchronous API.
First of all, what's the reason for including usb_control_msg() and usb_bulk_msg() in the API at all? Clearly, it's to make things easier for device drivers, in a number of ways: (1). It only requires a single function call. (2). There's no need to allocate/free memory or any other resources; the API does it automatically. (3). There's no need to write a completion routine. (4). It automatically waits for the transfer to complete before returning (i.e., it is synchronous). (5). It provides timeouts, which are not so easy to program using the lower-level API calls. There is basically just one disadvantage: you can't abort or unlink the transfer, either (6). for some internal driver-related purpose, or (7). because disconnect() was called and it needs to cancel all the ongoing transfers. A replacement API should keep (1) - (5) while providing a way to handle (7) at least. (6) isn't so much of an issue -- obviously no driver is trying to do it presently because it is currently impossible, so adding (6) would only serve to assist future driver development. Our recent efforts have been aimed at replacing the API with a pair of function calls: one to submit and one to wait-for-completion/timeout. This goes slightly against (1), but not enough to really matter. It provides a way to handle (6) and (7) because the submit function can return some sort of handle that can later be passed to a cancel routine if needed. David's last proposal was that this handle could simply be an urb (or more properly, a pointer to an urb). If a struct completion member were added into struct urb then all the required facilities would be present right there, which would make things very simple. Or would it? The urb in question has to be allocated and freed somehow, and either the driver or the core has to take care of this. By (2), this ought to be handled by the core. So the submit routine allocates the urb and passes a pointer to it back to the caller, while the wait-for-completion/timeout routine frees the urb. But this leads to an unavoidable race should the driver try to unlink the urb: There's no way to insure that the unlink call arrives before the urb has been freed. Okay, so this means that the driver has to allocate and free the urb. What used to be a simple function call has now turned into (without error checking): urb = usb_allocate_urb(...); usb_submit_bulk_msg(urb, ...); status = usb_wait_msg(urb, timeout, &bytes); usb_free_urb(urb); Now consider what happens when we add the following enhancements to the low-level API... David's io_wait_completion() is a good thing. It belongs in the kernel proper, of course, but we are better off having it available in any form. Adding a struct completion member to struct urb is also a powerful tool. For instance, consider this advantage: There will no longer be any need to have a completion routine for every urb. The core can simply check, and if the pointer to the completion handler is NULL then the core can just call complete_all() on the struct completion member. What if there is a completion function -- who should call complete_all() then? There are two concerns: (a). The completion function will most likely need to examine some of the fields in the urb. But the routines waiting on the struct completion might want to alter those same fields. Hence complete_all() can't be called _before_ the completion function. (b). The completion function might reinitialize and resubmit the urb. Once this is done it's too late to call complete_all() -- doing so would awaken tasks that could be waiting for the newly-submitted urb instead of the previously-finished one. Hence complete_all() can't be called _after_ the completion function returns. Putting (a) and (b) together, it's clear the complete_all() has to be called from within the completion function. That makes sense; after all, the driver best knows when the proper time is to awaken the waiting tasks. Another advantage of doing this is that it entirely removes the need for a synchronous form of usb_unlink_urb()! Provided the driver makes sure that the completion function calls comlete_all() at the proper time (or if there is no completion function), the effect of a synchronous unlink can be achieved just be doing: usb_unlink_urb(urb); wait_for_completion(&urb->cmplt); With just these two API additions, look at what usb_bulk_msg() turns into as a series of low-level API calls (again without error checking): urb = usb_allocate_urb(...); usb_fill_bulk_urb(urb, ...); // No completion handler usb_submit_urb(urb); if (io_wait_completion(&urb->cmplt, timeout) == -ETIMEDOUT) { usb_unlink_urb(urb); io_wait_completion(&urb->cmplt, 0); } /* Save urb->status, urb->actual_length */ usb_free_urb(urb); That's not too much worse than the high-level API. (Furthermore, the fourth-through-seventh lines could be encapsulated in a simple function.) Okay, the difference is more pronounced for control messages. But it certainly reduces the impetus for using usb_bulk_msg(). Here is a radical new thought. Suppose we could gain advantage (7) (although not (6)) without changing the synchronous API at all! Since (6) isn't really necessary, this would be among the better, if not the best, of all possible worlds. Unfortunately, to do this requires a far-reaching change. It probably should have been done earlier (for all I know, it was proposed but nobody thought it was a good idea or wanted to do it). Here's the idea: Change the struct usb_device *dev; member of struct urb into struct usb_interface *intf; This is only logical. Since drivers are bound to interfaces, not devices, they should submit request blocks bound to their interface, not their device. Of course the core can easily find the device given the interface pointer. One ramification would be the need to generate a pseudo-interface for each device, so that the core's own device-configuration I/O can be carried out without the need to refer to a real interface (which wouldn't be accessible anyway if the device had not yet been configured). That shouldn't present any problem. The main point of the change is that drivers would not need to be responsible for cancelling all the transfers when their disconnect() routine is called -- the core would be able to do it for them. Since urbs would now be marked as belonging to a particular interface, it would be easy for the HC driver code to find and unlink all urbs attached to a given interface. The implementation would be simple. Before calling a driver's disconnect() for whatever reason (physical disconnect, rmmod, configuration change, altsetting change, etc.) -- and also following an unsuccessful probe() -- first flag the struct interface not to allow any new urb submissions. After calling disconnect(), tell the HC driver to unlink all the urbs belonging to the interface in question. When the entire procedure is finished, flag the interface to allow submissions again (if appropriate). I realize this would be a big change. But maybe it's worthwhile. Alan Stern ------------------------------------------------------- This SF.net email is sponsored by: Etnus, makers of TotalView, The debugger for complex code. Debugging C/C++ programs can leave you feeling lost and disoriented. TotalView can help you find your way. Available on major UNIX and Linux platforms. Try it free. www.etnus.com _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel