18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Net1080 based USB host-to-host cables 48c2ecf20Sopenharmony_ci * Copyright (C) 2000-2005 by David Brownell 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci// #define DEBUG // error path messages, extra info 88c2ecf20Sopenharmony_ci// #define VERBOSE // more; success messages 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 148c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 158c2ecf20Sopenharmony_ci#include <linux/mii.h> 168c2ecf20Sopenharmony_ci#include <linux/usb.h> 178c2ecf20Sopenharmony_ci#include <linux/usb/usbnet.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * Netchip 1080 driver ... http://www.netchip.com 258c2ecf20Sopenharmony_ci * (Sept 2004: End-of-life announcement has been sent.) 268c2ecf20Sopenharmony_ci * Used in (some) LapLink cables 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define frame_errors data[1] 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci * NetChip framing of ethernet packets, supporting additional error 338c2ecf20Sopenharmony_ci * checks for links that may drop bulk packets from inside messages. 348c2ecf20Sopenharmony_ci * Odd USB length == always short read for last usb packet. 358c2ecf20Sopenharmony_ci * - nc_header 368c2ecf20Sopenharmony_ci * - Ethernet header (14 bytes) 378c2ecf20Sopenharmony_ci * - payload 388c2ecf20Sopenharmony_ci * - (optional padding byte, if needed so length becomes odd) 398c2ecf20Sopenharmony_ci * - nc_trailer 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * This framing is to be avoided for non-NetChip devices. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistruct nc_header { // packed: 458c2ecf20Sopenharmony_ci __le16 hdr_len; // sizeof nc_header (LE, all) 468c2ecf20Sopenharmony_ci __le16 packet_len; // payload size (including ethhdr) 478c2ecf20Sopenharmony_ci __le16 packet_id; // detects dropped packets 488c2ecf20Sopenharmony_ci#define MIN_HEADER 6 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci // all else is optional, and must start with: 518c2ecf20Sopenharmony_ci // __le16 vendorId; // from usb-if 528c2ecf20Sopenharmony_ci // __le16 productId; 538c2ecf20Sopenharmony_ci} __packed; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define PAD_BYTE ((unsigned char)0xAC) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct nc_trailer { 588c2ecf20Sopenharmony_ci __le16 packet_id; 598c2ecf20Sopenharmony_ci} __packed; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci// packets may use FLAG_FRAMING_NC and optional pad 628c2ecf20Sopenharmony_ci#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \ 638c2ecf20Sopenharmony_ci + sizeof (struct ethhdr) \ 648c2ecf20Sopenharmony_ci + (mtu) \ 658c2ecf20Sopenharmony_ci + 1 \ 668c2ecf20Sopenharmony_ci + sizeof (struct nc_trailer)) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define MIN_FRAMED FRAMED_SIZE(0) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* packets _could_ be up to 64KB... */ 718c2ecf20Sopenharmony_ci#define NC_MAX_PACKET 32767 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * Zero means no timeout; else, how long a 64 byte bulk packet may be queued 768c2ecf20Sopenharmony_ci * before the hardware drops it. If that's done, the driver will need to 778c2ecf20Sopenharmony_ci * frame network packets to guard against the dropped USB packets. The win32 788c2ecf20Sopenharmony_ci * driver sets this for both sides of the link. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci#define NC_READ_TTL_MS ((u8)255) // ms 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* 838c2ecf20Sopenharmony_ci * We ignore most registers and EEPROM contents. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci#define REG_USBCTL ((u8)0x04) 868c2ecf20Sopenharmony_ci#define REG_TTL ((u8)0x10) 878c2ecf20Sopenharmony_ci#define REG_STATUS ((u8)0x11) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* 908c2ecf20Sopenharmony_ci * Vendor specific requests to read/write data 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci#define REQUEST_REGISTER ((u8)0x10) 938c2ecf20Sopenharmony_ci#define REQUEST_EEPROM ((u8)0x11) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int 968c2ecf20Sopenharmony_cinc_vendor_read(struct usbnet *dev, u8 req, u8 regnum, u16 *retval_ptr) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int status = usbnet_read_cmd(dev, req, 998c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | 1008c2ecf20Sopenharmony_ci USB_RECIP_DEVICE, 1018c2ecf20Sopenharmony_ci 0, regnum, retval_ptr, 1028c2ecf20Sopenharmony_ci sizeof *retval_ptr); 1038c2ecf20Sopenharmony_ci if (status > 0) 1048c2ecf20Sopenharmony_ci status = 0; 1058c2ecf20Sopenharmony_ci if (!status) 1068c2ecf20Sopenharmony_ci le16_to_cpus(retval_ptr); 1078c2ecf20Sopenharmony_ci return status; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic inline int 1118c2ecf20Sopenharmony_cinc_register_read(struct usbnet *dev, u8 regnum, u16 *retval_ptr) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci return nc_vendor_read(dev, REQUEST_REGISTER, regnum, retval_ptr); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void 1178c2ecf20Sopenharmony_cinc_vendor_write(struct usbnet *dev, u8 req, u8 regnum, u16 value) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci usbnet_write_cmd(dev, req, 1208c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1218c2ecf20Sopenharmony_ci value, regnum, NULL, 0); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic inline void 1258c2ecf20Sopenharmony_cinc_register_write(struct usbnet *dev, u8 regnum, u16 value) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci nc_vendor_write(dev, REQUEST_REGISTER, regnum, value); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#if 0 1328c2ecf20Sopenharmony_cistatic void nc_dump_registers(struct usbnet *dev) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci u8 reg; 1358c2ecf20Sopenharmony_ci u16 *vp = kmalloc(sizeof (u16)); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (!vp) 1388c2ecf20Sopenharmony_ci return; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "registers:\n"); 1418c2ecf20Sopenharmony_ci for (reg = 0; reg < 0x20; reg++) { 1428c2ecf20Sopenharmony_ci int retval; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci // reading some registers is trouble 1458c2ecf20Sopenharmony_ci if (reg >= 0x08 && reg <= 0xf) 1468c2ecf20Sopenharmony_ci continue; 1478c2ecf20Sopenharmony_ci if (reg >= 0x12 && reg <= 0x1e) 1488c2ecf20Sopenharmony_ci continue; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci retval = nc_register_read(dev, reg, vp); 1518c2ecf20Sopenharmony_ci if (retval < 0) 1528c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "reg [0x%x] ==> error %d\n", 1538c2ecf20Sopenharmony_ci reg, retval); 1548c2ecf20Sopenharmony_ci else 1558c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "reg [0x%x] = 0x%x\n", reg, *vp); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci kfree(vp); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci#endif 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* 1658c2ecf20Sopenharmony_ci * Control register 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci#define USBCTL_WRITABLE_MASK 0x1f0f 1698c2ecf20Sopenharmony_ci// bits 15-13 reserved, r/o 1708c2ecf20Sopenharmony_ci#define USBCTL_ENABLE_LANG (1 << 12) 1718c2ecf20Sopenharmony_ci#define USBCTL_ENABLE_MFGR (1 << 11) 1728c2ecf20Sopenharmony_ci#define USBCTL_ENABLE_PROD (1 << 10) 1738c2ecf20Sopenharmony_ci#define USBCTL_ENABLE_SERIAL (1 << 9) 1748c2ecf20Sopenharmony_ci#define USBCTL_ENABLE_DEFAULTS (1 << 8) 1758c2ecf20Sopenharmony_ci// bits 7-4 reserved, r/o 1768c2ecf20Sopenharmony_ci#define USBCTL_FLUSH_OTHER (1 << 3) 1778c2ecf20Sopenharmony_ci#define USBCTL_FLUSH_THIS (1 << 2) 1788c2ecf20Sopenharmony_ci#define USBCTL_DISCONN_OTHER (1 << 1) 1798c2ecf20Sopenharmony_ci#define USBCTL_DISCONN_THIS (1 << 0) 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic inline void nc_dump_usbctl(struct usbnet *dev, u16 usbctl) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci netif_dbg(dev, link, dev->net, 1848c2ecf20Sopenharmony_ci "net1080 %s-%s usbctl 0x%x:%s%s%s%s%s; this%s%s; other%s%s; r/o 0x%x\n", 1858c2ecf20Sopenharmony_ci dev->udev->bus->bus_name, dev->udev->devpath, 1868c2ecf20Sopenharmony_ci usbctl, 1878c2ecf20Sopenharmony_ci (usbctl & USBCTL_ENABLE_LANG) ? " lang" : "", 1888c2ecf20Sopenharmony_ci (usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "", 1898c2ecf20Sopenharmony_ci (usbctl & USBCTL_ENABLE_PROD) ? " prod" : "", 1908c2ecf20Sopenharmony_ci (usbctl & USBCTL_ENABLE_SERIAL) ? " serial" : "", 1918c2ecf20Sopenharmony_ci (usbctl & USBCTL_ENABLE_DEFAULTS) ? " defaults" : "", 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci (usbctl & USBCTL_FLUSH_THIS) ? " FLUSH" : "", 1948c2ecf20Sopenharmony_ci (usbctl & USBCTL_DISCONN_THIS) ? " DIS" : "", 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci (usbctl & USBCTL_FLUSH_OTHER) ? " FLUSH" : "", 1978c2ecf20Sopenharmony_ci (usbctl & USBCTL_DISCONN_OTHER) ? " DIS" : "", 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci usbctl & ~USBCTL_WRITABLE_MASK); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci/* 2058c2ecf20Sopenharmony_ci * Status register 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci#define STATUS_PORT_A (1 << 15) 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci#define STATUS_CONN_OTHER (1 << 14) 2118c2ecf20Sopenharmony_ci#define STATUS_SUSPEND_OTHER (1 << 13) 2128c2ecf20Sopenharmony_ci#define STATUS_MAILBOX_OTHER (1 << 12) 2138c2ecf20Sopenharmony_ci#define STATUS_PACKETS_OTHER(n) (((n) >> 8) & 0x03) 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci#define STATUS_CONN_THIS (1 << 6) 2168c2ecf20Sopenharmony_ci#define STATUS_SUSPEND_THIS (1 << 5) 2178c2ecf20Sopenharmony_ci#define STATUS_MAILBOX_THIS (1 << 4) 2188c2ecf20Sopenharmony_ci#define STATUS_PACKETS_THIS(n) (((n) >> 0) & 0x03) 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci#define STATUS_UNSPEC_MASK 0x0c8c 2218c2ecf20Sopenharmony_ci#define STATUS_NOISE_MASK ((u16)~(0x0303|STATUS_UNSPEC_MASK)) 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic inline void nc_dump_status(struct usbnet *dev, u16 status) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci netif_dbg(dev, link, dev->net, 2278c2ecf20Sopenharmony_ci "net1080 %s-%s status 0x%x: this (%c) PKT=%d%s%s%s; other PKT=%d%s%s%s; unspec 0x%x\n", 2288c2ecf20Sopenharmony_ci dev->udev->bus->bus_name, dev->udev->devpath, 2298c2ecf20Sopenharmony_ci status, 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci // XXX the packet counts don't seem right 2328c2ecf20Sopenharmony_ci // (1 at reset, not 0); maybe UNSPEC too 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci (status & STATUS_PORT_A) ? 'A' : 'B', 2358c2ecf20Sopenharmony_ci STATUS_PACKETS_THIS(status), 2368c2ecf20Sopenharmony_ci (status & STATUS_CONN_THIS) ? " CON" : "", 2378c2ecf20Sopenharmony_ci (status & STATUS_SUSPEND_THIS) ? " SUS" : "", 2388c2ecf20Sopenharmony_ci (status & STATUS_MAILBOX_THIS) ? " MBOX" : "", 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci STATUS_PACKETS_OTHER(status), 2418c2ecf20Sopenharmony_ci (status & STATUS_CONN_OTHER) ? " CON" : "", 2428c2ecf20Sopenharmony_ci (status & STATUS_SUSPEND_OTHER) ? " SUS" : "", 2438c2ecf20Sopenharmony_ci (status & STATUS_MAILBOX_OTHER) ? " MBOX" : "", 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci status & STATUS_UNSPEC_MASK); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/* 2518c2ecf20Sopenharmony_ci * TTL register 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci#define TTL_OTHER(ttl) (0x00ff & (ttl >> 8)) 2558c2ecf20Sopenharmony_ci#define MK_TTL(this,other) ((u16)(((other)<<8)|(0x00ff&(this)))) 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int net1080_reset(struct usbnet *dev) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci u16 usbctl, status, ttl; 2628c2ecf20Sopenharmony_ci u16 vp; 2638c2ecf20Sopenharmony_ci int retval; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci // nc_dump_registers(dev); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if ((retval = nc_register_read(dev, REG_STATUS, &vp)) < 0) { 2688c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "can't read %s-%s status: %d\n", 2698c2ecf20Sopenharmony_ci dev->udev->bus->bus_name, dev->udev->devpath, retval); 2708c2ecf20Sopenharmony_ci goto done; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci status = vp; 2738c2ecf20Sopenharmony_ci nc_dump_status(dev, status); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if ((retval = nc_register_read(dev, REG_USBCTL, &vp)) < 0) { 2768c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "can't read USBCTL, %d\n", retval); 2778c2ecf20Sopenharmony_ci goto done; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci usbctl = vp; 2808c2ecf20Sopenharmony_ci nc_dump_usbctl(dev, usbctl); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci nc_register_write(dev, REG_USBCTL, 2838c2ecf20Sopenharmony_ci USBCTL_FLUSH_THIS | USBCTL_FLUSH_OTHER); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if ((retval = nc_register_read(dev, REG_TTL, &vp)) < 0) { 2868c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "can't read TTL, %d\n", retval); 2878c2ecf20Sopenharmony_ci goto done; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci ttl = vp; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci nc_register_write(dev, REG_TTL, 2928c2ecf20Sopenharmony_ci MK_TTL(NC_READ_TTL_MS, TTL_OTHER(ttl)) ); 2938c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "assigned TTL, %d ms\n", NC_READ_TTL_MS); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci netif_info(dev, link, dev->net, "port %c, peer %sconnected\n", 2968c2ecf20Sopenharmony_ci (status & STATUS_PORT_A) ? 'A' : 'B', 2978c2ecf20Sopenharmony_ci (status & STATUS_CONN_OTHER) ? "" : "dis"); 2988c2ecf20Sopenharmony_ci retval = 0; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cidone: 3018c2ecf20Sopenharmony_ci return retval; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int net1080_check_connect(struct usbnet *dev) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci int retval; 3078c2ecf20Sopenharmony_ci u16 status; 3088c2ecf20Sopenharmony_ci u16 vp; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci retval = nc_register_read(dev, REG_STATUS, &vp); 3118c2ecf20Sopenharmony_ci status = vp; 3128c2ecf20Sopenharmony_ci if (retval != 0) { 3138c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "net1080_check_conn read - %d\n", retval); 3148c2ecf20Sopenharmony_ci return retval; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci if ((status & STATUS_CONN_OTHER) != STATUS_CONN_OTHER) 3178c2ecf20Sopenharmony_ci return -ENOLINK; 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void nc_ensure_sync(struct usbnet *dev) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci if (++dev->frame_errors <= 5) 3248c2ecf20Sopenharmony_ci return; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (usbnet_write_cmd_async(dev, REQUEST_REGISTER, 3278c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | 3288c2ecf20Sopenharmony_ci USB_RECIP_DEVICE, 3298c2ecf20Sopenharmony_ci USBCTL_FLUSH_THIS | 3308c2ecf20Sopenharmony_ci USBCTL_FLUSH_OTHER, 3318c2ecf20Sopenharmony_ci REG_USBCTL, NULL, 0)) 3328c2ecf20Sopenharmony_ci return; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, 3358c2ecf20Sopenharmony_ci "flush net1080; too many framing errors\n"); 3368c2ecf20Sopenharmony_ci dev->frame_errors = 0; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct nc_header *header; 3428c2ecf20Sopenharmony_ci struct nc_trailer *trailer; 3438c2ecf20Sopenharmony_ci u16 hdr_len, packet_len; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* This check is no longer done by usbnet */ 3468c2ecf20Sopenharmony_ci if (skb->len < dev->net->hard_header_len) 3478c2ecf20Sopenharmony_ci return 0; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (!(skb->len & 0x01)) { 3508c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "rx framesize %d range %d..%d mtu %d\n", 3518c2ecf20Sopenharmony_ci skb->len, dev->net->hard_header_len, dev->hard_mtu, 3528c2ecf20Sopenharmony_ci dev->net->mtu); 3538c2ecf20Sopenharmony_ci dev->net->stats.rx_frame_errors++; 3548c2ecf20Sopenharmony_ci nc_ensure_sync(dev); 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci header = (struct nc_header *) skb->data; 3598c2ecf20Sopenharmony_ci hdr_len = le16_to_cpup(&header->hdr_len); 3608c2ecf20Sopenharmony_ci packet_len = le16_to_cpup(&header->packet_len); 3618c2ecf20Sopenharmony_ci if (FRAMED_SIZE(packet_len) > NC_MAX_PACKET) { 3628c2ecf20Sopenharmony_ci dev->net->stats.rx_frame_errors++; 3638c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "packet too big, %d\n", packet_len); 3648c2ecf20Sopenharmony_ci nc_ensure_sync(dev); 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci } else if (hdr_len < MIN_HEADER) { 3678c2ecf20Sopenharmony_ci dev->net->stats.rx_frame_errors++; 3688c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "header too short, %d\n", hdr_len); 3698c2ecf20Sopenharmony_ci nc_ensure_sync(dev); 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci } else if (hdr_len > MIN_HEADER) { 3728c2ecf20Sopenharmony_ci // out of band data for us? 3738c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "header OOB, %d bytes\n", hdr_len - MIN_HEADER); 3748c2ecf20Sopenharmony_ci nc_ensure_sync(dev); 3758c2ecf20Sopenharmony_ci // switch (vendor/product ids) { ... } 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci skb_pull(skb, hdr_len); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci trailer = (struct nc_trailer *) 3808c2ecf20Sopenharmony_ci (skb->data + skb->len - sizeof *trailer); 3818c2ecf20Sopenharmony_ci skb_trim(skb, skb->len - sizeof *trailer); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if ((packet_len & 0x01) == 0) { 3848c2ecf20Sopenharmony_ci if (skb->data [packet_len] != PAD_BYTE) { 3858c2ecf20Sopenharmony_ci dev->net->stats.rx_frame_errors++; 3868c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "bad pad\n"); 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci skb_trim(skb, skb->len - 1); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci if (skb->len != packet_len) { 3928c2ecf20Sopenharmony_ci dev->net->stats.rx_frame_errors++; 3938c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "bad packet len %d (expected %d)\n", 3948c2ecf20Sopenharmony_ci skb->len, packet_len); 3958c2ecf20Sopenharmony_ci nc_ensure_sync(dev); 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci if (header->packet_id != get_unaligned(&trailer->packet_id)) { 3998c2ecf20Sopenharmony_ci dev->net->stats.rx_fifo_errors++; 4008c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "(2+ dropped) rx packet_id mismatch 0x%x 0x%x\n", 4018c2ecf20Sopenharmony_ci le16_to_cpu(header->packet_id), 4028c2ecf20Sopenharmony_ci le16_to_cpu(trailer->packet_id)); 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci#if 0 4068c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "frame <rx h %d p %d id %d\n", header->hdr_len, 4078c2ecf20Sopenharmony_ci header->packet_len, header->packet_id); 4088c2ecf20Sopenharmony_ci#endif 4098c2ecf20Sopenharmony_ci dev->frame_errors = 0; 4108c2ecf20Sopenharmony_ci return 1; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic struct sk_buff * 4148c2ecf20Sopenharmony_cinet1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct sk_buff *skb2; 4178c2ecf20Sopenharmony_ci struct nc_header *header = NULL; 4188c2ecf20Sopenharmony_ci struct nc_trailer *trailer = NULL; 4198c2ecf20Sopenharmony_ci int padlen = sizeof (struct nc_trailer); 4208c2ecf20Sopenharmony_ci int len = skb->len; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (!((len + padlen + sizeof (struct nc_header)) & 0x01)) 4238c2ecf20Sopenharmony_ci padlen++; 4248c2ecf20Sopenharmony_ci if (!skb_cloned(skb)) { 4258c2ecf20Sopenharmony_ci int headroom = skb_headroom(skb); 4268c2ecf20Sopenharmony_ci int tailroom = skb_tailroom(skb); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (padlen <= tailroom && 4298c2ecf20Sopenharmony_ci sizeof(struct nc_header) <= headroom) 4308c2ecf20Sopenharmony_ci /* There's enough head and tail room */ 4318c2ecf20Sopenharmony_ci goto encapsulate; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if ((sizeof (struct nc_header) + padlen) < 4348c2ecf20Sopenharmony_ci (headroom + tailroom)) { 4358c2ecf20Sopenharmony_ci /* There's enough total room, so just readjust */ 4368c2ecf20Sopenharmony_ci skb->data = memmove(skb->head 4378c2ecf20Sopenharmony_ci + sizeof (struct nc_header), 4388c2ecf20Sopenharmony_ci skb->data, skb->len); 4398c2ecf20Sopenharmony_ci skb_set_tail_pointer(skb, len); 4408c2ecf20Sopenharmony_ci goto encapsulate; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Create a new skb to use with the correct size */ 4458c2ecf20Sopenharmony_ci skb2 = skb_copy_expand(skb, 4468c2ecf20Sopenharmony_ci sizeof (struct nc_header), 4478c2ecf20Sopenharmony_ci padlen, 4488c2ecf20Sopenharmony_ci flags); 4498c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4508c2ecf20Sopenharmony_ci if (!skb2) 4518c2ecf20Sopenharmony_ci return skb2; 4528c2ecf20Sopenharmony_ci skb = skb2; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ciencapsulate: 4558c2ecf20Sopenharmony_ci /* header first */ 4568c2ecf20Sopenharmony_ci header = skb_push(skb, sizeof *header); 4578c2ecf20Sopenharmony_ci header->hdr_len = cpu_to_le16(sizeof (*header)); 4588c2ecf20Sopenharmony_ci header->packet_len = cpu_to_le16(len); 4598c2ecf20Sopenharmony_ci header->packet_id = cpu_to_le16((u16)dev->xid++); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* maybe pad; then trailer */ 4628c2ecf20Sopenharmony_ci if (!((skb->len + sizeof *trailer) & 0x01)) 4638c2ecf20Sopenharmony_ci skb_put_u8(skb, PAD_BYTE); 4648c2ecf20Sopenharmony_ci trailer = skb_put(skb, sizeof *trailer); 4658c2ecf20Sopenharmony_ci put_unaligned(header->packet_id, &trailer->packet_id); 4668c2ecf20Sopenharmony_ci#if 0 4678c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "frame >tx h %d p %d id %d\n", 4688c2ecf20Sopenharmony_ci header->hdr_len, header->packet_len, 4698c2ecf20Sopenharmony_ci header->packet_id); 4708c2ecf20Sopenharmony_ci#endif 4718c2ecf20Sopenharmony_ci return skb; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int net1080_bind(struct usbnet *dev, struct usb_interface *intf) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci unsigned extra = sizeof (struct nc_header) 4778c2ecf20Sopenharmony_ci + 1 4788c2ecf20Sopenharmony_ci + sizeof (struct nc_trailer); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci dev->net->hard_header_len += extra; 4818c2ecf20Sopenharmony_ci dev->rx_urb_size = dev->net->hard_header_len + dev->net->mtu; 4828c2ecf20Sopenharmony_ci dev->hard_mtu = NC_MAX_PACKET; 4838c2ecf20Sopenharmony_ci return usbnet_get_endpoints (dev, intf); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic const struct driver_info net1080_info = { 4878c2ecf20Sopenharmony_ci .description = "NetChip TurboCONNECT", 4888c2ecf20Sopenharmony_ci .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_NC, 4898c2ecf20Sopenharmony_ci .bind = net1080_bind, 4908c2ecf20Sopenharmony_ci .reset = net1080_reset, 4918c2ecf20Sopenharmony_ci .check_connect = net1080_check_connect, 4928c2ecf20Sopenharmony_ci .rx_fixup = net1080_rx_fixup, 4938c2ecf20Sopenharmony_ci .tx_fixup = net1080_tx_fixup, 4948c2ecf20Sopenharmony_ci}; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic const struct usb_device_id products [] = { 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci USB_DEVICE(0x0525, 0x1080), // NetChip ref design 4998c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &net1080_info, 5008c2ecf20Sopenharmony_ci}, { 5018c2ecf20Sopenharmony_ci USB_DEVICE(0x06D0, 0x0622), // Laplink Gold 5028c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &net1080_info, 5038c2ecf20Sopenharmony_ci}, 5048c2ecf20Sopenharmony_ci { }, // END 5058c2ecf20Sopenharmony_ci}; 5068c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic struct usb_driver net1080_driver = { 5098c2ecf20Sopenharmony_ci .name = "net1080", 5108c2ecf20Sopenharmony_ci .id_table = products, 5118c2ecf20Sopenharmony_ci .probe = usbnet_probe, 5128c2ecf20Sopenharmony_ci .disconnect = usbnet_disconnect, 5138c2ecf20Sopenharmony_ci .suspend = usbnet_suspend, 5148c2ecf20Sopenharmony_ci .resume = usbnet_resume, 5158c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 5168c2ecf20Sopenharmony_ci}; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cimodule_usb_driver(net1080_driver); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 5218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NetChip 1080 based USB Host-to-Host Links"); 5228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 523