Fixed libusb1 configure bug, libusb1 updates for previous usrp_prims integration
[debian/gnuradio] / usrp / host / lib / fusb_libusb1.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_libusb1.h>
28 #include <libusb-1.0/libusb.h>
29 #include <stdexcept>
30 #include <cstdio>
31 #include <assert.h>
32 #include <string.h>
33 #include <algorithm>
34 #include <errno.h>
35 #include <string.h>
36
37 #define MINIMIZE_TX_BUFFERING true 
38
39 static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size(); 
40 static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE;
41 static const int DEFAULT_BUFFER_SIZE = 4 * (1L << 20);  // 4 MB endpoint
42 static const int LIBUSB_TIMEOUT = 0;                    // no timeout
43
44 inline static fusb_ephandle_libusb1*
45 lut_get_ephandle (libusb_transfer *lut)
46 {
47   return (fusb_ephandle_libusb1 *) lut->user_data;
48 }
49
50 // ------------------------------------------------------------------------
51 //      libusb_transfer allocation, deallocation, and callback 
52 // ------------------------------------------------------------------------
53
54 static void
55 free_lut (libusb_transfer *lut)
56 {
57
58   // if this was an input transfer, free the buffer
59   if (lut->endpoint & 0x80)
60     delete [] ((unsigned char *) lut->buffer);
61
62   libusb_free_transfer(lut); 
63
64 }
65
66 /*
67  * The callback means the libusb_transfer is completed whether sent, cancelled,
68  * or failed. Move the libusb_transfer from the pending list to the
69  * completed list. If the cancel is from the destructor then free the 
70  * transfer instead; normally this won't happen since all endpoints should be
71  * destroyed first leaving the pending list empty.
72  */
73
74 static void
75 generic_callback(struct libusb_transfer *lut)
76 {
77
78   // Fish out devhandle from endpoint
79   fusb_devhandle_libusb1* dev_handle = 
80     lut_get_ephandle(lut)->get_fusb_devhandle_libusb1();
81
82   dev_handle->pending_remove(lut);
83
84   if (lut->status == LIBUSB_TRANSFER_CANCELLED && dev_handle->_teardown() == 1)
85   {
86     free_lut (lut);
87     return;
88   }
89   
90   lut_get_ephandle(lut)->completed_list_add(lut);
91
92
93
94 static libusb_transfer*
95 alloc_lut (fusb_ephandle_libusb1 *self, int buffer_length, int endpoint,
96            bool input_p, unsigned char *write_buffer,
97            fusb_devhandle_libusb1 *dh)
98 {
99
100   struct libusb_transfer* lut = libusb_alloc_transfer(0);
101
102   endpoint = (endpoint & 0x7f) | (input_p ? 0x80 : 0);
103
104   if (input_p)
105     write_buffer = new unsigned char [buffer_length];
106
107   // We need the base class libusb_device_handle
108   libusb_device_handle *dev_handle = dh->get_usb_dev_handle();
109
110   // Load the libusb_transfer for bulk transfer
111   libusb_fill_bulk_transfer (lut,               // transfer
112                              dev_handle,        // dev_handle 
113                              endpoint,          // endpoint
114                              write_buffer,      // buffer
115                              buffer_length,     // length
116                              generic_callback,  // callback 
117                              self,              // user_data
118                              LIBUSB_TIMEOUT);   // timeout
119
120   return lut;
121 }
122
123 // ------------------------------------------------------------------------
124 //                              device handle
125 // ------------------------------------------------------------------------
126
127 fusb_devhandle_libusb1::fusb_devhandle_libusb1 (libusb_device_handle *udh,
128                                                 libusb_context *ctx)
129   : fusb_devhandle (udh), d_ctx (ctx), d_teardown (false)
130 {
131   // that's it 
132 }
133
134 fusb_devhandle_libusb1::~fusb_devhandle_libusb1 ()
135 {
136   d_teardown = true;
137
138   std::list<libusb_transfer*>::reverse_iterator it;
139
140   // After cancellation the libusb_transfer is still active so delay freeing
141   // transfer until callback occurs. In most cases the pending list should
142   // already be empty by the time this destructor is called.
143
144   for (it = d_pending_rqsts.rbegin (); it != d_pending_rqsts.rend (); it++) {
145     _cancel_lut (*it);
146   }
147
148   // Wait for pending list to empty
149   _wait_for_completion ();
150
151 }
152
153 fusb_ephandle*
154 fusb_devhandle_libusb1::make_ephandle (int endpoint, bool input_p,
155                                        int block_size, int nblocks)
156 {
157   return new fusb_ephandle_libusb1 (this, endpoint, input_p,
158                                     block_size, nblocks);
159 }
160
161 /*
162  * devhandle list manipulators 
163  */
164
165 void 
166 fusb_devhandle_libusb1::pending_add (libusb_transfer *lut)
167 {
168   d_pending_rqsts.push_back (lut);
169 }
170
171
172 /*
173  * Attempt to cancel all transations associated with eph
174  */
175
176 void
177 fusb_devhandle_libusb1::_cancel_pending_rqsts (fusb_ephandle_libusb1 *eph)
178 {
179   std::list<libusb_transfer*>::reverse_iterator it;
180
181   for (it = d_pending_rqsts.rbegin (); it != d_pending_rqsts.rend (); it++){
182     if (lut_get_ephandle (*it) == eph)
183       _cancel_lut (*it);
184   }
185 }
186
187 /*
188  * Pull from the pending list
189  */
190
191 libusb_transfer *
192 fusb_devhandle_libusb1::pending_get ()
193 {
194   if (d_pending_rqsts.empty ())
195     return 0;
196
197   libusb_transfer *lut = d_pending_rqsts.front ();
198   d_pending_rqsts.pop_front ();
199   return lut;
200 }
201
202 /*
203  * Match libusb_tranfer with the pending list and erase 
204  * Return true if found, false otherwise
205  */
206
207 bool
208 fusb_devhandle_libusb1::pending_remove (libusb_transfer *lut)
209 {
210   std::list<libusb_transfer*>::iterator result;
211   result = find (d_pending_rqsts.begin (), d_pending_rqsts.end (), lut);
212
213   if (result == d_pending_rqsts.end ()) {
214     fprintf (stderr, "fusb::pending_remove: failed to find lut in pending_rqsts: %p\n", lut);
215
216     return false;
217   }
218   d_pending_rqsts.erase (result);
219   return true;
220 }
221
222 /*
223  * Submit the libusb_transfer to libusb
224  * iff successful, the transfer will be placed on the devhandle pending list.
225  */
226
227 bool
228 fusb_devhandle_libusb1::_submit_lut (libusb_transfer *lut)
229 {
230
231   int ret = libusb_submit_transfer (lut);
232   if (ret < 0) {
233     fprintf(stderr, "fusb::_submit_lut %d", ret);
234     return false;
235   }
236
237   pending_add(lut);
238   return true;
239
240 }
241
242 /*
243  * Attempt to cancel any pending libusb_transfer transactions. 
244  * Return true in the absence of errors, which does not mean that the transfer
245  * is cancelled. Cancellation can be checked after the callback is fired off
246  * by libusb. 
247  */ 
248
249 bool
250 fusb_devhandle_libusb1::_cancel_lut (libusb_transfer *lut)
251 {
252
253   int ret = libusb_cancel_transfer (lut);
254   if (ret < 0) {
255     fprintf (stderr, "fusb::_cancel_lut");
256     return false;
257   }
258   return true;
259
260 }
261
262 /*
263  * Reimplementing _reap for context use and compatibiliy with libusb-0.12.
264  *
265  * Returns false on timeout or error, true if an event was handled
266  *
267  * If ok_to_block_p is false then handle already pending events and return
268  * immediately.
269  *
270  * If ok_to_block_p is true then call libusb_handle_events_timeout with default
271  * timeout value of 2 seconds, which waits and returns on event arrival or
272  * timeout.
273  */
274
275 bool
276 fusb_devhandle_libusb1::_reap (bool ok_to_block_p)
277 {
278   int ret;
279   struct timeval tv;
280
281   if (ok_to_block_p) {
282     tv.tv_sec = 2;
283     tv.tv_usec =  0;
284   }
285   else {
286     tv.tv_sec = 0;
287     tv.tv_usec =  0;
288   }
289  
290   if ((ret = libusb_handle_events_timeout(d_ctx, &tv)) < 0) {
291     fprintf (stderr, "fusb::_reap libusb_handle_events() %i\n", ret);
292     return false;
293   }
294   
295   return true;
296 }
297
298 void
299 fusb_devhandle_libusb1::_wait_for_completion ()
300 {
301
302   while (!d_pending_rqsts.empty ()) 
303     if (!_reap(true)) 
304       break; 
305
306 }
307
308 // ------------------------------------------------------------------------
309 //                              endpoint handle
310 // ------------------------------------------------------------------------
311
312 fusb_ephandle_libusb1::fusb_ephandle_libusb1 (fusb_devhandle_libusb1 *dh,
313                                               int endpoint, bool input_p,
314                                               int block_size, int nblocks)
315   : fusb_ephandle (endpoint, input_p, block_size, nblocks),
316     d_devhandle (dh),
317     d_write_work_in_progress (0), d_write_buffer (0),
318     d_read_work_in_progress (0), d_read_buffer (0), d_read_buffer_end (0)
319 {
320
321   if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE)
322     throw std::out_of_range ("fusb_ephandle_libusb1: block_size");
323
324   if (d_nblocks < 0)
325     throw std::out_of_range ("fusb_ephandle_libusb1: nblocks");
326
327   if (d_block_size == 0)
328     d_block_size = DEFAULT_BLOCK_SIZE;
329
330   if (d_nblocks == 0)
331     d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size);
332
333   if (!d_input_p)
334     if (!MINIMIZE_TX_BUFFERING)
335       d_write_buffer = new unsigned char [d_block_size];
336
337   if (0)
338     fprintf(stderr, "fusb_ephandle_libusb1::ctor: d_block_size = %d  d_nblocks = %d\n",
339       d_block_size, d_nblocks);
340
341   // allocate libusb_transfers 
342   for (int i = 0; i < d_nblocks; i++)
343     d_free_list.push_back (alloc_lut (this, d_block_size, d_endpoint,
344                                       d_input_p, d_write_buffer, d_devhandle));
345 }
346
347 fusb_ephandle_libusb1::~fusb_ephandle_libusb1 ()
348 {
349
350   stop ();
351
352   libusb_transfer *lut;
353
354   while ((lut = free_list_get ()) != 0)
355     free_lut (lut);
356
357   while ((lut = completed_list_get ()) != 0)
358     free_lut (lut);
359
360   if (d_write_work_in_progress)
361     free_lut (d_write_work_in_progress);
362
363   delete [] d_write_buffer;
364
365   if (d_read_work_in_progress)
366     free_lut (d_read_work_in_progress);
367
368 }
369
370 bool
371 fusb_ephandle_libusb1::start ()
372 {
373   if (d_started)
374     return true;
375
376   d_started = true;
377
378   if (d_input_p) {
379      libusb_transfer *lut;
380
381      int nerrors = 0;
382      while ((lut = free_list_get ()) !=0 && nerrors < d_nblocks) {
383        if (!submit_lut (lut))
384          nerrors++;
385      }
386   }
387
388   return true;
389
390 }
391
392 /*
393  * Cancel all transfers in progress or pending and return to initial state 
394  */
395
396 bool
397 fusb_ephandle_libusb1::stop ()
398 {
399
400   if (!d_started)
401     return true;
402
403   if (d_write_work_in_progress){
404     free_list_add (d_write_work_in_progress);
405     d_write_work_in_progress = 0;
406   }
407
408   if (d_read_work_in_progress){
409     free_list_add (d_read_work_in_progress);
410     d_read_work_in_progress = 0;
411     d_read_buffer = 0;
412     d_read_buffer_end = 0;
413   }
414
415   d_devhandle->_cancel_pending_rqsts (this);
416   d_devhandle->_reap (false);
417
418   while (1) {
419     libusb_transfer *lut;
420     while ((lut = completed_list_get ()) != 0)
421       free_list_add (lut);
422
423     if (d_free_list.size () == (unsigned) d_nblocks)
424       break;
425
426     if (!d_devhandle->_reap(true))
427       break;
428   }
429
430   d_started = false;
431
432   return true;
433 }
434
435 // ------------------------------------------------------------------------
436 //                      routines for writing    
437 // ------------------------------------------------------------------------
438
439 #if (MINIMIZE_TX_BUFFERING)
440
441 int
442 fusb_ephandle_libusb1::write (const void *buffer, int nbytes)
443 {
444   if (!d_started)       // doesn't matter here, but keeps semantics constant
445     return -1;
446   
447   if (d_input_p)
448     return -1;
449
450   assert(nbytes % 512 == 0);
451
452   unsigned char *src = (unsigned char *) buffer;
453
454   int n = 0;
455   while (n < nbytes){
456
457     struct libusb_transfer *lut = get_write_work_in_progress();
458     if (!lut)
459       return -1;
460     assert(lut->actual_length == 0);
461     int m = std::min(nbytes - n, MAX_BLOCK_SIZE);
462     lut->buffer = src;
463     lut->length = m;
464
465     n += m;
466     src += m;
467    
468     if (!submit_lut(lut))
469       return -1;
470
471     d_write_work_in_progress = 0;
472   }
473
474   return n;
475 }
476
477 #else
478
479 int
480 fusb_ephandle_libusb1::write (const void *buffer, int nbytes)
481 {
482   if (!d_started)
483     return -1;
484
485   if (d_input_p)
486     return -1;
487
488   unsigned char *src = (unsigned char *) buffer;
489
490   int n = 0;
491   while (n < nbytes){
492
493     libusb_transfer *lut = get_write_work_in_progress ();
494     if (!lut)
495       return -1;
496     unsigned char *dst = (unsigned char *) lut->buffer;
497     int m = std::min (nbytes - n, lut->length - lut->actual_length);
498
499     memcpy (&dst[lut->actual_length], &src[n], m);
500     lut->actual_length += m;
501     n += m;
502
503     if (lut->actual_length == lut->length){
504       if (!submit_lut (lut))
505         return -1;
506       d_write_work_in_progress = 0;
507     }
508   }
509
510   return n;
511 }
512
513 #endif
514
515 struct libusb_transfer *
516 fusb_ephandle_libusb1::get_write_work_in_progress ()
517 {
518   if (d_write_work_in_progress)
519     return d_write_work_in_progress;
520
521   while (1) {
522
523     reap_complete_writes ();
524
525     struct libusb_transfer *lut = free_list_get ();
526
527     if (lut != 0){
528       assert (lut->actual_length == 0);
529       d_write_work_in_progress = lut;
530       return lut;
531     }
532
533     if (!d_devhandle->_reap (true))
534       return 0;
535   }
536 }
537
538 void
539 fusb_ephandle_libusb1::reap_complete_writes ()
540 {
541   // take a look at the completed list and xfer to free list after
542   // checking for errors.
543
544   libusb_transfer *lut;
545
546   while ((lut = completed_list_get ()) != 0) {
547
548     // Check for any errors or short writes that were reporetd in the transfer.
549     // libusb1 sets status, actual_length. 
550
551     if (lut->status != LIBUSB_TRANSFER_COMPLETED) { 
552       fprintf (stderr, "fusb: (status %d) \n", lut->status );
553     } 
554     else if (lut->actual_length != lut->length){
555       fprintf (stderr, "fusb: short write xfer: %d != %d\n",
556                lut->actual_length, lut->length);
557     }
558
559     free_list_add (lut);
560   }
561 }
562
563 void
564 fusb_ephandle_libusb1::wait_for_completion ()
565 {
566   d_devhandle->_wait_for_completion ();
567 }
568
569 // ------------------------------------------------------------------------
570 //                      routines for reading    
571 // ------------------------------------------------------------------------
572
573 int
574 fusb_ephandle_libusb1::read (void *buffer, int nbytes)
575 {
576   if (!d_started)       // doesn't matter here, but keeps semantics constant
577     return -1;
578
579   if (!d_input_p)
580     return -1;
581
582   unsigned char *dst = (unsigned char *) buffer;
583
584   int n = 0;
585   while (n < nbytes) {
586
587     if (d_read_buffer >= d_read_buffer_end)
588       if (!reload_read_buffer ())
589         return -1;
590
591     int m = std::min (nbytes - n, (int) (d_read_buffer_end - d_read_buffer));
592
593     memcpy (&dst[n], d_read_buffer, m);
594     d_read_buffer += m;
595     n += m;
596   }
597
598   return n;
599
600 }
601
602 bool
603 fusb_ephandle_libusb1::reload_read_buffer ()
604 {
605   assert (d_read_buffer >= d_read_buffer_end);
606
607   libusb_transfer *lut;
608
609   if (d_read_work_in_progress) {
610     lut = d_read_work_in_progress;
611     d_read_work_in_progress = 0;
612     d_read_buffer = 0;
613     d_read_buffer_end = 0;
614     lut->actual_length = 0;
615     if (!submit_lut (lut))
616       return false;
617   }
618
619   while (1) {
620
621     while ((lut = completed_list_get ()) == 0 ) 
622       if (!d_devhandle->_reap(true))
623         return false; 
624
625     if (lut->status != LIBUSB_TRANSFER_COMPLETED) {
626       fprintf (stderr, "fust: (rd status %d) %s\n", lut->status, 
627                strerror (-lut->status));
628       lut->actual_length = 0;
629       free_list_add (lut);
630       return false;
631     }
632
633     d_read_work_in_progress = lut;
634     d_read_buffer = (unsigned char *) lut->buffer;
635     d_read_buffer_end = d_read_buffer + lut->actual_length;
636
637     return true;
638   }
639 }
640
641
642 /*
643  * ephandle list manipulation   
644  */
645
646
647 void
648 fusb_ephandle_libusb1::free_list_add (libusb_transfer *lut)
649 {
650   assert (lut_get_ephandle (lut) == this);
651   lut->actual_length = 0;
652   d_free_list.push_back (lut);
653 }
654
655 libusb_transfer *
656 fusb_ephandle_libusb1::free_list_get ()
657 {
658   if (d_free_list.empty ())
659     return 0;
660
661   libusb_transfer *lut = d_free_list.front ();
662   d_free_list.pop_front ();
663   return lut;
664 }
665
666 void
667 fusb_ephandle_libusb1::completed_list_add (libusb_transfer *lut)
668 {
669   assert (lut_get_ephandle (lut) == this);
670   d_completed_list.push_back (lut);
671 }
672
673 libusb_transfer *
674 fusb_ephandle_libusb1::completed_list_get ()
675 {
676   if (d_completed_list.empty ())
677     return 0;
678
679   libusb_transfer *lut = d_completed_list.front ();
680   d_completed_list.pop_front ();
681   return lut;
682 }
683
684 bool
685 fusb_ephandle_libusb1::submit_lut (libusb_transfer *lut)
686 {
687   if (!d_devhandle->_submit_lut (lut)) {
688     fprintf (stderr, "_submit_lut failed\n");
689     free_list_add (lut);
690     return false;
691   }
692   return true;
693 }