?? rtl8150.c
字號:
/* * Copyright (c) 2002 Petko Manolov (petkan@users.sourceforge.net) * * 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/config.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/signal.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/mii.h>#include <linux/ethtool.h>#include <linux/usb.h>#include <asm/uaccess.h>/* Version Information */#define DRIVER_VERSION "v0.6.2 (2004/08/27)"#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"#define DRIVER_DESC "rtl8150 based usb-ethernet driver"#define IDR 0x0120#define MAR 0x0126#define CR 0x012e#define TCR 0x012f#define RCR 0x0130#define TSR 0x0132#define RSR 0x0133#define CON0 0x0135#define CON1 0x0136#define MSR 0x0137#define PHYADD 0x0138#define PHYDAT 0x0139#define PHYCNT 0x013b#define GPPC 0x013d#define BMCR 0x0140#define BMSR 0x0142#define ANAR 0x0144#define ANLP 0x0146#define AER 0x0148#define CSCR 0x014C /* This one has the link status */#define CSCR_LINK_STATUS (1 << 3)#define IDR_EEPROM 0x1202#define PHY_READ 0#define PHY_WRITE 0x20#define PHY_GO 0x40#define MII_TIMEOUT 10#define INTBUFSIZE 8#define RTL8150_REQT_READ 0xc0#define RTL8150_REQT_WRITE 0x40#define RTL8150_REQ_GET_REGS 0x05#define RTL8150_REQ_SET_REGS 0x05/* Transmit status register errors */#define TSR_ECOL (1<<5)#define TSR_LCOL (1<<4)#define TSR_LOSS_CRS (1<<3)#define TSR_JBR (1<<2)#define TSR_ERRORS (TSR_ECOL | TSR_LCOL | TSR_LOSS_CRS | TSR_JBR)/* Receive status register errors */#define RSR_CRC (1<<2)#define RSR_FAE (1<<1)#define RSR_ERRORS (RSR_CRC | RSR_FAE)/* Media status register definitions */#define MSR_DUPLEX (1<<4)#define MSR_SPEED (1<<3)#define MSR_LINK (1<<2)/* Interrupt pipe data */#define INT_TSR 0x00#define INT_RSR 0x01#define INT_MSR 0x02#define INT_WAKSR 0x03#define INT_TXOK_CNT 0x04#define INT_RXLOST_CNT 0x05#define INT_CRERR_CNT 0x06#define INT_COL_CNT 0x07/* Transmit status register errors */#define TSR_ECOL (1<<5)#define TSR_LCOL (1<<4)#define TSR_LOSS_CRS (1<<3)#define TSR_JBR (1<<2)#define TSR_ERRORS (TSR_ECOL | TSR_LCOL | TSR_LOSS_CRS | TSR_JBR)/* Receive status register errors */#define RSR_CRC (1<<2)#define RSR_FAE (1<<1)#define RSR_ERRORS (RSR_CRC | RSR_FAE)/* Media status register definitions */#define MSR_DUPLEX (1<<4)#define MSR_SPEED (1<<3)#define MSR_LINK (1<<2)/* Interrupt pipe data */#define INT_TSR 0x00#define INT_RSR 0x01#define INT_MSR 0x02#define INT_WAKSR 0x03#define INT_TXOK_CNT 0x04#define INT_RXLOST_CNT 0x05#define INT_CRERR_CNT 0x06#define INT_COL_CNT 0x07#define RTL8150_MTU 1540#define RTL8150_TX_TIMEOUT (HZ)#define RX_SKB_POOL_SIZE 4/* rtl8150 flags */#define RTL8150_HW_CRC 0#define RX_REG_SET 1#define RTL8150_UNPLUG 2#define RX_URB_FAIL 3/* Define these values to match your device */#define VENDOR_ID_REALTEK 0x0bda#define VENDOR_ID_MELCO 0x0411#define VENDOR_ID_MICRONET 0x3980#define VENDOR_ID_LONGSHINE 0x07b8#define VENDOR_ID_ZYXEL 0x0586#define PRODUCT_ID_RTL8150 0x8150#define PRODUCT_ID_LUAKTX 0x0012#define PRODUCT_ID_LCS8138TX 0x401a#define PRODUCT_ID_SP128AR 0x0003#define PRODUCT_ID_PRESTIGE 0x401a#undef EEPROM_WRITE/* table of devices that work with this driver */static struct usb_device_id rtl8150_table[] = { {USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150)}, {USB_DEVICE(VENDOR_ID_MELCO, PRODUCT_ID_LUAKTX)}, {USB_DEVICE(VENDOR_ID_MICRONET, PRODUCT_ID_SP128AR)}, {USB_DEVICE(VENDOR_ID_LONGSHINE, PRODUCT_ID_LCS8138TX)}, {USB_DEVICE(VENDOR_ID_ZYXEL, PRODUCT_ID_PRESTIGE)}, {}};MODULE_DEVICE_TABLE(usb, rtl8150_table);struct rtl8150 { unsigned long flags; struct usb_device *udev; struct tasklet_struct tl; struct net_device_stats stats; struct net_device *netdev; struct urb *rx_urb, *tx_urb, *intr_urb, *ctrl_urb; struct sk_buff *tx_skb, *rx_skb; struct sk_buff *rx_skb_pool[RX_SKB_POOL_SIZE]; spinlock_t rx_pool_lock; struct usb_ctrlrequest dr; int intr_interval; __le16 rx_creg; u8 *intr_buff; u8 phy;};typedef struct rtl8150 rtl8150_t;static void fill_skb_pool(rtl8150_t *);static void free_skb_pool(rtl8150_t *);static inline struct sk_buff *pull_skb(rtl8150_t *);static void rtl8150_disconnect(struct usb_interface *intf);static int rtl8150_probe(struct usb_interface *intf, const struct usb_device_id *id);static int rtl8150_suspend(struct usb_interface *intf, pm_message_t message);static int rtl8150_resume(struct usb_interface *intf);static const char driver_name [] = "rtl8150";static struct usb_driver rtl8150_driver = { .name = driver_name, .probe = rtl8150_probe, .disconnect = rtl8150_disconnect, .id_table = rtl8150_table, .suspend = rtl8150_suspend, .resume = rtl8150_resume};/***** device related part of the code***/static int get_registers(rtl8150_t * dev, u16 indx, u16 size, void *data){ return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), RTL8150_REQ_GET_REGS, RTL8150_REQT_READ, indx, 0, data, size, 500);}static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data){ return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE, indx, 0, data, size, 500);}static void ctrl_callback(struct urb *urb, struct pt_regs *regs){ rtl8150_t *dev; switch (urb->status) { case 0: break; case -EINPROGRESS: break; case -ENOENT: break; default: warn("ctrl urb status %d", urb->status); } dev = urb->context; clear_bit(RX_REG_SET, &dev->flags);}static int async_set_registers(rtl8150_t * dev, u16 indx, u16 size){ int ret; if (test_bit(RX_REG_SET, &dev->flags)) return -EAGAIN; dev->dr.bRequestType = RTL8150_REQT_WRITE; dev->dr.bRequest = RTL8150_REQ_SET_REGS; dev->dr.wValue = cpu_to_le16(indx); dev->dr.wIndex = 0; dev->dr.wLength = cpu_to_le16(size); dev->ctrl_urb->transfer_buffer_length = size; usb_fill_control_urb(dev->ctrl_urb, dev->udev, usb_sndctrlpipe(dev->udev, 0), (char *) &dev->dr, &dev->rx_creg, size, ctrl_callback, dev); if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC))) { if (ret == -ENODEV) netif_device_detach(dev->netdev); err("control request submission failed: %d", ret); } else set_bit(RX_REG_SET, &dev->flags); return ret;}static int read_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 * reg){ int i; u8 data[3], tmp; data[0] = phy; data[1] = data[2] = 0; tmp = indx | PHY_READ | PHY_GO; i = 0; set_registers(dev, PHYADD, sizeof(data), data); set_registers(dev, PHYCNT, 1, &tmp); do { get_registers(dev, PHYCNT, 1, data); } while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT)); if (i < MII_TIMEOUT) { get_registers(dev, PHYDAT, 2, data); *reg = data[0] | (data[1] << 8); return 0; } else return 1;}static int write_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 reg){ int i; u8 data[3], tmp; data[0] = phy; *(data + 1) = cpu_to_le16p(®); tmp = indx | PHY_WRITE | PHY_GO; i = 0; set_registers(dev, PHYADD, sizeof(data), data); set_registers(dev, PHYCNT, 1, &tmp); do { get_registers(dev, PHYCNT, 1, data); } while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT)); if (i < MII_TIMEOUT) return 0; else return 1;}static inline void set_ethernet_addr(rtl8150_t * dev){ u8 node_id[6]; get_registers(dev, IDR, sizeof(node_id), node_id); memcpy(dev->netdev->dev_addr, node_id, sizeof(node_id));}static int rtl8150_set_mac_address(struct net_device *netdev, void *p){ struct sockaddr *addr = p; rtl8150_t *dev = netdev_priv(netdev); int i; if (netif_running(netdev)) return -EBUSY; memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); dbg("%s: Setting MAC address to ", netdev->name); for (i = 0; i < 5; i++) dbg("%02X:", netdev->dev_addr[i]); dbg("%02X\n", netdev->dev_addr[i]); /* Set the IDR registers. */ set_registers(dev, IDR, sizeof(netdev->dev_addr), netdev->dev_addr);#ifdef EEPROM_WRITE { u8 cr; /* Get the CR contents. */ get_registers(dev, CR, 1, &cr); /* Set the WEPROM bit (eeprom write enable). */ cr |= 0x20; set_registers(dev, CR, 1, &cr); /* Write the MAC address into eeprom. Eeprom writes must be word-sized, so we need to split them up. */ for (i = 0; i * 2 < netdev->addr_len; i++) { set_registers(dev, IDR_EEPROM + (i * 2), 2, netdev->dev_addr + (i * 2)); } /* Clear the WEPROM bit (preventing accidental eeprom writes). */ cr &= 0xdf; set_registers(dev, CR, 1, &cr); }#endif return 0;}static int rtl8150_reset(rtl8150_t * dev){ u8 data = 0x10; int i = HZ; set_registers(dev, CR, 1, &data); do { get_registers(dev, CR, 1, &data); } while ((data & 0x10) && --i); return (i > 0) ? 1 : 0;}static int alloc_all_urbs(rtl8150_t * dev){ dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->rx_urb) return 0; dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->tx_urb) { usb_free_urb(dev->rx_urb); return 0; } dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->intr_urb) { usb_free_urb(dev->rx_urb); usb_free_urb(dev->tx_urb); return 0; } dev->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->intr_urb) { usb_free_urb(dev->rx_urb); usb_free_urb(dev->tx_urb); usb_free_urb(dev->intr_urb); return 0; } return 1;}static void free_all_urbs(rtl8150_t * dev){ usb_free_urb(dev->rx_urb); usb_free_urb(dev->tx_urb); usb_free_urb(dev->intr_urb); usb_free_urb(dev->ctrl_urb);}static void unlink_all_urbs(rtl8150_t * dev){ usb_kill_urb(dev->rx_urb); usb_kill_urb(dev->tx_urb); usb_kill_urb(dev->intr_urb); usb_kill_urb(dev->ctrl_urb);}static inline struct sk_buff *pull_skb(rtl8150_t *dev){ struct sk_buff *skb; int i; for (i = 0; i < RX_SKB_POOL_SIZE; i++) { if (dev->rx_skb_pool[i]) { skb = dev->rx_skb_pool[i]; dev->rx_skb_pool[i] = NULL; return skb; } } return NULL;}static void read_bulk_callback(struct urb *urb, struct pt_regs *regs){ rtl8150_t *dev; unsigned pkt_len, res; struct sk_buff *skb; struct net_device *netdev; u16 rx_stat; int status; dev = urb->context; if (!dev) return; if (test_bit(RTL8150_UNPLUG, &dev->flags)) return; netdev = dev->netdev; if (!netif_device_present(netdev)) return; switch (urb->status) { case 0: break; case -ENOENT: return; /* the urb is in unlink state */ case -ETIMEDOUT: warn("may be reset is needed?.."); goto goon; default: warn("Rx status %d", urb->status); goto goon; } if (!dev->rx_skb) goto resched; /* protect against short packets (tell me why we got some?!?) */ if (urb->actual_length < 4) goto goon; res = urb->actual_length; rx_stat = le16_to_cpu(*(__le16 *)(urb->transfer_buffer + res - 4)); pkt_len = res - 4; skb_put(dev->rx_skb, pkt_len); dev->rx_skb->protocol = eth_type_trans(dev->rx_skb, netdev); netif_rx(dev->rx_skb); dev->stats.rx_packets++; dev->stats.rx_bytes += pkt_len; spin_lock(&dev->rx_pool_lock); skb = pull_skb(dev); spin_unlock(&dev->rx_pool_lock); if (!skb) goto resched; dev->rx_skb = skb;goon: usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1), dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev); status = usb_submit_urb(dev->rx_urb, GFP_ATOMIC); if (status == -ENODEV) netif_device_detach(dev->netdev); else if (status) { set_bit(RX_URB_FAIL, &dev->flags); goto resched; } else { clear_bit(RX_URB_FAIL, &dev->flags); } return;resched: tasklet_schedule(&dev->tl);}static void rx_fixup(unsigned long data){ rtl8150_t *dev; struct sk_buff *skb; int status; dev = (rtl8150_t *)data; spin_lock_irq(&dev->rx_pool_lock); fill_skb_pool(dev); spin_unlock_irq(&dev->rx_pool_lock); if (test_bit(RX_URB_FAIL, &dev->flags)) if (dev->rx_skb)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -