fusb_linux_2.cc

Updated .cc file - Johnathan Corgan, 03/02/2009 07:14 pm

Download (15.8 kB)

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