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