mirror of
				https://github.com/gnss-sdr/gnss-sdr
				synced 2025-10-31 07:13:03 +00:00 
			
		
		
		
	 b849b20a8c
			
		
	
	b849b20a8c
	
	
	
		
			
			- Added a pre-compiled custom GN3S firmware. - Added a fully-compliant GNU Radio source block for GN3S USB dongle. It can be used also from GNU Radio companion and from Python applications. - Added a new GN3S_Signal_Source block. It is possible to disable the GN3S signal source compilation. See README. git-svn-id: https://svn.code.sf.net/p/gnss-sdr/code/trunk@217 64b25241-fba3-4117-9849-534c7e92360d
		
			
				
	
	
		
			697 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			697 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- c++ -*- */
 | ||
| /*
 | ||
|  * Copyright 2003 Free Software Foundation, Inc.
 | ||
|  * 
 | ||
|  * This file is part of GNU Radio
 | ||
|  * 
 | ||
|  * GNU Radio is free software; you can redistribute it and/or modify
 | ||
|  * it under the terms of the GNU General Public License as published by
 | ||
|  * the Free Software Foundation; either version 3, or (at your option)
 | ||
|  * any later version.
 | ||
|  * 
 | ||
|  * GNU Radio is distributed in the hope that it will be useful,
 | ||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||
|  * GNU General Public License for more details.
 | ||
|  * 
 | ||
|  * You should have received a copy of the GNU General Public License
 | ||
|  * along with GNU Radio; see the file COPYING.  If not, write to
 | ||
|  * the Free Software Foundation, Inc., 51 Franklin Street,
 | ||
|  * Boston, MA 02110-1301, USA.
 | ||
|  */
 | ||
| 
 | ||
| #ifdef HAVE_CONFIG_H
 | ||
| #include "config.h"
 | ||
| #endif
 | ||
| 
 | ||
| #include "fusb_linux.h"
 | ||
| #include "usb.h"		// libusb header
 | ||
| #include <stdexcept>
 | ||
| #ifdef HAVE_LINUX_COMPILER_H
 | ||
| #include <linux/compiler.h>
 | ||
| #endif
 | ||
| #include <linux/usbdevice_fs.h>	// interface to kernel portion of user mode usb driver
 | ||
| #include <sys/ioctl.h>
 | ||
| #include <assert.h>
 | ||
| #include <string.h>
 | ||
| #include <algorithm>
 | ||
| #include <errno.h>
 | ||
| #include <string.h>
 | ||
| #include <cstdio>
 | ||
| 
 | ||
| 
 | ||
| #define MINIMIZE_TX_BUFFERING 1		// must be defined to 0 or 1
 | ||
| 
 | ||
| 
 | ||
| //static const int MAX_BLOCK_SIZE = fusb_sysconfig::max_block_size();		// hard limit
 | ||
| //static const int DEFAULT_BLOCK_SIZE = MAX_BLOCK_SIZE;
 | ||
| static const int DEFAULT_BUFFER_SIZE = 4 * (1L << 20);				// 4 MB / endpoint
 | ||
| 
 | ||
| static const int MAX_BLOCK_SIZE     =  16 * 1024;	// hard limit
 | ||
| static const int DEFAULT_BLOCK_SIZE =   4 * 1024;	// fewer kernel memory problems
 | ||
| static const int FUSB_BUFFER_SIZE   =   1 * (1L << 20); // 1MB
 | ||
| 
 | ||
| // Totally evil and fragile extraction of file descriptor from
 | ||
| // guts of libusb.  They don't install usbi.h, which is what we'd need
 | ||
| // to do this nicely.
 | ||
| //
 | ||
| // FIXME if everything breaks someday in the future, look here...
 | ||
| 
 | ||
| static int
 | ||
| fd_from_usb_dev_handle (usb_dev_handle *udh)
 | ||
| {
 | ||
|   return *((int *) udh);
 | ||
| }
 | ||
| 
 | ||
| inline static void
 | ||
| urb_set_ephandle (usbdevfs_urb *urb, fusb_ephandle_linux *handle)
 | ||
| {
 | ||
|   urb->usercontext = handle;
 | ||
| }
 | ||
| 
 | ||
| inline static fusb_ephandle_linux *
 | ||
| urb_get_ephandle (usbdevfs_urb *urb)
 | ||
| {
 | ||
|   return (fusb_ephandle_linux *) urb->usercontext;
 | ||
| }
 | ||
| 
 | ||
| // ------------------------------------------------------------------------
 | ||
| // 		   USB request block (urb) allocation
 | ||
| // ------------------------------------------------------------------------
 | ||
| 
 | ||
| static usbdevfs_urb *
 | ||
| alloc_urb (fusb_ephandle_linux *self, int buffer_length, int endpoint,
 | ||
| 	   bool input_p, unsigned char *write_buffer)
 | ||
| {
 | ||
|   usbdevfs_urb	*urb = new usbdevfs_urb;
 | ||
|   memset (urb, 0, sizeof (*urb));
 | ||
| 
 | ||
|   urb->buffer_length = buffer_length;
 | ||
| 
 | ||
|   // We allocate dedicated memory only for input buffers.
 | ||
|   // For output buffers we reuse the same buffer (the kernel 
 | ||
|   // copies the data at submital time)
 | ||
| 
 | ||
|   if (input_p)
 | ||
|     urb->buffer = new unsigned char [buffer_length];
 | ||
|   else
 | ||
|     urb->buffer = write_buffer;
 | ||
| 
 | ||
|   // init common values
 | ||
| 
 | ||
|   urb->type = USBDEVFS_URB_TYPE_BULK;
 | ||
|   urb->endpoint = (endpoint & 0x7f) | (input_p ? 0x80 : 0);
 | ||
| 
 | ||
|   // USBDEVFS_URB_QUEUE_BULK goes away in linux 2.5, but is needed if
 | ||
|   // we are using a 2.4 usb-uhci host controller driver.  This is
 | ||
|   // unlikely since we're almost always going to be plugged into a
 | ||
|   // high speed host controller (ehci)
 | ||
| #if 0 && defined (USBDEVFS_URB_QUEUE_BULK)
 | ||
|   urb->flags = USBDEVFS_URB_QUEUE_BULK;
 | ||
| #endif
 | ||
| 
 | ||
|   urb->signr = 0;
 | ||
|   urb_set_ephandle (urb, self);
 | ||
| 
 | ||
|   return urb;
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| free_urb (usbdevfs_urb *urb)
 | ||
| {
 | ||
|   // if this was an input urb, free the buffer
 | ||
|   if (urb->endpoint & 0x80)
 | ||
|     delete [] ((unsigned char *) urb->buffer);
 | ||
| 
 | ||
|   delete urb;
 | ||
| }
 | ||
| 
 | ||
| // ------------------------------------------------------------------------
 | ||
| // 				device handle
 | ||
| // ------------------------------------------------------------------------
 | ||
| 
 | ||
| fusb_devhandle_linux::fusb_devhandle_linux (usb_dev_handle *udh)
 | ||
|   : fusb_devhandle (udh)
 | ||
| {
 | ||
|   // that's all
 | ||
| }
 | ||
| 
 | ||
| fusb_devhandle_linux::~fusb_devhandle_linux ()
 | ||
| {
 | ||
|   // if there are any pending requests, cancel them and free the urbs.
 | ||
|   
 | ||
|   std::list<usbdevfs_urb*>::reverse_iterator it;
 | ||
| 
 | ||
|   for (it = d_pending_rqsts.rbegin (); it != d_pending_rqsts.rend (); it++){
 | ||
|     _cancel_urb (*it);
 | ||
|     free_urb (*it);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| fusb_ephandle *
 | ||
| fusb_devhandle_linux::make_ephandle (int endpoint, bool input_p,
 | ||
| 				     int block_size, int nblocks)
 | ||
| {
 | ||
|   return new fusb_ephandle_linux (this, endpoint, input_p,
 | ||
| 				  block_size, nblocks);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| // Attempt to cancel all transactions associated with eph.
 | ||
| 
 | ||
| void
 | ||
| fusb_devhandle_linux::_cancel_pending_rqsts (fusb_ephandle_linux *eph)
 | ||
| {
 | ||
|   std::list<usbdevfs_urb*>::reverse_iterator it;
 | ||
| 
 | ||
|   for (it = d_pending_rqsts.rbegin (); it != d_pending_rqsts.rend (); it++){
 | ||
|     if (urb_get_ephandle (*it) == eph)
 | ||
|       _cancel_urb (*it);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| void 
 | ||
| fusb_devhandle_linux::pending_add (usbdevfs_urb *urb)
 | ||
| {
 | ||
|   d_pending_rqsts.push_back (urb);
 | ||
| }
 | ||
| 
 | ||
| usbdevfs_urb *
 | ||
| fusb_devhandle_linux::pending_get ()
 | ||
| {
 | ||
|   if (d_pending_rqsts.empty ())
 | ||
|     return 0;
 | ||
| 
 | ||
|   usbdevfs_urb *urb = d_pending_rqsts.front ();
 | ||
|   d_pending_rqsts.pop_front ();
 | ||
|   return urb;
 | ||
| }
 | ||
| 
 | ||
| bool
 | ||
| fusb_devhandle_linux::pending_remove (usbdevfs_urb *urb)
 | ||
| {
 | ||
|   std::list<usbdevfs_urb*>::iterator	result = find (d_pending_rqsts.begin (),
 | ||
| 						       d_pending_rqsts.end (),
 | ||
| 						       urb);
 | ||
|   if (result == d_pending_rqsts.end ()){
 | ||
|     fprintf (stderr, "fusb::pending_remove: failed to find urb in pending_rqsts: %p\n", urb);
 | ||
|     return false;
 | ||
|   }
 | ||
|   d_pending_rqsts.erase (result);
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  * Submit the urb to the kernel.
 | ||
|  * iff successful, the urb will be placed on the devhandle's pending list.
 | ||
|  */
 | ||
| bool
 | ||
| fusb_devhandle_linux::_submit_urb (usbdevfs_urb *urb)
 | ||
| {
 | ||
|   int	ret;
 | ||
| 
 | ||
|   ret = ioctl (fd_from_usb_dev_handle (d_udh), USBDEVFS_SUBMITURB, urb);
 | ||
|   if (ret < 0){
 | ||
|     perror ("fusb::_submit_urb");
 | ||
|     return false;
 | ||
|   }
 | ||
|   
 | ||
|   pending_add (urb);
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  * Attempt to cancel the in pending or in-progress urb transaction.
 | ||
|  * Return true iff transaction was sucessfully cancelled.
 | ||
|  *
 | ||
|  * Failure to cancel should not be considered a problem.  This frequently
 | ||
|  * occurs if the transaction has already completed in the kernel but hasn't
 | ||
|  * yet been reaped by the user mode code.
 | ||
|  *
 | ||
|  * urbs which were cancelled have their status field set to -ENOENT when
 | ||
|  * they are reaped.
 | ||
|  */
 | ||
| bool
 | ||
| fusb_devhandle_linux::_cancel_urb (usbdevfs_urb *urb)
 | ||
| {
 | ||
|   int ret = ioctl (fd_from_usb_dev_handle (d_udh), USBDEVFS_DISCARDURB, urb);
 | ||
|   if (ret < 0){
 | ||
|     // perror ("fusb::_cancel_urb");
 | ||
|     return false;
 | ||
|   }
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  * Check with the kernel and see if any of our outstanding requests
 | ||
|  * have completed.  For each completed transaction, remove it from the
 | ||
|  * devhandle's pending list and append it to the completed list for
 | ||
|  * the corresponding endpoint.
 | ||
|  *
 | ||
|  * If any transactions are reaped return true.
 | ||
|  *
 | ||
|  * If ok_to_block_p is true, then this will block until at least one
 | ||
|  * transaction completes or an unrecoverable error occurs.
 | ||
|  */
 | ||
| bool
 | ||
| fusb_devhandle_linux::_reap (bool ok_to_block_p)
 | ||
| {
 | ||
|   int		ret;
 | ||
|   int		nreaped = 0;
 | ||
|   usbdevfs_urb	*urb = 0;
 | ||
| 
 | ||
|   int	fd = fd_from_usb_dev_handle (d_udh);
 | ||
|   
 | ||
|   // try to reap as many as possible without blocking...
 | ||
| 
 | ||
|   while ((ret = ioctl (fd, USBDEVFS_REAPURBNDELAY, &urb)) == 0){
 | ||
|     if (urb->status != 0 && urb->status != -ENOENT){
 | ||
|       fprintf (stderr, "_reap: usb->status = %d, actual_length = %5d\n",
 | ||
| 	       urb->status, urb->actual_length);
 | ||
|     }
 | ||
|     pending_remove (urb);
 | ||
|     urb_get_ephandle (urb)->completed_list_add (urb);
 | ||
|     nreaped++;
 | ||
|   }
 | ||
| 
 | ||
|   if (nreaped > 0)		// if we got any, return w/o blocking
 | ||
|     return true;
 | ||
| 
 | ||
|   if (!ok_to_block_p)
 | ||
|     return false;
 | ||
|   
 | ||
|   ret = ioctl (fd, USBDEVFS_REAPURB, &urb);
 | ||
|   if (ret < 0){
 | ||
|     perror ("fusb::_reap");
 | ||
|     return false;
 | ||
|   }
 | ||
| 
 | ||
|   pending_remove (urb);
 | ||
|   urb_get_ephandle (urb)->completed_list_add (urb);
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| fusb_devhandle_linux::_wait_for_completion ()
 | ||
| {
 | ||
|   while (!d_pending_rqsts.empty ())
 | ||
|     if (!_reap(true))
 | ||
|       break;
 | ||
| }
 | ||
| // ------------------------------------------------------------------------
 | ||
| // 			     end point handle
 | ||
| // ------------------------------------------------------------------------
 | ||
| 
 | ||
| fusb_ephandle_linux::fusb_ephandle_linux (fusb_devhandle_linux *devhandle,
 | ||
| 					  int endpoint,
 | ||
| 					  bool input_p,
 | ||
| 					  int block_size, int nblocks)
 | ||
|   : fusb_ephandle (endpoint, input_p, block_size, nblocks),
 | ||
|     d_devhandle (devhandle), 
 | ||
|     d_write_work_in_progress (0), d_write_buffer (0),
 | ||
|     d_read_work_in_progress (0), d_read_buffer (0), d_read_buffer_end (0)
 | ||
| {
 | ||
| 
 | ||
|   if (d_block_size < 0 || d_block_size > MAX_BLOCK_SIZE)
 | ||
|     throw std::out_of_range ("fusb_ephandle_linux: block_size");
 | ||
| 
 | ||
|   if (d_nblocks < 0)
 | ||
|     throw std::out_of_range ("fusb_ephandle_linux: nblocks");
 | ||
| 
 | ||
|   if (d_block_size == 0)
 | ||
|     d_block_size = DEFAULT_BLOCK_SIZE;
 | ||
| 
 | ||
|   if (d_nblocks == 0)
 | ||
|     d_nblocks = std::max (1, DEFAULT_BUFFER_SIZE / d_block_size);
 | ||
| 
 | ||
|   if (!d_input_p)
 | ||
|     if (!MINIMIZE_TX_BUFFERING)
 | ||
|       d_write_buffer = new unsigned char [d_block_size];
 | ||
| 
 | ||
|   if (0)
 | ||
|     fprintf(stderr, "fusb_ephandle_linux::ctor: d_block_size = %d  d_nblocks = %d\n",
 | ||
| 	    d_block_size, d_nblocks);
 | ||
| 
 | ||
|   // allocate urbs
 | ||
| 
 | ||
|   for (int i = 0; i < d_nblocks; i++)
 | ||
|     d_free_list.push_back (alloc_urb (this, d_block_size, d_endpoint,
 | ||
| 				      d_input_p, d_write_buffer));
 | ||
| }
 | ||
| 
 | ||
| fusb_ephandle_linux::~fusb_ephandle_linux ()
 | ||
| {
 | ||
|   stop ();
 | ||
| 
 | ||
|   usbdevfs_urb *urb;
 | ||
| 
 | ||
|   while ((urb = free_list_get ()) != 0)
 | ||
|     free_urb (urb);
 | ||
| 
 | ||
|   while ((urb = completed_list_get ()) != 0)
 | ||
|     free_urb (urb);
 | ||
| 
 | ||
|   if (d_write_work_in_progress)
 | ||
|     free_urb (d_write_work_in_progress);
 | ||
| 
 | ||
|   delete [] d_write_buffer;
 | ||
| 
 | ||
|   if (d_read_work_in_progress)
 | ||
|     free_urb (d_read_work_in_progress);
 | ||
| }
 | ||
| 
 | ||
| // ----------------------------------------------------------------
 | ||
| 
 | ||
| bool
 | ||
| fusb_ephandle_linux::start ()
 | ||
| {
 | ||
|   if (d_started)
 | ||
|     return true;		// already running
 | ||
| 
 | ||
|   d_started = true;
 | ||
| 
 | ||
|   if (d_input_p){		// fire off all the reads
 | ||
|     usbdevfs_urb *urb;
 | ||
| 
 | ||
|     int nerrors = 0;
 | ||
|     while ((urb = free_list_get ()) != 0 && nerrors < d_nblocks){
 | ||
|       if (!submit_urb (urb))
 | ||
| 	nerrors++;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| //
 | ||
| // kill all i/o in progress.
 | ||
| // kill any completed but unprocessed transactions.
 | ||
| //
 | ||
| bool
 | ||
| fusb_ephandle_linux::stop ()
 | ||
| {
 | ||
|   if (!d_started)
 | ||
|     return true;
 | ||
| 
 | ||
|   if (d_write_work_in_progress){
 | ||
|     free_list_add (d_write_work_in_progress);
 | ||
|     d_write_work_in_progress = 0;
 | ||
|   }
 | ||
| 
 | ||
|   if (d_read_work_in_progress){
 | ||
|     free_list_add (d_read_work_in_progress);
 | ||
|     d_read_work_in_progress = 0;
 | ||
|     d_read_buffer = 0;
 | ||
|     d_read_buffer_end = 0;
 | ||
|   }
 | ||
| 
 | ||
|   d_devhandle->_cancel_pending_rqsts (this);
 | ||
|   d_devhandle->_reap (false);
 | ||
| 
 | ||
|   while (1){
 | ||
|     usbdevfs_urb *urb;
 | ||
|     while ((urb = completed_list_get ()) != 0)
 | ||
|       free_list_add (urb);
 | ||
| 
 | ||
|     if (d_free_list.size () == (unsigned) d_nblocks)
 | ||
|       break;
 | ||
| 
 | ||
|     if (!d_devhandle->_reap(true))
 | ||
|       break;
 | ||
|   }
 | ||
| 
 | ||
|   d_started = false;
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| // ----------------------------------------------------------------
 | ||
| //			routines for writing 
 | ||
| // ----------------------------------------------------------------
 | ||
| 
 | ||
| #if (MINIMIZE_TX_BUFFERING)
 | ||
| 
 | ||
| int 
 | ||
| fusb_ephandle_linux::write(const void *buffer, int nbytes)
 | ||
| {
 | ||
|   if (!d_started)
 | ||
|     return -1;
 | ||
|   
 | ||
|   if (d_input_p)
 | ||
|     return -1;
 | ||
| 
 | ||
|   assert(nbytes % 512 == 0);
 | ||
| 
 | ||
|   unsigned char *src = (unsigned char *) buffer;
 | ||
| 
 | ||
|   int n = 0;
 | ||
|   while (n < nbytes){
 | ||
| 
 | ||
|     usbdevfs_urb *urb = get_write_work_in_progress();
 | ||
|     if (!urb)
 | ||
|       return -1;
 | ||
|     assert(urb->actual_length == 0);
 | ||
|     int m = std::min(nbytes - n, MAX_BLOCK_SIZE);
 | ||
|     urb->buffer = src;
 | ||
|     urb->buffer_length = m;
 | ||
| 
 | ||
|     n += m;
 | ||
|     src += m;
 | ||
| 
 | ||
|     if (!submit_urb(urb))
 | ||
|       return -1;
 | ||
| 
 | ||
|     d_write_work_in_progress = 0;
 | ||
|   }
 | ||
| 
 | ||
|   return n;
 | ||
| }
 | ||
| 
 | ||
| #else
 | ||
| 
 | ||
| int 
 | ||
| fusb_ephandle_linux::write (const void *buffer, int nbytes)
 | ||
| {
 | ||
|   if (!d_started)
 | ||
|     return -1;
 | ||
|   
 | ||
|   if (d_input_p)
 | ||
|     return -1;
 | ||
| 
 | ||
|   unsigned char *src = (unsigned char *) buffer;
 | ||
| 
 | ||
|   int n = 0;
 | ||
|   while (n < nbytes){
 | ||
| 
 | ||
|     usbdevfs_urb *urb = get_write_work_in_progress ();
 | ||
|     if (!urb)
 | ||
|       return -1;
 | ||
|     unsigned char *dst = (unsigned char *) urb->buffer;
 | ||
|     int m = std::min (nbytes - n, urb->buffer_length - urb->actual_length);
 | ||
| 
 | ||
|     memcpy (&dst[urb->actual_length], &src[n], m);
 | ||
|     urb->actual_length += m;
 | ||
|     n += m;
 | ||
| 
 | ||
|     if (urb->actual_length == urb->buffer_length){
 | ||
|       if (!submit_urb (urb))
 | ||
| 	return -1;
 | ||
|       d_write_work_in_progress = 0;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   return n;
 | ||
| }
 | ||
| 
 | ||
| #endif
 | ||
| 
 | ||
| usbdevfs_urb *
 | ||
| fusb_ephandle_linux::get_write_work_in_progress ()
 | ||
| {
 | ||
|   // if we've already got some work in progress, return it
 | ||
| 
 | ||
|   if (d_write_work_in_progress)
 | ||
|     return d_write_work_in_progress;
 | ||
| 
 | ||
|   while (1){
 | ||
| 
 | ||
|     reap_complete_writes ();
 | ||
| 
 | ||
|     usbdevfs_urb *urb = free_list_get ();
 | ||
| 
 | ||
|     if (urb != 0){
 | ||
|       assert (urb->actual_length == 0);
 | ||
|       d_write_work_in_progress = urb;
 | ||
|       return urb;
 | ||
|     }
 | ||
| 
 | ||
|     // The free list is empty.  Tell the device handle to reap.
 | ||
|     // Anything it reaps for us will end up on our completed list.
 | ||
| 
 | ||
|     if (!d_devhandle->_reap (true))
 | ||
|       return 0;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| fusb_ephandle_linux::reap_complete_writes ()
 | ||
| {
 | ||
|   // take a look at the completed_list and xfer to free list after
 | ||
|   // checking for errors.
 | ||
| 
 | ||
|   usbdevfs_urb *urb;
 | ||
|   
 | ||
|   while ((urb = completed_list_get ()) != 0){
 | ||
| 
 | ||
|     // Check for any errors or short writes that were reported in the urb.
 | ||
|     // The kernel sets status, actual_length and error_count.
 | ||
|     // error_count is only used for ISO xfers.
 | ||
|     // status is 0 if successful, else is an errno kind of thing
 | ||
| 
 | ||
|     if (urb->status != 0){
 | ||
|       fprintf (stderr, "fusb: (status %d) %s\n", urb->status, strerror (-urb->status));
 | ||
|     }
 | ||
|     else if (urb->actual_length != urb->buffer_length){
 | ||
|       fprintf (stderr, "fusb: short write xfer: %d != %d\n",
 | ||
| 	       urb->actual_length, urb->buffer_length);
 | ||
|     }
 | ||
| 
 | ||
|     free_list_add (urb);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| fusb_ephandle_linux::wait_for_completion ()
 | ||
| {
 | ||
|   d_devhandle->_wait_for_completion ();
 | ||
| }
 | ||
| 
 | ||
| // ----------------------------------------------------------------
 | ||
| //		       routines for reading
 | ||
| // ----------------------------------------------------------------
 | ||
| 
 | ||
| int
 | ||
| fusb_ephandle_linux::read (void *buffer, int nbytes)
 | ||
| {
 | ||
|   if (!d_started)
 | ||
|     return -1;
 | ||
|   
 | ||
|   if (!d_input_p)
 | ||
|     return -1;
 | ||
| 
 | ||
|   unsigned char *dst = (unsigned char *) buffer;
 | ||
| 
 | ||
|   int n = 0;
 | ||
|   while (n < nbytes){
 | ||
| 
 | ||
|     if (d_read_buffer >= d_read_buffer_end)
 | ||
|       if (!reload_read_buffer ())
 | ||
| 	return -1;
 | ||
| 
 | ||
|     int m = std::min (nbytes - n, (int) (d_read_buffer_end - d_read_buffer));
 | ||
| 
 | ||
|     memcpy (&dst[n], d_read_buffer, m);
 | ||
|     d_read_buffer += m;
 | ||
|     n += m;
 | ||
|   }
 | ||
| 
 | ||
|   return n;
 | ||
| }
 | ||
| 
 | ||
| bool
 | ||
| fusb_ephandle_linux::reload_read_buffer ()
 | ||
| {
 | ||
|   assert (d_read_buffer >= d_read_buffer_end);
 | ||
| 
 | ||
|   usbdevfs_urb *urb;
 | ||
| 
 | ||
|   if (d_read_work_in_progress){
 | ||
|     // We're done with this urb.  Fire off a read to refill it.
 | ||
|     urb = d_read_work_in_progress;
 | ||
|     d_read_work_in_progress = 0;
 | ||
|     d_read_buffer = 0;
 | ||
|     d_read_buffer_end = 0;
 | ||
|     urb->actual_length = 0;
 | ||
|     if (!submit_urb (urb))
 | ||
|       return false;
 | ||
|   }
 | ||
| 
 | ||
|   while (1){
 | ||
| 
 | ||
|     while ((urb = completed_list_get ()) == 0)
 | ||
|       if (!d_devhandle->_reap (true))
 | ||
| 	return false;
 | ||
| 
 | ||
|     // check result of completed read
 | ||
| 
 | ||
|     if (urb->status != 0){
 | ||
|       // We've got a problem. Report it and fail.
 | ||
|       fprintf (stderr, "fusb: (rd status %d) %s\n", urb->status, strerror (-urb->status));
 | ||
|       urb->actual_length = 0;
 | ||
|       free_list_add (urb);
 | ||
|       return false;
 | ||
|     }
 | ||
| 
 | ||
|     // we've got a happy urb, full of data...
 | ||
| 
 | ||
|     d_read_work_in_progress = urb;
 | ||
|     d_read_buffer = (unsigned char *) urb->buffer;
 | ||
|     d_read_buffer_end = d_read_buffer + urb->actual_length;
 | ||
| 
 | ||
|     return true;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| // ----------------------------------------------------------------
 | ||
| 
 | ||
| void
 | ||
| fusb_ephandle_linux::free_list_add (usbdevfs_urb *urb)
 | ||
| {
 | ||
|   assert (urb_get_ephandle (urb) == this);
 | ||
|   urb->actual_length = 0;
 | ||
|   d_free_list.push_back (urb);
 | ||
| }
 | ||
| 
 | ||
| usbdevfs_urb *
 | ||
| fusb_ephandle_linux::free_list_get ()
 | ||
| {
 | ||
|   if (d_free_list.empty ())
 | ||
|     return 0;
 | ||
| 
 | ||
|   usbdevfs_urb *urb = d_free_list.front ();
 | ||
|   d_free_list.pop_front ();
 | ||
|   return urb;
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| fusb_ephandle_linux::completed_list_add (usbdevfs_urb *urb)
 | ||
| {
 | ||
|   assert (urb_get_ephandle (urb) == this);
 | ||
|   d_completed_list.push_back (urb);
 | ||
| }
 | ||
| 
 | ||
| usbdevfs_urb *
 | ||
| fusb_ephandle_linux::completed_list_get ()
 | ||
| {
 | ||
|   if (d_completed_list.empty ())
 | ||
|     return 0;
 | ||
| 
 | ||
|   usbdevfs_urb *urb = d_completed_list.front ();
 | ||
|   d_completed_list.pop_front ();
 | ||
|   return urb;
 | ||
| }
 | ||
| 
 | ||
| /*
 | ||
|  * Submit the urb.  If successful the urb ends up on the devhandle's
 | ||
|  * pending list, otherwise, it's back on our free list.
 | ||
|  */
 | ||
| bool
 | ||
| fusb_ephandle_linux::submit_urb (usbdevfs_urb *urb)
 | ||
| {
 | ||
|   if (!d_devhandle->_submit_urb (urb)){    // FIXME record the problem somewhere
 | ||
|     fprintf (stderr, "_submit_urb failed\n");
 | ||
|     free_list_add (urb);
 | ||
|     return false;
 | ||
|   }
 | ||
|   return true;
 | ||
| }
 |