rtl8150.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 997 行 · 第 1/2 页

C
997
字号
	if (skb == NULL)		goto tlsched;	dev->rx_skb = skb;	usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);try_again:	if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) {		set_bit(RX_URB_FAIL, &dev->flags);		goto tlsched;	 } else {		clear_bit(RX_URB_FAIL, &dev->flags);	}	return;tlsched:	tasklet_schedule(&dev->tl);}static void write_bulk_callback(struct urb *urb, struct pt_regs *regs){	rtl8150_t *dev;	dev = urb->context;	if (!dev)		return;	dev_kfree_skb_irq(dev->tx_skb);	if (!netif_device_present(dev->netdev))		return;	if (urb->status)		info("%s: Tx status %d", dev->netdev->name, urb->status);	dev->netdev->trans_start = jiffies;	netif_wake_queue(dev->netdev);}static void intr_callback(struct urb *urb, struct pt_regs *regs){	rtl8150_t *dev;	__u8 *d;	int status;	dev = urb->context;	if (!dev)		return;	switch (urb->status) {	case 0:			/* success */		break;	case -ECONNRESET:	/* unlink */	case -ENOENT:	case -ESHUTDOWN:		return;	/* -EPIPE:  should clear the halt */	default:		info("%s: intr status %d", dev->netdev->name, urb->status);		goto resubmit;	}	d = urb->transfer_buffer;	if (d[0] & TSR_ERRORS) {		dev->stats.tx_errors++;		if (d[INT_TSR] & (TSR_ECOL | TSR_JBR))			dev->stats.tx_aborted_errors++;		if (d[INT_TSR] & TSR_LCOL)			dev->stats.tx_window_errors++;		if (d[INT_TSR] & TSR_LOSS_CRS)			dev->stats.tx_carrier_errors++;	}	/* Report link status changes to the network stack */	if ((d[INT_MSR] & MSR_LINK) == 0) {		if (netif_carrier_ok(dev->netdev)) {			netif_carrier_off(dev->netdev);			dbg("%s: LINK LOST\n", __func__);		}	} else {		if (!netif_carrier_ok(dev->netdev)) {			netif_carrier_on(dev->netdev);			dbg("%s: LINK CAME BACK\n", __func__);		}	}resubmit:	status = usb_submit_urb (urb, SLAB_ATOMIC);	if (status)		err ("can't resubmit intr, %s-%s/input0, status %d",				dev->udev->bus->bus_name,				dev->udev->devpath, status);}/*****	network related part of the code***/static void fill_skb_pool(rtl8150_t *dev){	struct sk_buff *skb;	int i;	for (i = 0; i < RX_SKB_POOL_SIZE; i++) {		if (dev->rx_skb_pool[i])			continue;		skb = dev_alloc_skb(RTL8150_MTU + 2);		if (!skb) {			return;		}		skb->dev = dev->netdev;		skb_reserve(skb, 2);		dev->rx_skb_pool[i] = skb;	}}static void free_skb_pool(rtl8150_t *dev){	int i;	for (i = 0; i < RX_SKB_POOL_SIZE; i++)		if (dev->rx_skb_pool[i])			dev_kfree_skb(dev->rx_skb_pool[i]);}static int enable_net_traffic(rtl8150_t * dev){	u8 cr, tcr, rcr, msr;	if (!rtl8150_reset(dev)) {		warn("%s - device reset failed", __FUNCTION__);	}	/* RCR bit7=1 attach Rx info at the end;  =0 HW CRC (which is broken) */	rcr = 0x9e;	dev->rx_creg = cpu_to_le16(rcr);	tcr = 0xd8;	cr = 0x0c;	if (!(rcr & 0x80))		set_bit(RTL8150_HW_CRC, &dev->flags);	set_registers(dev, RCR, 1, &rcr);	set_registers(dev, TCR, 1, &tcr);	set_registers(dev, CR, 1, &cr);	get_registers(dev, MSR, 1, &msr);	return 0;}static void disable_net_traffic(rtl8150_t * dev){	u8 cr;	get_registers(dev, CR, 1, &cr);	cr &= 0xf3;	set_registers(dev, CR, 1, &cr);}static struct net_device_stats *rtl8150_netdev_stats(struct net_device *dev){	return &((rtl8150_t *) dev->priv)->stats;}static void rtl8150_tx_timeout(struct net_device *netdev){	rtl8150_t *dev;	dev = netdev->priv;	if (!dev)		return;	warn("%s: Tx timeout.", netdev->name);	dev->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;	usb_unlink_urb(dev->tx_urb);	dev->stats.tx_errors++;}static void rtl8150_set_multicast(struct net_device *netdev){	rtl8150_t *dev;	dev = netdev->priv;	netif_stop_queue(netdev);	if (netdev->flags & IFF_PROMISC) {		dev->rx_creg |= cpu_to_le16(0x0001);		info("%s: promiscuous mode", netdev->name);	} else if ((netdev->mc_count > multicast_filter_limit) ||		   (netdev->flags & IFF_ALLMULTI)) {		dev->rx_creg &= cpu_to_le16(0xfffe);		dev->rx_creg |= cpu_to_le16(0x0002);		info("%s: allmulti set", netdev->name);	} else {		/* ~RX_MULTICAST, ~RX_PROMISCUOUS */		dev->rx_creg &= cpu_to_le16(0x00fc);	}	async_set_registers(dev, RCR, 2);	netif_wake_queue(netdev);}static int rtl8150_start_xmit(struct sk_buff *skb, struct net_device *netdev){	rtl8150_t *dev;	int count, res;	netif_stop_queue(netdev);	dev = netdev->priv;	count = (skb->len < 60) ? 60 : skb->len;	count = (count & 0x3f) ? count : count + 1;	dev->tx_skb = skb;	usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2),		      skb->data, count, write_bulk_callback, dev);	if ((res = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))) {		warn("failed tx_urb %d\n", res);		dev->stats.tx_errors++;		netif_start_queue(netdev);	} else {		dev->stats.tx_packets++;		dev->stats.tx_bytes += skb->len;		netdev->trans_start = jiffies;	}	return 0;}static void set_carrier(struct net_device *netdev){	rtl8150_t *dev = netdev->priv;	short tmp;	get_registers(dev, CSCR, 2, &tmp);	if (tmp & CSCR_LINK_STATUS)		netif_carrier_on(netdev);	else		netif_carrier_off(netdev);}static int rtl8150_open(struct net_device *netdev){	rtl8150_t *dev;	int res;	dev = netdev->priv;	if (dev == NULL) {		return -ENODEV;	}	if (dev->rx_skb == NULL)		dev->rx_skb = pull_skb(dev);	if (!dev->rx_skb)		return -ENOMEM;	set_registers(dev, IDR, 6, netdev->dev_addr);		usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);	if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL)))		warn("%s: rx_urb submit failed: %d", __FUNCTION__, res);	usb_fill_int_urb(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev, 3),		     dev->intr_buff, INTBUFSIZE, intr_callback,		     dev, dev->intr_interval);	if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL)))		warn("%s: intr_urb submit failed: %d", __FUNCTION__, res);	netif_start_queue(netdev);	enable_net_traffic(dev);	set_carrier(netdev);	return res;}static int rtl8150_close(struct net_device *netdev){	rtl8150_t *dev;	int res = 0;	dev = netdev->priv;	if (!dev)		return -ENODEV;	netif_stop_queue(netdev);	if (!test_bit(RTL8150_UNPLUG, &dev->flags))		disable_net_traffic(dev);	unlink_all_urbs(dev);	return res;}static int rtl8150_ethtool_ioctl(struct net_device *netdev, void __user *uaddr){	rtl8150_t *dev;	int cmd;	dev = netdev->priv;	if (get_user(cmd, (int __user *) uaddr))		return -EFAULT;	switch (cmd) {	case ETHTOOL_GDRVINFO:{		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };		strncpy(info.driver, driver_name, ETHTOOL_BUSINFO_LEN);		strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN);		usb_make_path(dev->udev, info.bus_info, sizeof info.bus_info);		if (copy_to_user(uaddr, &info, sizeof(info)))			return -EFAULT;		return 0;		}	case ETHTOOL_GSET:{		struct ethtool_cmd ecmd;		short lpa, bmcr;		if (copy_from_user(&ecmd, uaddr, sizeof(ecmd)))			return -EFAULT;		ecmd.supported = (SUPPORTED_10baseT_Half |				  SUPPORTED_10baseT_Full |				  SUPPORTED_100baseT_Half |				  SUPPORTED_100baseT_Full |				  SUPPORTED_Autoneg |				  SUPPORTED_TP | SUPPORTED_MII);		ecmd.port = PORT_TP;		ecmd.transceiver = XCVR_INTERNAL;		ecmd.phy_address = dev->phy;		get_registers(dev, BMCR, 2, &bmcr);		get_registers(dev, ANLP, 2, &lpa);		if (bmcr & BMCR_ANENABLE) {			ecmd.autoneg = AUTONEG_ENABLE;			ecmd.speed = (lpa & (LPA_100HALF | LPA_100FULL)) ?			             SPEED_100 : SPEED_10;			if (ecmd.speed == SPEED_100)				ecmd.duplex = (lpa & LPA_100FULL) ?				    DUPLEX_FULL : DUPLEX_HALF;			else				ecmd.duplex = (lpa & LPA_10FULL) ?				    DUPLEX_FULL : DUPLEX_HALF;		} else {			ecmd.autoneg = AUTONEG_DISABLE;			ecmd.speed = (bmcr & BMCR_SPEED100) ?			    SPEED_100 : SPEED_10;			ecmd.duplex = (bmcr & BMCR_FULLDPLX) ?			    DUPLEX_FULL : DUPLEX_HALF;		}		if (copy_to_user(uaddr, &ecmd, sizeof(ecmd)))			return -EFAULT;		return 0;		}	case ETHTOOL_SSET:		return -ENOTSUPP;	case ETHTOOL_GLINK:{		struct ethtool_value edata = { ETHTOOL_GLINK };		edata.data = netif_carrier_ok(netdev);		if (copy_to_user(uaddr, &edata, sizeof(edata)))			return -EFAULT;		return 0;		}	default:		return -EOPNOTSUPP;	}}static int rtl8150_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd){	rtl8150_t *dev;	u16 *data;	int res;	dev = netdev->priv;	data = (u16 *) & rq->ifr_ifru;	res = 0;	switch (cmd) {	case SIOCETHTOOL:		res = rtl8150_ethtool_ioctl(netdev, rq->ifr_data);		break;	case SIOCDEVPRIVATE:		data[0] = dev->phy;	case SIOCDEVPRIVATE + 1:		read_mii_word(dev, dev->phy, (data[1] & 0x1f), &data[3]);		break;	case SIOCDEVPRIVATE + 2:		if (!capable(CAP_NET_ADMIN))			return -EPERM;		write_mii_word(dev, dev->phy, (data[1] & 0x1f), data[2]);		break;	default:		res = -EOPNOTSUPP;	}	return res;}static int rtl8150_probe(struct usb_interface *intf,			 const struct usb_device_id *id){	struct usb_device *udev = interface_to_usbdev(intf);	rtl8150_t *dev;	struct net_device *netdev;	dev = kmalloc(sizeof(rtl8150_t), GFP_KERNEL);	if (!dev) {		err("Out of memory");		return -ENOMEM;	} else		memset(dev, 0, sizeof(rtl8150_t));	dev->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL);	if (!dev->intr_buff) {		kfree(dev);		return -ENOMEM;	}	netdev = alloc_etherdev(0);	if (!netdev) {		kfree(dev->intr_buff);		kfree(dev);		err("Oh boy, out of memory again?!?");		return -ENOMEM;	}	tasklet_init(&dev->tl, rx_fixup, (unsigned long)dev);	spin_lock_init(&dev->rx_pool_lock);		dev->udev = udev;	dev->netdev = netdev;	SET_MODULE_OWNER(netdev);	netdev->priv = dev;	netdev->open = rtl8150_open;	netdev->stop = rtl8150_close;	netdev->do_ioctl = rtl8150_ioctl;	netdev->watchdog_timeo = RTL8150_TX_TIMEOUT;	netdev->tx_timeout = rtl8150_tx_timeout;	netdev->hard_start_xmit = rtl8150_start_xmit;	netdev->set_multicast_list = rtl8150_set_multicast;	netdev->set_mac_address = rtl8150_set_mac_address;	netdev->get_stats = rtl8150_netdev_stats;	netdev->mtu = RTL8150_MTU;	dev->intr_interval = 100;	/* 100ms */	if (!alloc_all_urbs(dev)) {		err("out of memory");		goto out;	}	if (!rtl8150_reset(dev)) {		err("couldn't reset the device");		goto out1;	}	fill_skb_pool(dev);	set_ethernet_addr(dev);	info("%s: rtl8150 is detected", netdev->name);		usb_set_intfdata(intf, dev);	SET_NETDEV_DEV(netdev, &intf->dev);	if (register_netdev(netdev) != 0) {		err("couldn't register the device");		goto out2;	}	return 0;out2:	usb_set_intfdata(intf, NULL);	free_skb_pool(dev);out1:	free_all_urbs(dev);out:	kfree(dev->intr_buff);	free_netdev(netdev);	kfree(dev);	return -EIO;}static void rtl8150_disconnect(struct usb_interface *intf){	rtl8150_t *dev = usb_get_intfdata(intf);	usb_set_intfdata(intf, NULL);	if (dev) {		set_bit(RTL8150_UNPLUG, &dev->flags);		unregister_netdev(dev->netdev);		unlink_all_urbs(dev);		free_all_urbs(dev);		free_skb_pool(dev);		if (dev->rx_skb)			dev_kfree_skb(dev->rx_skb);		free_netdev(dev->netdev);		kfree(dev->intr_buff);		kfree(dev);	}}static int __init usb_rtl8150_init(void){	info(DRIVER_DESC " " DRIVER_VERSION);	return usb_register(&rtl8150_driver);}static void __exit usb_rtl8150_exit(void){	usb_deregister(&rtl8150_driver);}module_init(usb_rtl8150_init);module_exit(usb_rtl8150_exit);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?