From: Ludovic R. <lud...@gm...> - 2008-02-24 16:34:15
|
Hello, I am using libusb 0.1.12 and adapted my application to use usb_interrupt_read() instead of polling my USB device. The problem is that usb_interrupt_read() is really CPU intensive unless the patch to use poll() is applied. This patch has been applied in revision 657 [1] of libusb and I would be really happy if a libusb-0.1.13 version were released soon. I know that libusb-0.1.x will not evolved much. But at least provide new stable/official versions so they are packaged by Linux distributions and available to normal users. Thanks for your work [1] http://libusb.svn.sourceforge.net/viewvc/libusb?view=rev&revision=657 -- Dr. Ludovic Rousseau |
From: Daniel D. <ds...@ge...> - 2008-02-24 23:47:23
|
Ludovic Rousseau wrote: > I am using libusb 0.1.12 and adapted my application to use > usb_interrupt_read() instead of polling my USB device. The problem is > that usb_interrupt_read() is really CPU intensive unless the patch to > use poll() is applied. This patch has been applied in revision 657 [1] > of libusb and I would be really happy if a libusb-0.1.13 version were > released soon. Yes, planning on it, just short on time. If you would like to help, there are 2 things you could do (and provide feedback on): 1. apply the patch and confirm it helps the CPU usage or wakeups 2. confirm that timeouts are still reasonably accurate when the patch is applied Thanks, Daniel |
From: Ludovic R. <lud...@gm...> - 2008-02-25 07:42:56
|
On Mon, Feb 25, 2008 at 12:49 AM, Daniel Drake <ds...@ge...> wrote: > Ludovic Rousseau wrote: > > I am using libusb 0.1.12 and adapted my application to use > > usb_interrupt_read() instead of polling my USB device. The problem is > > that usb_interrupt_read() is really CPU intensive unless the patch to > > use poll() is applied. This patch has been applied in revision 657 [1] > > of libusb and I would be really happy if a libusb-0.1.13 version were > > released soon. > > Yes, planning on it, just short on time. Great news. > If you would like to help, > there are 2 things you could do (and provide feedback on): > > 1. apply the patch and confirm it helps the CPU usage or wakeups I only examined usb_interrupt_read() using strace. CPU usage is at zero if nothing happens (as it should be). The function returns as soon as an USB interrupt is received. I am not sure this is what you call "wakeups". > 2. confirm that timeouts are still reasonably accurate when the patch is > applied I will try to do some mesurements with and without the patch. I guess you are also interested in mesurements for usb_bulk_write() and usb_bulk_read(). Aren't you ? bye -- Dr. Ludovic Rousseau |
From: Daniel D. <ds...@ge...> - 2008-02-25 10:03:07
|
Ludovic Rousseau wrote: >> 2. confirm that timeouts are still reasonably accurate when the patch is >> applied > > I will try to do some mesurements with and without the patch. > > I guess you are also interested in mesurements for usb_bulk_write() > and usb_bulk_read(). Aren't you ? No - they use the same codepath, so just one test will be enough. Thanks, Daniel |
From: Ludovic R. <lud...@gm...> - 2008-02-25 12:50:45
|
On Mon, Feb 25, 2008 at 11:05 AM, Daniel Drake <ds...@ge...> wrote: > Ludovic Rousseau wrote: > >> 2. confirm that timeouts are still reasonably accurate when the patch is > >> applied > > > > I will try to do some mesurements with and without the patch. > > > > I guess you are also interested in mesurements for usb_bulk_write() > > and usb_bulk_read(). Aren't you ? > > No - they use the same codepath, so just one test will be enough. I made some measurements of usb_interrupt_read() and analysis using gnumeric: With the patch: * timeout = 10ms Mean 101411.787234043 Standard Error 170.018637993897 Median 100956 Mode 100956 Standard Deviation 2018.8594687118 Sample Variance 4075793.5544073 Kurtosis 53.2611818973682 Skewness 6.80444364681593 Range 19442 Minimum 99947 Maximum 119389 Sum 14299062 Count 141 max error: 19% * timeout = 100ms Mean 101501.342857143 Standard Error 138.887498289193 Median 100953.5 Mode 100952 Standard Deviation 1643.33904150796 Sample Variance 2700563.2053443 Kurtosis 35.5553920564072 Skewness 5.03065529213846 Range 15336 Minimum 99927 Maximum 115263 Sum 14210188 Count 140 Max error : 15% * timeout = 1000ms (1s) Mean 1001269.15789474 Standard Error 235.230908866806 Median 1000826 Mode 1000826 Standard Deviation 1025.34776014763 Sample Variance 1051338.02923977 Kurtosis 8.04576600757842 Skewness 2.38403375528394 Range 5025 Minimum 999799 Maximum 1004824 Sum 19024114 Count 19 Max error: 0.4% * timeout = 10000ms (10s) Mean 10000477.7333333 Standard Error 346.532824173635 Median 10000501 Mode 9997522 Standard Deviation 2684.23171387742 Sample Variance 7205099.89378531 Kurtosis 8.93120645474251 Skewness 2.17457325598902 Range 16245 Minimum 9997440 Maximum 10013685 Sum 600028664 Count 60 Max error: 0.13% Without the patch: * timeout = 10ms Mean 13265.9287671233 Standard Error 60.2334858247588 Median 12963 Mode 12964 Standard Deviation 1150.75913089122 Sample Variance 1324246.57732952 Kurtosis -0.397349417432585 Skewness 0.699974710207873 Range 6003 Minimum 10963 Maximum 16966 Sum 4842064 Count 365 max error: 69% * timeout = 100ms Mean 104965.746974697 Standard Error 21.8742690374952 Median 104952 Mode 104951 Standard Deviation 659.501049389289 Sample Variance 434941.634145573 Kurtosis 231.666532473663 Skewness 9.98803050158192 Range 17916 Minimum 101020 Maximum 118936 Sum 95413864 Count 909 max error: 18% * timeout = 1000ms (1s) Mean 1004667.98113208 Standard Error 143.7370186997 Median 1004823 Mode 1004823 Standard Deviation 1046.42129129138 Sample Variance 1094997.51886792 Kurtosis 3.28400836076095 Skewness -1.89600502906717 Range 4052 Minimum 1001774 Maximum 1005826 Sum 53247403 Count 53 max error: 0.58% * timeout = 10000ms (10s) Mean 10002392.9375 Standard Error 125.166471440704 Median 10002518.5 Mode 10002519 Standard Deviation 500.665885762818 Sample Variance 250666.329166667 Kurtosis 1.64619650943271 Skewness -0.348071866581465 Range 2010 Minimum 10001508 Maximum 10003518 Sum 160038287 Count 16 max error: 0.035% For a timeout of 10ms we have an error max of 69% without the patch and 19% with the patch. For 100ms it is 15% without the patch and 18% with the patch. I would say that the use of the patch (greatly) improves the precision of timeout for a short value of timeout. And the error is not really worse for longer timeouts. Do you want more results? Note: I am not a statistician -- Dr. Ludovic Rousseau |
From: Daniel D. <ds...@ge...> - 2008-02-25 13:46:37
|
Ludovic Rousseau wrote: > For a timeout of 10ms we have an error max of 69% without the patch > and 19% with the patch. > For 100ms it is 15% without the patch and 18% with the patch. > > I would say that the use of the patch (greatly) improves the precision > of timeout for a short value of timeout. And the error is not really > worse for longer timeouts. That's great, thanks so much for checking it in such detail :) Daniel |
From: Ludovic R. <lud...@gm...> - 2008-02-25 15:06:08
|
On Mon, Feb 25, 2008 at 2:48 PM, Daniel Drake <ds...@ge...> wrote: > Ludovic Rousseau wrote: > > For a timeout of 10ms we have an error max of 69% without the patch > > and 19% with the patch. > > For 100ms it is 15% without the patch and 18% with the patch. > > > > I would say that the use of the patch (greatly) improves the precision > > of timeout for a short value of timeout. And the error is not really > > worse for longer timeouts. > > That's great, thanks so much for checking it in such detail :) I am using the SVN version and find that the patch solves the CPU consumption problem with usb_interrupt_read() but usb_bulk_write() and usb_bulk_read() are not working correctly now. I try to understand what's wrong. Maybe two versions of usb_urb_transfer() will be needed? -- Dr. Ludovic Rousseau |
From: Ludovic R. <lud...@gm...> - 2008-02-25 15:27:33
|
On Mon, Feb 25, 2008 at 4:05 PM, Ludovic Rousseau <lud...@gm...> wrote: > I am using the SVN version and find that the patch solves the CPU > consumption problem with usb_interrupt_read() but usb_bulk_write() and > usb_bulk_read() are not working correctly now. > > I try to understand what's wrong. I have the problem only if I use usb_interrupt_read() in one thread (this call is blocked until the next interrupt) and usb_bulk_read()/usb_bulk_write() in another thread. In the two threads I use the same usb_dev_handle *dev but with different end points. So I have two threads using poll() on the same dev->fd. Maybe one thread is getting the result awaited by the other thread? I tried using a different usb_dev_handle *dev by calling usb_open() a second time just to be able to use usb_interrupt_read() on a different file descriptor. But usb_interrupt_read() fails with "Device or resource busy". My question: is it supported to use usb_bulk_read()/usb_bulk_write() while a usb_interrupt_read() is already running on the device? bye -- Dr. Ludovic Rousseau |
From: Ludovic R. <lud...@gm...> - 2008-02-25 16:00:10
Attachments:
patch.txt
|
On Mon, Feb 25, 2008 at 4:27 PM, Ludovic Rousseau <lud...@gm...> wrote: > I have the problem only if I use usb_interrupt_read() in one thread > (this call is blocked until the next interrupt) and > usb_bulk_read()/usb_bulk_write() in another thread. > > In the two threads I use the same usb_dev_handle *dev but with > different end points. So I have two threads using poll() on the same > dev->fd. Maybe one thread is getting the result awaited by the other > thread? > > I tried using a different usb_dev_handle *dev by calling usb_open() a > second time just to be able to use usb_interrupt_read() on a different > file descriptor. But usb_interrupt_read() fails with "Device or > resource busy". > > My question: is it supported to use usb_bulk_read()/usb_bulk_write() > while a usb_interrupt_read() is already running on the device? I changed the code so that usb_bulk_read/usb_bulk_write are using the old code with select() and usb_interrupt_read/usb_interrupt_write are using the new code with poll(). I do not have the problem now. Patch attached. The patch is not very nice sine some code is duplicated. I can improve it (factorise code) if you decide it is the way to go. bye -- Dr. Ludovic Rousseau |
From: Graeme G. <gr...@ar...> - 2008-02-26 06:55:28
|
Ludovic Rousseau wrote: > I have the problem only if I use usb_interrupt_read() in one thread > (this call is blocked until the next interrupt) and > usb_bulk_read()/usb_bulk_write() in another thread. > > In the two threads I use the same usb_dev_handle *dev but with > different end points. So I have two threads using poll() on the same > dev->fd. Maybe one thread is getting the result awaited by the other > thread? I think I've been bitten by this one, when a downstream user of my software substituted a (linux distro) version of libusb with this patch in it in place of the unpatched version I was supplying. I'm relying on being able to access different end points with different threads, and the unpatched libusb works well in my particular application. I'm not sure that merely treating interrupt and bulk end points differently is going to solve the problem, since my end points may well be of the same type. Graeme Gill. |
From: Ludovic R. <lud...@gm...> - 2008-02-26 07:39:54
|
On Tue, Feb 26, 2008 at 7:52 AM, Graeme Gill <gr...@ar...> wrote: > Ludovic Rousseau wrote: > > I have the problem only if I use usb_interrupt_read() in one thread > > (this call is blocked until the next interrupt) and > > usb_bulk_read()/usb_bulk_write() in another thread. > > > > In the two threads I use the same usb_dev_handle *dev but with > > different end points. So I have two threads using poll() on the same > > dev->fd. Maybe one thread is getting the result awaited by the other > > thread? > > I think I've been bitten by this one, when a downstream > user of my software substituted a (linux distro) version > of libusb with this patch in it in place of the unpatched version > I was supplying. I'm relying on being able to access different > end points with different threads, and the unpatched libusb works > well in my particular application. > > I'm not sure that merely treating interrupt and bulk end points > differently is going to solve the problem, since my end points may > well be of the same type. With my latest patch your application should work as before. usb_bulk_read/usb_bulk_write will use the old code as before so no problem here. usb_interrupt_read/usb_interrupt_write will use the new code (using poll). You may have problems if you call usb_interrupt_* from two threads at the same time on the _same_ end-point. But I guess you should NOT use the SAME end point from two threads at the same time. Is it what you do? Bye -- Dr. Ludovic Rousseau |
From: Daniel D. <ds...@ge...> - 2008-02-26 09:22:16
|
Ludovic Rousseau wrote: > On Tue, Feb 26, 2008 at 7:52 AM, Graeme Gill <gr...@ar...> wrote: >> Ludovic Rousseau wrote: >> > I have the problem only if I use usb_interrupt_read() in one thread >> > (this call is blocked until the next interrupt) and >> > usb_bulk_read()/usb_bulk_write() in another thread. >> > >> > In the two threads I use the same usb_dev_handle *dev but with >> > different end points. So I have two threads using poll() on the same >> > dev->fd. Maybe one thread is getting the result awaited by the other >> > thread? >> >> I think I've been bitten by this one, when a downstream >> user of my software substituted a (linux distro) version >> of libusb with this patch in it in place of the unpatched version >> I was supplying. I'm relying on being able to access different >> end points with different threads, and the unpatched libusb works >> well in my particular application. >> >> I'm not sure that merely treating interrupt and bulk end points >> differently is going to solve the problem, since my end points may >> well be of the same type. > > With my latest patch your application should work as before. > usb_bulk_read/usb_bulk_write will use the old code as before so no > problem here. > > usb_interrupt_read/usb_interrupt_write will use the new code (using > poll). You may have problems if you call usb_interrupt_* from two > threads at the same time on the _same_ end-point. Looking closer at the code, it does appear that current libusb will work (at least in terms of bulk/intr I/O) in a multithreaded application. The patch to reduce wakeups will break that. The reason for the breakage is that one thread will reap the URB for another endpoint (thread). The current code works fine here due to checking every 1ms regardless of if another thread has reaped your URB. Imagine the select() call as a simple usleep(1000) instead - this is more or less what is happening. Also see this comment: /* * HACK: The use of urb.usercontext is a hack to get threaded applications * sort of working again. Threaded support is still not recommended, but * this should allow applications to work in the common cases. Basically, * if we get the completion for an URB we're not waiting for, then we update * the usercontext pointer to 1 for the other threads URB and it will see * the change after it wakes up from the the timeout. Ugly, but it works. */ Your patch which treats interrupt and bulk separately may work for your application but it will break in other circumstances. As Tim pointed out, these are effectively the same thing. So given that people seem to be successfully running libusb-0.1 in multithreaded applications, it seems we need to stick with the current system of 1ms wakeups. libusb-1.0 will fix this properly. Daniel |
From: Ludovic R. <lud...@gm...> - 2008-02-26 09:46:26
|
On Tue, Feb 26, 2008 at 10:24 AM, Daniel Drake <ds...@ge...> wrote: > So given that people seem to be successfully running libusb-0.1 in > multithreaded applications, it seems we need to stick with the current > system of 1ms wakeups. Argh! But you are right. > libusb-1.0 will fix this properly. Any planned date for a libusb-1.0 release? bye -- Dr. Ludovic Rousseau |
From: Graeme G. <gr...@ar...> - 2008-02-26 12:45:53
|
Daniel Drake wrote: > So given that people seem to be successfully running libusb-0.1 in > multithreaded applications, it seems we need to stick with the current > system of 1ms wakeups. I've just done some testing that confirms that the (for instance) Fedora libusb-0.1.12-wakeups.patch breaks my application (ArgyllCMS) because it breaks the ability to access different end points with different threads. Are there any ideas on how to fix this properly (short of the almost-mythical-V1.0 :-) ? Would doing a reap after a poll timeout to check if it's the right context solve the problem ? Graeme Gill. |
From: Daniel D. <ds...@ge...> - 2008-02-26 13:06:29
|
Graeme Gill wrote: > Would doing a reap after a poll timeout to check if it's > the right context solve the problem ? It would solve the hang problems people have described but would cause the transfer to only complete after the timeout, rather than when it actually completed (typically much earlier). Daniel |
From: Graeme G. <gr...@ar...> - 2008-02-26 14:59:48
|
Daniel Drake wrote: > It would solve the hang problems people have described but would cause > the transfer to only complete after the timeout, rather than when it > actually completed (typically much earlier). It seems to me that the problem is that poll() only wakes up one (random) thread, whereas what is needed is something that will wake up all the threads sleeping on the fd, so that they can check if it's their URB or timeout. Graeme Gill. |
From: Daniel D. <ds...@ge...> - 2008-02-26 15:07:02
|
Graeme Gill wrote: > It seems to me that the problem is that poll() only wakes > up one (random) thread, whereas what is needed is > something that will wake up all the threads sleeping on > the fd, so that they can check if it's their URB or timeout. It probably wakes up all the threads (that would be easy enough to test with a printf). The more important part of the loop is the REAPURBNDELAY ioctl. If all the threads do get woken up by poll(), only one will reap the completed URB, and the others will go back to poll(). Once an urb is reaped it will not trigger further poll() activity. Daniel |
From: Graeme G. <gr...@ar...> - 2008-02-26 16:59:53
|
Daniel Drake wrote: > It probably wakes up all the threads (that would be easy enough to test > with a printf). I don't think it does. I tried using poll() as as a "smart" delay in the REAPURBNDELAY ioctl(), and this sort of works, but causes unnecessary delays when the wrong thread gets woken up. ie. Thread A gets the URB from thread B, notices that it is not for it, so goes back to sleep. Thread A doesn't get woken up until it times out, whereupon it notices that it's URB is already there. > The more important part of the loop is the REAPURBNDELAY > ioctl. If all the threads do get woken up by poll(), only one will reap > the completed URB, and the others will go back to poll(). Once an urb is > reaped it will not trigger further poll() activity. Yes, it would work fine if all the threads get woken up. But I don't think this is the case. It might be possible to persuade some other system call to be woken up in all the threads such as epoll, but it's hard to know exactly how to set things up such that this will work. Graeme Gill. |
From: Graeme G. <gr...@ar...> - 2008-02-28 06:49:39
Attachments:
ggpatch
|
Hi there, After going through about 4 different approaches to solve the threading issue on Linux for interrupt and bulk end point transfers to improve on the spin-with- 1msec-sleeps approach, I have hit upon something that seems to work (at least for my test set). In the tradition of this endeavour, it's a bit of a hack, but there don't seem to be too many alternative that don't involve adding threads etc. like OpenUSB does. The key thing is the observation that the usbfs will issue a real time signal if you put it in the urb.signr slot. It's just a matter then of making each thread use it's own RT signal (that's the hacky part). To make it work reliably and avoid race conditions some locking is needed with a semaphore, and signals don't play very nice with threads, but it does seem to hang together. I think I've probably fixed a race condition or two that shows up in the existing code - my application fails on occasions when the wrong urb is reaped after discard a timed out urb. With the re-write, it now deals with this properly. So here for others to evaluate is a patch set against the libusb-0.1.12 release, the changes being in libusb.h (minor), and libusb.c. The changed functions are: device_open() Addition of semaphore allocation usb_os_open() " usb_os_close() " usb_urb_transfer() Almost totally re-written. Graeme Gill. |
From: Daniel D. <ds...@ge...> - 2008-03-20 22:06:20
|
Hi Graeme, Sorry for the delay in getting to this. Graeme Gill wrote: > OK, well how about a less radical fix for the problem where a timeout > in one thread causes the discarding of another threads URB ? > (This one causes failures for my application using 0.1.12). Yes, this is the kind of thing I will consider. As you know this code wasn't written by me, and as it seems that you've spent a few grueling hours inside this function, would you mind clarifying the sequence of events that can lead to the bug your describe? The only code I can see inside the function that discards an URB is this bit: /* If the URB didn't complete in success or error, then let's unlink it */ if (ret < 0 && !urb.usercontext) { [...] ret = ioctl(dev->fd, IOCTL_USB_DISCARDURB, &urb); So, "urb" gets discarded. But looking through the rest of that function, I cannot see where urb gets set to anything *other* than the URB that was allocated for I/O in that particular thread, so I haven't entirely grasped how an URB belonging to another thread can be discarded. > Attached is such a patch. Waiting for URB's is still done by polling > at 1msec intervals (even faster when multiple threads are doing it) > rather than employing signals and waking the appropriate thread. I've had a look at the patch, it looks OK, but I'm worried that there is one behaviour change. The current code discards the URB if it gets confused (see above: if the URB didn't complete in success or error), whereas your version seems to just bail out: if (errno != EAGAIN) { fprintf(stderr, "error reaping URB: %s", strerror(errno)); break; } Thanks! Daniel |
From: Graeme G. <gr...@ar...> - 2008-03-25 15:43:01
|
Daniel Drake wrote: > As you know this code wasn't written by me, and as it seems that you've > spent a few grueling hours inside this function, would you mind > clarifying the sequence of events that can lead to the bug your describe? > > The only code I can see inside the function that discards an URB is this > bit: > /* If the URB didn't complete in success or error, then let's unlink > it */ > if (ret < 0 && !urb.usercontext) { > [...] > ret = ioctl(dev->fd, IOCTL_USB_DISCARDURB, &urb); > > So, "urb" gets discarded. But looking through the rest of that function, > I cannot see where urb gets set to anything *other* than the URB that > was allocated for I/O in that particular thread, so I haven't entirely > grasped how an URB belonging to another thread can be discarded. The problem is that while this function discards the urb that timed out (thereby causing it to move from "pending" to "done"), the subsequent ioctl(dev->fd, IOCTL_USB_REAPURB, &context); grabs whatever turns up next as "done", which sometimes is the other threads urb completing normally. My change effectively puts the timeout discard within the normal polling loop (timeout is now another way of "completing" an outstanding urb), thereby letting the usual code sort out which urb belongs to which thread. > I've had a look at the patch, it looks OK, but I'm worried that there is > one behaviour change. The current code discards the URB if it gets > confused (see above: if the URB didn't complete in success or error), > whereas your version seems to just bail out: > > if (errno != EAGAIN) { > fprintf(stderr, "error reaping URB: %s", strerror(errno)); > break; > } Right, but since this is the loop that also cleans up after a timeout, the urb may already have been discarded. The result of discarding a urb should be that ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context); returns 0, the very thing that hasn't happened. I guess it may be possible to track whether the urb has been discarded yet or not, but I'm not sure this is really a useful response to this error, since it it seemed rather more fundamental than anything to do with a particular urb's state (ie. it seemed to indicate that it is not possible to monitor any urb's state for that fd). I hope this helps. (I've given up on my signal based modifications. The interactions with other signals were simply too severe. Signals seem to be a poor mechanism to do anything with other than clean up after an abort.) Graeme Gill. |
From: Daniel D. <ds...@ge...> - 2008-02-29 12:25:18
|
Hi Graeme, Graeme Gill wrote: > After going through about 4 different approaches to solve > the threading issue on Linux for interrupt and bulk > end point transfers to improve on the spin-with- > 1msec-sleeps approach, I have hit upon something > that seems to work (at least for my test set). Thanks for working on this, I do appreciate it but I think it's too intrusive to include in the maintenance-mode stable tree. I don't think we have a simple solution for the excessive wakeups, so I guess it will have to stay that way until 1.0. Daniel |
From: Graeme G. <gr...@ar...> - 2008-03-02 01:53:43
Attachments:
ggfix
|
Daniel Drake wrote: > Thanks for working on this, I do appreciate it but I think it's too > intrusive to include in the maintenance-mode stable tree. I don't think > we have a simple solution for the excessive wakeups, so I guess it will > have to stay that way until 1.0. OK, well how about a less radical fix for the problem where a timeout in one thread causes the discarding of another threads URB ? (This one causes failures for my application using 0.1.12). Attached is such a patch. Waiting for URB's is still done by polling at 1msec intervals (even faster when multiple threads are doing it) rather than employing signals and waking the appropriate thread. Graeme Gill. |
From: Ludovic R. <lud...@gm...> - 2008-03-03 13:31:27
|
On Sat, Mar 1, 2008 at 1:18 AM, Graeme Gill <gr...@ar...> wrote: > Ludovic Rousseau wrote: > > The SVN version of libusb 1.0 works for me. > > Maybe it is a better idea to put efforts and energy in this version? > > Given that the API is different it's not an option for my application unless > it also works on MSWindows. In fact I was using the SVN version of libusb0.1 with the problematic patch reverted :-) I have not yet use libusb 1.0 since it uses git and I have not yet found a way to get the source code using a web proxy. I can only use http and https. bye -- Dr. Ludovic Rousseau |
From: Daniel D. <ds...@ge...> - 2008-03-03 17:49:48
|
Ludovic Rousseau wrote: > I have not yet use libusb 1.0 since it uses git and I have not yet > found a way to get the source code using a web proxy. I can only use > http and https. http://projects.reactivated.net/snapshots/libusb/ Daniel |