mirror of
https://github.com/torvalds/linux.git
synced 2024-12-05 18:41:23 +00:00
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:
parent
5bd2388174
commit
40a82917b1
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user