Imported Upstream version 3.0.2
[debian/gnuradio] / usrp / host / lib / fusb_linux.cc
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2003 Free Software Foundation, Inc.
4  * 
5  * This file is part of GNU Radio
6  * 
7  * GNU Radio is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  * 
12  * GNU Radio is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with GNU Radio; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <fusb_linux.h>
28 #include <usb.h>                // libusb header
29 #include <stdexcept>
30 #ifdef HAVE_LINUX_COMPILER_H
31 #include <linux/compiler.h>
32 #endif
33 #include <linux/usbdevice_fs.h> // interface to kernel portion of user mode usb driver
34 #include <sys/ioctl.h>
35 #include <assert.h>
36 #include <string.h>
37 #include <algorithm>
38 #include <errno.h>
39 #include <string.h>
40
41 #define MINIMIZE_TX_BUFFERING 1         // must be defined to 0 or 1
42
43
44 static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size();             // hard limit
45 static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE;
46 static const int DEFAULT_BUFFER_SIZE = 4 * (1L << 20);                          // 4 MB / endpoint
47
48
49 // Totally evil and fragile extraction of file descriptor from
50 // guts of libusb.  They don't install usbi.h, which is what we'd need
51 // to do this nicely.
52 //
53 // FIXME if everything breaks someday in the future, look here...
54
55 static int
56 fd_from_usb_dev_handle (usb_dev_handle *udh)
57 {
58   return *((int *) udh);
59 }
60
61 inline static void
62 urb_set_ephandle (usbdevfs_urb *urb, fusb_ephandle_linux *handle)
63 {
64   urb->usercontext = handle;
65 }
66
67 inline static fusb_ephandle_linux *
68 urb_get_ephandle (usbdevfs_urb *urb)
69 {
70   return (fusb_ephandle_linux *) urb->usercontext;
71 }
72
73 // ------------------------------------------------------------------------
74 //                 USB request block (urb) allocation
75 // ------------------------------------------------------------------------
76
77 static usbdevfs_urb *
78 alloc_urb (fusb_ephandle_linux *self, int buffer_length, int endpoint,
79            bool input_p, unsigned char *write_buffer)
80 {
81   usbdevfs_urb  *urb = new usbdevfs_urb;
82   memset (urb, 0, sizeof (*urb));
83
84   urb->buffer_length = buffer_length;
85
86   // We allocate dedicated memory only for input buffers.
87   // For output buffers we reuse the same buffer (the kernel 
88   // copies the data at submital time)
89
90   if (input_p)
91     urb->buffer = new unsigned char [buffer_length];
92   else
93     urb->buffer = write_buffer;
94
95   // init common values
96
97   urb->type = USBDEVFS_URB_TYPE_BULK;
98   urb->endpoint = (endpoint & 0x7f) | (input_p ? 0x80 : 0);
99
100   // USBDEVFS_URB_QUEUE_BULK goes away in linux 2.5, but is needed if
101   // we are using a 2.4 usb-uhci host controller driver.  This is
102   // unlikely since we're almost always going to be plugged into a
103   // high speed host controller (ehci)
104 #if 0 && defined (USBDEVFS_URB_QUEUE_BULK)
105   urb->flags = USBDEVFS_URB_QUEUE_BULK;
106 #endif
107
108   urb->signr = 0;
109   urb_set_ephandle (urb, self);
110
111   return urb;
112 }
113
114 static void
115 free_urb (usbdevfs_urb *urb)
116 {
117   // if this was an input urb, free the buffer
118   if (urb->endpoint & 0x80)
119     delete [] ((unsigned char *) urb->buffer);
120
121   delete urb;
122 }
123
124 // ------------------------------------------------------------------------
125 //                              device handle
126 // ------------------------------------------------------------------------
127
128 fusb_devhandle_linux::fusb_devhandle_linux (usb_dev_handle *udh)
129   : fusb_devhandle (udh)
130 {
131   // that's all
132 }
133
134 fusb_devhandle_linux::~fusb_devhandle_linux ()
135 {
136   // if there are any pending requests, cancel them and free the urbs.
137   
138   std::list<usbdevfs_urb*>::reverse_iterator it;
139
140   for (it = d_pending_rqsts.rbegin (); it != d_pending_rqsts.rend (); it++){
141     _cancel_urb (*it);
142     free_urb (*it);
143   }
144 }
145
146 fusb_ephandle *
147 fusb_devhandle_linux::make_ephandle (int endpoint, bool input_p,
148                                      int block_size, int nblocks)
149 {
150   return new fusb_ephandle_linux (this, endpoint, input_p,
151                                   block_size, nblocks);
152 }
153
154
155 // Attempt to cancel all transactions associated with eph.
156
157 void
158 fusb_devhandle_linux::_cancel_pending_rqsts (fusb_ephandle_linux *eph)
159 {
160   std::list<usbdevfs_urb*>::reverse_iterator it;
161
162   for (it = d_pending_rqsts.rbegin (); it != d_pending_rqsts.rend (); it++){
163     if (urb_get_ephandle (*it) == eph)
164       _cancel_urb (*it);
165   }
166 }
167
168 void 
169 fusb_devhandle_linux::pending_add (usbdevfs_urb *urb)
170 {
171   d_pending_rqsts.push_back (urb);
172 }
173
174 usbdevfs_urb *
175 fusb_devhandle_linux::pending_get ()
176 {
177   if (d_pending_rqsts.empty ())
178     return 0;
179
180   usbdevfs_urb *urb = d_pending_rqsts.front ();
181   d_pending_rqsts.pop_front ();
182   return urb;
183 }
184
185 bool
186 fusb_devhandle_linux::pending_remove (usbdevfs_urb *urb)
187 {
188   std::list<usbdevfs_urb*>::iterator    result = find (d_pending_rqsts.begin (),
189                                                        d_pending_rqsts.end (),
190                                                        urb);
191   if (result == d_pending_rqsts.end ()){
192     fprintf (stderr, "fusb::pending_remove: failed to find urb in pending_rqsts: %p\n", urb);
193     return false;
194   }
195   d_pending_rqsts.erase (result);
196   return true;
197 }
198
199 /*
200  * Submit the urb to the kernel.
201  * iff successful, the urb will be placed on the devhandle's pending list.
202  */
203 bool
204 fusb_devhandle_linux::_submit_urb (usbdevfs_urb *urb)
205 {
206   int   ret;
207
208   ret = ioctl (fd_from_usb_dev_handle (d_udh), USBDEVFS_SUBMITURB, urb);
209   if (ret < 0){
210     perror ("fusb::_submit_urb");
211     return false;
212   }
213   
214   pending_add (urb);
215   return true;
216 }
217
218 /*
219  * Attempt to cancel the in pending or in-progress urb transaction.
220  * Return true iff transaction was sucessfully cancelled.
221  *
222  * Failure to cancel should not be considered a problem.  This frequently
223  * occurs if the transaction has already completed in the kernel but hasn't
224  * yet been reaped by the user mode code.
225  *
226  * urbs which were cancelled have their status field set to -ENOENT when
227  * they are reaped.
228  */
229 bool
230 fusb_devhandle_linux::_cancel_urb (usbdevfs_urb *urb)
231 {
232   int ret = ioctl (fd_from_usb_dev_handle (d_udh), USBDEVFS_DISCARDURB, urb);
233   if (ret < 0){
234     // perror ("fusb::_cancel_urb");
235     return false;
236   }
237   return true;
238 }
239
240 /*
241  * Check with the kernel and see if any of our outstanding requests
242  * have completed.  For each completed transaction, remove it from the
243  * devhandle's pending list and append it to the completed list for
244  * the corresponding endpoint.
245  *
246  * If any transactions are reaped return true.
247  *
248  * If ok_to_block_p is true, then this will block until at least one
249  * transaction completes.
250  */
251 bool
252 fusb_devhandle_linux::_reap (bool ok_to_block_p)
253 {
254   int           ret;
255   int           nreaped = 0;
256   usbdevfs_urb  *urb = 0;
257
258   int   fd = fd_from_usb_dev_handle (d_udh);
259   
260   // try to reap as many as possible without blocking...
261
262   while ((ret = ioctl (fd, USBDEVFS_REAPURBNDELAY, &urb)) == 0){
263     if (urb->status != 0 && urb->status != -ENOENT){
264       fprintf (stderr, "_reap: usb->status = %d, actual_length = %5d\n",
265                urb->status, urb->actual_length);
266     }
267     pending_remove (urb);
268     urb_get_ephandle (urb)->completed_list_add (urb);
269     nreaped++;
270   }
271
272   if (nreaped > 0)              // if we got any, return w/o blocking
273     return true;
274
275   if (!ok_to_block_p)
276     return false;
277   
278   ret = ioctl (fd, USBDEVFS_REAPURB, &urb);
279   if (ret < 0){
280     perror ("fusb::_reap");
281     return false;
282   }
283
284   pending_remove (urb);
285   urb_get_ephandle (urb)->completed_list_add (urb);
286   return true;
287 }
288
289 void
290 fusb_devhandle_linux::_wait_for_completion ()
291 {
292   while (!d_pending_rqsts.empty ())
293     _reap (true);
294 }
295 \f// ------------------------------------------------------------------------
296 //                           end point handle
297 // ------------------------------------------------------------------------
298
299 fusb_ephandle_linux::fusb_ephandle_linux (fusb_devhandle_linux *devhandle,
300                                           int endpoint,
301                                           bool input_p,
302                                           int block_size, int nblocks)
303   : fusb_ephandle (endpoint, input_p, block_size, nblocks),
304     d_devhandle (devhandle), 
305     d_write_work_in_progress (0), d_write_buffer (0),
306     d_read_work_in_progress (0), d_read_buffer (0), d_read_buffer_end (0)
307 {
308
309   if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE)
310     throw std::out_of_range ("fusb_ephandle_linux: block_size");
311
312   if (d_nblocks < 0)
313     throw std::out_of_range ("fusb_ephandle_linux: nblocks");
314
315   if (d_block_size == 0)
316     d_block_size = DEFAULT_BLOCK_SIZE;
317
318   if (d_nblocks == 0)
319     d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size);
320
321   if (!d_input_p)
322     if (!MINIMIZE_TX_BUFFERING)
323       d_write_buffer = new unsigned char [d_block_size];
324
325   if (0)
326     fprintf(stderr, "fusb_ephandle_linux::ctor: d_block_size = %d  d_nblocks = %d\n",
327             d_block_size, d_nblocks);
328
329   // allocate urbs
330
331   for (int i = 0; i < d_nblocks; i++)
332     d_free_list.push_back (alloc_urb (this, d_block_size, d_endpoint,
333                                       d_input_p, d_write_buffer));
334 }
335
336 fusb_ephandle_linux::~fusb_ephandle_linux ()
337 {
338   stop ();
339
340   usbdevfs_urb *urb;
341
342   while ((urb = free_list_get ()) != 0)
343     free_urb (urb);
344
345   while ((urb = completed_list_get ()) != 0)
346     free_urb (urb);
347
348   if (d_write_work_in_progress)
349     free_urb (d_write_work_in_progress);
350
351   delete [] d_write_buffer;
352
353   if (d_read_work_in_progress)
354     free_urb (d_read_work_in_progress);
355 }
356
357 // ----------------------------------------------------------------
358
359 bool
360 fusb_ephandle_linux::start ()
361 {
362   if (d_started)
363     return true;                // already running
364
365   d_started = true;
366
367   if (d_input_p){               // fire off all the reads
368     usbdevfs_urb *urb;
369
370     int nerrors = 0;
371     while ((urb = free_list_get ()) != 0 && nerrors < d_nblocks){
372       if (!submit_urb (urb))
373         nerrors++;
374     }
375   }
376
377   return true;
378 }
379
380 //
381 // kill all i/o in progress.
382 // kill any completed but unprocessed transactions.
383 //
384 bool
385 fusb_ephandle_linux::stop ()
386 {
387   if (!d_started)
388     return true;
389   
390   d_devhandle->_cancel_pending_rqsts (this);
391   d_devhandle->_reap (false);
392
393
394   usbdevfs_urb *urb;
395   while ((urb = completed_list_get ()) != 0)
396     free_list_add (urb);
397
398   if (d_write_work_in_progress){
399     free_list_add (d_write_work_in_progress);
400     d_write_work_in_progress = 0;
401   }
402
403   if (d_read_work_in_progress){
404     free_list_add (d_read_work_in_progress);
405     d_read_work_in_progress = 0;
406     d_read_buffer = 0;
407     d_read_buffer_end = 0;
408   }
409
410   if (d_free_list.size () != (unsigned) d_nblocks)
411     fprintf (stderr, "d_free_list.size () = %d, d_nblocks = %d\n",
412              d_free_list.size (), d_nblocks);
413     
414   assert (d_free_list.size () == (unsigned) d_nblocks);
415
416   d_started = false;
417   return true;
418 }
419
420 // ----------------------------------------------------------------
421 //                      routines for writing 
422 // ----------------------------------------------------------------
423
424 #if (MINIMIZE_TX_BUFFERING)
425
426 int 
427 fusb_ephandle_linux::write(const void *buffer, int nbytes)
428 {
429   if (!d_started)
430     return -1;
431   
432   if (d_input_p)
433     return -1;
434
435   assert(nbytes % 512 == 0);
436
437   unsigned char *src = (unsigned char *) buffer;
438
439   int n = 0;
440   while (n < nbytes){
441
442     usbdevfs_urb *urb = get_write_work_in_progress();
443     assert(urb->actual_length == 0);
444     int m = std::min(nbytes - n, MAX_BLOCK_SIZE);
445     urb->buffer = src;
446     urb->buffer_length = m;
447
448     n += m;
449     src += m;
450
451     if (!submit_urb(urb))
452       return -1;
453
454     d_write_work_in_progress = 0;
455   }
456
457   return n;
458 }
459
460 #else
461
462 int 
463 fusb_ephandle_linux::write (const void *buffer, int nbytes)
464 {
465   if (!d_started)
466     return -1;
467   
468   if (d_input_p)
469     return -1;
470
471   unsigned char *src = (unsigned char *) buffer;
472
473   int n = 0;
474   while (n < nbytes){
475
476     usbdevfs_urb *urb = get_write_work_in_progress ();
477     unsigned char *dst = (unsigned char *) urb->buffer;
478     int m = std::min (nbytes - n, urb->buffer_length - urb->actual_length);
479
480     memcpy (&dst[urb->actual_length], &src[n], m);
481     urb->actual_length += m;
482     n += m;
483
484     if (urb->actual_length == urb->buffer_length){
485       if (!submit_urb (urb))
486         return -1;
487       d_write_work_in_progress = 0;
488     }
489   }
490
491   return n;
492 }
493
494 #endif
495
496 usbdevfs_urb *
497 fusb_ephandle_linux::get_write_work_in_progress ()
498 {
499   // if we've already got some work in progress, return it
500
501   if (d_write_work_in_progress)
502     return d_write_work_in_progress;
503
504   while (1){
505
506     reap_complete_writes ();
507
508     usbdevfs_urb *urb = free_list_get ();
509
510     if (urb != 0){
511       assert (urb->actual_length == 0);
512       d_write_work_in_progress = urb;
513       return urb;
514     }
515
516     // The free list is empty.  Tell the device handle to reap.
517     // Anything it reaps for us will end up on our completed list.
518
519     d_devhandle->_reap (true);
520   }
521 }
522
523 void
524 fusb_ephandle_linux::reap_complete_writes ()
525 {
526   // take a look at the completed_list and xfer to free list after
527   // checking for errors.
528
529   usbdevfs_urb *urb;
530   
531   while ((urb = completed_list_get ()) != 0){
532
533     // Check for any errors or short writes that were reported in the urb.
534     // The kernel sets status, actual_length and error_count.
535     // error_count is only used for ISO xfers.
536     // status is 0 if successful, else is an errno kind of thing
537
538     if (urb->status != 0){
539       fprintf (stderr, "fusb: (status %d) %s\n", urb->status, strerror (-urb->status));
540     }
541     else if (urb->actual_length != urb->buffer_length){
542       fprintf (stderr, "fusb: short write xfer: %d != %d\n",
543                urb->actual_length, urb->buffer_length);
544     }
545
546     free_list_add (urb);
547   }
548 }
549
550 void
551 fusb_ephandle_linux::wait_for_completion ()
552 {
553   d_devhandle->_wait_for_completion ();
554 }
555
556 // ----------------------------------------------------------------
557 //                     routines for reading
558 // ----------------------------------------------------------------
559
560 int
561 fusb_ephandle_linux::read (void *buffer, int nbytes)
562 {
563   if (!d_started)
564     return -1;
565   
566   if (!d_input_p)
567     return -1;
568
569   unsigned char *dst = (unsigned char *) buffer;
570
571   int n = 0;
572   while (n < nbytes){
573
574     if (d_read_buffer >= d_read_buffer_end)
575       if (!reload_read_buffer ())
576         return -1;
577
578     int m = std::min (nbytes - n, (int) (d_read_buffer_end - d_read_buffer));
579
580     memcpy (&dst[n], d_read_buffer, m);
581     d_read_buffer += m;
582     n += m;
583   }
584
585   return n;
586 }
587
588 bool
589 fusb_ephandle_linux::reload_read_buffer ()
590 {
591   assert (d_read_buffer >= d_read_buffer_end);
592
593   usbdevfs_urb *urb;
594
595   if (d_read_work_in_progress){
596     // We're done with this urb.  Fire off a read to refill it.
597     urb = d_read_work_in_progress;
598     d_read_work_in_progress = 0;
599     d_read_buffer = 0;
600     d_read_buffer_end = 0;
601     urb->actual_length = 0;
602     if (!submit_urb (urb))
603       return false;
604   }
605
606   while (1){
607
608     while ((urb = completed_list_get ()) == 0)
609       d_devhandle->_reap (true);
610
611     // check result of completed read
612
613     if (urb->status != 0){
614       // We've got a problem.
615       // Report the problem and resubmit.
616       fprintf (stderr, "fusb: (rd status %d) %s\n", urb->status, strerror (-urb->status));
617       urb->actual_length = 0;
618       if (!submit_urb (urb))
619         return false;
620
621       continue;
622     }
623
624     // we've got a happy urb, full of data...
625
626     d_read_work_in_progress = urb;
627     d_read_buffer = (unsigned char *) urb->buffer;
628     d_read_buffer_end = d_read_buffer + urb->actual_length;
629
630     return true;
631   }
632 }
633
634 // ----------------------------------------------------------------
635
636 void
637 fusb_ephandle_linux::free_list_add (usbdevfs_urb *urb)
638 {
639   assert (urb_get_ephandle (urb) == this);
640   urb->actual_length = 0;
641   d_free_list.push_back (urb);
642 }
643
644 usbdevfs_urb *
645 fusb_ephandle_linux::free_list_get ()
646 {
647   if (d_free_list.empty ())
648     return 0;
649
650   usbdevfs_urb *urb = d_free_list.front ();
651   d_free_list.pop_front ();
652   return urb;
653 }
654
655 void
656 fusb_ephandle_linux::completed_list_add (usbdevfs_urb *urb)
657 {
658   assert (urb_get_ephandle (urb) == this);
659   d_completed_list.push_back (urb);
660 }
661
662 usbdevfs_urb *
663 fusb_ephandle_linux::completed_list_get ()
664 {
665   if (d_completed_list.empty ())
666     return 0;
667
668   usbdevfs_urb *urb = d_completed_list.front ();
669   d_completed_list.pop_front ();
670   return urb;
671 }
672
673 /*
674  * Submit the urb.  If successful the urb ends up on the devhandle's
675  * pending list, otherwise, it's back on our free list.
676  */
677 bool
678 fusb_ephandle_linux::submit_urb (usbdevfs_urb *urb)
679 {
680   if (!d_devhandle->_submit_urb (urb)){    // FIXME record the problem somewhere
681     fprintf (stderr, "_submit_urb failed\n");
682     free_list_add (urb);
683     return false;
684   }
685   return true;
686 }