📄 usb-eth.c
字号:
/* * Ethernet driver for the SA1100 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 "sa1100_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 theseint 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_COPYstatic char *dmabuf; // we need that, as dma expect it's buffers to be aligned on 8 bytes boundary#endifstatic 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 voidusb_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; } /* 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 sa1100_usb_recv(dmabuf, usb_rsize, usb_recv_callback);#else sa1100_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 skb\n", __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 (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 %d\n",status); } else {error: /* * 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 sa1100_usb_recv(dmabuf, usb_rsize, usb_recv_callback);#else sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), usb_recv_callback);#endif }}static voidusb_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 = sa1100_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 intusb_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 != NULL\n", __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 = sa1100_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 voidusb_xmit_timeout(struct net_device *dev ){ sa1100_usb_send_reset(); dev->trans_start = jiffies; netif_wake_queue(dev);}static intusb_eth_open(struct net_device *dev){ 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 skb\n", __FUNCTION__); if (cur_rx_skb) kfree_skb(cur_rx_skb); if (next_rx_skb) kfree_skb(next_rx_skb); return -ENOMEM;; } MOD_INC_USE_COUNT;#ifndef DMA_NO_COPY sa1100_usb_recv(dmabuf, usb_rsize, usb_recv_callback);#else sa1100_usb_recv(cur_rx_skb->tail, MIN(usb_rsize, skb_tailroom (cur_rx_skb)), usb_recv_callback);#endif return 0;}static intusb_eth_release(struct net_device *dev){ terminating = 1; sa1100_usb_send_reset(); sa1100_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); 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 intusb_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 MODULEMODULE_PARM(usb_rsize, "1i");MODULE_PARM_DESC(usb_rsize, "number of bytes in packets from host to sa1100");MODULE_PARM(usb_wsize, "1i");MODULE_PARM_DESC(usb_wsize, "number of bytes in packets from sa1100 to host");#endifstatic int __initusb_eth_init(void){ int rc;#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; rc = sa1100_usb_open( "usb-eth" ); if ( rc == 0 ) { string_desc_t * pstr; desc_t * pd = sa1100_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 = sa1100_usb_kmalloc_string_descriptor( "SA1100 USB NIC" ); if ( pstr ) { sa1100_usb_set_string_descriptor( 1, pstr ); pd->dev.iProduct = 1; } rc = sa1100_usb_start(); } return rc;}module_init(usb_eth_init);static void __exitusb_eth_cleanup(void){ string_desc_t * pstr; sa1100_usb_stop(); sa1100_usb_close(); if ( (pstr = sa1100_usb_get_string_descriptor(1)) != NULL ) kfree( pstr );#ifndef DMA_NO_COPY kfree(dmabuf);#endif unregister_netdev(&usb_eth_device);}module_exit(usb_eth_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -