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