Cannot read using USB Skeleton Driver
Hi, I have been trying to communicate with a custom usb device from a SAM9G20-EK board using the usb-skeleton.c driver example in the linux source. I modified the driver to add my device's vendor and product id. The USB device enumerates with two bulk endpoints - one IN and one OUT. The (modified) skel driver successfully detects my device and I get a minor number allocated. After creating a device file with that minor number, I am able to write into the device by typing 'echo "HI"
/dev/mydevice'. I verified that this data arrives at my device intact. Next, I started sending data back from the device and tried to read it back using 'cat /dev/mydevice'. The problem is that read not only doesn't work, but also makes the app get totally stuck. Ctrl-C doesn't work and I have to reboot the board.
Some debugging using printk's led me to the following snippet which seems to be causing the hang. If I change the wait to a wait_for_completion_interruptible, Ctrl-C starts working, but still no data is received. if (!dev->processed_urb) { /* * the URB hasn't been processed * do it now */ wait_for_completion(&dev->bulk_in_completion); dev->bulk_in_copied = 0; dev->processed_urb = 1; } Oh, btw I am using linux kernel 2.6.39.4. Any Ideas anyone?? Regards, Felix.
On Thu, Sep 15, 2011 at 07:02:59PM +0530, Felix Varghese wrote:
Hi,
I have been trying to communicate with a custom usb device from a SAM9G20-EK board using the usb-skeleton.c driver example in the linux source. I modified the driver to add my device's vendor and product id. The USB device enumerates with two bulk endpoints - one IN and one OUT. The (modified) skel driver successfully detects my device and I get a minor number allocated. After creating a device file with that minor number, I am able to write into the device by typing 'echo "HI"
/dev/mydevice'. I verified that this data arrives at my device intact. Next, I started sending data back from the device and tried to read it back using 'cat /dev/mydevice'. The problem is that read not only doesn't work, but also makes the app get totally stuck. Ctrl-C doesn't work and I have to reboot the board.
Some debugging using printk's led me to the following snippet which seems to be causing the hang. If I change the wait to a wait_for_completion_interruptible, Ctrl-C starts working, but still no data is received.
if (!dev->processed_urb) { /* * the URB hasn't been processed * do it now */ wait_for_completion(&dev->bulk_in_completion); dev->bulk_in_copied = 0; dev->processed_urb = 1; }
Oh, btw I am using linux kernel 2.6.39.4. Any Ideas anyone??
Odds are you need to send the proper command to your device in order to get it to send data to you. You are hanging, waiting for device to be sent from the device to userspace, and if no data is sent, then it will continue to wait. So, please read the documentation on how to talk to your device and send the proper USB commands to it and all should be fine. But note, the usb-skeleton driver really isn't a good framework for a "real" USB device in that it doesn't expose the proper userspace interface for it. What type of device is this you are wishing to write a driver for? That will determine the type of interface you need to use, odds are it is not going to be a simple char device. greg k-h
What type of device is this you are wishing to write a driver for? That will determine the type of interface you need to use, odds are it is not going to be a simple char device.
The device is a custom USB dongle whose firmware is also under development. We are still in the process of deciding what it should actually show up as on the linux side but right now, we decided to use a character device for simplicity reasons until we have simple two-way communication over USB up and running. Since I am writing the firmware on the device side too, it had indeed been (and still is) my first suspect :). But I checked the code multiple times and now I am reasonably sure that I am in fact, doing all that is required to send out data properly. Besides, data transfer in the other direction works fine. I have registered only one IN and one OUT endpoint apart from endpoint 0 and I try to send my data through the IN end-point. I noticed that after making the wait interruptible, if I hit Ctrl-C while we are hanging on a read, the device side shows transfer complete. So the device, apparently, thinks the data has been sent successfully when we have actually aborted the transfer at the other end! Shouldn't this mean that something happens after coming out of the wait_for_completion that really makes the transfer happen? Regards, Felix.
On Fri, Sep 16, 2011 at 04:59:08PM +0530, Felix Varghese wrote:
What type of device is this you are wishing to write a driver for? That will determine the type of interface you need to use, odds are it is not going to be a simple char device.
The device is a custom USB dongle whose firmware is also under development. We are still in the process of deciding what it should actually show up as on the linux side but right now, we decided to use a character device for simplicity reasons until we have simple two-way communication over USB up and running. Since I am writing the firmware on the device side too, it had indeed been (and still is) my first suspect :). But I checked the code multiple times and now I am reasonably sure that I am in fact, doing all that is required to send out data properly. Besides, data transfer in the other direction works fine. I have registered only one IN and one OUT endpoint apart from endpoint 0 and I try to send my data through the IN end-point.
For real simplicity, just use the usb-serial generic interface and you will get a tty device node created for you that you can read and write data from. Just do: modprobe usb_serial vendor=0x0000 product=0x0000 with the proper vendor and product ids for your device, then plug it in. No kernel changes needed at all, just have a pair of bulk in/out endpoints and all will work automatically for you. But that interface is slow as there is only one urb in flight at any point in time, so don't rely on that for anything but basic development and testing.
I noticed that after making the wait interruptible, if I hit Ctrl-C while we are hanging on a read, the device side shows transfer complete. So the device, apparently, thinks the data has been sent successfully when we have actually aborted the transfer at the other end! Shouldn't this mean that something happens after coming out of the wait_for_completion that really makes the transfer happen?
Look at the USB data on the wire to make sure that you really are sending data properly. good luck, greg k-h
Just do: modprobe usb_serial vendor=0x0000 product=0x0000 with the proper vendor and product ids for your device, then plug it in.
No kernel changes needed at all, just have a pair of bulk in/out endpoints and all will work automatically for you.
Thanks Greg, I had thought that the usbserial driver would only work with proper CDC class virtual COM devices. I compiled it and tried it on my device and it worked fine! I was able to do bidirectional communication. So at least now I can be sure that one end is working. But the same device with the same firmware still doesn't work with the (customized) usb-skeleton. What could be wrong? Regards, Felix.
On Mon, Sep 19, 2011 at 11:13:33AM +0530, Felix Varghese wrote:
Just do: modprobe usb_serial vendor=0x0000 product=0x0000 with the proper vendor and product ids for your device, then plug it in.
No kernel changes needed at all, just have a pair of bulk in/out endpoints and all will work automatically for you.
Thanks Greg, I had thought that the usbserial driver would only work with proper CDC class virtual COM devices. I compiled it and tried it on my device and it worked fine! I was able to do bidirectional communication. So at least now I can be sure that one end is working. But the same device with the same firmware still doesn't work with the (customized) usb-skeleton. What could be wrong?
As I don't know what you customized exactly, nor how your hardware exactly works, I could not say, sorry. greg k-h
Oh I don't think I have customized it to the point of breaking it. I just modified the VID and PID to those of my device. And later, when I noticed where it was getting stuck, I changed the wait_for_completion to wait_for_completion_interruptible (in the code snippet quoted previously) so that I could recover with Ctrl-C instead of a reboot. And my hardware works fine with the usbserial driver you suggested (I can read/write to it from a simple terminal program). But, to develop a full-fledged driver, the skeleton driver seemed like a better starting point, which is I am still trying to get it working. The condition which causes the code to wait for eternity (from the line I quoted in one of my previous mails) seems to be: if (!dev->processed_urb) But I notice that this variable is not true even for the first read. So the code seems to wait for the completion of something that has not yet started. Also, I do not see this variable being explicitly set to false either, anywhere within the driver. I may be on the wrong track, but isn't this variable one that is used only within our driver? If so, what exactly is it used for, considering that it is only set to 1 (never to 0) and is 0 initially? Regards, Felix.
On Mon, Sep 19, 2011 at 06:51:18PM +0530, Felix Varghese wrote:
Oh I don't think I have customized it to the point of breaking it. I just modified the VID and PID to those of my device. And later, when I noticed where it was getting stuck, I changed the wait_for_completion to wait_for_completion_interruptible (in the code snippet quoted previously) so that I could recover with Ctrl-C instead of a reboot. And my hardware works fine with the usbserial driver you suggested (I can read/write to it from a simple terminal program). But, to develop a full-fledged driver, the skeleton driver seemed like a better starting point, which is I am still trying to get it working.
The condition which causes the code to wait for eternity (from the line I quoted in one of my previous mails) seems to be: if (!dev->processed_urb) But I notice that this variable is not true even for the first read. So the code seems to wait for the completion of something that has not yet started. Also, I do not see this variable being explicitly set to false either, anywhere within the driver. I may be on the wrong track, but isn't this variable one that is used only within our driver? If so, what exactly is it used for, considering that it is only set to 1 (never to 0) and is 0 initially?
You might be right and the code might be wrong, care to send a patch for it correcting the issue? thanks, greg k-h
With some modification, I have finally made the skel driver work with my device. I think I have some initial theories about the root cause of the problems that I ran into with skel: 1) The condition if (!dev->processed_urb) is always false in the beginning and this induces a wait without any scope for completion, so it has to be removed. 2) A blocking read and blocking write cannot happen simultaneously. This is because in read, we go to sleep after locking a mutex 'iomutex'. Before writing, we try to lock the same mutex, so if we try to write() from one thread (or process) while waiting for read() to complete in another, we will have both threads blocked, waiting indefinitely for each other (the device won't respond until you send data to it). This can be fixed by unlocking the mutex before entering into the wait but then that mutex might be there for a reason and I might be doing the wrong thing. 3) If the buffer associated with your read urb is not big enough to accommodate the packet that arrives over USB, the transfer request will fail with error -75 in the completion callback. We could handle this by either propagating the error back to the user or by issuing transfer requests with big enough buffers and keeping the data buffered until the user asks for it. Then we could give as much data as he asks for and keep the rest buffered. But in the latter case, the user may issue a read() call expecting a transfer to happen, but he might not necessarily see one (in case there is data already buffered). Also the buffer size would be somewhat arbitrarily chosen which is not nice. Does anybody have anything to add or oppose on this or would you guys rather have me send a patch along with my changes? Regards, Felix.
participants (2)
-
Felix Varghese -
Greg KH