📄 usb-char.c
字号:
/* * (C) Copyright 2000-2001 MIZI Research, Inc. * * 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 S3C2410 USB function * driver. */#include <linux/module.h>#include <linux/config.h>#include <linux/miscdevice.h>#include <linux/slab.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 "s3c2410_usb.h"#ifdef CONFIG_MIZI#include <asm/irq.h>#ifdef CONFIG_PM#include <linux/pm.h>static struct pm_dev *usb_char_pm_dev;#endif#endif//////////////////////////////////////////////////////////////////////////////// Driver Options//////////////////////////////////////////////////////////////////////////////#ifdef CONFIG_MIZI#define VERSION "0.5"#define LINUETTE_VENDOR_ID 0x49f#define LINUETTE_PRODUCT_ID 0x505A#define LINUETTE_REV_NUM 1 #define USB_CHAR_MANUFACTURE "MIZI Research Inc."#define USB_CHAR_FOR_WHAT "linu@usb"#define USB_CHAR_BUILD_DATE "20020115"#define USB_CHAR_INTERFACE "Bulk Transfer"#else#define VERSION "0.4"#endif//#define VERBOSITY 1#if VERBOSITY# define PRINTK(a...) printk(__FILE__":"__FUNCTION__"(): " ##a)#else# define PRINTK(a...) (void)(0)#endif//////////////////////////////////////////////////////////////////////////////// Globals - Macros - Enums - Structures//////////////////////////////////////////////////////////////////////////////#ifndef MIN#define MIN( a, b ) ((a)<(b)?(a):(b))#endiftypedef 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//////////////////////////////////////////////////////////////////////////////#ifndef CONFIG_MIZIstatic char * what_the_f( int e );#endifstatic 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_S3C2410_EXTENEX1static void extenex_configured_notify_proc( void );#endif#ifdef CONFIG_MIZIstatic int usbc_activate(void);static void usbc_deactivate(void);#endif//////////////////////////////////////////////////////////////////////////////// Private Helpers//////////////////////////////////////////////////////////////////////////////#if defined(CONFIG_PM) && defined(CONFIG_MIZI)extern void usbctl_suspend(void);extern void usbctl_resume(void);static intusbchar_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data){ PRINTK("pm_callback: %d\n", req); switch (req) { case PM_RESUME: usbctl_resume(); usbc_activate(); PRINTK("usb-char wakeup..."); break; case PM_SUSPEND: usbc_deactivate(); usbctl_suspend(); PRINTK("usb-char sleep..."); break; } PRINTK("done\n"); return 0;}#endif#ifndef CONFIG_MIZIstatic 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;}#endifstatic void free_txrx_buffers( void ){ PRINTK("\n"); 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 = s3c2410_usb_get_descriptor_ptr(); string_desc_t * pString; PRINTK("\n"); 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;#ifdef CONFIG_MIZI pDesc->dev.idVendor = LINUETTE_VENDOR_ID; pDesc->dev.idProduct = LINUETTE_PRODUCT_ID; pDesc->dev.bcdDevice = LINUETTE_REV_NUM; pDesc->b.cfg.bmAttributes = USB_CONFIG_SELFPOWERED; pDesc->b.cfg.MaxPower = 0; if (s3c2410_usb_get_string_descriptor(4)) return; pString = s3c2410_usb_kmalloc_string_descriptor( USB_CHAR_MANUFACTURE); if ( pString ) { s3c2410_usb_set_string_descriptor( 1, pString ); pDesc->dev.iManufacturer = 1; } pString = s3c2410_usb_kmalloc_string_descriptor( USB_CHAR_FOR_WHAT ); if ( pString ) { s3c2410_usb_set_string_descriptor( 2, pString ); pDesc->dev.iProduct = 2; } pString = s3c2410_usb_kmalloc_string_descriptor( USB_CHAR_BUILD_DATE ); if ( pString ) { s3c2410_usb_set_string_descriptor( 3, pString ); pDesc->dev.iSerialNumber = 3; } pString = s3c2410_usb_kmalloc_string_descriptor( USB_CHAR_INTERFACE ); if ( pString ) { s3c2410_usb_set_string_descriptor( 4, pString ); pDesc->b.intf.iInterface = 4; }#endif}static void free_string_descriptors( void ){ PRINTK("\n"); if ( machine_is_extenex1() ) { string_desc_t * pString; int i; for( i = 1 ; i <= 4 ; i++ ) { pString = s3c2410_usb_get_string_descriptor( i ); if ( pString ) kfree( pString ); } }#ifdef CONFIG_MIZI { string_desc_t * pString; int i; for( i = 1 ; i <= 4 ; i++ ) { pString = s3c2410_usb_get_string_descriptor( i ); if ( pString ) kfree( pString ); s3c2410_usb_set_string_descriptor( i , NULL); } }#endif}//////////////////////////////////////////////////////////////////////////////// ASYNCHRONOUS//////////////////////////////////////////////////////////////////////////////static void kick_start_rx( void ){ PRINTK("\n");#if defined(CONFIG_MIZI) {#else // PRINTK("i'm not MIZI - %d\n", usb_ref_count); if ( usb_ref_count ) {#endif int total_space = CIRC_SPACE( rx_ring.in, rx_ring.out, RBUF_SIZE );// PRINTK("total_space = %d, %d\n", total_space, RX_PACKET_SIZE); if ( total_space >= RX_PACKET_SIZE ) { s3c2410_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 voidrx_done_callback_packet_buffer( int flag, int size ){ charstats.cnt_rx_complete++; PRINTK("\n"); 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 ); s3c2410_usb_send_reset(); charstats.cnt_tx_timeouts++;}// on init, -EAGAIN// on reset, -EINTR// on TPE, -EIOstatic void tx_done_callback( int flags, int size ){ PRINTK("\n"); 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 );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -