Cleanup in preparation for merge
[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   if (ok_to_block_p) {
283     tv.tv_sec = 2;
284     tv.tv_usec =  0;
285   }
286   else {
287     tv.tv_sec = 0;
288     tv.tv_usec =  0;
289   }
290
291   if ((ret = libusb_handle_events_timeout(d_ctx, &tv)) < 0) {
292     fprintf (stderr, "fusb::_reap libusb_handle_events() %i\n", ret);
293     return false;
294   }
295
296   return true;
297 }
298
299 void
300 fusb_devhandle_libusb1::_wait_for_completion ()
301 {
302
303   while (!d_pending_rqsts.empty ())
304     if (!_reap(true))
305       break;
306
307 }
308
309 // ------------------------------------------------------------------------
310 //                              endpoint handle
311 // ------------------------------------------------------------------------
312
313 fusb_ephandle_libusb1::fusb_ephandle_libusb1 (fusb_devhandle_libusb1 *dh,
314                                               int endpoint, bool input_p,
315                                               int block_size, int nblocks)
316   : fusb_ephandle (endpoint, input_p, block_size, nblocks),
317     d_devhandle (dh),
318     d_write_work_in_progress (0), d_write_buffer (0),
319     d_read_work_in_progress (0), d_read_buffer (0), d_read_buffer_end (0)
320 {
321
322   if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE)
323     throw std::out_of_range ("fusb_ephandle_libusb1: block_size");
324
325   if (d_nblocks < 0)
326     throw std::out_of_range ("fusb_ephandle_libusb1: nblocks");
327
328   if (d_block_size == 0)
329     d_block_size = DEFAULT_BLOCK_SIZE;
330
331   if (d_nblocks == 0)
332     d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size);
333
334   if (!d_input_p)
335     if (!MINIMIZE_TX_BUFFERING)
336       d_write_buffer = new unsigned char [d_block_size];
337
338   if (0)
339     fprintf(stderr, "fusb_ephandle_libusb1::ctor: d_block_size = %d  d_nblocks = %d\n",
340       d_block_size, d_nblocks);
341
342   // allocate libusb_transfers
343   for (int i = 0; i < d_nblocks; i++)
344     d_free_list.push_back (alloc_lut (this, d_block_size, d_endpoint,
345                                       d_input_p, d_write_buffer, d_devhandle));
346 }
347
348 fusb_ephandle_libusb1::~fusb_ephandle_libusb1 ()
349 {
350
351   stop ();
352
353   libusb_transfer *lut;
354
355   while ((lut = free_list_get ()) != 0)
356     free_lut (lut);
357
358   while ((lut = completed_list_get ()) != 0)
359     free_lut (lut);
360
361   if (d_write_work_in_progress)
362     free_lut (d_write_work_in_progress);
363
364   delete [] d_write_buffer;
365
366   if (d_read_work_in_progress)
367     free_lut (d_read_work_in_progress);
368
369 }
370
371 bool
372 fusb_ephandle_libusb1::start ()
373 {
374   if (d_started)
375     return true;
376
377   d_started = true;
378
379   if (d_input_p) {
380      libusb_transfer *lut;
381
382      int nerrors = 0;
383      while ((lut = free_list_get ()) !=0 && nerrors < d_nblocks) {
384        if (!submit_lut (lut))
385          nerrors++;
386      }
387   }
388
389   return true;
390
391 }
392
393 /*
394  * Cancel all transfers in progress or pending and return to initial state
395  */
396
397 bool
398 fusb_ephandle_libusb1::stop ()
399 {
400
401   if (!d_started)
402     return true;
403
404   if (d_write_work_in_progress){
405     free_list_add (d_write_work_in_progress);
406     d_write_work_in_progress = 0;
407   }
408
409   if (d_read_work_in_progress){
410     free_list_add (d_read_work_in_progress);
411     d_read_work_in_progress = 0;
412     d_read_buffer = 0;
413     d_read_buffer_end = 0;
414   }
415
416   d_devhandle->_cancel_pending_rqsts (this);
417   d_devhandle->_reap (false);
418
419   while (1) {
420     libusb_transfer *lut;
421     while ((lut = completed_list_get ()) != 0)
422       free_list_add (lut);
423
424     if (d_free_list.size () == (unsigned) d_nblocks)
425       break;
426
427     if (!d_devhandle->_reap(true))
428       break;
429   }
430
431   d_started = false;
432
433   return true;
434 }
435
436 // ------------------------------------------------------------------------
437 //                      routines for writing
438 // ------------------------------------------------------------------------
439
440 #if (MINIMIZE_TX_BUFFERING)
441
442 int
443 fusb_ephandle_libusb1::write (const void *buffer, int nbytes)
444 {
445   if (!d_started)       // doesn't matter here, but keeps semantics constant
446     return -1;
447
448   if (d_input_p)
449     return -1;
450
451   assert(nbytes % 512 == 0);
452
453   unsigned char *src = (unsigned char *) buffer;
454
455   int n = 0;
456   while (n < nbytes){
457
458     struct libusb_transfer *lut = get_write_work_in_progress();
459     if (!lut)
460       return -1;
461     assert(lut->actual_length == 0);
462     int m = std::min(nbytes - n, MAX_BLOCK_SIZE);
463     lut->buffer = src;
464     lut->length = m;
465
466     n += m;
467     src += m;
468
469     if (!submit_lut(lut))
470       return -1;
471
472     d_write_work_in_progress = 0;
473   }
474
475   return n;
476 }
477
478 #else
479
480 int
481 fusb_ephandle_libusb1::write (const void *buffer, int nbytes)
482 {
483   if (!d_started)
484     return -1;
485
486   if (d_input_p)
487     return -1;
488
489   unsigned char *src = (unsigned char *) buffer;
490
491   int n = 0;
492   while (n < nbytes){
493
494     libusb_transfer *lut = get_write_work_in_progress ();
495     if (!lut)
496       return -1;
497     unsigned char *dst = (unsigned char *) lut->buffer;
498     int m = std::min (nbytes - n, lut->length - lut->actual_length);
499
500     memcpy (&dst[lut->actual_length], &src[n], m);
501     lut->actual_length += m;
502     n += m;
503
504     if (lut->actual_length == lut->length){
505       if (!submit_lut (lut))
506         return -1;
507       d_write_work_in_progress = 0;
508     }
509   }
510
511   return n;
512 }
513
514 #endif
515
516 struct libusb_transfer *
517 fusb_ephandle_libusb1::get_write_work_in_progress ()
518 {
519   if (d_write_work_in_progress)
520     return d_write_work_in_progress;
521
522   while (1) {
523
524     reap_complete_writes ();
525
526     struct libusb_transfer *lut = free_list_get ();
527
528     if (lut != 0){
529       assert (lut->actual_length == 0);
530       d_write_work_in_progress = lut;
531       return lut;
532     }
533
534     if (!d_devhandle->_reap (true))
535       return 0;
536   }
537 }
538
539 void
540 fusb_ephandle_libusb1::reap_complete_writes ()
541 {
542   // take a look at the completed list and xfer to free list after
543   // checking for errors.
544
545   libusb_transfer *lut;
546
547   while ((lut = completed_list_get ()) != 0) {
548
549     // Check for any errors or short writes that were reporetd in the transfer.
550     // libusb1 sets status, actual_length.
551
552     if (lut->status != LIBUSB_TRANSFER_COMPLETED) {
553       fprintf (stderr, "fusb: (status %d) \n", lut->status );
554     }
555     else if (lut->actual_length != lut->length){
556       fprintf (stderr, "fusb: short write xfer: %d != %d\n",
557                lut->actual_length, lut->length);
558     }
559
560     free_list_add (lut);
561   }
562 }
563
564 void
565 fusb_ephandle_libusb1::wait_for_completion ()
566 {
567   d_devhandle->_wait_for_completion ();
568 }
569
570 // ------------------------------------------------------------------------
571 //                      routines for reading
572 // ------------------------------------------------------------------------
573
574 int
575 fusb_ephandle_libusb1::read (void *buffer, int nbytes)
576 {
577   if (!d_started)       // doesn't matter here, but keeps semantics constant
578     return -1;
579
580   if (!d_input_p)
581     return -1;
582
583   unsigned char *dst = (unsigned char *) buffer;
584
585   int n = 0;
586   while (n < nbytes) {
587
588     if (d_read_buffer >= d_read_buffer_end)
589       if (!reload_read_buffer ())
590         return -1;
591
592     int m = std::min (nbytes - n, (int) (d_read_buffer_end - d_read_buffer));
593
594     memcpy (&dst[n], d_read_buffer, m);
595     d_read_buffer += m;
596     n += m;
597   }
598
599   return n;
600
601 }
602
603 bool
604 fusb_ephandle_libusb1::reload_read_buffer ()
605 {
606   assert (d_read_buffer >= d_read_buffer_end);
607
608   libusb_transfer *lut;
609
610   if (d_read_work_in_progress) {
611     lut = d_read_work_in_progress;
612     d_read_work_in_progress = 0;
613     d_read_buffer = 0;
614     d_read_buffer_end = 0;
615     lut->actual_length = 0;
616     if (!submit_lut (lut))
617       return false;
618   }
619
620   while (1) {
621
622     while ((lut = completed_list_get ()) == 0 )
623       if (!d_devhandle->_reap(true))
624         return false;
625
626     if (lut->status != LIBUSB_TRANSFER_COMPLETED) {
627       fprintf (stderr, "fust: (rd status %d) %s\n", lut->status,
628                strerror (-lut->status));
629       lut->actual_length = 0;
630       free_list_add (lut);
631       return false;
632     }
633
634     d_read_work_in_progress = lut;
635     d_read_buffer = (unsigned char *) lut->buffer;
636     d_read_buffer_end = d_read_buffer + lut->actual_length;
637
638     return true;
639   }
640 }
641
642
643 /*
644  * ephandle list manipulation
645  */
646
647
648 void
649 fusb_ephandle_libusb1::free_list_add (libusb_transfer *lut)
650 {
651   assert (lut_get_ephandle (lut) == this);
652   lut->actual_length = 0;
653   d_free_list.push_back (lut);
654 }
655
656 libusb_transfer *
657 fusb_ephandle_libusb1::free_list_get ()
658 {
659   if (d_free_list.empty ())
660     return 0;
661
662   libusb_transfer *lut = d_free_list.front ();
663   d_free_list.pop_front ();
664   return lut;
665 }
666
667 void
668 fusb_ephandle_libusb1::completed_list_add (libusb_transfer *lut)
669 {
670   assert (lut_get_ephandle (lut) == this);
671   d_completed_list.push_back (lut);
672 }
673
674 libusb_transfer *
675 fusb_ephandle_libusb1::completed_list_get ()
676 {
677   if (d_completed_list.empty ())
678     return 0;
679
680   libusb_transfer *lut = d_completed_list.front ();
681   d_completed_list.pop_front ();
682   return lut;
683 }
684
685 bool
686 fusb_ephandle_libusb1::submit_lut (libusb_transfer *lut)
687 {
688   if (!d_devhandle->_submit_lut (lut)) {
689     fprintf (stderr, "_submit_lut failed\n");
690     free_list_add (lut);
691     return false;
692   }
693   return true;
694 }