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 + -
显示快捷键?