18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 1999-2013 Petko Manolov (petkan@nucleusys.com) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * ChangeLog: 68c2ecf20Sopenharmony_ci * .... Most of the time spent on reading sources & docs. 78c2ecf20Sopenharmony_ci * v0.2.x First official release for the Linux kernel. 88c2ecf20Sopenharmony_ci * v0.3.0 Beutified and structured, some bugs fixed. 98c2ecf20Sopenharmony_ci * v0.3.x URBifying bulk requests and bugfixing. First relatively 108c2ecf20Sopenharmony_ci * stable release. Still can touch device's registers only 118c2ecf20Sopenharmony_ci * from top-halves. 128c2ecf20Sopenharmony_ci * v0.4.0 Control messages remained unurbified are now URBs. 138c2ecf20Sopenharmony_ci * Now we can touch the HW at any time. 148c2ecf20Sopenharmony_ci * v0.4.9 Control urbs again use process context to wait. Argh... 158c2ecf20Sopenharmony_ci * Some long standing bugs (enable_net_traffic) fixed. 168c2ecf20Sopenharmony_ci * Also nasty trick about resubmiting control urb from 178c2ecf20Sopenharmony_ci * interrupt context used. Please let me know how it 188c2ecf20Sopenharmony_ci * behaves. Pegasus II support added since this version. 198c2ecf20Sopenharmony_ci * TODO: suppressing HCD warnings spewage on disconnect. 208c2ecf20Sopenharmony_ci * v0.4.13 Ethernet address is now set at probe(), not at open() 218c2ecf20Sopenharmony_ci * time as this seems to break dhcpd. 228c2ecf20Sopenharmony_ci * v0.5.0 branch to 2.5.x kernels 238c2ecf20Sopenharmony_ci * v0.5.1 ethtool support added 248c2ecf20Sopenharmony_ci * v0.5.5 rx socket buffers are in a pool and the their allocation 258c2ecf20Sopenharmony_ci * is out of the interrupt routine. 268c2ecf20Sopenharmony_ci * ... 278c2ecf20Sopenharmony_ci * v0.9.3 simplified [get|set]_register(s), async update registers 288c2ecf20Sopenharmony_ci * logic revisited, receive skb_pool removed. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/sched.h> 328c2ecf20Sopenharmony_ci#include <linux/slab.h> 338c2ecf20Sopenharmony_ci#include <linux/init.h> 348c2ecf20Sopenharmony_ci#include <linux/delay.h> 358c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 368c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 378c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 388c2ecf20Sopenharmony_ci#include <linux/mii.h> 398c2ecf20Sopenharmony_ci#include <linux/usb.h> 408c2ecf20Sopenharmony_ci#include <linux/module.h> 418c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 428c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 438c2ecf20Sopenharmony_ci#include "pegasus.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * Version Information 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci#define DRIVER_VERSION "v0.9.3 (2013/04/25)" 498c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Petko Manolov <petkan@nucleusys.com>" 508c2ecf20Sopenharmony_ci#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic const char driver_name[] = "pegasus"; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#undef PEGASUS_WRITE_EEPROM 558c2ecf20Sopenharmony_ci#define BMSR_MEDIA (BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | \ 568c2ecf20Sopenharmony_ci BMSR_100FULL | BMSR_ANEGCAPABLE) 578c2ecf20Sopenharmony_ci#define CARRIER_CHECK_DELAY (2 * HZ) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic bool loopback; 608c2ecf20Sopenharmony_cistatic bool mii_mode; 618c2ecf20Sopenharmony_cistatic char *devid; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic struct usb_eth_dev usb_dev_id[] = { 648c2ecf20Sopenharmony_ci#define PEGASUS_DEV(pn, vid, pid, flags) \ 658c2ecf20Sopenharmony_ci {.name = pn, .vendor = vid, .device = pid, .private = flags}, 668c2ecf20Sopenharmony_ci#define PEGASUS_DEV_CLASS(pn, vid, pid, dclass, flags) \ 678c2ecf20Sopenharmony_ci PEGASUS_DEV(pn, vid, pid, flags) 688c2ecf20Sopenharmony_ci#include "pegasus.h" 698c2ecf20Sopenharmony_ci#undef PEGASUS_DEV 708c2ecf20Sopenharmony_ci#undef PEGASUS_DEV_CLASS 718c2ecf20Sopenharmony_ci {NULL, 0, 0, 0}, 728c2ecf20Sopenharmony_ci {NULL, 0, 0, 0} 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic struct usb_device_id pegasus_ids[] = { 768c2ecf20Sopenharmony_ci#define PEGASUS_DEV(pn, vid, pid, flags) \ 778c2ecf20Sopenharmony_ci {.match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = vid, .idProduct = pid}, 788c2ecf20Sopenharmony_ci/* 798c2ecf20Sopenharmony_ci * The Belkin F8T012xx1 bluetooth adaptor has the same vendor and product 808c2ecf20Sopenharmony_ci * IDs as the Belkin F5D5050, so we need to teach the pegasus driver to 818c2ecf20Sopenharmony_ci * ignore adaptors belonging to the "Wireless" class 0xE0. For this one 828c2ecf20Sopenharmony_ci * case anyway, seeing as the pegasus is for "Wired" adaptors. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci#define PEGASUS_DEV_CLASS(pn, vid, pid, dclass, flags) \ 858c2ecf20Sopenharmony_ci {.match_flags = (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_CLASS), \ 868c2ecf20Sopenharmony_ci .idVendor = vid, .idProduct = pid, .bDeviceClass = dclass}, 878c2ecf20Sopenharmony_ci#include "pegasus.h" 888c2ecf20Sopenharmony_ci#undef PEGASUS_DEV 898c2ecf20Sopenharmony_ci#undef PEGASUS_DEV_CLASS 908c2ecf20Sopenharmony_ci {}, 918c2ecf20Sopenharmony_ci {} 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 978c2ecf20Sopenharmony_cimodule_param(loopback, bool, 0); 988c2ecf20Sopenharmony_cimodule_param(mii_mode, bool, 0); 998c2ecf20Sopenharmony_cimodule_param(devid, charp, 0); 1008c2ecf20Sopenharmony_ciMODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)"); 1018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0"); 1028c2ecf20Sopenharmony_ciMODULE_PARM_DESC(devid, "The format is: 'DEV_name:VendorID:DeviceID:Flags'"); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* use ethtool to change the level for any given device */ 1058c2ecf20Sopenharmony_cistatic int msg_level = -1; 1068c2ecf20Sopenharmony_cimodule_param(msg_level, int, 0); 1078c2ecf20Sopenharmony_ciMODULE_PARM_DESC(msg_level, "Override default message level"); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, pegasus_ids); 1108c2ecf20Sopenharmony_cistatic const struct net_device_ops pegasus_netdev_ops; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/*****/ 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void async_ctrl_callback(struct urb *urb) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context; 1178c2ecf20Sopenharmony_ci int status = urb->status; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (status < 0) 1208c2ecf20Sopenharmony_ci dev_dbg(&urb->dev->dev, "%s failed with %d", __func__, status); 1218c2ecf20Sopenharmony_ci kfree(req); 1228c2ecf20Sopenharmony_ci usb_free_urb(urb); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci return usb_control_msg_recv(pegasus->usb, 0, PEGASUS_REQ_GET_REGS, 1288c2ecf20Sopenharmony_ci PEGASUS_REQT_READ, 0, indx, data, size, 1298c2ecf20Sopenharmony_ci 1000, GFP_NOIO); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, 1338c2ecf20Sopenharmony_ci const void *data) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int ret; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = usb_control_msg_send(pegasus->usb, 0, PEGASUS_REQ_SET_REGS, 1388c2ecf20Sopenharmony_ci PEGASUS_REQT_WRITE, 0, indx, data, size, 1398c2ecf20Sopenharmony_ci 1000, GFP_NOIO); 1408c2ecf20Sopenharmony_ci if (ret < 0) 1418c2ecf20Sopenharmony_ci netif_dbg(pegasus, drv, pegasus->net, "%s failed with %d\n", __func__, ret); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return ret; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* 1478c2ecf20Sopenharmony_ci * There is only one way to write to a single ADM8511 register and this is via 1488c2ecf20Sopenharmony_ci * specific control request. 'data' is ignored by the device, but it is here to 1498c2ecf20Sopenharmony_ci * not break the API. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic int set_register(pegasus_t *pegasus, __u16 indx, __u8 data) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci void *buf = &data; 1548c2ecf20Sopenharmony_ci int ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = usb_control_msg_send(pegasus->usb, 0, PEGASUS_REQ_SET_REG, 1578c2ecf20Sopenharmony_ci PEGASUS_REQT_WRITE, data, indx, buf, 1, 1588c2ecf20Sopenharmony_ci 1000, GFP_NOIO); 1598c2ecf20Sopenharmony_ci if (ret < 0) 1608c2ecf20Sopenharmony_ci netif_dbg(pegasus, drv, pegasus->net, "%s failed with %d\n", __func__, ret); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return ret; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int update_eth_regs_async(pegasus_t *pegasus) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci int ret = -ENOMEM; 1688c2ecf20Sopenharmony_ci struct urb *async_urb; 1698c2ecf20Sopenharmony_ci struct usb_ctrlrequest *req; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); 1728c2ecf20Sopenharmony_ci if (req == NULL) 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci async_urb = usb_alloc_urb(0, GFP_ATOMIC); 1768c2ecf20Sopenharmony_ci if (async_urb == NULL) { 1778c2ecf20Sopenharmony_ci kfree(req); 1788c2ecf20Sopenharmony_ci return ret; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci req->bRequestType = PEGASUS_REQT_WRITE; 1818c2ecf20Sopenharmony_ci req->bRequest = PEGASUS_REQ_SET_REGS; 1828c2ecf20Sopenharmony_ci req->wValue = cpu_to_le16(0); 1838c2ecf20Sopenharmony_ci req->wIndex = cpu_to_le16(EthCtrl0); 1848c2ecf20Sopenharmony_ci req->wLength = cpu_to_le16(3); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci usb_fill_control_urb(async_urb, pegasus->usb, 1878c2ecf20Sopenharmony_ci usb_sndctrlpipe(pegasus->usb, 0), (void *)req, 1888c2ecf20Sopenharmony_ci pegasus->eth_regs, 3, async_ctrl_callback, req); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = usb_submit_urb(async_urb, GFP_ATOMIC); 1918c2ecf20Sopenharmony_ci if (ret) { 1928c2ecf20Sopenharmony_ci if (ret == -ENODEV) 1938c2ecf20Sopenharmony_ci netif_device_detach(pegasus->net); 1948c2ecf20Sopenharmony_ci netif_err(pegasus, drv, pegasus->net, 1958c2ecf20Sopenharmony_ci "%s returned %d\n", __func__, ret); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci return ret; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int __mii_op(pegasus_t *p, __u8 phy, __u8 indx, __u16 *regd, __u8 cmd) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int i, ret; 2038c2ecf20Sopenharmony_ci __le16 regdi; 2048c2ecf20Sopenharmony_ci __u8 data[4] = { phy, 0, 0, indx }; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (cmd & PHY_WRITE) { 2078c2ecf20Sopenharmony_ci __le16 *t = (__le16 *) & data[1]; 2088c2ecf20Sopenharmony_ci *t = cpu_to_le16(*regd); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci set_register(p, PhyCtrl, 0); 2118c2ecf20Sopenharmony_ci set_registers(p, PhyAddr, sizeof(data), data); 2128c2ecf20Sopenharmony_ci set_register(p, PhyCtrl, (indx | cmd)); 2138c2ecf20Sopenharmony_ci for (i = 0; i < REG_TIMEOUT; i++) { 2148c2ecf20Sopenharmony_ci ret = get_registers(p, PhyCtrl, 1, data); 2158c2ecf20Sopenharmony_ci if (ret < 0) 2168c2ecf20Sopenharmony_ci goto fail; 2178c2ecf20Sopenharmony_ci if (data[0] & PHY_DONE) 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci if (i >= REG_TIMEOUT) { 2218c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2228c2ecf20Sopenharmony_ci goto fail; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci if (cmd & PHY_READ) { 2258c2ecf20Sopenharmony_ci ret = get_registers(p, PhyData, 2, ®di); 2268c2ecf20Sopenharmony_ci if (ret < 0) 2278c2ecf20Sopenharmony_ci goto fail; 2288c2ecf20Sopenharmony_ci *regd = le16_to_cpu(regdi); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_cifail: 2328c2ecf20Sopenharmony_ci netif_dbg(p, drv, p->net, "%s failed\n", __func__); 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* Returns non-negative int on success, error on failure */ 2378c2ecf20Sopenharmony_cistatic int read_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci return __mii_op(pegasus, phy, indx, regd, PHY_READ); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* Returns zero on success, error on failure */ 2438c2ecf20Sopenharmony_cistatic int write_mii_word(pegasus_t *pegasus, __u8 phy, __u8 indx, __u16 *regd) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci return __mii_op(pegasus, phy, indx, regd, PHY_WRITE); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int loc) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 2518c2ecf20Sopenharmony_ci int ret; 2528c2ecf20Sopenharmony_ci u16 res; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = read_mii_word(pegasus, phy_id, loc, &res); 2558c2ecf20Sopenharmony_ci if (ret < 0) 2568c2ecf20Sopenharmony_ci return ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return (int)res; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int loc, int val) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 2648c2ecf20Sopenharmony_ci u16 data = val; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci write_mii_word(pegasus, phy_id, loc, &data); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int read_eprom_word(pegasus_t *pegasus, __u8 index, __u16 *retdata) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci int ret, i; 2728c2ecf20Sopenharmony_ci __le16 retdatai; 2738c2ecf20Sopenharmony_ci __u8 tmp = 0; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci set_register(pegasus, EpromCtrl, 0); 2768c2ecf20Sopenharmony_ci set_register(pegasus, EpromOffset, index); 2778c2ecf20Sopenharmony_ci set_register(pegasus, EpromCtrl, EPROM_READ); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci for (i = 0; i < REG_TIMEOUT; i++) { 2808c2ecf20Sopenharmony_ci ret = get_registers(pegasus, EpromCtrl, 1, &tmp); 2818c2ecf20Sopenharmony_ci if (ret < 0) 2828c2ecf20Sopenharmony_ci goto fail; 2838c2ecf20Sopenharmony_ci if (tmp & EPROM_DONE) 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci if (i >= REG_TIMEOUT) { 2878c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2888c2ecf20Sopenharmony_ci goto fail; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ret = get_registers(pegasus, EpromData, 2, &retdatai); 2928c2ecf20Sopenharmony_ci if (ret < 0) 2938c2ecf20Sopenharmony_ci goto fail; 2948c2ecf20Sopenharmony_ci *retdata = le16_to_cpu(retdatai); 2958c2ecf20Sopenharmony_ci return ret; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cifail: 2988c2ecf20Sopenharmony_ci netif_dbg(pegasus, drv, pegasus->net, "%s failed\n", __func__); 2998c2ecf20Sopenharmony_ci return ret; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci#ifdef PEGASUS_WRITE_EEPROM 3038c2ecf20Sopenharmony_cistatic inline void enable_eprom_write(pegasus_t *pegasus) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci __u8 tmp; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci get_registers(pegasus, EthCtrl2, 1, &tmp); 3088c2ecf20Sopenharmony_ci set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic inline void disable_eprom_write(pegasus_t *pegasus) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci __u8 tmp; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci get_registers(pegasus, EthCtrl2, 1, &tmp); 3168c2ecf20Sopenharmony_ci set_register(pegasus, EpromCtrl, 0); 3178c2ecf20Sopenharmony_ci set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int write_eprom_word(pegasus_t *pegasus, __u8 index, __u16 data) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci int i; 3238c2ecf20Sopenharmony_ci __u8 tmp, d[4] = { 0x3f, 0, 0, EPROM_WRITE }; 3248c2ecf20Sopenharmony_ci int ret; 3258c2ecf20Sopenharmony_ci __le16 le_data = cpu_to_le16(data); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci set_registers(pegasus, EpromOffset, 4, d); 3288c2ecf20Sopenharmony_ci enable_eprom_write(pegasus); 3298c2ecf20Sopenharmony_ci set_register(pegasus, EpromOffset, index); 3308c2ecf20Sopenharmony_ci set_registers(pegasus, EpromData, 2, &le_data); 3318c2ecf20Sopenharmony_ci set_register(pegasus, EpromCtrl, EPROM_WRITE); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for (i = 0; i < REG_TIMEOUT; i++) { 3348c2ecf20Sopenharmony_ci ret = get_registers(pegasus, EpromCtrl, 1, &tmp); 3358c2ecf20Sopenharmony_ci if (ret == -ESHUTDOWN) 3368c2ecf20Sopenharmony_ci goto fail; 3378c2ecf20Sopenharmony_ci if (tmp & EPROM_DONE) 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci disable_eprom_write(pegasus); 3418c2ecf20Sopenharmony_ci if (i >= REG_TIMEOUT) 3428c2ecf20Sopenharmony_ci goto fail; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cifail: 3478c2ecf20Sopenharmony_ci netif_dbg(pegasus, drv, pegasus->net, "%s failed\n", __func__); 3488c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci#endif /* PEGASUS_WRITE_EEPROM */ 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic inline int get_node_id(pegasus_t *pegasus, u8 *id) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci int i, ret; 3558c2ecf20Sopenharmony_ci u16 w16; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 3588c2ecf20Sopenharmony_ci ret = read_eprom_word(pegasus, i, &w16); 3598c2ecf20Sopenharmony_ci if (ret < 0) 3608c2ecf20Sopenharmony_ci return ret; 3618c2ecf20Sopenharmony_ci ((__le16 *) id)[i] = cpu_to_le16(w16); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic void set_ethernet_addr(pegasus_t *pegasus) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci int ret; 3708c2ecf20Sopenharmony_ci u8 node_id[6]; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (pegasus->features & PEGASUS_II) { 3738c2ecf20Sopenharmony_ci ret = get_registers(pegasus, 0x10, sizeof(node_id), node_id); 3748c2ecf20Sopenharmony_ci if (ret < 0) 3758c2ecf20Sopenharmony_ci goto err; 3768c2ecf20Sopenharmony_ci } else { 3778c2ecf20Sopenharmony_ci ret = get_node_id(pegasus, node_id); 3788c2ecf20Sopenharmony_ci if (ret < 0) 3798c2ecf20Sopenharmony_ci goto err; 3808c2ecf20Sopenharmony_ci ret = set_registers(pegasus, EthID, sizeof(node_id), node_id); 3818c2ecf20Sopenharmony_ci if (ret < 0) 3828c2ecf20Sopenharmony_ci goto err; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci memcpy(pegasus->net->dev_addr, node_id, sizeof(node_id)); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return; 3888c2ecf20Sopenharmony_cierr: 3898c2ecf20Sopenharmony_ci eth_hw_addr_random(pegasus->net); 3908c2ecf20Sopenharmony_ci netif_dbg(pegasus, drv, pegasus->net, "software assigned MAC address.\n"); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic inline int reset_mac(pegasus_t *pegasus) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci int ret, i; 3988c2ecf20Sopenharmony_ci __u8 data = 0x8; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci set_register(pegasus, EthCtrl1, data); 4018c2ecf20Sopenharmony_ci for (i = 0; i < REG_TIMEOUT; i++) { 4028c2ecf20Sopenharmony_ci ret = get_registers(pegasus, EthCtrl1, 1, &data); 4038c2ecf20Sopenharmony_ci if (ret < 0) 4048c2ecf20Sopenharmony_ci goto fail; 4058c2ecf20Sopenharmony_ci if (~data & 0x08) { 4068c2ecf20Sopenharmony_ci if (loopback) 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci if (mii_mode && (pegasus->features & HAS_HOME_PNA)) 4098c2ecf20Sopenharmony_ci set_register(pegasus, Gpio1, 0x34); 4108c2ecf20Sopenharmony_ci else 4118c2ecf20Sopenharmony_ci set_register(pegasus, Gpio1, 0x26); 4128c2ecf20Sopenharmony_ci set_register(pegasus, Gpio0, pegasus->features); 4138c2ecf20Sopenharmony_ci set_register(pegasus, Gpio0, DEFAULT_GPIO_SET); 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci if (i == REG_TIMEOUT) 4188c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || 4218c2ecf20Sopenharmony_ci usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { 4228c2ecf20Sopenharmony_ci set_register(pegasus, Gpio0, 0x24); 4238c2ecf20Sopenharmony_ci set_register(pegasus, Gpio0, 0x26); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) { 4268c2ecf20Sopenharmony_ci __u16 auxmode; 4278c2ecf20Sopenharmony_ci ret = read_mii_word(pegasus, 3, 0x1b, &auxmode); 4288c2ecf20Sopenharmony_ci if (ret < 0) 4298c2ecf20Sopenharmony_ci goto fail; 4308c2ecf20Sopenharmony_ci auxmode |= 4; 4318c2ecf20Sopenharmony_ci write_mii_word(pegasus, 3, 0x1b, &auxmode); 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_cifail: 4368c2ecf20Sopenharmony_ci netif_dbg(pegasus, drv, pegasus->net, "%s failed\n", __func__); 4378c2ecf20Sopenharmony_ci return ret; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic int enable_net_traffic(struct net_device *dev, struct usb_device *usb) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 4438c2ecf20Sopenharmony_ci int ret; 4448c2ecf20Sopenharmony_ci __u16 linkpart; 4458c2ecf20Sopenharmony_ci __u8 data[4]; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ret = read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart); 4488c2ecf20Sopenharmony_ci if (ret < 0) 4498c2ecf20Sopenharmony_ci goto fail; 4508c2ecf20Sopenharmony_ci data[0] = 0xc8; /* TX & RX enable, append status, no CRC */ 4518c2ecf20Sopenharmony_ci data[1] = 0; 4528c2ecf20Sopenharmony_ci if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL)) 4538c2ecf20Sopenharmony_ci data[1] |= 0x20; /* set full duplex */ 4548c2ecf20Sopenharmony_ci if (linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF)) 4558c2ecf20Sopenharmony_ci data[1] |= 0x10; /* set 100 Mbps */ 4568c2ecf20Sopenharmony_ci if (mii_mode) 4578c2ecf20Sopenharmony_ci data[1] = 0; 4588c2ecf20Sopenharmony_ci data[2] = loopback ? 0x09 : 0x01; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci memcpy(pegasus->eth_regs, data, sizeof(data)); 4618c2ecf20Sopenharmony_ci ret = set_registers(pegasus, EthCtrl0, 3, data); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || 4648c2ecf20Sopenharmony_ci usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS2 || 4658c2ecf20Sopenharmony_ci usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { 4668c2ecf20Sopenharmony_ci u16 auxmode; 4678c2ecf20Sopenharmony_ci ret = read_mii_word(pegasus, 0, 0x1b, &auxmode); 4688c2ecf20Sopenharmony_ci if (ret < 0) 4698c2ecf20Sopenharmony_ci goto fail; 4708c2ecf20Sopenharmony_ci auxmode |= 4; 4718c2ecf20Sopenharmony_ci write_mii_word(pegasus, 0, 0x1b, &auxmode); 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return ret; 4758c2ecf20Sopenharmony_cifail: 4768c2ecf20Sopenharmony_ci netif_dbg(pegasus, drv, pegasus->net, "%s failed\n", __func__); 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic void read_bulk_callback(struct urb *urb) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci pegasus_t *pegasus = urb->context; 4838c2ecf20Sopenharmony_ci struct net_device *net; 4848c2ecf20Sopenharmony_ci u8 *buf = urb->transfer_buffer; 4858c2ecf20Sopenharmony_ci int rx_status, count = urb->actual_length; 4868c2ecf20Sopenharmony_ci int status = urb->status; 4878c2ecf20Sopenharmony_ci __u16 pkt_len; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (!pegasus) 4908c2ecf20Sopenharmony_ci return; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci net = pegasus->net; 4938c2ecf20Sopenharmony_ci if (!netif_device_present(net) || !netif_running(net)) 4948c2ecf20Sopenharmony_ci return; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci switch (status) { 4978c2ecf20Sopenharmony_ci case 0: 4988c2ecf20Sopenharmony_ci break; 4998c2ecf20Sopenharmony_ci case -ETIME: 5008c2ecf20Sopenharmony_ci netif_dbg(pegasus, rx_err, net, "reset MAC\n"); 5018c2ecf20Sopenharmony_ci pegasus->flags &= ~PEGASUS_RX_BUSY; 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci case -EPIPE: /* stall, or disconnect from TT */ 5048c2ecf20Sopenharmony_ci /* FIXME schedule work to clear the halt */ 5058c2ecf20Sopenharmony_ci netif_warn(pegasus, rx_err, net, "no rx stall recovery\n"); 5068c2ecf20Sopenharmony_ci return; 5078c2ecf20Sopenharmony_ci case -ENOENT: 5088c2ecf20Sopenharmony_ci case -ECONNRESET: 5098c2ecf20Sopenharmony_ci case -ESHUTDOWN: 5108c2ecf20Sopenharmony_ci netif_dbg(pegasus, ifdown, net, "rx unlink, %d\n", status); 5118c2ecf20Sopenharmony_ci return; 5128c2ecf20Sopenharmony_ci default: 5138c2ecf20Sopenharmony_ci netif_dbg(pegasus, rx_err, net, "RX status %d\n", status); 5148c2ecf20Sopenharmony_ci goto goon; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (count < 4) 5188c2ecf20Sopenharmony_ci goto goon; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci rx_status = buf[count - 2]; 5218c2ecf20Sopenharmony_ci if (rx_status & 0x1c) { 5228c2ecf20Sopenharmony_ci netif_dbg(pegasus, rx_err, net, 5238c2ecf20Sopenharmony_ci "RX packet error %x\n", rx_status); 5248c2ecf20Sopenharmony_ci net->stats.rx_errors++; 5258c2ecf20Sopenharmony_ci if (rx_status & 0x04) /* runt */ 5268c2ecf20Sopenharmony_ci net->stats.rx_length_errors++; 5278c2ecf20Sopenharmony_ci if (rx_status & 0x08) 5288c2ecf20Sopenharmony_ci net->stats.rx_crc_errors++; 5298c2ecf20Sopenharmony_ci if (rx_status & 0x10) /* extra bits */ 5308c2ecf20Sopenharmony_ci net->stats.rx_frame_errors++; 5318c2ecf20Sopenharmony_ci goto goon; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci if (pegasus->chip == 0x8513) { 5348c2ecf20Sopenharmony_ci pkt_len = le32_to_cpu(*(__le32 *)urb->transfer_buffer); 5358c2ecf20Sopenharmony_ci pkt_len &= 0x0fff; 5368c2ecf20Sopenharmony_ci pegasus->rx_skb->data += 2; 5378c2ecf20Sopenharmony_ci } else { 5388c2ecf20Sopenharmony_ci pkt_len = buf[count - 3] << 8; 5398c2ecf20Sopenharmony_ci pkt_len += buf[count - 4]; 5408c2ecf20Sopenharmony_ci pkt_len &= 0xfff; 5418c2ecf20Sopenharmony_ci pkt_len -= 4; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* 5458c2ecf20Sopenharmony_ci * If the packet is unreasonably long, quietly drop it rather than 5468c2ecf20Sopenharmony_ci * kernel panicing by calling skb_put. 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci if (pkt_len > PEGASUS_MTU) 5498c2ecf20Sopenharmony_ci goto goon; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* 5528c2ecf20Sopenharmony_ci * at this point we are sure pegasus->rx_skb != NULL 5538c2ecf20Sopenharmony_ci * so we go ahead and pass up the packet. 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_ci skb_put(pegasus->rx_skb, pkt_len); 5568c2ecf20Sopenharmony_ci pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net); 5578c2ecf20Sopenharmony_ci netif_rx(pegasus->rx_skb); 5588c2ecf20Sopenharmony_ci net->stats.rx_packets++; 5598c2ecf20Sopenharmony_ci net->stats.rx_bytes += pkt_len; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (pegasus->flags & PEGASUS_UNPLUG) 5628c2ecf20Sopenharmony_ci return; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, PEGASUS_MTU, 5658c2ecf20Sopenharmony_ci GFP_ATOMIC); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (pegasus->rx_skb == NULL) 5688c2ecf20Sopenharmony_ci goto tl_sched; 5698c2ecf20Sopenharmony_cigoon: 5708c2ecf20Sopenharmony_ci usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, 5718c2ecf20Sopenharmony_ci usb_rcvbulkpipe(pegasus->usb, 1), 5728c2ecf20Sopenharmony_ci pegasus->rx_skb->data, PEGASUS_MTU, 5738c2ecf20Sopenharmony_ci read_bulk_callback, pegasus); 5748c2ecf20Sopenharmony_ci rx_status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); 5758c2ecf20Sopenharmony_ci if (rx_status == -ENODEV) 5768c2ecf20Sopenharmony_ci netif_device_detach(pegasus->net); 5778c2ecf20Sopenharmony_ci else if (rx_status) { 5788c2ecf20Sopenharmony_ci pegasus->flags |= PEGASUS_RX_URB_FAIL; 5798c2ecf20Sopenharmony_ci goto tl_sched; 5808c2ecf20Sopenharmony_ci } else { 5818c2ecf20Sopenharmony_ci pegasus->flags &= ~PEGASUS_RX_URB_FAIL; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci return; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_citl_sched: 5878c2ecf20Sopenharmony_ci tasklet_schedule(&pegasus->rx_tl); 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic void rx_fixup(unsigned long data) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci pegasus_t *pegasus; 5938c2ecf20Sopenharmony_ci int status; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci pegasus = (pegasus_t *) data; 5968c2ecf20Sopenharmony_ci if (pegasus->flags & PEGASUS_UNPLUG) 5978c2ecf20Sopenharmony_ci return; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (pegasus->flags & PEGASUS_RX_URB_FAIL) 6008c2ecf20Sopenharmony_ci if (pegasus->rx_skb) 6018c2ecf20Sopenharmony_ci goto try_again; 6028c2ecf20Sopenharmony_ci if (pegasus->rx_skb == NULL) 6038c2ecf20Sopenharmony_ci pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, 6048c2ecf20Sopenharmony_ci PEGASUS_MTU, 6058c2ecf20Sopenharmony_ci GFP_ATOMIC); 6068c2ecf20Sopenharmony_ci if (pegasus->rx_skb == NULL) { 6078c2ecf20Sopenharmony_ci netif_warn(pegasus, rx_err, pegasus->net, "low on memory\n"); 6088c2ecf20Sopenharmony_ci tasklet_schedule(&pegasus->rx_tl); 6098c2ecf20Sopenharmony_ci return; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, 6128c2ecf20Sopenharmony_ci usb_rcvbulkpipe(pegasus->usb, 1), 6138c2ecf20Sopenharmony_ci pegasus->rx_skb->data, PEGASUS_MTU, 6148c2ecf20Sopenharmony_ci read_bulk_callback, pegasus); 6158c2ecf20Sopenharmony_citry_again: 6168c2ecf20Sopenharmony_ci status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); 6178c2ecf20Sopenharmony_ci if (status == -ENODEV) 6188c2ecf20Sopenharmony_ci netif_device_detach(pegasus->net); 6198c2ecf20Sopenharmony_ci else if (status) { 6208c2ecf20Sopenharmony_ci pegasus->flags |= PEGASUS_RX_URB_FAIL; 6218c2ecf20Sopenharmony_ci tasklet_schedule(&pegasus->rx_tl); 6228c2ecf20Sopenharmony_ci } else { 6238c2ecf20Sopenharmony_ci pegasus->flags &= ~PEGASUS_RX_URB_FAIL; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic void write_bulk_callback(struct urb *urb) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci pegasus_t *pegasus = urb->context; 6308c2ecf20Sopenharmony_ci struct net_device *net; 6318c2ecf20Sopenharmony_ci int status = urb->status; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (!pegasus) 6348c2ecf20Sopenharmony_ci return; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci net = pegasus->net; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (!netif_device_present(net) || !netif_running(net)) 6398c2ecf20Sopenharmony_ci return; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci switch (status) { 6428c2ecf20Sopenharmony_ci case -EPIPE: 6438c2ecf20Sopenharmony_ci /* FIXME schedule_work() to clear the tx halt */ 6448c2ecf20Sopenharmony_ci netif_stop_queue(net); 6458c2ecf20Sopenharmony_ci netif_warn(pegasus, tx_err, net, "no tx stall recovery\n"); 6468c2ecf20Sopenharmony_ci return; 6478c2ecf20Sopenharmony_ci case -ENOENT: 6488c2ecf20Sopenharmony_ci case -ECONNRESET: 6498c2ecf20Sopenharmony_ci case -ESHUTDOWN: 6508c2ecf20Sopenharmony_ci netif_dbg(pegasus, ifdown, net, "tx unlink, %d\n", status); 6518c2ecf20Sopenharmony_ci return; 6528c2ecf20Sopenharmony_ci default: 6538c2ecf20Sopenharmony_ci netif_info(pegasus, tx_err, net, "TX status %d\n", status); 6548c2ecf20Sopenharmony_ci fallthrough; 6558c2ecf20Sopenharmony_ci case 0: 6568c2ecf20Sopenharmony_ci break; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci netif_trans_update(net); /* prevent tx timeout */ 6608c2ecf20Sopenharmony_ci netif_wake_queue(net); 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic void intr_callback(struct urb *urb) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci pegasus_t *pegasus = urb->context; 6668c2ecf20Sopenharmony_ci struct net_device *net; 6678c2ecf20Sopenharmony_ci int res, status = urb->status; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (!pegasus) 6708c2ecf20Sopenharmony_ci return; 6718c2ecf20Sopenharmony_ci net = pegasus->net; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci switch (status) { 6748c2ecf20Sopenharmony_ci case 0: 6758c2ecf20Sopenharmony_ci break; 6768c2ecf20Sopenharmony_ci case -ECONNRESET: /* unlink */ 6778c2ecf20Sopenharmony_ci case -ENOENT: 6788c2ecf20Sopenharmony_ci case -ESHUTDOWN: 6798c2ecf20Sopenharmony_ci return; 6808c2ecf20Sopenharmony_ci default: 6818c2ecf20Sopenharmony_ci /* some Pegasus-I products report LOTS of data 6828c2ecf20Sopenharmony_ci * toggle errors... avoid log spamming 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_ci netif_dbg(pegasus, timer, net, "intr status %d\n", status); 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (urb->actual_length >= 6) { 6888c2ecf20Sopenharmony_ci u8 *d = urb->transfer_buffer; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* byte 0 == tx_status1, reg 2B */ 6918c2ecf20Sopenharmony_ci if (d[0] & (TX_UNDERRUN|EXCESSIVE_COL 6928c2ecf20Sopenharmony_ci |LATE_COL|JABBER_TIMEOUT)) { 6938c2ecf20Sopenharmony_ci net->stats.tx_errors++; 6948c2ecf20Sopenharmony_ci if (d[0] & TX_UNDERRUN) 6958c2ecf20Sopenharmony_ci net->stats.tx_fifo_errors++; 6968c2ecf20Sopenharmony_ci if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT)) 6978c2ecf20Sopenharmony_ci net->stats.tx_aborted_errors++; 6988c2ecf20Sopenharmony_ci if (d[0] & LATE_COL) 6998c2ecf20Sopenharmony_ci net->stats.tx_window_errors++; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* d[5].LINK_STATUS lies on some adapters. 7038c2ecf20Sopenharmony_ci * d[0].NO_CARRIER kicks in only with failed TX. 7048c2ecf20Sopenharmony_ci * ... so monitoring with MII may be safest. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci /* bytes 3-4 == rx_lostpkt, reg 2E/2F */ 7088c2ecf20Sopenharmony_ci net->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4]; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci res = usb_submit_urb(urb, GFP_ATOMIC); 7128c2ecf20Sopenharmony_ci if (res == -ENODEV) 7138c2ecf20Sopenharmony_ci netif_device_detach(pegasus->net); 7148c2ecf20Sopenharmony_ci if (res) 7158c2ecf20Sopenharmony_ci netif_err(pegasus, timer, net, 7168c2ecf20Sopenharmony_ci "can't resubmit interrupt urb, %d\n", res); 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic void pegasus_tx_timeout(struct net_device *net, unsigned int txqueue) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(net); 7228c2ecf20Sopenharmony_ci netif_warn(pegasus, timer, net, "tx timeout\n"); 7238c2ecf20Sopenharmony_ci usb_unlink_urb(pegasus->tx_urb); 7248c2ecf20Sopenharmony_ci net->stats.tx_errors++; 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic netdev_tx_t pegasus_start_xmit(struct sk_buff *skb, 7288c2ecf20Sopenharmony_ci struct net_device *net) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(net); 7318c2ecf20Sopenharmony_ci int count = ((skb->len + 2) & 0x3f) ? skb->len + 2 : skb->len + 3; 7328c2ecf20Sopenharmony_ci int res; 7338c2ecf20Sopenharmony_ci __u16 l16 = skb->len; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci netif_stop_queue(net); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci ((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16); 7388c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len); 7398c2ecf20Sopenharmony_ci usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb, 7408c2ecf20Sopenharmony_ci usb_sndbulkpipe(pegasus->usb, 2), 7418c2ecf20Sopenharmony_ci pegasus->tx_buff, count, 7428c2ecf20Sopenharmony_ci write_bulk_callback, pegasus); 7438c2ecf20Sopenharmony_ci if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) { 7448c2ecf20Sopenharmony_ci netif_warn(pegasus, tx_err, net, "fail tx, %d\n", res); 7458c2ecf20Sopenharmony_ci switch (res) { 7468c2ecf20Sopenharmony_ci case -EPIPE: /* stall, or disconnect from TT */ 7478c2ecf20Sopenharmony_ci /* cleanup should already have been scheduled */ 7488c2ecf20Sopenharmony_ci break; 7498c2ecf20Sopenharmony_ci case -ENODEV: /* disconnect() upcoming */ 7508c2ecf20Sopenharmony_ci case -EPERM: 7518c2ecf20Sopenharmony_ci netif_device_detach(pegasus->net); 7528c2ecf20Sopenharmony_ci break; 7538c2ecf20Sopenharmony_ci default: 7548c2ecf20Sopenharmony_ci net->stats.tx_errors++; 7558c2ecf20Sopenharmony_ci netif_start_queue(net); 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci } else { 7588c2ecf20Sopenharmony_ci net->stats.tx_packets++; 7598c2ecf20Sopenharmony_ci net->stats.tx_bytes += skb->len; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic inline void disable_net_traffic(pegasus_t *pegasus) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci __le16 tmp = cpu_to_le16(0); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci set_registers(pegasus, EthCtrl0, sizeof(tmp), &tmp); 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic inline int get_interrupt_interval(pegasus_t *pegasus) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci u16 data; 7768c2ecf20Sopenharmony_ci u8 interval; 7778c2ecf20Sopenharmony_ci int ret; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci ret = read_eprom_word(pegasus, 4, &data); 7808c2ecf20Sopenharmony_ci if (ret < 0) 7818c2ecf20Sopenharmony_ci return ret; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci interval = data >> 8; 7848c2ecf20Sopenharmony_ci if (pegasus->usb->speed != USB_SPEED_HIGH) { 7858c2ecf20Sopenharmony_ci if (interval < 0x80) { 7868c2ecf20Sopenharmony_ci netif_info(pegasus, timer, pegasus->net, 7878c2ecf20Sopenharmony_ci "intr interval changed from %ums to %ums\n", 7888c2ecf20Sopenharmony_ci interval, 0x80); 7898c2ecf20Sopenharmony_ci interval = 0x80; 7908c2ecf20Sopenharmony_ci data = (data & 0x00FF) | ((u16)interval << 8); 7918c2ecf20Sopenharmony_ci#ifdef PEGASUS_WRITE_EEPROM 7928c2ecf20Sopenharmony_ci write_eprom_word(pegasus, 4, data); 7938c2ecf20Sopenharmony_ci#endif 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci pegasus->intr_interval = interval; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci return 0; 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic void set_carrier(struct net_device *net) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(net); 8048c2ecf20Sopenharmony_ci u16 tmp; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp)) 8078c2ecf20Sopenharmony_ci return; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (tmp & BMSR_LSTATUS) 8108c2ecf20Sopenharmony_ci netif_carrier_on(net); 8118c2ecf20Sopenharmony_ci else 8128c2ecf20Sopenharmony_ci netif_carrier_off(net); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic void free_all_urbs(pegasus_t *pegasus) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci usb_free_urb(pegasus->intr_urb); 8188c2ecf20Sopenharmony_ci usb_free_urb(pegasus->tx_urb); 8198c2ecf20Sopenharmony_ci usb_free_urb(pegasus->rx_urb); 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic void unlink_all_urbs(pegasus_t *pegasus) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci usb_kill_urb(pegasus->intr_urb); 8258c2ecf20Sopenharmony_ci usb_kill_urb(pegasus->tx_urb); 8268c2ecf20Sopenharmony_ci usb_kill_urb(pegasus->rx_urb); 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic int alloc_urbs(pegasus_t *pegasus) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci int res = -ENOMEM; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); 8348c2ecf20Sopenharmony_ci if (!pegasus->rx_urb) { 8358c2ecf20Sopenharmony_ci return res; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); 8388c2ecf20Sopenharmony_ci if (!pegasus->tx_urb) { 8398c2ecf20Sopenharmony_ci usb_free_urb(pegasus->rx_urb); 8408c2ecf20Sopenharmony_ci return res; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL); 8438c2ecf20Sopenharmony_ci if (!pegasus->intr_urb) { 8448c2ecf20Sopenharmony_ci usb_free_urb(pegasus->tx_urb); 8458c2ecf20Sopenharmony_ci usb_free_urb(pegasus->rx_urb); 8468c2ecf20Sopenharmony_ci return res; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return 0; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic int pegasus_open(struct net_device *net) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(net); 8558c2ecf20Sopenharmony_ci int res=-ENOMEM; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (pegasus->rx_skb == NULL) 8588c2ecf20Sopenharmony_ci pegasus->rx_skb = __netdev_alloc_skb_ip_align(pegasus->net, 8598c2ecf20Sopenharmony_ci PEGASUS_MTU, 8608c2ecf20Sopenharmony_ci GFP_KERNEL); 8618c2ecf20Sopenharmony_ci if (!pegasus->rx_skb) 8628c2ecf20Sopenharmony_ci goto exit; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci set_registers(pegasus, EthID, 6, net->dev_addr); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, 8678c2ecf20Sopenharmony_ci usb_rcvbulkpipe(pegasus->usb, 1), 8688c2ecf20Sopenharmony_ci pegasus->rx_skb->data, PEGASUS_MTU, 8698c2ecf20Sopenharmony_ci read_bulk_callback, pegasus); 8708c2ecf20Sopenharmony_ci if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) { 8718c2ecf20Sopenharmony_ci if (res == -ENODEV) 8728c2ecf20Sopenharmony_ci netif_device_detach(pegasus->net); 8738c2ecf20Sopenharmony_ci netif_dbg(pegasus, ifup, net, "failed rx_urb, %d\n", res); 8748c2ecf20Sopenharmony_ci goto exit; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci usb_fill_int_urb(pegasus->intr_urb, pegasus->usb, 8788c2ecf20Sopenharmony_ci usb_rcvintpipe(pegasus->usb, 3), 8798c2ecf20Sopenharmony_ci pegasus->intr_buff, sizeof(pegasus->intr_buff), 8808c2ecf20Sopenharmony_ci intr_callback, pegasus, pegasus->intr_interval); 8818c2ecf20Sopenharmony_ci if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) { 8828c2ecf20Sopenharmony_ci if (res == -ENODEV) 8838c2ecf20Sopenharmony_ci netif_device_detach(pegasus->net); 8848c2ecf20Sopenharmony_ci netif_dbg(pegasus, ifup, net, "failed intr_urb, %d\n", res); 8858c2ecf20Sopenharmony_ci usb_kill_urb(pegasus->rx_urb); 8868c2ecf20Sopenharmony_ci goto exit; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci res = enable_net_traffic(net, pegasus->usb); 8898c2ecf20Sopenharmony_ci if (res < 0) { 8908c2ecf20Sopenharmony_ci netif_dbg(pegasus, ifup, net, 8918c2ecf20Sopenharmony_ci "can't enable_net_traffic() - %d\n", res); 8928c2ecf20Sopenharmony_ci res = -EIO; 8938c2ecf20Sopenharmony_ci usb_kill_urb(pegasus->rx_urb); 8948c2ecf20Sopenharmony_ci usb_kill_urb(pegasus->intr_urb); 8958c2ecf20Sopenharmony_ci goto exit; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci set_carrier(net); 8988c2ecf20Sopenharmony_ci netif_start_queue(net); 8998c2ecf20Sopenharmony_ci netif_dbg(pegasus, ifup, net, "open\n"); 9008c2ecf20Sopenharmony_ci res = 0; 9018c2ecf20Sopenharmony_ciexit: 9028c2ecf20Sopenharmony_ci return res; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic int pegasus_close(struct net_device *net) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(net); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci netif_stop_queue(net); 9108c2ecf20Sopenharmony_ci if (!(pegasus->flags & PEGASUS_UNPLUG)) 9118c2ecf20Sopenharmony_ci disable_net_traffic(pegasus); 9128c2ecf20Sopenharmony_ci tasklet_kill(&pegasus->rx_tl); 9138c2ecf20Sopenharmony_ci unlink_all_urbs(pegasus); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci return 0; 9168c2ecf20Sopenharmony_ci} 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_cistatic void pegasus_get_drvinfo(struct net_device *dev, 9198c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci strlcpy(info->driver, driver_name, sizeof(info->driver)); 9248c2ecf20Sopenharmony_ci strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); 9258c2ecf20Sopenharmony_ci usb_make_path(pegasus->usb, info->bus_info, sizeof(info->bus_info)); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci/* also handles three patterns of some kind in hardware */ 9298c2ecf20Sopenharmony_ci#define WOL_SUPPORTED (WAKE_MAGIC|WAKE_PHY) 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic void 9328c2ecf20Sopenharmony_cipegasus_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci wol->supported = WAKE_MAGIC | WAKE_PHY; 9378c2ecf20Sopenharmony_ci wol->wolopts = pegasus->wolopts; 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic int 9418c2ecf20Sopenharmony_cipegasus_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 9448c2ecf20Sopenharmony_ci u8 reg78 = 0x04; 9458c2ecf20Sopenharmony_ci int ret; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (wol->wolopts & ~WOL_SUPPORTED) 9488c2ecf20Sopenharmony_ci return -EINVAL; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) 9518c2ecf20Sopenharmony_ci reg78 |= 0x80; 9528c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_PHY) 9538c2ecf20Sopenharmony_ci reg78 |= 0x40; 9548c2ecf20Sopenharmony_ci /* FIXME this 0x10 bit still needs to get set in the chip... */ 9558c2ecf20Sopenharmony_ci if (wol->wolopts) 9568c2ecf20Sopenharmony_ci pegasus->eth_regs[0] |= 0x10; 9578c2ecf20Sopenharmony_ci else 9588c2ecf20Sopenharmony_ci pegasus->eth_regs[0] &= ~0x10; 9598c2ecf20Sopenharmony_ci pegasus->wolopts = wol->wolopts; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci ret = set_register(pegasus, WakeupControl, reg78); 9628c2ecf20Sopenharmony_ci if (!ret) 9638c2ecf20Sopenharmony_ci ret = device_set_wakeup_enable(&pegasus->usb->dev, 9648c2ecf20Sopenharmony_ci wol->wolopts); 9658c2ecf20Sopenharmony_ci return ret; 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic inline void pegasus_reset_wol(struct net_device *dev) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci struct ethtool_wolinfo wol; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci memset(&wol, 0, sizeof wol); 9738c2ecf20Sopenharmony_ci (void) pegasus_set_wol(dev, &wol); 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic int 9778c2ecf20Sopenharmony_cipegasus_get_link_ksettings(struct net_device *dev, 9788c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *ecmd) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci pegasus_t *pegasus; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci pegasus = netdev_priv(dev); 9838c2ecf20Sopenharmony_ci mii_ethtool_get_link_ksettings(&pegasus->mii, ecmd); 9848c2ecf20Sopenharmony_ci return 0; 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistatic int 9888c2ecf20Sopenharmony_cipegasus_set_link_ksettings(struct net_device *dev, 9898c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *ecmd) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 9928c2ecf20Sopenharmony_ci return mii_ethtool_set_link_ksettings(&pegasus->mii, ecmd); 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic int pegasus_nway_reset(struct net_device *dev) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 9988c2ecf20Sopenharmony_ci return mii_nway_restart(&pegasus->mii); 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_cistatic u32 pegasus_get_link(struct net_device *dev) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 10048c2ecf20Sopenharmony_ci return mii_link_ok(&pegasus->mii); 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic u32 pegasus_get_msglevel(struct net_device *dev) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 10108c2ecf20Sopenharmony_ci return pegasus->msg_enable; 10118c2ecf20Sopenharmony_ci} 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_cistatic void pegasus_set_msglevel(struct net_device *dev, u32 v) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(dev); 10168c2ecf20Sopenharmony_ci pegasus->msg_enable = v; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic const struct ethtool_ops ops = { 10208c2ecf20Sopenharmony_ci .get_drvinfo = pegasus_get_drvinfo, 10218c2ecf20Sopenharmony_ci .nway_reset = pegasus_nway_reset, 10228c2ecf20Sopenharmony_ci .get_link = pegasus_get_link, 10238c2ecf20Sopenharmony_ci .get_msglevel = pegasus_get_msglevel, 10248c2ecf20Sopenharmony_ci .set_msglevel = pegasus_set_msglevel, 10258c2ecf20Sopenharmony_ci .get_wol = pegasus_get_wol, 10268c2ecf20Sopenharmony_ci .set_wol = pegasus_set_wol, 10278c2ecf20Sopenharmony_ci .get_link_ksettings = pegasus_get_link_ksettings, 10288c2ecf20Sopenharmony_ci .set_link_ksettings = pegasus_set_link_ksettings, 10298c2ecf20Sopenharmony_ci}; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cistatic int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci __u16 *data = (__u16 *) &rq->ifr_ifru; 10348c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(net); 10358c2ecf20Sopenharmony_ci int res; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci switch (cmd) { 10388c2ecf20Sopenharmony_ci case SIOCDEVPRIVATE: 10398c2ecf20Sopenharmony_ci data[0] = pegasus->phy; 10408c2ecf20Sopenharmony_ci fallthrough; 10418c2ecf20Sopenharmony_ci case SIOCDEVPRIVATE + 1: 10428c2ecf20Sopenharmony_ci res = read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]); 10438c2ecf20Sopenharmony_ci break; 10448c2ecf20Sopenharmony_ci case SIOCDEVPRIVATE + 2: 10458c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 10468c2ecf20Sopenharmony_ci return -EPERM; 10478c2ecf20Sopenharmony_ci write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, &data[2]); 10488c2ecf20Sopenharmony_ci res = 0; 10498c2ecf20Sopenharmony_ci break; 10508c2ecf20Sopenharmony_ci default: 10518c2ecf20Sopenharmony_ci res = -EOPNOTSUPP; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci return res; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic void pegasus_set_multicast(struct net_device *net) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci pegasus_t *pegasus = netdev_priv(net); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (net->flags & IFF_PROMISC) { 10618c2ecf20Sopenharmony_ci pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS; 10628c2ecf20Sopenharmony_ci netif_info(pegasus, link, net, "Promiscuous mode enabled\n"); 10638c2ecf20Sopenharmony_ci } else if (!netdev_mc_empty(net) || (net->flags & IFF_ALLMULTI)) { 10648c2ecf20Sopenharmony_ci pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST; 10658c2ecf20Sopenharmony_ci pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; 10668c2ecf20Sopenharmony_ci netif_dbg(pegasus, link, net, "set allmulti\n"); 10678c2ecf20Sopenharmony_ci } else { 10688c2ecf20Sopenharmony_ci pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST; 10698c2ecf20Sopenharmony_ci pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci update_eth_regs_async(pegasus); 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_cistatic __u8 mii_phy_probe(pegasus_t *pegasus) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci int i, ret; 10778c2ecf20Sopenharmony_ci __u16 tmp; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) { 10808c2ecf20Sopenharmony_ci ret = read_mii_word(pegasus, i, MII_BMSR, &tmp); 10818c2ecf20Sopenharmony_ci if (ret < 0) 10828c2ecf20Sopenharmony_ci goto fail; 10838c2ecf20Sopenharmony_ci if (tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0) 10848c2ecf20Sopenharmony_ci continue; 10858c2ecf20Sopenharmony_ci else 10868c2ecf20Sopenharmony_ci return i; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_cifail: 10898c2ecf20Sopenharmony_ci return 0xff; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic inline void setup_pegasus_II(pegasus_t *pegasus) 10938c2ecf20Sopenharmony_ci{ 10948c2ecf20Sopenharmony_ci int ret; 10958c2ecf20Sopenharmony_ci __u8 data = 0xa5; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci set_register(pegasus, Reg1d, 0); 10988c2ecf20Sopenharmony_ci set_register(pegasus, Reg7b, 1); 10998c2ecf20Sopenharmony_ci msleep(100); 11008c2ecf20Sopenharmony_ci if ((pegasus->features & HAS_HOME_PNA) && mii_mode) 11018c2ecf20Sopenharmony_ci set_register(pegasus, Reg7b, 0); 11028c2ecf20Sopenharmony_ci else 11038c2ecf20Sopenharmony_ci set_register(pegasus, Reg7b, 2); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci set_register(pegasus, 0x83, data); 11068c2ecf20Sopenharmony_ci ret = get_registers(pegasus, 0x83, 1, &data); 11078c2ecf20Sopenharmony_ci if (ret < 0) 11088c2ecf20Sopenharmony_ci goto fail; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci if (data == 0xa5) 11118c2ecf20Sopenharmony_ci pegasus->chip = 0x8513; 11128c2ecf20Sopenharmony_ci else 11138c2ecf20Sopenharmony_ci pegasus->chip = 0; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci set_register(pegasus, 0x80, 0xc0); 11168c2ecf20Sopenharmony_ci set_register(pegasus, 0x83, 0xff); 11178c2ecf20Sopenharmony_ci set_register(pegasus, 0x84, 0x01); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (pegasus->features & HAS_HOME_PNA && mii_mode) 11208c2ecf20Sopenharmony_ci set_register(pegasus, Reg81, 6); 11218c2ecf20Sopenharmony_ci else 11228c2ecf20Sopenharmony_ci set_register(pegasus, Reg81, 2); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci return; 11258c2ecf20Sopenharmony_cifail: 11268c2ecf20Sopenharmony_ci netif_dbg(pegasus, drv, pegasus->net, "%s failed\n", __func__); 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic void check_carrier(struct work_struct *work) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci pegasus_t *pegasus = container_of(work, pegasus_t, carrier_check.work); 11328c2ecf20Sopenharmony_ci set_carrier(pegasus->net); 11338c2ecf20Sopenharmony_ci if (!(pegasus->flags & PEGASUS_UNPLUG)) { 11348c2ecf20Sopenharmony_ci queue_delayed_work(system_long_wq, &pegasus->carrier_check, 11358c2ecf20Sopenharmony_ci CARRIER_CHECK_DELAY); 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic int pegasus_blacklisted(struct usb_device *udev) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct usb_device_descriptor *udd = &udev->descriptor; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci /* Special quirk to keep the driver from handling the Belkin Bluetooth 11448c2ecf20Sopenharmony_ci * dongle which happens to have the same ID. 11458c2ecf20Sopenharmony_ci */ 11468c2ecf20Sopenharmony_ci if ((udd->idVendor == cpu_to_le16(VENDOR_BELKIN)) && 11478c2ecf20Sopenharmony_ci (udd->idProduct == cpu_to_le16(0x0121)) && 11488c2ecf20Sopenharmony_ci (udd->bDeviceClass == USB_CLASS_WIRELESS_CONTROLLER) && 11498c2ecf20Sopenharmony_ci (udd->bDeviceProtocol == 1)) 11508c2ecf20Sopenharmony_ci return 1; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci return 0; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cistatic int pegasus_probe(struct usb_interface *intf, 11568c2ecf20Sopenharmony_ci const struct usb_device_id *id) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci struct usb_device *dev = interface_to_usbdev(intf); 11598c2ecf20Sopenharmony_ci struct net_device *net; 11608c2ecf20Sopenharmony_ci pegasus_t *pegasus; 11618c2ecf20Sopenharmony_ci int dev_index = id - pegasus_ids; 11628c2ecf20Sopenharmony_ci int res = -ENOMEM; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci if (pegasus_blacklisted(dev)) 11658c2ecf20Sopenharmony_ci return -ENODEV; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci net = alloc_etherdev(sizeof(struct pegasus)); 11688c2ecf20Sopenharmony_ci if (!net) 11698c2ecf20Sopenharmony_ci goto out; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci pegasus = netdev_priv(net); 11728c2ecf20Sopenharmony_ci pegasus->dev_index = dev_index; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci res = alloc_urbs(pegasus); 11758c2ecf20Sopenharmony_ci if (res < 0) { 11768c2ecf20Sopenharmony_ci dev_err(&intf->dev, "can't allocate %s\n", "urbs"); 11778c2ecf20Sopenharmony_ci goto out1; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci tasklet_init(&pegasus->rx_tl, rx_fixup, (unsigned long) pegasus); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&pegasus->carrier_check, check_carrier); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci pegasus->intf = intf; 11858c2ecf20Sopenharmony_ci pegasus->usb = dev; 11868c2ecf20Sopenharmony_ci pegasus->net = net; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci net->watchdog_timeo = PEGASUS_TX_TIMEOUT; 11908c2ecf20Sopenharmony_ci net->netdev_ops = &pegasus_netdev_ops; 11918c2ecf20Sopenharmony_ci net->ethtool_ops = &ops; 11928c2ecf20Sopenharmony_ci pegasus->mii.dev = net; 11938c2ecf20Sopenharmony_ci pegasus->mii.mdio_read = mdio_read; 11948c2ecf20Sopenharmony_ci pegasus->mii.mdio_write = mdio_write; 11958c2ecf20Sopenharmony_ci pegasus->mii.phy_id_mask = 0x1f; 11968c2ecf20Sopenharmony_ci pegasus->mii.reg_num_mask = 0x1f; 11978c2ecf20Sopenharmony_ci pegasus->msg_enable = netif_msg_init(msg_level, NETIF_MSG_DRV 11988c2ecf20Sopenharmony_ci | NETIF_MSG_PROBE | NETIF_MSG_LINK); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci pegasus->features = usb_dev_id[dev_index].private; 12018c2ecf20Sopenharmony_ci res = get_interrupt_interval(pegasus); 12028c2ecf20Sopenharmony_ci if (res) 12038c2ecf20Sopenharmony_ci goto out2; 12048c2ecf20Sopenharmony_ci if (reset_mac(pegasus)) { 12058c2ecf20Sopenharmony_ci dev_err(&intf->dev, "can't reset MAC\n"); 12068c2ecf20Sopenharmony_ci res = -EIO; 12078c2ecf20Sopenharmony_ci goto out2; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci set_ethernet_addr(pegasus); 12108c2ecf20Sopenharmony_ci if (pegasus->features & PEGASUS_II) { 12118c2ecf20Sopenharmony_ci dev_info(&intf->dev, "setup Pegasus II specific registers\n"); 12128c2ecf20Sopenharmony_ci setup_pegasus_II(pegasus); 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci pegasus->phy = mii_phy_probe(pegasus); 12158c2ecf20Sopenharmony_ci if (pegasus->phy == 0xff) { 12168c2ecf20Sopenharmony_ci dev_warn(&intf->dev, "can't locate MII phy, using default\n"); 12178c2ecf20Sopenharmony_ci pegasus->phy = 1; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci pegasus->mii.phy_id = pegasus->phy; 12208c2ecf20Sopenharmony_ci usb_set_intfdata(intf, pegasus); 12218c2ecf20Sopenharmony_ci SET_NETDEV_DEV(net, &intf->dev); 12228c2ecf20Sopenharmony_ci pegasus_reset_wol(net); 12238c2ecf20Sopenharmony_ci res = register_netdev(net); 12248c2ecf20Sopenharmony_ci if (res) 12258c2ecf20Sopenharmony_ci goto out3; 12268c2ecf20Sopenharmony_ci queue_delayed_work(system_long_wq, &pegasus->carrier_check, 12278c2ecf20Sopenharmony_ci CARRIER_CHECK_DELAY); 12288c2ecf20Sopenharmony_ci dev_info(&intf->dev, "%s, %s, %pM\n", net->name, 12298c2ecf20Sopenharmony_ci usb_dev_id[dev_index].name, net->dev_addr); 12308c2ecf20Sopenharmony_ci return 0; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ciout3: 12338c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 12348c2ecf20Sopenharmony_ciout2: 12358c2ecf20Sopenharmony_ci free_all_urbs(pegasus); 12368c2ecf20Sopenharmony_ciout1: 12378c2ecf20Sopenharmony_ci free_netdev(net); 12388c2ecf20Sopenharmony_ciout: 12398c2ecf20Sopenharmony_ci return res; 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cistatic void pegasus_disconnect(struct usb_interface *intf) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci struct pegasus *pegasus = usb_get_intfdata(intf); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 12478c2ecf20Sopenharmony_ci if (!pegasus) { 12488c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "unregistering non-bound device?\n"); 12498c2ecf20Sopenharmony_ci return; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci pegasus->flags |= PEGASUS_UNPLUG; 12538c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&pegasus->carrier_check); 12548c2ecf20Sopenharmony_ci unregister_netdev(pegasus->net); 12558c2ecf20Sopenharmony_ci unlink_all_urbs(pegasus); 12568c2ecf20Sopenharmony_ci free_all_urbs(pegasus); 12578c2ecf20Sopenharmony_ci if (pegasus->rx_skb != NULL) { 12588c2ecf20Sopenharmony_ci dev_kfree_skb(pegasus->rx_skb); 12598c2ecf20Sopenharmony_ci pegasus->rx_skb = NULL; 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci free_netdev(pegasus->net); 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_cistatic int pegasus_suspend(struct usb_interface *intf, pm_message_t message) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct pegasus *pegasus = usb_get_intfdata(intf); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci netif_device_detach(pegasus->net); 12698c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&pegasus->carrier_check); 12708c2ecf20Sopenharmony_ci if (netif_running(pegasus->net)) { 12718c2ecf20Sopenharmony_ci usb_kill_urb(pegasus->rx_urb); 12728c2ecf20Sopenharmony_ci usb_kill_urb(pegasus->intr_urb); 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci return 0; 12758c2ecf20Sopenharmony_ci} 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic int pegasus_resume(struct usb_interface *intf) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci struct pegasus *pegasus = usb_get_intfdata(intf); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci netif_device_attach(pegasus->net); 12828c2ecf20Sopenharmony_ci if (netif_running(pegasus->net)) { 12838c2ecf20Sopenharmony_ci pegasus->rx_urb->status = 0; 12848c2ecf20Sopenharmony_ci pegasus->rx_urb->actual_length = 0; 12858c2ecf20Sopenharmony_ci read_bulk_callback(pegasus->rx_urb); 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci pegasus->intr_urb->status = 0; 12888c2ecf20Sopenharmony_ci pegasus->intr_urb->actual_length = 0; 12898c2ecf20Sopenharmony_ci intr_callback(pegasus->intr_urb); 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci queue_delayed_work(system_long_wq, &pegasus->carrier_check, 12928c2ecf20Sopenharmony_ci CARRIER_CHECK_DELAY); 12938c2ecf20Sopenharmony_ci return 0; 12948c2ecf20Sopenharmony_ci} 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_cistatic const struct net_device_ops pegasus_netdev_ops = { 12978c2ecf20Sopenharmony_ci .ndo_open = pegasus_open, 12988c2ecf20Sopenharmony_ci .ndo_stop = pegasus_close, 12998c2ecf20Sopenharmony_ci .ndo_do_ioctl = pegasus_ioctl, 13008c2ecf20Sopenharmony_ci .ndo_start_xmit = pegasus_start_xmit, 13018c2ecf20Sopenharmony_ci .ndo_set_rx_mode = pegasus_set_multicast, 13028c2ecf20Sopenharmony_ci .ndo_tx_timeout = pegasus_tx_timeout, 13038c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 13048c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 13058c2ecf20Sopenharmony_ci}; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_cistatic struct usb_driver pegasus_driver = { 13088c2ecf20Sopenharmony_ci .name = driver_name, 13098c2ecf20Sopenharmony_ci .probe = pegasus_probe, 13108c2ecf20Sopenharmony_ci .disconnect = pegasus_disconnect, 13118c2ecf20Sopenharmony_ci .id_table = pegasus_ids, 13128c2ecf20Sopenharmony_ci .suspend = pegasus_suspend, 13138c2ecf20Sopenharmony_ci .resume = pegasus_resume, 13148c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 13158c2ecf20Sopenharmony_ci}; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_cistatic void __init parse_id(char *id) 13188c2ecf20Sopenharmony_ci{ 13198c2ecf20Sopenharmony_ci unsigned int vendor_id = 0, device_id = 0, flags = 0, i = 0; 13208c2ecf20Sopenharmony_ci char *token, *name = NULL; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if ((token = strsep(&id, ":")) != NULL) 13238c2ecf20Sopenharmony_ci name = token; 13248c2ecf20Sopenharmony_ci /* name now points to a null terminated string*/ 13258c2ecf20Sopenharmony_ci if ((token = strsep(&id, ":")) != NULL) 13268c2ecf20Sopenharmony_ci vendor_id = simple_strtoul(token, NULL, 16); 13278c2ecf20Sopenharmony_ci if ((token = strsep(&id, ":")) != NULL) 13288c2ecf20Sopenharmony_ci device_id = simple_strtoul(token, NULL, 16); 13298c2ecf20Sopenharmony_ci flags = simple_strtoul(id, NULL, 16); 13308c2ecf20Sopenharmony_ci pr_info("%s: new device %s, vendor ID 0x%04x, device ID 0x%04x, flags: 0x%x\n", 13318c2ecf20Sopenharmony_ci driver_name, name, vendor_id, device_id, flags); 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci if (vendor_id > 0x10000 || vendor_id == 0) 13348c2ecf20Sopenharmony_ci return; 13358c2ecf20Sopenharmony_ci if (device_id > 0x10000 || device_id == 0) 13368c2ecf20Sopenharmony_ci return; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci for (i = 0; usb_dev_id[i].name; i++); 13398c2ecf20Sopenharmony_ci usb_dev_id[i].name = name; 13408c2ecf20Sopenharmony_ci usb_dev_id[i].vendor = vendor_id; 13418c2ecf20Sopenharmony_ci usb_dev_id[i].device = device_id; 13428c2ecf20Sopenharmony_ci usb_dev_id[i].private = flags; 13438c2ecf20Sopenharmony_ci pegasus_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE; 13448c2ecf20Sopenharmony_ci pegasus_ids[i].idVendor = vendor_id; 13458c2ecf20Sopenharmony_ci pegasus_ids[i].idProduct = device_id; 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic int __init pegasus_init(void) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION); 13518c2ecf20Sopenharmony_ci if (devid) 13528c2ecf20Sopenharmony_ci parse_id(devid); 13538c2ecf20Sopenharmony_ci return usb_register(&pegasus_driver); 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic void __exit pegasus_exit(void) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci usb_deregister(&pegasus_driver); 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cimodule_init(pegasus_init); 13628c2ecf20Sopenharmony_cimodule_exit(pegasus_exit); 1363