usb-char.c

来自「linux下的USB和网络驱动程序代码」· C语言 代码 · 共 804 行 · 第 1/2 页

C
804
字号
	 if ( flags == 0 )
		  charstats.bytes_tx += size;
	 else
		  charstats.cnt_tx_errors++;
	 last_tx_size = size;
	 last_tx_result = flags;
	 sending = 0;
	 wake_up_interruptible( &wq_write );
	 wake_up_interruptible( &wq_poll );
}


//////////////////////////////////////////////////////////////////////////////
// Workers
//////////////////////////////////////////////////////////////////////////////

static int usbc_open( struct inode *pInode, struct file *pFile )
{
	 int retval = 0;

	 PRINTK( KERN_DEBUG "%sopen()\n", pszMe );

	 /* start usb core */
	 retval = sa1100_usb_open( "usb-char" );
	 if ( retval ) return retval;

	 /* allocate memory */
	 if ( usb_ref_count == 0 ) {
		  tx_buf = (char*) kmalloc( TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA );
		  if ( tx_buf == NULL ) {
			   printk( "%sARGHH! COULD NOT ALLOCATE TX BUFFER\n", pszMe );
			   goto malloc_fail;
		  }
		  rx_ring.buf = 
			(char*) kmalloc( RBUF_SIZE, GFP_KERNEL );

		  if ( rx_ring.buf == NULL ) {
			   printk( "%sARGHH! COULD NOT ALLOCATE RX BUFFER\n", pszMe );
			   goto malloc_fail;
		  }

		  packet_buffer = 
			(char*) kmalloc( RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA  );

		  if ( packet_buffer == NULL ) {
			   printk( "%sARGHH! COULD NOT ALLOCATE RX PACKET BUFFER\n", pszMe );
			   goto malloc_fail;
		  }
		  rx_ring.in = rx_ring.out = 0;
		  memset( &charstats, 0, sizeof( charstats ) );
		  sending = 0;
		  last_tx_result = 0;
		  last_tx_size = 0;
	 }
	 
	 /* modify default descriptors */
	 twiddle_descriptors();

	 retval = sa1100_usb_start();
	 if ( retval ) {
		  printk( "%sAGHH! Could not USB core\n", pszMe );
		  free_txrx_buffers();
		  return retval;
	 }
	 usb_ref_count++;   /* must do _before_ kick_start() */
	 MOD_INC_USE_COUNT;
	 kick_start_rx();
	 return 0;

 malloc_fail:
	 free_txrx_buffers();
	 return -ENOMEM;
}

/*
 * Read endpoint. Note that you can issue a read to an 
 * unconfigured endpoint. Eventually, the host may come along
 * and configure underneath this module and data will appear.
 */
static ssize_t usbc_read( struct file *pFile, char *pUserBuffer,
                        size_t stCount, loff_t *pPos )
{
	 ssize_t retval;
	 int flags;
	 DECLARE_WAITQUEUE( wait, current );

	 PRINTK( KERN_DEBUG "%sread()\n", pszMe );

	 local_irq_save( flags );
	 if ( last_rx_result == 0 ) {
    	 local_irq_restore( flags );
	 } else {  /* an error happended and receiver is paused */
		  local_irq_restore( flags );
		  last_rx_result = 0;
		  kick_start_rx();
	 }

	 add_wait_queue( &wq_read, &wait );
	 while( 1 ) {
		  ssize_t bytes_avail;
		  ssize_t bytes_to_end;

		  set_current_state( TASK_INTERRUPTIBLE );

		  /* snap ring buf state */
		  local_irq_save( flags );
		  bytes_avail  = CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE );
		  bytes_to_end = CIRC_CNT_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE );
		  local_irq_restore( flags );

		  if ( bytes_avail != 0 ) {
			   ssize_t bytes_to_move = MIN( stCount, bytes_avail );
			   retval = 0;		// will be bytes transfered
			   if ( bytes_to_move != 0 ) {
					size_t n = MIN( bytes_to_end, bytes_to_move );
					if ( copy_to_user( pUserBuffer,
									   &rx_ring.buf[ rx_ring.out ],
									   n ) ) {
						 retval = -EFAULT;
						 break;
					}
					bytes_to_move -= n;
 					retval += n;
					// might go 1 char off end, so wrap
					rx_ring.out = ( rx_ring.out + n ) & (RBUF_SIZE-1);
					if ( copy_to_user( pUserBuffer + n,
									   &rx_ring.buf[ rx_ring.out ],
									   bytes_to_move )
						 ) {
						 retval = -EFAULT;
						 break;
					}
					rx_ring.out += bytes_to_move;		// cannot wrap
					retval += bytes_to_move;
					kick_start_rx();
			   }
			   break;
		  }
		  else if ( last_rx_result ) {
			   retval = last_rx_result;
			   break;
		  }
		  else if ( pFile->f_flags & O_NONBLOCK ) {  // no data, can't sleep
			   retval = -EAGAIN;
			   break;
		  }
		  else if ( signal_pending( current ) ) {   // no data, can sleep, but signal
			   retval = -ERESTARTSYS;
			   break;
		  }
		  schedule();					   			// no data, can sleep
	 }			
	 set_current_state( TASK_RUNNING );
	 remove_wait_queue( &wq_read, &wait );

	 if ( retval < 0 )
		  printk( "%sread error %d - %s\n", pszMe, retval, what_the_f( retval ) ); 
	 return retval;
}

