📄 usb-cdc-eth-raw.c
字号:
/* * CDC Ethernet Raw Access Mode driver for the PXA USB client function * Based on linux/arch/arm/mach-pxa/usb-cdc-eth.c and usb-char.c * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */#include <linux/module.h>#include <linux/config.h>#include <linux/miscdevice.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/circ_buf.h>#include <linux/timer.h>#ifdef CONFIG_PM#include <linux/pm.h>#endif#include <asm/hardware.h>#include "s3c2440_usb.h"/* for using major, minor and ioctl command numbers of usb-char */#include "usb-char.h"//////////////////////////////////////////////////////////////////////////////// Globals - Macros - Enums - Structures///////////////////////////////////////////////////////////////////////////////* Define this for debug messages */#undef DEBUG#ifdef DEBUG#define DPRINTK(X...) { printk(__FUNCTION__); printk ("(): "); printk(X); }#else#define DPRINTK(X...) do { } while (0);#endif/* Define this for random remote MAC address */#undef USE_RANDOM_REMOTE_MAC#ifdef CONFIG_S3C2440_SMDK#define ETHERNET_VENDOR_ID 0x4e8#define ETHERNET_PRODUCT_ID 0x661d#define ETHERNET_REV_NUM 0x100#define ETHERNET_MANUFACTURE "SAMSUNG Electronics Co.,Ltd."#define ETHERNET_FOR_WHAT "i519"#else#define ETHERNET_VENDOR_ID 0x49f#define ETHERNET_PRODUCT_ID 0x505A#endif#ifndef MIN#define MIN( a, b ) ((a)<(b)?(a):(b))#endif/* size of usb data tx/rx packets. */#define TX_PACKET_SIZE 64#define RX_PACKET_SIZE 64/* RX buffer size */#define RBUF_SIZE (8*PAGE_SIZE)/* I/O wait queues */static DECLARE_WAIT_QUEUE_HEAD(wq_read);static DECLARE_WAIT_QUEUE_HEAD(wq_write);static DECLARE_WAIT_QUEUE_HEAD(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 );static struct wcirc_buf { char *buf; int in; int out;} rx_ring = { NULL, 0, 0 };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;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;//////////////////////////////////////////////////////////////////////////////// Private Helpers//////////////////////////////////////////////////////////////////////////////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; }}static int alloc_txrx_buffers( void ){ tx_buf = (char*) kmalloc( TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA ); if ( tx_buf == NULL ) { DPRINTK( "ARGHH! COULD NOT ALLOCATE TX BUFFER\n" ); goto malloc_fail; } rx_ring.buf = (char*) kmalloc( RBUF_SIZE, GFP_KERNEL ); if ( rx_ring.buf == NULL ) { DPRINTK( "ARGHH! COULD NOT ALLOCATE RX BUFFER\n" ); goto malloc_fail; } packet_buffer = (char*) kmalloc( RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA ); if ( packet_buffer == NULL ) { DPRINTK( "ARGHH! COULD NOT ALLOCATE RX PACKET BUFFER\n" ); 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; return 0; malloc_fail: free_txrx_buffers(); return -ENOMEM;}/* twiddle_descriptors() * It is between open() and start(). Setup descriptors. */static void twiddle_descriptors( void ){ desc_t * pd = elfin_usb_get_descriptor_ptr(); string_desc_t * pstr; u8 remote_mac_address[13];#ifdef USE_RANDOM_REMOTE_MAC u8 node_id [ETH_ALEN];#endif pd->dev.bDeviceClass = CDC_DEVICE_CLASS; pd->dev.bDeviceSubClass = 0; pd->dev.bDeviceProtocol = 0; pd->b.ep1.wMaxPacketSize = make_word( RX_PACKET_SIZE ); pd->b.ep2.wMaxPacketSize = make_word( TX_PACKET_SIZE ); pd->dev.idVendor = ETHERNET_VENDOR_ID; pd->dev.idProduct = ETHERNET_PRODUCT_ID;#ifdef CONFIG_S3C2440_SMDK pd->dev.bcdDevice = ETHERNET_REV_NUM; if (elfin_usb_get_string_descriptor(3)) return; pstr = elfin_usb_kmalloc_string_descriptor( ETHERNET_MANUFACTURE ); if ( pstr ) { elfin_usb_set_string_descriptor( 1, pstr ); pd->dev.iManufacturer = 1; } pstr = elfin_usb_kmalloc_string_descriptor( ETHERNET_FOR_WHAT ); if ( pstr ) { elfin_usb_set_string_descriptor( 3, pstr ); pd->dev.iProduct = 3; }#else if (elfin_usb_get_string_descriptor(2)) return; pstr = elfin_usb_kmalloc_string_descriptor( "PXA USB CDC Ethernet Raw Access Mode" ); if ( pstr ) { elfin_usb_set_string_descriptor( 1, pstr ); pd->dev.iProduct = 1; }#endif /* set remote mac address */#ifdef USE_RANDOM_REMOTE_MAC get_random_bytes(node_id, ETH_ALEN); node_id[0] &= 0xfe; // clear multicast bit node_id[1] &= 0xfe; // second byte must be even sprintf(remote_mac_address, "%02X%02X%02X%02X%02X%02X", node_id[0], node_id[1], node_id[2], node_id[3], node_id[4], node_id[5]);#else strcpy(remote_mac_address, "400002000001");#endif pstr = elfin_usb_kmalloc_string_descriptor(remote_mac_address); if ( pstr ) { elfin_usb_set_string_descriptor( 2, pstr ); pd->cdc_b.func.eth.iMACAddress = 2; }}static void free_string_descriptors( void ){ int i; string_desc_t * pstr; for( i = 1 ; i <= 2 ; i++ ) { pstr = elfin_usb_get_string_descriptor( i ); if ( pstr ) kfree( pstr ); elfin_usb_set_string_descriptor( i , NULL); }}//////////////////////////////////////////////////////////////////////////////// ASYNCHRONOUS//////////////////////////////////////////////////////////////////////////////static void rx_done_callback_packet_buffer( int flag, int size );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 ) { elfin_usb_recv( packet_buffer, RX_PACKET_SIZE, rx_done_callback_packet_buffer ); } else { DPRINTK("buffer overflow: total space = %d\n", total_space); } }}/* * 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++; DPRINTK("flag = %d, size = %d\n", flag, size); 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 ){ DPRINTK( "tx timeout\n" ); elfin_usb_send_reset(); charstats.cnt_tx_timeouts++;}static void tx_done_callback( int flag, int size ){ DPRINTK("flag = %d, size = %d\n", flag, size); if ( flag == 0 ) charstats.bytes_tx += size; else charstats.cnt_tx_errors++; last_tx_size = size; last_tx_result = flag; sending = 0; wake_up_interruptible( &wq_write ); wake_up_interruptible( &wq_poll );}//////////////////////////////////////////////////////////////////////////////// Workers//////////////////////////////////////////////////////////////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -