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