/*
 * Write endpoint. This routine attempts to break the passed in buffer
 * into usb DATA0/1 packet size chunks and send them to the host. 
 * (The lower-level driver tries to do this too, but easier for us
 * to manage things here.) 
 *
 * We are at the mercy of the host here, in that it must send an IN 
 * token to us to pull this data back, so hopefully some higher level
 * protocol is expecting traffic to flow in that direction so the host
 * is actually polling us. To guard against hangs, a 5 second timeout
 * is used.
 *
 * This routine takes some care to only report bytes sent that have
 * actually made it across the wire. Thus we try to stay in lockstep
 * with the completion routine and only have one packet on the xmit
 * hardware at a time. Multiple simultaneous writers will get 
 * "undefined" results.
 *
  */
static ssize_t  usbc_write( struct file *pFile, const char * pUserBuffer,
							 size_t stCount, loff_t *pPos )
{
	 ssize_t retval = 0;
	 ssize_t stSent = 0;

	 DECLARE_WAITQUEUE( wait, current );

	 PRINTK( KERN_DEBUG "%swrite() %d bytes\n", pszMe, stCount );

	 down( &xmit_sem );  // only one thread onto the hardware at a time

	 while( stCount != 0 && retval == 0 ) {
		  int nThisTime  = MIN( TX_PACKET_SIZE, stCount );
		  copy_from_user( tx_buf, pUserBuffer, nThisTime );
		  sending = nThisTime;
 		  retval = sa1100_usb_send( tx_buf, nThisTime, tx_done_callback );
		  if ( retval < 0 ) {
			   char * p = what_the_f( retval );
			   printk( "%sCould not queue xmission. rc=%d - %s\n", 
					   pszMe, retval, p );
			   sending = 0;
			   break;
		  }
		  /* now have something on the diving board */
		  add_wait_queue( &wq_write, &wait );
		  tx_timer.expires = jiffies + ( HZ * 5 );
		  add_timer( &tx_timer );
		  while( 1 ) {
			   set_current_state( TASK_INTERRUPTIBLE );
			   if ( sending == 0 ) {  /* it jumped into the pool */
					del_timer( &tx_timer );
					retval = last_tx_result;
					if ( retval == 0 ) {
						 stSent		 += last_tx_size;
						 pUserBuffer += last_tx_size;
						 stCount     -= last_tx_size;
					}
					else
						 printk( "%sxmission error rc=%d - %s\n", 
								 pszMe, retval, what_the_f(retval) );
					break;
			   }
			   else if ( signal_pending( current ) ) {
					del_timer( &tx_timer );
					printk( "%ssignal\n", pszMe  );
					retval = -ERESTARTSYS;
					break;
			   }
			   schedule();
		  }
		  set_current_state( TASK_RUNNING );
		  remove_wait_queue( &wq_write, &wait );
	 }

	 up( &xmit_sem );

	 if ( 0 == retval )
		  retval = stSent;
	 return retval;
}

