⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usb-char.c

📁 linux下的USB和网络驱动程序代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * (C) Copyright 2000-2001 Extenex Corporation
 *
 *	This program 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 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program 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 this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 *  usb-char.c 
 *  
 *  Miscellaneous character device interface for SA1100 USB function
 *	driver.
 *
 *  Background:
 *  The SA1100 function driver ported from the Compaq Itsy project 
 *  has an interface, usb-eth.c, to feed network packets over the
 *  usb wire and into the Linux TCP/IP stack. 
 *
 *  This file replaces that one with a simple character device 
 *  interface that allows unstructured "byte pipe" style reads and
 *  writes over the USB bulk endpoints by userspace programs.
 *
 *  A new define, CONFIG_SA1100_USB_NETLINK, has been created that,
 *  when set, (the default) causes the ethernet interface to be used.
 *  When not set, this more pedestrian character interface is linked
 *  in instead.
 *
 *  Please see linux/Documentation/arm/SA1100/SA1100_USB for details.
 *
 *  ward.willats@extenex.com
 *
 *  To do:
 *  - Can't dma into ring buffer directly with pci_map/unmap usb_recv
 *    uses and get bytes out at the same time DMA is going on. Investigate:
 *    a) changing usb_recv to use alloc_consistent() at client request; or
 *    b) non-ring-buffer based data structures. In the meantime, I am using
 *    a bounce buffer. Simple, but wasteful.
 */

#include <linux/module.h>
#include <linux/config.h>
#include <linux/miscdevice.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/cache.h>
#include <linux/poll.h>
#include <linux/circ_buf.h>
#include <linux/timer.h>

#include <asm/io.h>
#include <asm/semaphore.h>
#include <asm/proc/page.h>
#include <asm/mach-types.h>

#include "usb-char.h"
#include "sa1100_usb.h"


#warning TODO: update config.in to add Etchstone device.

#define machine_is_etchstone1() (1)
#define CONFIG_SA1100_ETCHSTONE1

#ifdef CONFIG_SA1100_EXTENEX1
#error TODO: Do not select Extenex device.  Bad things will happen.
#endif

//////////////////////////////////////////////////////////////////////////////
// Driver  Options
//////////////////////////////////////////////////////////////////////////////

#define VERSION 	"0.4"


#define VERBOSITY 0

#if VERBOSITY
# define	PRINTK(x, a...)	printk (x, ## a)
#else
# define	PRINTK(x, a...)	/**/
#endif

//////////////////////////////////////////////////////////////////////////////
// Globals - Macros - Enums - Structures
//////////////////////////////////////////////////////////////////////////////
#ifndef MIN
#define MIN( a, b ) ((a)<(b)?(a):(b))
#endif

typedef int bool; enum { false = 0, true = 1 };

static const char pszMe[] = "usbchr: ";

static wait_queue_head_t wq_read;
static wait_queue_head_t wq_write;
static wait_queue_head_t wq_poll;

/* Serialze multiple writers onto the transmit hardware
.. since we sleep the writer during transmit to stay in
.. sync. (Multiple writers don't make much sense, but..) */
static DECLARE_MUTEX( xmit_sem );

// size of usb DATA0/1 packets. 64 is standard maximum
// for bulk transport, though most hosts seem to be able
// to handle larger.
#define TX_PACKET_SIZE 64
#define RX_PACKET_SIZE 64
#define RBUF_SIZE  (4*PAGE_SIZE)

static struct wcirc_buf {
  char *buf;
  int in;
  int out;
} rx_ring = { NULL, 0, 0 };

static struct {
	 unsigned long  cnt_rx_complete;
	 unsigned long  cnt_rx_errors;
	 unsigned long  bytes_rx;
	 unsigned long  cnt_tx_timeouts;
	 unsigned long  cnt_tx_errors;
	 unsigned long  bytes_tx;
} charstats;


static char * tx_buf = NULL;
static char * packet_buffer = NULL;
static int sending = 0;
static int usb_ref_count = 0;
static int last_tx_result = 0;
static int last_rx_result = 0;
static int last_tx_size = 0;
static struct timer_list tx_timer;

//////////////////////////////////////////////////////////////////////////////
// Prototypes
//////////////////////////////////////////////////////////////////////////////
static char * 	what_the_f( int e );
static void 	free_txrx_buffers( void );
static void     twiddle_descriptors( void );
static void     free_string_descriptors( void ) ;
static int      usbc_open( struct inode *pInode, struct file *pFile );
static void     rx_done_callback_packet_buffer( int flag, int size );

