net/usb/r8152: enable interrupt transfer

Use the interrupt transfer to replace polling link status.

Signed-off-by: Hayes Wang <hayeswang@realtek.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
hayeswang 2013-08-14 20:54:40 +08:00 committed by David S. Miller
parent 5bd2388174
commit 40a82917b1
1 changed files with 113 additions and 27 deletions

View File

@ -272,6 +272,9 @@ enum rtl_register_content {
#define RTL8152_MAX_TX 10 #define RTL8152_MAX_TX 10
#define RTL8152_MAX_RX 10 #define RTL8152_MAX_RX 10
#define INTBUFSIZE 2
#define INTR_LINK 0x0004
#define RTL8152_REQT_READ 0xc0 #define RTL8152_REQT_READ 0xc0
#define RTL8152_REQT_WRITE 0x40 #define RTL8152_REQT_WRITE 0x40
@ -292,7 +295,8 @@ enum rtl_register_content {
enum rtl8152_flags { enum rtl8152_flags {
RTL8152_UNPLUG = 0, RTL8152_UNPLUG = 0,
RTL8152_SET_RX_MODE, RTL8152_SET_RX_MODE,
WORK_ENABLE WORK_ENABLE,
RTL8152_LINK_CHG,
}; };
/* Define these values to match your device */ /* Define these values to match your device */
@ -347,7 +351,9 @@ struct r8152 {
unsigned long flags; unsigned long flags;
struct usb_device *udev; struct usb_device *udev;
struct tasklet_struct tl; struct tasklet_struct tl;
struct usb_interface *intf;
struct net_device *netdev; struct net_device *netdev;
struct urb *intr_urb;
struct tx_agg tx_info[RTL8152_MAX_TX]; struct tx_agg tx_info[RTL8152_MAX_TX];
struct rx_agg rx_info[RTL8152_MAX_RX]; struct rx_agg rx_info[RTL8152_MAX_RX];
struct list_head rx_done, tx_free; struct list_head rx_done, tx_free;
@ -355,8 +361,10 @@ struct r8152 {
spinlock_t rx_lock, tx_lock; spinlock_t rx_lock, tx_lock;
struct delayed_work schedule; struct delayed_work schedule;
struct mii_if_info mii; struct mii_if_info mii;
int intr_interval;
u32 msg_enable; u32 msg_enable;
u16 ocp_base; u16 ocp_base;
u8 *intr_buff;
u8 version; u8 version;
u8 speed; u8 speed;
}; };
@ -860,6 +868,62 @@ static void write_bulk_callback(struct urb *urb)
tasklet_schedule(&tp->tl); tasklet_schedule(&tp->tl);
} }
static void intr_callback(struct urb *urb)
{
struct r8152 *tp;
__u16 *d;
int status = urb->status;
int res;
tp = urb->context;
if (!tp)
return;
if (!test_bit(WORK_ENABLE, &tp->flags))
return;
if (test_bit(RTL8152_UNPLUG, &tp->flags))
return;
switch (status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ESHUTDOWN:
netif_device_detach(tp->netdev);
case -ENOENT:
return;
case -EOVERFLOW:
netif_info(tp, intr, tp->netdev, "intr status -EOVERFLOW\n");
goto resubmit;
/* -EPIPE: should clear the halt */
default:
netif_info(tp, intr, tp->netdev, "intr status %d\n", status);
goto resubmit;
}
d = urb->transfer_buffer;
if (INTR_LINK & __le16_to_cpu(d[0])) {
if (!(tp->speed & LINK_STATUS)) {
set_bit(RTL8152_LINK_CHG, &tp->flags);
schedule_delayed_work(&tp->schedule, 0);
}
} else {
if (tp->speed & LINK_STATUS) {
set_bit(RTL8152_LINK_CHG, &tp->flags);
schedule_delayed_work(&tp->schedule, 0);
}
}
resubmit:
res = usb_submit_urb(urb, GFP_ATOMIC);
if (res == -ENODEV)
netif_device_detach(tp->netdev);
else if (res)
netif_err(tp, intr, tp->netdev,
"can't resubmit intr, status %d\n", res);
}
static inline void *rx_agg_align(void *data) static inline void *rx_agg_align(void *data)
{ {
return (void *)ALIGN((uintptr_t)data, 8); return (void *)ALIGN((uintptr_t)data, 8);
@ -899,11 +963,24 @@ static void free_all_mem(struct r8152 *tp)
tp->tx_info[i].head = NULL; tp->tx_info[i].head = NULL;
} }
} }
if (tp->intr_urb) {
usb_free_urb(tp->intr_urb);
tp->intr_urb = NULL;
}
if (tp->intr_buff) {
kfree(tp->intr_buff);
tp->intr_buff = NULL;
}
} }
static int alloc_all_mem(struct r8152 *tp) static int alloc_all_mem(struct r8152 *tp)
{ {
struct net_device *netdev = tp->netdev; struct net_device *netdev = tp->netdev;
struct usb_interface *intf = tp->intf;
struct usb_host_interface *alt = intf->cur_altsetting;
struct usb_host_endpoint *ep_intr = alt->endpoint + 2;
struct urb *urb; struct urb *urb;
int node, i; int node, i;
u8 *buf; u8 *buf;
@ -968,6 +1045,19 @@ static int alloc_all_mem(struct r8152 *tp)
list_add_tail(&tp->tx_info[i].list, &tp->tx_free); list_add_tail(&tp->tx_info[i].list, &tp->tx_free);
} }
tp->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!tp->intr_urb)
goto err1;
tp->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL);
if (!tp->intr_buff)
goto err1;
tp->intr_interval = (int)ep_intr->desc.bInterval;
usb_fill_int_urb(tp->intr_urb, tp->udev, usb_rcvintpipe(tp->udev, 3),
tp->intr_buff, INTBUFSIZE, intr_callback,
tp, tp->intr_interval);
return 0; return 0;
err1: err1:
@ -1228,8 +1318,10 @@ static void rtl8152_set_rx_mode(struct net_device *netdev)
{ {
struct r8152 *tp = netdev_priv(netdev); struct r8152 *tp = netdev_priv(netdev);
if (tp->speed & LINK_STATUS) if (tp->speed & LINK_STATUS) {
set_bit(RTL8152_SET_RX_MODE, &tp->flags); set_bit(RTL8152_SET_RX_MODE, &tp->flags);
schedule_delayed_work(&tp->schedule, 0);
}
} }
static void _rtl8152_set_rx_mode(struct net_device *netdev) static void _rtl8152_set_rx_mode(struct net_device *netdev)
@ -1648,7 +1740,6 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)
r8152_mdio_write(tp, MII_BMCR, bmcr); r8152_mdio_write(tp, MII_BMCR, bmcr);
out: out:
schedule_delayed_work(&tp->schedule, 5 * HZ);
return ret; return ret;
} }
@ -1671,6 +1762,7 @@ static void set_carrier(struct r8152 *tp)
struct net_device *netdev = tp->netdev; struct net_device *netdev = tp->netdev;
u8 speed; u8 speed;
clear_bit(RTL8152_LINK_CHG, &tp->flags);
speed = rtl8152_get_speed(tp); speed = rtl8152_get_speed(tp);
if (speed & LINK_STATUS) { if (speed & LINK_STATUS) {
@ -1700,13 +1792,12 @@ static void rtl_work_func_t(struct work_struct *work)
if (test_bit(RTL8152_UNPLUG, &tp->flags)) if (test_bit(RTL8152_UNPLUG, &tp->flags))
goto out1; goto out1;
set_carrier(tp); if (test_bit(RTL8152_LINK_CHG, &tp->flags))
set_carrier(tp);
if (test_bit(RTL8152_SET_RX_MODE, &tp->flags)) if (test_bit(RTL8152_SET_RX_MODE, &tp->flags))
_rtl8152_set_rx_mode(tp->netdev); _rtl8152_set_rx_mode(tp->netdev);
schedule_delayed_work(&tp->schedule, HZ);
out1: out1:
return; return;
} }
@ -1716,28 +1807,20 @@ static int rtl8152_open(struct net_device *netdev)
struct r8152 *tp = netdev_priv(netdev); struct r8152 *tp = netdev_priv(netdev);
int res = 0; int res = 0;
tp->speed = rtl8152_get_speed(tp); res = usb_submit_urb(tp->intr_urb, GFP_KERNEL);
if (tp->speed & LINK_STATUS) { if (res) {
res = rtl8152_enable(tp); if (res == -ENODEV)
if (res) { netif_device_detach(tp->netdev);
if (res == -ENODEV) netif_warn(tp, ifup, netdev,
netif_device_detach(tp->netdev); "intr_urb submit failed: %d\n", res);
return res;
netif_err(tp, ifup, netdev,
"rtl8152_open failed: %d\n", res);
return res;
}
netif_carrier_on(netdev);
} else {
netif_stop_queue(netdev);
netif_carrier_off(netdev);
} }
rtl8152_set_speed(tp, AUTONEG_ENABLE, SPEED_100, DUPLEX_FULL); rtl8152_set_speed(tp, AUTONEG_ENABLE, SPEED_100, DUPLEX_FULL);
tp->speed = 0;
netif_carrier_off(netdev);
netif_start_queue(netdev); netif_start_queue(netdev);
set_bit(WORK_ENABLE, &tp->flags); set_bit(WORK_ENABLE, &tp->flags);
schedule_delayed_work(&tp->schedule, 0);
return res; return res;
} }
@ -1747,6 +1830,7 @@ static int rtl8152_close(struct net_device *netdev)
struct r8152 *tp = netdev_priv(netdev); struct r8152 *tp = netdev_priv(netdev);
int res = 0; int res = 0;
usb_kill_urb(tp->intr_urb);
clear_bit(WORK_ENABLE, &tp->flags); clear_bit(WORK_ENABLE, &tp->flags);
cancel_delayed_work_sync(&tp->schedule); cancel_delayed_work_sync(&tp->schedule);
netif_stop_queue(netdev); netif_stop_queue(netdev);
@ -1872,6 +1956,7 @@ static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
if (netif_running(tp->netdev)) { if (netif_running(tp->netdev)) {
clear_bit(WORK_ENABLE, &tp->flags); clear_bit(WORK_ENABLE, &tp->flags);
usb_kill_urb(tp->intr_urb);
cancel_delayed_work_sync(&tp->schedule); cancel_delayed_work_sync(&tp->schedule);
tasklet_disable(&tp->tl); tasklet_disable(&tp->tl);
} }
@ -1888,10 +1973,11 @@ static int rtl8152_resume(struct usb_interface *intf)
r8152b_init(tp); r8152b_init(tp);
netif_device_attach(tp->netdev); netif_device_attach(tp->netdev);
if (netif_running(tp->netdev)) { if (netif_running(tp->netdev)) {
rtl8152_enable(tp); rtl8152_set_speed(tp, AUTONEG_ENABLE, SPEED_100, DUPLEX_FULL);
tp->speed = 0;
netif_carrier_off(tp->netdev);
set_bit(WORK_ENABLE, &tp->flags); set_bit(WORK_ENABLE, &tp->flags);
set_bit(RTL8152_SET_RX_MODE, &tp->flags); usb_submit_urb(tp->intr_urb, GFP_KERNEL);
schedule_delayed_work(&tp->schedule, 0);
tasklet_enable(&tp->tl); tasklet_enable(&tp->tl);
} }
@ -2027,13 +2113,13 @@ static int rtl8152_probe(struct usb_interface *intf,
tp->udev = udev; tp->udev = udev;
tp->netdev = netdev; tp->netdev = netdev;
tp->intf = intf;
netdev->netdev_ops = &rtl8152_netdev_ops; netdev->netdev_ops = &rtl8152_netdev_ops;
netdev->watchdog_timeo = RTL8152_TX_TIMEOUT; netdev->watchdog_timeo = RTL8152_TX_TIMEOUT;
netdev->features |= NETIF_F_IP_CSUM; netdev->features |= NETIF_F_IP_CSUM;
netdev->hw_features = NETIF_F_IP_CSUM; netdev->hw_features = NETIF_F_IP_CSUM;
SET_ETHTOOL_OPS(netdev, &ops); SET_ETHTOOL_OPS(netdev, &ops);
tp->speed = 0;
tp->mii.dev = netdev; tp->mii.dev = netdev;
tp->mii.mdio_read = read_mii_word; tp->mii.mdio_read = read_mii_word;