static unsigned int usbc_poll( struct file *pFile, poll_table * pWait )
{
	 unsigned int retval = 0;
	 int flags;

	 PRINTK( KERN_DEBUG "%spoll()\n", pszMe );

	 local_irq_save( flags );
	 if ( last_rx_result == 0 ) {
    	 local_irq_restore( flags );
	 } else {  /* an error happended and receiver is paused */
		  local_irq_restore( flags );
		  last_rx_result = 0;
		  kick_start_rx();
	 }

	 poll_wait( pFile, &wq_poll, pWait );

	 if ( CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ) )
		  retval |= POLLIN | POLLRDNORM;
	 if ( sa1100_usb_xmitter_avail() )
		  retval |= POLLOUT | POLLWRNORM;

	 PRINTK( KERN_DEBUG "%spoll() returns %d\n", pszMe, retval );
	 return retval;
}

static int usbc_ioctl( struct inode *pInode, struct file *pFile,
                       unsigned int nCmd, unsigned long argument )
{
	 int retval = 0;

	 switch( nCmd ) {

	 case USBC_IOC_FLUSH_RECEIVER:
		  sa1100_usb_recv_reset();	   	
		  rx_ring.in = rx_ring.out = 0; 
		  break;

	 case USBC_IOC_FLUSH_TRANSMITTER:
		  sa1100_usb_send_reset();
		  break;

	 case USBC_IOC_FLUSH_ALL:
		  sa1100_usb_recv_reset();	   	
		  rx_ring.in = rx_ring.out = 0; 
		  sa1100_usb_send_reset();
		  break;

	 default:
		  retval = -ENOIOCTLCMD;
		  break;

	 }
	 return retval;
}


static int usbc_close( struct inode *pInode, struct file * pFile )
{
	PRINTK( KERN_DEBUG "%sclose()\n", pszMe );
	if ( --usb_ref_count == 0 ) {
		 down( &xmit_sem );
		 sa1100_usb_stop();
		 free_txrx_buffers();
		 free_string_descriptors();
		 del_timer( &tx_timer );
		 sa1100_usb_close();
		 up( &xmit_sem );
	}
    MOD_DEC_USE_COUNT;  
    return 0;
}

#ifdef CONFIG_SA1100_EXTENEX1
#include "../../../drivers/char/ex_gpio.h"
void extenex_configured_notify_proc( void ) 
{
	 if ( exgpio_play_string( "440,1:698,1" ) == -EAGAIN )
		  printk( "%sWanted to BEEP but ex_gpio not open\n", pszMe );
}
#endif

#ifdef CONFIG_SA1100_ETCHSTONE1
void etchstone_configured_notify_proc(void)
{
  printk("%s" __FUNCTION__" called.\n", pszMe);
}
#endif

//////////////////////////////////////////////////////////////////////////////
// Initialization
//////////////////////////////////////////////////////////////////////////////

static struct file_operations usbc_fops = {
		owner:      THIS_MODULE,
		open:		usbc_open,
		read:		usbc_read,
		write:		usbc_write,
		poll:		usbc_poll,
		ioctl:		usbc_ioctl,
		release:	usbc_close,
};

static struct miscdevice usbc_misc_device = {
    USBC_MINOR, "usb_char", &usbc_fops
};

/*
 * usbc_init()
 */

int __init usbc_init( void )
{
	 int rc;

#if !defined( CONFIG_ARCH_SA1100 )
	 return -ENODEV;
#endif

	 if ( (rc = misc_register( &usbc_misc_device )) != 0 ) {
		  printk( KERN_WARNING "%sCould not register device 10, "
				  "%d. (%d)\n", pszMe, USBC_MINOR, rc );
		  return -EBUSY;
	 }
	 else
	   printk( KERN_WARNING "%sRegistered as 10, "
		   "%d. (%d)\n", pszMe, USBC_MINOR, rc );

	 // initialize wait queues
	 init_waitqueue_head( &wq_read );
	 init_waitqueue_head( &wq_write );
	 init_waitqueue_head( &wq_poll );

	 // initialize tx timeout timer
	 init_timer( &tx_timer );
	 tx_timer.function = tx_timeout;

	  printk( KERN_INFO "USB Function Character Driver Interface"
				  " - %s, (C) 2001, Extenex Corp.\n", VERSION 
		   );
        
	 return rc;
}

void __exit usbc_exit( void )
{
}

EXPORT_NO_SYMBOLS;

module_init(usbc_init);
module_exit(usbc_exit);



// end: usb-char.c



⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?