static void     tx_timeout( unsigned long );
static void     tx_done_callback( int flag, int size );

static ssize_t  usbc_read( struct file *, char *, size_t, loff_t * );
static ssize_t  usbc_write( struct file *, const char *, size_t, loff_t * );
static unsigned int usbc_poll( struct file *pFile, poll_table * pWait );
static int usbc_ioctl( struct inode *pInode, struct file *pFile,
                       unsigned int nCmd, unsigned long argument );
static int      usbc_close( struct inode *pInode, struct file *pFile );

#ifdef CONFIG_SA1100_EXTENEX1
static void     extenex_configured_notify_proc( void );
#endif

#ifdef CONFIG_SA1100_ETCHSTONE1
static void     etchstone_configured_notify_proc( void );
#endif
//////////////////////////////////////////////////////////////////////////////
// Private Helpers
//////////////////////////////////////////////////////////////////////////////

static char * what_the_f( int e )
{
	 char * p;
	 switch( e ) {
	 case 0:
		  p = "noErr";
		  break;
	 case -ENODEV:
		  p = "ENODEV - usb not in config state";
		  break;
	 case -EBUSY:
		  p = "EBUSY - another request on the hardware";
		  break;
	 case -EAGAIN:
		  p = "EAGAIN";
		  break;
	 case -EINTR:
		  p = "EINTR - interrupted\n";
		  break;
	 case -EPIPE:
		  p = "EPIPE - zero length xfer\n";
		  break;
	 default:
		  p = "????";
		  break;
	 }
	 return p;
}

static void free_txrx_buffers( void )
{
	 if ( rx_ring.buf != NULL ) {
		  kfree( rx_ring.buf );
		  rx_ring.buf = NULL;
	 }
	 if ( packet_buffer != NULL ) {
		  kfree( packet_buffer );
		  packet_buffer = NULL;
	 }
	 if ( tx_buf != NULL ) {
		  kfree( tx_buf );
		  tx_buf = NULL;
	 }
}

/* twiddle_descriptors()
 * It is between open() and start(). Setup descriptors.
 */
static void twiddle_descriptors( void )
{
	 desc_t * pDesc = sa1100_usb_get_descriptor_ptr();
	 string_desc_t * pString;

	 pDesc->b.ep1.wMaxPacketSize = make_word_c( RX_PACKET_SIZE );
	 pDesc->b.ep1.bmAttributes   = USB_EP_BULK;
	 pDesc->b.ep2.wMaxPacketSize = make_word_c( TX_PACKET_SIZE );
	 pDesc->b.ep2.bmAttributes   = USB_EP_BULK;

	 if ( machine_is_extenex1() ) {
#ifdef CONFIG_SA1100_EXTENEX1
		  pDesc->dev.idVendor = make_word_c( 0xC9F );
		  pDesc->dev.idProduct = 1;
		  pDesc->dev.bcdDevice = make_word_c( 0x0001 );
		  pDesc->b.cfg.bmAttributes = USB_CONFIG_SELFPOWERED;
		  pDesc->b.cfg.MaxPower = 0;

		  pString = sa1100_usb_kmalloc_string_descriptor( "Extenex" );
		  if ( pString ) {
			   sa1100_usb_set_string_descriptor( 1, pString );
			   pDesc->dev.iManufacturer = 1;
		  }

		  pString = sa1100_usb_kmalloc_string_descriptor( "Handheld Theater" );
		  if ( pString ) {
			   sa1100_usb_set_string_descriptor( 2, pString );
			   pDesc->dev.iProduct = 2;
		  }

		  pString = sa1100_usb_kmalloc_string_descriptor( "00000000" );
		  if ( pString ) {
			   sa1100_usb_set_string_descriptor( 3, pString );
			   pDesc->dev.iSerialNumber = 3;
		  }

		  pString = sa1100_usb_kmalloc_string_descriptor( "HHT Bulk Transfer" );
		  if ( pString ) {
			   sa1100_usb_set_string_descriptor( 4, pString );
			   pDesc->b.intf.iInterface = 4;
		  }
		  sa1100_set_configured_callback( extenex_configured_notify_proc );
#endif
	 }

	 else if ( machine_is_etchstone1() ) {
#ifdef CONFIG_SA1100_ETCHSTONE1

#define ETCHSTONE_VENDOR_ID  0x0471
#define ETCHSTONE_ESLATE_ID  0x0222

	   printk( "%s: using vendor=0x%04x, product=0x%04x.\n", pszMe,
			   ETCHSTONE_VENDOR_ID, ETCHSTONE_ESLATE_ID );

		  pDesc->dev.idVendor = make_word_c( ETCHSTONE_VENDOR_ID );
		  pDesc->dev.idProduct = make_word_c( ETCHSTONE_ESLATE_ID );
		  pDesc->dev.bcdDevice = make_word_c( 0 );
		  pDesc->b.cfg.bmAttributes = USB_CONFIG_SELFPOWERED;
		  pDesc->b.cfg.MaxPower = 0;

		  pString = sa1100_usb_kmalloc_string_descriptor( "Etchstone" );
		  if ( pString ) {
			   sa1100_usb_set_string_descriptor( 1, pString );
			   pDesc->dev.iManufacturer = 1;
		  }

		  pString = sa1100_usb_kmalloc_string_descriptor( "eSlate" );
		  if ( pString ) {
			   sa1100_usb_set_string_descriptor( 2, pString );
			   pDesc->dev.iProduct = 2;
		  }

#warning TODO: USB serial number
		  printk("%s: using hardcoded 00000000 serial number.\n", pszMe);
		  pString = sa1100_usb_kmalloc_string_descriptor( "00000000" );
		  if ( pString ) {
			   sa1100_usb_set_string_descriptor( 3, pString );
			   pDesc->dev.iSerialNumber = 3;
		  }

		  pString = sa1100_usb_kmalloc_string_descriptor( "HHT Bulk Transfer" );
		  if ( pString ) {
			   sa1100_usb_set_string_descriptor( 4, pString );
			   pDesc->b.intf.iInterface = 4;
		  }
		  sa1100_set_configured_callback( etchstone_configured_notify_proc );
#endif
	 }



}

