162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Net1080 based USB host-to-host cables 462306a36Sopenharmony_ci * Copyright (C) 2000-2005 by David Brownell 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci// #define DEBUG // error path messages, extra info 862306a36Sopenharmony_ci// #define VERBOSE // more; success messages 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/netdevice.h> 1262306a36Sopenharmony_ci#include <linux/etherdevice.h> 1362306a36Sopenharmony_ci#include <linux/ethtool.h> 1462306a36Sopenharmony_ci#include <linux/workqueue.h> 1562306a36Sopenharmony_ci#include <linux/mii.h> 1662306a36Sopenharmony_ci#include <linux/usb.h> 1762306a36Sopenharmony_ci#include <linux/usb/usbnet.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/unaligned.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * Netchip 1080 driver ... http://www.netchip.com 2562306a36Sopenharmony_ci * (Sept 2004: End-of-life announcement has been sent.) 2662306a36Sopenharmony_ci * Used in (some) LapLink cables 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define frame_errors data[1] 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 3262306a36Sopenharmony_ci * NetChip framing of ethernet packets, supporting additional error 3362306a36Sopenharmony_ci * checks for links that may drop bulk packets from inside messages. 3462306a36Sopenharmony_ci * Odd USB length == always short read for last usb packet. 3562306a36Sopenharmony_ci * - nc_header 3662306a36Sopenharmony_ci * - Ethernet header (14 bytes) 3762306a36Sopenharmony_ci * - payload 3862306a36Sopenharmony_ci * - (optional padding byte, if needed so length becomes odd) 3962306a36Sopenharmony_ci * - nc_trailer 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * This framing is to be avoided for non-NetChip devices. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct nc_header { // packed: 4562306a36Sopenharmony_ci __le16 hdr_len; // sizeof nc_header (LE, all) 4662306a36Sopenharmony_ci __le16 packet_len; // payload size (including ethhdr) 4762306a36Sopenharmony_ci __le16 packet_id; // detects dropped packets 4862306a36Sopenharmony_ci#define MIN_HEADER 6 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci // all else is optional, and must start with: 5162306a36Sopenharmony_ci // __le16 vendorId; // from usb-if 5262306a36Sopenharmony_ci // __le16 productId; 5362306a36Sopenharmony_ci} __packed; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define PAD_BYTE ((unsigned char)0xAC) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct nc_trailer { 5862306a36Sopenharmony_ci __le16 packet_id; 5962306a36Sopenharmony_ci} __packed; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci// packets may use FLAG_FRAMING_NC and optional pad 6262306a36Sopenharmony_ci#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \ 6362306a36Sopenharmony_ci + sizeof (struct ethhdr) \ 6462306a36Sopenharmony_ci + (mtu) \ 6562306a36Sopenharmony_ci + 1 \ 6662306a36Sopenharmony_ci + sizeof (struct nc_trailer)) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define MIN_FRAMED FRAMED_SIZE(0) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* packets _could_ be up to 64KB... */ 7162306a36Sopenharmony_ci#define NC_MAX_PACKET 32767 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * Zero means no timeout; else, how long a 64 byte bulk packet may be queued 7662306a36Sopenharmony_ci * before the hardware drops it. If that's done, the driver will need to 7762306a36Sopenharmony_ci * frame network packets to guard against the dropped USB packets. The win32 7862306a36Sopenharmony_ci * driver sets this for both sides of the link. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci#define NC_READ_TTL_MS ((u8)255) // ms 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* 8362306a36Sopenharmony_ci * We ignore most registers and EEPROM contents. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci#define REG_USBCTL ((u8)0x04) 8662306a36Sopenharmony_ci#define REG_TTL ((u8)0x10) 8762306a36Sopenharmony_ci#define REG_STATUS ((u8)0x11) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * Vendor specific requests to read/write data 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci#define REQUEST_REGISTER ((u8)0x10) 9362306a36Sopenharmony_ci#define REQUEST_EEPROM ((u8)0x11) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int 9662306a36Sopenharmony_cinc_vendor_read(struct usbnet *dev, u8 req, u8 regnum, u16 *retval_ptr) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci int status = usbnet_read_cmd(dev, req, 9962306a36Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | 10062306a36Sopenharmony_ci USB_RECIP_DEVICE, 10162306a36Sopenharmony_ci 0, regnum, retval_ptr, 10262306a36Sopenharmony_ci sizeof *retval_ptr); 10362306a36Sopenharmony_ci if (status > 0) 10462306a36Sopenharmony_ci status = 0; 10562306a36Sopenharmony_ci if (!status) 10662306a36Sopenharmony_ci le16_to_cpus(retval_ptr); 10762306a36Sopenharmony_ci return status; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic inline int 11162306a36Sopenharmony_cinc_register_read(struct usbnet *dev, u8 regnum, u16 *retval_ptr) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci return nc_vendor_read(dev, REQUEST_REGISTER, regnum, retval_ptr); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void 11762306a36Sopenharmony_cinc_vendor_write(struct usbnet *dev, u8 req, u8 regnum, u16 value) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci usbnet_write_cmd(dev, req, 12062306a36Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 12162306a36Sopenharmony_ci value, regnum, NULL, 0); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic inline void 12562306a36Sopenharmony_cinc_register_write(struct usbnet *dev, u8 regnum, u16 value) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci nc_vendor_write(dev, REQUEST_REGISTER, regnum, value); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#if 0 13262306a36Sopenharmony_cistatic void nc_dump_registers(struct usbnet *dev) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci u8 reg; 13562306a36Sopenharmony_ci u16 *vp = kmalloc(sizeof (u16)); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!vp) 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci netdev_dbg(dev->net, "registers:\n"); 14162306a36Sopenharmony_ci for (reg = 0; reg < 0x20; reg++) { 14262306a36Sopenharmony_ci int retval; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci // reading some registers is trouble 14562306a36Sopenharmony_ci if (reg >= 0x08 && reg <= 0xf) 14662306a36Sopenharmony_ci continue; 14762306a36Sopenharmony_ci if (reg >= 0x12 && reg <= 0x1e) 14862306a36Sopenharmony_ci continue; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci retval = nc_register_read(dev, reg, vp); 15162306a36Sopenharmony_ci if (retval < 0) 15262306a36Sopenharmony_ci netdev_dbg(dev->net, "reg [0x%x] ==> error %d\n", 15362306a36Sopenharmony_ci reg, retval); 15462306a36Sopenharmony_ci else 15562306a36Sopenharmony_ci netdev_dbg(dev->net, "reg [0x%x] = 0x%x\n", reg, *vp); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci kfree(vp); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci#endif 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* 16562306a36Sopenharmony_ci * Control register 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci#define USBCTL_WRITABLE_MASK 0x1f0f 16962306a36Sopenharmony_ci// bits 15-13 reserved, r/o 17062306a36Sopenharmony_ci#define USBCTL_ENABLE_LANG (1 << 12) 17162306a36Sopenharmony_ci#define USBCTL_ENABLE_MFGR (1 << 11) 17262306a36Sopenharmony_ci#define USBCTL_ENABLE_PROD (1 << 10) 17362306a36Sopenharmony_ci#define USBCTL_ENABLE_SERIAL (1 << 9) 17462306a36Sopenharmony_ci#define USBCTL_ENABLE_DEFAULTS (1 << 8) 17562306a36Sopenharmony_ci// bits 7-4 reserved, r/o 17662306a36Sopenharmony_ci#define USBCTL_FLUSH_OTHER (1 << 3) 17762306a36Sopenharmony_ci#define USBCTL_FLUSH_THIS (1 << 2) 17862306a36Sopenharmony_ci#define USBCTL_DISCONN_OTHER (1 << 1) 17962306a36Sopenharmony_ci#define USBCTL_DISCONN_THIS (1 << 0) 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic inline void nc_dump_usbctl(struct usbnet *dev, u16 usbctl) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci netif_dbg(dev, link, dev->net, 18462306a36Sopenharmony_ci "net1080 %s-%s usbctl 0x%x:%s%s%s%s%s; this%s%s; other%s%s; r/o 0x%x\n", 18562306a36Sopenharmony_ci dev->udev->bus->bus_name, dev->udev->devpath, 18662306a36Sopenharmony_ci usbctl, 18762306a36Sopenharmony_ci (usbctl & USBCTL_ENABLE_LANG) ? " lang" : "", 18862306a36Sopenharmony_ci (usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "", 18962306a36Sopenharmony_ci (usbctl & USBCTL_ENABLE_PROD) ? " prod" : "", 19062306a36Sopenharmony_ci (usbctl & USBCTL_ENABLE_SERIAL) ? " serial" : "", 19162306a36Sopenharmony_ci (usbctl & USBCTL_ENABLE_DEFAULTS) ? " defaults" : "", 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci (usbctl & USBCTL_FLUSH_THIS) ? " FLUSH" : "", 19462306a36Sopenharmony_ci (usbctl & USBCTL_DISCONN_THIS) ? " DIS" : "", 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci (usbctl & USBCTL_FLUSH_OTHER) ? " FLUSH" : "", 19762306a36Sopenharmony_ci (usbctl & USBCTL_DISCONN_OTHER) ? " DIS" : "", 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci usbctl & ~USBCTL_WRITABLE_MASK); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * Status register 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci#define STATUS_PORT_A (1 << 15) 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci#define STATUS_CONN_OTHER (1 << 14) 21162306a36Sopenharmony_ci#define STATUS_SUSPEND_OTHER (1 << 13) 21262306a36Sopenharmony_ci#define STATUS_MAILBOX_OTHER (1 << 12) 21362306a36Sopenharmony_ci#define STATUS_PACKETS_OTHER(n) (((n) >> 8) & 0x03) 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci#define STATUS_CONN_THIS (1 << 6) 21662306a36Sopenharmony_ci#define STATUS_SUSPEND_THIS (1 << 5) 21762306a36Sopenharmony_ci#define STATUS_MAILBOX_THIS (1 << 4) 21862306a36Sopenharmony_ci#define STATUS_PACKETS_THIS(n) (((n) >> 0) & 0x03) 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci#define STATUS_UNSPEC_MASK 0x0c8c 22162306a36Sopenharmony_ci#define STATUS_NOISE_MASK ((u16)~(0x0303|STATUS_UNSPEC_MASK)) 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic inline void nc_dump_status(struct usbnet *dev, u16 status) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci netif_dbg(dev, link, dev->net, 22762306a36Sopenharmony_ci "net1080 %s-%s status 0x%x: this (%c) PKT=%d%s%s%s; other PKT=%d%s%s%s; unspec 0x%x\n", 22862306a36Sopenharmony_ci dev->udev->bus->bus_name, dev->udev->devpath, 22962306a36Sopenharmony_ci status, 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci // XXX the packet counts don't seem right 23262306a36Sopenharmony_ci // (1 at reset, not 0); maybe UNSPEC too 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci (status & STATUS_PORT_A) ? 'A' : 'B', 23562306a36Sopenharmony_ci STATUS_PACKETS_THIS(status), 23662306a36Sopenharmony_ci (status & STATUS_CONN_THIS) ? " CON" : "", 23762306a36Sopenharmony_ci (status & STATUS_SUSPEND_THIS) ? " SUS" : "", 23862306a36Sopenharmony_ci (status & STATUS_MAILBOX_THIS) ? " MBOX" : "", 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci STATUS_PACKETS_OTHER(status), 24162306a36Sopenharmony_ci (status & STATUS_CONN_OTHER) ? " CON" : "", 24262306a36Sopenharmony_ci (status & STATUS_SUSPEND_OTHER) ? " SUS" : "", 24362306a36Sopenharmony_ci (status & STATUS_MAILBOX_OTHER) ? " MBOX" : "", 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci status & STATUS_UNSPEC_MASK); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* 25162306a36Sopenharmony_ci * TTL register 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci#define TTL_OTHER(ttl) (0x00ff & (ttl >> 8)) 25562306a36Sopenharmony_ci#define MK_TTL(this,other) ((u16)(((other)<<8)|(0x00ff&(this)))) 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int net1080_reset(struct usbnet *dev) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci u16 usbctl, status, ttl; 26262306a36Sopenharmony_ci u16 vp; 26362306a36Sopenharmony_ci int retval; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci // nc_dump_registers(dev); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if ((retval = nc_register_read(dev, REG_STATUS, &vp)) < 0) { 26862306a36Sopenharmony_ci netdev_dbg(dev->net, "can't read %s-%s status: %d\n", 26962306a36Sopenharmony_ci dev->udev->bus->bus_name, dev->udev->devpath, retval); 27062306a36Sopenharmony_ci goto done; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci status = vp; 27362306a36Sopenharmony_ci nc_dump_status(dev, status); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if ((retval = nc_register_read(dev, REG_USBCTL, &vp)) < 0) { 27662306a36Sopenharmony_ci netdev_dbg(dev->net, "can't read USBCTL, %d\n", retval); 27762306a36Sopenharmony_ci goto done; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci usbctl = vp; 28062306a36Sopenharmony_ci nc_dump_usbctl(dev, usbctl); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci nc_register_write(dev, REG_USBCTL, 28362306a36Sopenharmony_ci USBCTL_FLUSH_THIS | USBCTL_FLUSH_OTHER); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if ((retval = nc_register_read(dev, REG_TTL, &vp)) < 0) { 28662306a36Sopenharmony_ci netdev_dbg(dev->net, "can't read TTL, %d\n", retval); 28762306a36Sopenharmony_ci goto done; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci ttl = vp; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci nc_register_write(dev, REG_TTL, 29262306a36Sopenharmony_ci MK_TTL(NC_READ_TTL_MS, TTL_OTHER(ttl)) ); 29362306a36Sopenharmony_ci netdev_dbg(dev->net, "assigned TTL, %d ms\n", NC_READ_TTL_MS); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci netif_info(dev, link, dev->net, "port %c, peer %sconnected\n", 29662306a36Sopenharmony_ci (status & STATUS_PORT_A) ? 'A' : 'B', 29762306a36Sopenharmony_ci (status & STATUS_CONN_OTHER) ? "" : "dis"); 29862306a36Sopenharmony_ci retval = 0; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cidone: 30162306a36Sopenharmony_ci return retval; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int net1080_check_connect(struct usbnet *dev) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci int retval; 30762306a36Sopenharmony_ci u16 status; 30862306a36Sopenharmony_ci u16 vp; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci retval = nc_register_read(dev, REG_STATUS, &vp); 31162306a36Sopenharmony_ci status = vp; 31262306a36Sopenharmony_ci if (retval != 0) { 31362306a36Sopenharmony_ci netdev_dbg(dev->net, "net1080_check_conn read - %d\n", retval); 31462306a36Sopenharmony_ci return retval; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci if ((status & STATUS_CONN_OTHER) != STATUS_CONN_OTHER) 31762306a36Sopenharmony_ci return -ENOLINK; 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void nc_ensure_sync(struct usbnet *dev) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci if (++dev->frame_errors <= 5) 32462306a36Sopenharmony_ci return; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (usbnet_write_cmd_async(dev, REQUEST_REGISTER, 32762306a36Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | 32862306a36Sopenharmony_ci USB_RECIP_DEVICE, 32962306a36Sopenharmony_ci USBCTL_FLUSH_THIS | 33062306a36Sopenharmony_ci USBCTL_FLUSH_OTHER, 33162306a36Sopenharmony_ci REG_USBCTL, NULL, 0)) 33262306a36Sopenharmony_ci return; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci netif_dbg(dev, rx_err, dev->net, 33562306a36Sopenharmony_ci "flush net1080; too many framing errors\n"); 33662306a36Sopenharmony_ci dev->frame_errors = 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct nc_header *header; 34262306a36Sopenharmony_ci struct nc_trailer *trailer; 34362306a36Sopenharmony_ci u16 hdr_len, packet_len; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* This check is no longer done by usbnet */ 34662306a36Sopenharmony_ci if (skb->len < dev->net->hard_header_len) 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!(skb->len & 0x01)) { 35062306a36Sopenharmony_ci netdev_dbg(dev->net, "rx framesize %d range %d..%d mtu %d\n", 35162306a36Sopenharmony_ci skb->len, dev->net->hard_header_len, dev->hard_mtu, 35262306a36Sopenharmony_ci dev->net->mtu); 35362306a36Sopenharmony_ci dev->net->stats.rx_frame_errors++; 35462306a36Sopenharmony_ci nc_ensure_sync(dev); 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci header = (struct nc_header *) skb->data; 35962306a36Sopenharmony_ci hdr_len = le16_to_cpup(&header->hdr_len); 36062306a36Sopenharmony_ci packet_len = le16_to_cpup(&header->packet_len); 36162306a36Sopenharmony_ci if (FRAMED_SIZE(packet_len) > NC_MAX_PACKET) { 36262306a36Sopenharmony_ci dev->net->stats.rx_frame_errors++; 36362306a36Sopenharmony_ci netdev_dbg(dev->net, "packet too big, %d\n", packet_len); 36462306a36Sopenharmony_ci nc_ensure_sync(dev); 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci } else if (hdr_len < MIN_HEADER) { 36762306a36Sopenharmony_ci dev->net->stats.rx_frame_errors++; 36862306a36Sopenharmony_ci netdev_dbg(dev->net, "header too short, %d\n", hdr_len); 36962306a36Sopenharmony_ci nc_ensure_sync(dev); 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci } else if (hdr_len > MIN_HEADER) { 37262306a36Sopenharmony_ci // out of band data for us? 37362306a36Sopenharmony_ci netdev_dbg(dev->net, "header OOB, %d bytes\n", hdr_len - MIN_HEADER); 37462306a36Sopenharmony_ci nc_ensure_sync(dev); 37562306a36Sopenharmony_ci // switch (vendor/product ids) { ... } 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci skb_pull(skb, hdr_len); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci trailer = (struct nc_trailer *) 38062306a36Sopenharmony_ci (skb->data + skb->len - sizeof *trailer); 38162306a36Sopenharmony_ci skb_trim(skb, skb->len - sizeof *trailer); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if ((packet_len & 0x01) == 0) { 38462306a36Sopenharmony_ci if (skb->data [packet_len] != PAD_BYTE) { 38562306a36Sopenharmony_ci dev->net->stats.rx_frame_errors++; 38662306a36Sopenharmony_ci netdev_dbg(dev->net, "bad pad\n"); 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci skb_trim(skb, skb->len - 1); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci if (skb->len != packet_len) { 39262306a36Sopenharmony_ci dev->net->stats.rx_frame_errors++; 39362306a36Sopenharmony_ci netdev_dbg(dev->net, "bad packet len %d (expected %d)\n", 39462306a36Sopenharmony_ci skb->len, packet_len); 39562306a36Sopenharmony_ci nc_ensure_sync(dev); 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci if (header->packet_id != get_unaligned(&trailer->packet_id)) { 39962306a36Sopenharmony_ci dev->net->stats.rx_fifo_errors++; 40062306a36Sopenharmony_ci netdev_dbg(dev->net, "(2+ dropped) rx packet_id mismatch 0x%x 0x%x\n", 40162306a36Sopenharmony_ci le16_to_cpu(header->packet_id), 40262306a36Sopenharmony_ci le16_to_cpu(trailer->packet_id)); 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci#if 0 40662306a36Sopenharmony_ci netdev_dbg(dev->net, "frame <rx h %d p %d id %d\n", header->hdr_len, 40762306a36Sopenharmony_ci header->packet_len, header->packet_id); 40862306a36Sopenharmony_ci#endif 40962306a36Sopenharmony_ci dev->frame_errors = 0; 41062306a36Sopenharmony_ci return 1; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic struct sk_buff * 41462306a36Sopenharmony_cinet1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct sk_buff *skb2; 41762306a36Sopenharmony_ci struct nc_header *header = NULL; 41862306a36Sopenharmony_ci struct nc_trailer *trailer = NULL; 41962306a36Sopenharmony_ci int padlen = sizeof (struct nc_trailer); 42062306a36Sopenharmony_ci int len = skb->len; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (!((len + padlen + sizeof (struct nc_header)) & 0x01)) 42362306a36Sopenharmony_ci padlen++; 42462306a36Sopenharmony_ci if (!skb_cloned(skb)) { 42562306a36Sopenharmony_ci int headroom = skb_headroom(skb); 42662306a36Sopenharmony_ci int tailroom = skb_tailroom(skb); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (padlen <= tailroom && 42962306a36Sopenharmony_ci sizeof(struct nc_header) <= headroom) 43062306a36Sopenharmony_ci /* There's enough head and tail room */ 43162306a36Sopenharmony_ci goto encapsulate; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if ((sizeof (struct nc_header) + padlen) < 43462306a36Sopenharmony_ci (headroom + tailroom)) { 43562306a36Sopenharmony_ci /* There's enough total room, so just readjust */ 43662306a36Sopenharmony_ci skb->data = memmove(skb->head 43762306a36Sopenharmony_ci + sizeof (struct nc_header), 43862306a36Sopenharmony_ci skb->data, skb->len); 43962306a36Sopenharmony_ci skb_set_tail_pointer(skb, len); 44062306a36Sopenharmony_ci goto encapsulate; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* Create a new skb to use with the correct size */ 44562306a36Sopenharmony_ci skb2 = skb_copy_expand(skb, 44662306a36Sopenharmony_ci sizeof (struct nc_header), 44762306a36Sopenharmony_ci padlen, 44862306a36Sopenharmony_ci flags); 44962306a36Sopenharmony_ci dev_kfree_skb_any(skb); 45062306a36Sopenharmony_ci if (!skb2) 45162306a36Sopenharmony_ci return skb2; 45262306a36Sopenharmony_ci skb = skb2; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciencapsulate: 45562306a36Sopenharmony_ci /* header first */ 45662306a36Sopenharmony_ci header = skb_push(skb, sizeof *header); 45762306a36Sopenharmony_ci header->hdr_len = cpu_to_le16(sizeof (*header)); 45862306a36Sopenharmony_ci header->packet_len = cpu_to_le16(len); 45962306a36Sopenharmony_ci header->packet_id = cpu_to_le16((u16)dev->xid++); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* maybe pad; then trailer */ 46262306a36Sopenharmony_ci if (!((skb->len + sizeof *trailer) & 0x01)) 46362306a36Sopenharmony_ci skb_put_u8(skb, PAD_BYTE); 46462306a36Sopenharmony_ci trailer = skb_put(skb, sizeof *trailer); 46562306a36Sopenharmony_ci put_unaligned(header->packet_id, &trailer->packet_id); 46662306a36Sopenharmony_ci#if 0 46762306a36Sopenharmony_ci netdev_dbg(dev->net, "frame >tx h %d p %d id %d\n", 46862306a36Sopenharmony_ci header->hdr_len, header->packet_len, 46962306a36Sopenharmony_ci header->packet_id); 47062306a36Sopenharmony_ci#endif 47162306a36Sopenharmony_ci return skb; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int net1080_bind(struct usbnet *dev, struct usb_interface *intf) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci unsigned extra = sizeof (struct nc_header) 47762306a36Sopenharmony_ci + 1 47862306a36Sopenharmony_ci + sizeof (struct nc_trailer); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci dev->net->hard_header_len += extra; 48162306a36Sopenharmony_ci dev->rx_urb_size = dev->net->hard_header_len + dev->net->mtu; 48262306a36Sopenharmony_ci dev->hard_mtu = NC_MAX_PACKET; 48362306a36Sopenharmony_ci return usbnet_get_endpoints (dev, intf); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic const struct driver_info net1080_info = { 48762306a36Sopenharmony_ci .description = "NetChip TurboCONNECT", 48862306a36Sopenharmony_ci .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_NC, 48962306a36Sopenharmony_ci .bind = net1080_bind, 49062306a36Sopenharmony_ci .reset = net1080_reset, 49162306a36Sopenharmony_ci .check_connect = net1080_check_connect, 49262306a36Sopenharmony_ci .rx_fixup = net1080_rx_fixup, 49362306a36Sopenharmony_ci .tx_fixup = net1080_tx_fixup, 49462306a36Sopenharmony_ci}; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic const struct usb_device_id products [] = { 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci USB_DEVICE(0x0525, 0x1080), // NetChip ref design 49962306a36Sopenharmony_ci .driver_info = (unsigned long) &net1080_info, 50062306a36Sopenharmony_ci}, { 50162306a36Sopenharmony_ci USB_DEVICE(0x06D0, 0x0622), // Laplink Gold 50262306a36Sopenharmony_ci .driver_info = (unsigned long) &net1080_info, 50362306a36Sopenharmony_ci}, 50462306a36Sopenharmony_ci { }, // END 50562306a36Sopenharmony_ci}; 50662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic struct usb_driver net1080_driver = { 50962306a36Sopenharmony_ci .name = "net1080", 51062306a36Sopenharmony_ci .id_table = products, 51162306a36Sopenharmony_ci .probe = usbnet_probe, 51262306a36Sopenharmony_ci .disconnect = usbnet_disconnect, 51362306a36Sopenharmony_ci .suspend = usbnet_suspend, 51462306a36Sopenharmony_ci .resume = usbnet_resume, 51562306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 51662306a36Sopenharmony_ci}; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cimodule_usb_driver(net1080_driver); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 52162306a36Sopenharmony_ciMODULE_DESCRIPTION("NetChip 1080 based USB Host-to-Host Links"); 52262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 523