usb-eth.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:11k
- /*
- * Ethernet driver for the PXA USB client function
- * Copyright (c) 2001 by Nicolas Pitre
- *
- * This code was loosely inspired by the original initial ethernet test driver
- * Copyright (c) Compaq Computer Corporation, 1999
- *
- * 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.
- *
- * This is still work in progress...
- *
- * 19/02/2001 - Now we are compatible with generic usbnet driver. green@iXcelerator.com
- * 09/03/2001 - Dropped 'framing' scheme, as it seems to cause a lot of problems with little benefit.
- * Now, since we do not know what size of packet we are receiving
- * last usb packet in sequence will always be less than max packet
- * receive endpoint can accept.
- * Now the only way to check correct start of frame is to compare
- * MAC address. Also now we are stalling on each receive error.
- *
- * 15/03/2001 - Using buffer to get data from UDC. DMA needs to have 8 byte
- * aligned buffer, but this breaks IP code (unaligned access).
- *
- * 01/04/2001 - stall endpoint operations appeared to be very unstable, so
- * they are disabled now.
- *
- * 03/06/2001 - Readded "zerocopy" receive path (tunable).
- *
- */
- // Define DMA_NO_COPY if you want data to arrive directly into the
- // receive network buffers, instead of arriving into bounce buffer
- // and then get copied to network buffer.
- // This does not work correctly right now.
- #undef DMA_NO_COPY
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/sched.h>
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/timer.h>
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/skbuff.h>
- #include <linux/random.h>
- #include "pxa_usb.h"
- #define ETHERNET_VENDOR_ID 0x49f
- #define ETHERNET_PRODUCT_ID 0x505A
- #define MAX_PACKET 32768
- #define MIN(a, b) (((a) < (b)) ? (a) : (b))
- // Should be global, so that insmod can change these
- int usb_rsize=64;
- int usb_wsize=64;
- static struct usbe_info_t {
- struct net_device *dev;
- u16 packet_id;
- struct net_device_stats stats;
- } usbe_info;
- static char usb_eth_name[16] = "usbf";
- static struct net_device usb_eth_device;
- static struct sk_buff *cur_tx_skb, *next_tx_skb;
- static struct sk_buff *cur_rx_skb, *next_rx_skb;
- static volatile int terminating;
- #ifndef DMA_NO_COPY
- static char *dmabuf; // we need that, as dma expect it's buffers to be aligned on 8 bytes boundary
- #endif
- static int usb_change_mtu (struct net_device *net, int new_mtu)
- {
- if (new_mtu <= sizeof (struct ethhdr) || new_mtu > MAX_PACKET)
- return -EINVAL;
- // no second zero-length packet read wanted after mtu-sized packets
- if (((new_mtu + sizeof (struct ethhdr)) % usb_rsize) == 0)
- return -EDOM;
- net->mtu = new_mtu;
- return 0;
- }
- static struct sk_buff *
- usb_new_recv_skb(void)
- {
- struct sk_buff *skb = alloc_skb( 2 + sizeof (struct ethhdr) + usb_eth_device.mtu,GFP_ATOMIC);
- if (skb) {
- skb_reserve(skb, 2);
- }
- return skb;
- }
- static u8 bcast_hwaddr[ETH_ALEN]={0xff,0xff,0xff,0xff,0xff,0xff};
- static void
- usb_recv_callback(int flag, int size)
- {
- struct sk_buff *skb;
- if (terminating)
- return;
- skb = cur_rx_skb;
- /* flag validation */
- if (flag == 0) {
- if ( skb_tailroom (skb) < size ) { // hey! we are overloaded!!!
- usbe_info.stats.rx_over_errors++;
- goto error;
- }
- #ifndef DMA_NO_COPY
- memcpy(skb->tail,dmabuf,size);
- #endif
- skb_put(skb, size);
- } else {
- if (flag == -EIO) {
- usbe_info.stats.rx_errors++;
- }
- goto error;
- }
-
- /*
- * If the real size of the packet is divisible by usb_rsize
- * an extra byte will be added. Thus size == usb_rsize
- * should only happen if more data is to come.
- */
- /* validate packet length */
- if (size == usb_rsize ) {
- /* packet not complete yet */
- skb = NULL;
- }
- /*
- * At this point skb is non null if we have a complete packet.
- * If so take a fresh skb right away and restart USB receive without
- * further delays, then process the packet. Otherwise resume USB
- * receive on the current skb and exit.
- */
- if (skb)
- cur_rx_skb = next_rx_skb;
- #ifndef DMA_NO_COPY
- pxa_usb_recv(dmabuf, usb_rsize,
- usb_recv_callback);
- #else
- pxa_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)),
- usb_recv_callback);
- #endif
- if (!skb)
- return;
- next_rx_skb = usb_new_recv_skb();
- if (!next_rx_skb) {
- /*
- * We can't aford loosing buffer space...
- * So we drop the current packet and recycle its skb.
- */
- printk("%s: can't allocate new skbn", __FUNCTION__);
- usbe_info.stats.rx_dropped++;
- skb_trim(skb, 0);
- next_rx_skb = skb;
- return;
- }
- if ( skb->len >= sizeof(struct ethhdr)) {
- if (memcmp(skb->data,usb_eth_device.dev_addr,ETH_ALEN) && memcmp(skb->data,bcast_hwaddr,ETH_ALEN) ) {
- // This frame is not for us. nor it is broadcast
- usbe_info.stats.rx_frame_errors++;
- kfree_skb(skb);
- goto error;
- }
- #if 0
- {
- int i;
- for (i = 0; i < skb->len; i++)
- {
- printk("%02X ", skb->data[i]);
- if( (i%8)==7) printk("n");
- }
- printk("...n");
- }
- #endif
- }
- if (skb->len) {
- int status;
- // FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ?
- skb->dev = &usb_eth_device;
- skb->protocol = eth_type_trans (skb, &usb_eth_device);
- usbe_info.stats.rx_packets++;
- usbe_info.stats.rx_bytes += skb->len;
- skb->ip_summed = CHECKSUM_NONE;
- status = netif_rx (skb);
- if (status != NET_RX_SUCCESS)
- printk("netif_rx failed with code %dn",status);
- } else {
- error:
- //printk("ERROR... tailroom=%d size=%d len=%d flag=%dn", skb_tailroom(skb), size, skb->len, flag);
- /*
- * Error due to HW addr mismatch, or IO error.
- * Recycle the current skb and reset USB reception.
- */
- skb_trim(cur_rx_skb, 0);
- // if ( flag == -EINTR || flag == -EAGAIN ) // only if we are coming out of stall
- #ifndef DMA_NO_COPY
- pxa_usb_recv(dmabuf, usb_rsize, usb_recv_callback);
- #else
- pxa_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), usb_recv_callback);
- #endif
- }
- }
- static void
- usb_send_callback(int flag, int size)
- {
- struct net_device *dev = usbe_info.dev;
- struct net_device_stats *stats;
- struct sk_buff *skb=cur_tx_skb;
- int ret;
- if (terminating)
- return;
- stats = &usbe_info.stats;
- switch (flag) {
- case 0:
- stats->tx_packets++;
- stats->tx_bytes += size;
- break;
- case -EIO:
- stats->tx_errors++;
- break;
- default:
- stats->tx_dropped++;
- break;
- }
- cur_tx_skb = next_tx_skb;
- next_tx_skb = NULL;
- dev_kfree_skb_irq(skb);
- if (!cur_tx_skb)
- return;
- dev->trans_start = jiffies;
- ret = pxa_usb_send(cur_tx_skb->data, cur_tx_skb->len, usb_send_callback);
- if (ret) {
- /* If the USB core can't accept the packet, we drop it. */
- dev_kfree_skb_irq(cur_tx_skb);
- cur_tx_skb = NULL;
- usbe_info.stats.tx_carrier_errors++;
- }
- netif_wake_queue(dev);
- }
- static int
- usb_eth_xmit(struct sk_buff *skb, struct net_device *dev)
- {
- int ret;
- long flags;
- if (next_tx_skb) {
- printk("%s: called with next_tx_skb != NULLn", __FUNCTION__);
- return 1;
- }
- if (skb_shared (skb)) {
- struct sk_buff *skb2 = skb_unshare(skb, GFP_ATOMIC);
- if (!skb2) {
- usbe_info.stats.tx_dropped++;
- dev_kfree_skb(skb);
- return 1;
- }
- skb = skb2;
- }
- if ((skb->len % usb_wsize) == 0) {
- skb->len++; // other side will ignore this one, anyway.
- }
- save_flags_cli(flags);
- if (cur_tx_skb) {
- next_tx_skb = skb;
- netif_stop_queue(dev);
- } else {
- cur_tx_skb = skb;
- dev->trans_start = jiffies;
- ret = pxa_usb_send(skb->data, skb->len, usb_send_callback);
- if (ret) {
- /* If the USB core can't accept the packet, we drop it. */
- dev_kfree_skb(skb);
- cur_tx_skb = NULL;
- usbe_info.stats.tx_carrier_errors++;
- }
- }
- restore_flags(flags);
- return 0;
- }
- static void
- usb_xmit_timeout(struct net_device *dev )
- {
- pxa_usb_send_reset();
- dev->trans_start = jiffies;
- netif_wake_queue(dev);
- }
- static int
- usb_eth_open(struct net_device *dev)
- {
- int rc;
- rc = pxa_usb_open( "usb-eth" );
- if ( rc == 0 ) {
- string_desc_t * pstr;
- desc_t * pd = pxa_usb_get_descriptor_ptr();
- pd->b.ep1.wMaxPacketSize = make_word( usb_rsize );
- pd->b.ep2.wMaxPacketSize = make_word( usb_wsize );
- pd->dev.idVendor = ETHERNET_VENDOR_ID;
- pd->dev.idProduct = ETHERNET_PRODUCT_ID;
- pstr = pxa_usb_kmalloc_string_descriptor( "PXA USB NIC" );
- if ( pstr ) {
- pxa_usb_set_string_descriptor( 1, pstr );
- pd->dev.iProduct = 1;
- }
- rc = pxa_usb_start();
- }
- if( rc == 0)
- {
- terminating = 0;
- cur_tx_skb = next_tx_skb = NULL;
- cur_rx_skb = usb_new_recv_skb();
- next_rx_skb = usb_new_recv_skb();
- if (!cur_rx_skb || !next_rx_skb) {
- printk("%s: can't allocate new skbn", __FUNCTION__);
- if (cur_rx_skb)
- kfree_skb(cur_rx_skb);
- if (next_rx_skb)
- kfree_skb(next_rx_skb);
- pxa_usb_stop();
- pxa_usb_close();
- return -ENOMEM;;
- }
- MOD_INC_USE_COUNT;
- #ifndef DMA_NO_COPY
- pxa_usb_recv(dmabuf, usb_rsize, usb_recv_callback);
- #else
- pxa_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)),
- usb_recv_callback);
- #endif
- }
- return rc;
- }
- static int
- usb_eth_release(struct net_device *dev)
- {
- string_desc_t * pstr;
- terminating = 1;
- pxa_usb_send_reset();
- pxa_usb_recv_reset();
- if (cur_tx_skb)
- kfree_skb(cur_tx_skb);
- if (next_tx_skb)
- kfree_skb(next_tx_skb);
- if (cur_rx_skb)
- kfree_skb(cur_rx_skb);
- if (next_rx_skb)
- kfree_skb(next_rx_skb);
- pxa_usb_stop();
- pxa_usb_close();
- if ( (pstr = pxa_usb_get_string_descriptor(1)) != NULL )
- kfree( pstr );
- MOD_DEC_USE_COUNT;
- return 0;
- }
- static struct net_device_stats *
- usb_eth_stats(struct net_device *dev)
- {
- struct usbe_info_t *priv = (struct usbe_info_t*) dev->priv;
- struct net_device_stats *stats=NULL;
- if (priv)
- stats = &priv->stats;
- return stats;
- }
- static int
- usb_eth_probe(struct net_device *dev)
- {
- u8 node_id [ETH_ALEN];
- get_random_bytes (node_id, sizeof node_id);
- node_id [0] &= 0xfe; // clear multicast bit
- /*
- * Assign the hardware address of the board:
- * generate it randomly, as there can be many such
- * devices on the bus.
- */
- memcpy (dev->dev_addr, node_id, sizeof node_id);
- dev->open = usb_eth_open;
- dev->change_mtu = usb_change_mtu;
- dev->stop = usb_eth_release;
- dev->hard_start_xmit = usb_eth_xmit;
- dev->get_stats = usb_eth_stats;
- dev->watchdog_timeo = 1*HZ;
- dev->tx_timeout = usb_xmit_timeout;
- dev->priv = &usbe_info;
- usbe_info.dev = dev;
- /* clear the statistics */
- memset(&usbe_info.stats, 0, sizeof(struct net_device_stats));
- ether_setup(dev);
- dev->flags &= ~IFF_MULTICAST;
- dev->flags &= ~IFF_BROADCAST;
- //dev->flags |= IFF_NOARP;
- return 0;
- }
- #ifdef MODULE
- MODULE_PARM(usb_rsize, "1i");
- MODULE_PARM_DESC(usb_rsize, "number of bytes in packets from host to pxa");
- MODULE_PARM(usb_wsize, "1i");
- MODULE_PARM_DESC(usb_wsize, "number of bytes in packets from pxa to host");
- #endif
- static int __init
- usb_eth_init(void)
- {
- #ifndef DMA_NO_COPY
- dmabuf = kmalloc( usb_rsize, GFP_KERNEL | GFP_DMA );
- if (!dmabuf)
- return -ENOMEM;
- #endif
- strncpy(usb_eth_device.name, usb_eth_name, IFNAMSIZ);
- usb_eth_device.init = usb_eth_probe;
- if (register_netdev(&usb_eth_device) != 0)
- return -EIO;
- printk( KERN_INFO "USB Function Ethernet Driver Interfacen");
- return 0;
- }
- static void __exit
- usb_eth_cleanup(void)
- {
- #ifndef DMA_NO_COPY
- kfree(dmabuf);
- #endif
- unregister_netdev(&usb_eth_device);
- }
- module_init(usb_eth_init);
- module_exit(usb_eth_cleanup);