static void free_string_descriptors( void ) 
{
	 if ( machine_is_extenex1() ) {
		  string_desc_t * pString;
		  int i;
		  for( i = 1 ; i <= 4 ; i++ ) {
			   pString = sa1100_usb_get_string_descriptor( i );
			   if ( pString )
					kfree( pString );
		  }
	 }
}

//////////////////////////////////////////////////////////////////////////////
// ASYNCHRONOUS
//////////////////////////////////////////////////////////////////////////////
static  void kick_start_rx( void )
{
  if ( usb_ref_count ) {
    int total_space  = CIRC_SPACE( rx_ring.in, rx_ring.out, RBUF_SIZE );
    if ( total_space >= RX_PACKET_SIZE ) {
      sa1100_usb_recv( packet_buffer, RX_PACKET_SIZE,
		       rx_done_callback_packet_buffer );
    }
  }
}
/* 
 * rx_done_callback_packet_buffer()
 * We have completed a DMA xfer into the temp packet buffer.
 * Move to ring.
 *
 * flag values:
 * on init,  -EAGAIN
 * on reset, -EINTR
 * on RPE, -EIO
 * on short packet -EPIPE
 */
static void
rx_done_callback_packet_buffer( int flag, int size )
{
	 charstats.cnt_rx_complete++;

	 if ( flag == 0 || flag == -EPIPE ) {
		  size_t n;

		  charstats.bytes_rx += size;

		  n = CIRC_SPACE_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE );
		  n = MIN( n, size );
		  size -= n;

		  memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer, n );
		  rx_ring.in = (rx_ring.in + n) & (RBUF_SIZE-1);  
		  memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer + n, size );
		  rx_ring.in = (rx_ring.in + size) & (RBUF_SIZE-1);  

		  wake_up_interruptible( &wq_read );
		  wake_up_interruptible( &wq_poll );

		  last_rx_result = 0;

		  kick_start_rx();

	 } else if ( flag != -EAGAIN ) {
		  charstats.cnt_rx_errors++;
		  last_rx_result = flag;
		  wake_up_interruptible( &wq_read );
		  wake_up_interruptible( &wq_poll );
	 }
	 else  /* init, start a read */
		  kick_start_rx();
}


static void tx_timeout( unsigned long unused )
{
	printk( "%stx timeout\n", pszMe );
	sa1100_usb_send_reset();
	charstats.cnt_tx_timeouts++;
}


// on init, -EAGAIN
// on reset, -EINTR
// on TPE, -EIO
static void tx_done_callback( int flags, int size )
{

⌨️ 快捷键说明

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