162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * GeneSys GL620USB-A based links 462306a36Sopenharmony_ci * Copyright (C) 2001 by Jiun-Jie Huang <huangjj@genesyslogic.com.tw> 562306a36Sopenharmony_ci * Copyright (C) 2001 by Stanislav Brabec <utx@penguin.cz> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci// #define DEBUG // error path messages, extra info 962306a36Sopenharmony_ci// #define VERBOSE // more; success messages 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/etherdevice.h> 1462306a36Sopenharmony_ci#include <linux/ethtool.h> 1562306a36Sopenharmony_ci#include <linux/workqueue.h> 1662306a36Sopenharmony_ci#include <linux/mii.h> 1762306a36Sopenharmony_ci#include <linux/usb.h> 1862306a36Sopenharmony_ci#include <linux/usb/usbnet.h> 1962306a36Sopenharmony_ci#include <linux/gfp.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * GeneSys GL620USB-A (www.genesyslogic.com.tw) 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * ... should partially interop with the Win32 driver for this hardware. 2662306a36Sopenharmony_ci * The GeneSys docs imply there's some NDIS issue motivating this framing. 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * Some info from GeneSys: 2962306a36Sopenharmony_ci * - GL620USB-A is full duplex; GL620USB is only half duplex for bulk. 3062306a36Sopenharmony_ci * (Some cables, like the BAFO-100c, use the half duplex version.) 3162306a36Sopenharmony_ci * - For the full duplex model, the low bit of the version code says 3262306a36Sopenharmony_ci * which side is which ("left/right"). 3362306a36Sopenharmony_ci * - For the half duplex type, a control/interrupt handshake settles 3462306a36Sopenharmony_ci * the transfer direction. (That's disabled here, partially coded.) 3562306a36Sopenharmony_ci * A control URB would block until other side writes an interrupt. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * Original code from Jiun-Jie Huang <huangjj@genesyslogic.com.tw> 3862306a36Sopenharmony_ci * and merged into "usbnet" by Stanislav Brabec <utx@penguin.cz>. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci// control msg write command 4262306a36Sopenharmony_ci#define GENELINK_CONNECT_WRITE 0xF0 4362306a36Sopenharmony_ci// interrupt pipe index 4462306a36Sopenharmony_ci#define GENELINK_INTERRUPT_PIPE 0x03 4562306a36Sopenharmony_ci// interrupt read buffer size 4662306a36Sopenharmony_ci#define INTERRUPT_BUFSIZE 0x08 4762306a36Sopenharmony_ci// interrupt pipe interval value 4862306a36Sopenharmony_ci#define GENELINK_INTERRUPT_INTERVAL 0x10 4962306a36Sopenharmony_ci// max transmit packet number per transmit 5062306a36Sopenharmony_ci#define GL_MAX_TRANSMIT_PACKETS 32 5162306a36Sopenharmony_ci// max packet length 5262306a36Sopenharmony_ci#define GL_MAX_PACKET_LEN 1514 5362306a36Sopenharmony_ci// max receive buffer size 5462306a36Sopenharmony_ci#define GL_RCV_BUF_SIZE \ 5562306a36Sopenharmony_ci (((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct gl_packet { 5862306a36Sopenharmony_ci __le32 packet_length; 5962306a36Sopenharmony_ci char packet_data[]; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct gl_header { 6362306a36Sopenharmony_ci __le32 packet_count; 6462306a36Sopenharmony_ci struct gl_packet packets; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct gl_header *header; 7062306a36Sopenharmony_ci struct gl_packet *packet; 7162306a36Sopenharmony_ci struct sk_buff *gl_skb; 7262306a36Sopenharmony_ci u32 size; 7362306a36Sopenharmony_ci u32 count; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* This check is no longer done by usbnet */ 7662306a36Sopenharmony_ci if (skb->len < dev->net->hard_header_len) 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci header = (struct gl_header *) skb->data; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci // get the packet count of the received skb 8262306a36Sopenharmony_ci count = le32_to_cpu(header->packet_count); 8362306a36Sopenharmony_ci if (count > GL_MAX_TRANSMIT_PACKETS) { 8462306a36Sopenharmony_ci netdev_dbg(dev->net, 8562306a36Sopenharmony_ci "genelink: invalid received packet count %u\n", 8662306a36Sopenharmony_ci count); 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci // set the current packet pointer to the first packet 9162306a36Sopenharmony_ci packet = &header->packets; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci // decrement the length for the packet count size 4 bytes 9462306a36Sopenharmony_ci skb_pull(skb, 4); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci while (count > 1) { 9762306a36Sopenharmony_ci // get the packet length 9862306a36Sopenharmony_ci size = le32_to_cpu(packet->packet_length); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci // this may be a broken packet 10162306a36Sopenharmony_ci if (size > GL_MAX_PACKET_LEN) { 10262306a36Sopenharmony_ci netdev_dbg(dev->net, "genelink: invalid rx length %d\n", 10362306a36Sopenharmony_ci size); 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci // allocate the skb for the individual packet 10862306a36Sopenharmony_ci gl_skb = alloc_skb(size, GFP_ATOMIC); 10962306a36Sopenharmony_ci if (gl_skb) { 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci // copy the packet data to the new skb 11262306a36Sopenharmony_ci skb_put_data(gl_skb, packet->packet_data, size); 11362306a36Sopenharmony_ci usbnet_skb_return(dev, gl_skb); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci // advance to the next packet 11762306a36Sopenharmony_ci packet = (struct gl_packet *)&packet->packet_data[size]; 11862306a36Sopenharmony_ci count--; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci // shift the data pointer to the next gl_packet 12162306a36Sopenharmony_ci skb_pull(skb, size + 4); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci // skip the packet length field 4 bytes 12562306a36Sopenharmony_ci skb_pull(skb, 4); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (skb->len > GL_MAX_PACKET_LEN) { 12862306a36Sopenharmony_ci netdev_dbg(dev->net, "genelink: invalid rx length %d\n", 12962306a36Sopenharmony_ci skb->len); 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci return 1; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic struct sk_buff * 13662306a36Sopenharmony_cigenelink_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int padlen; 13962306a36Sopenharmony_ci int length = skb->len; 14062306a36Sopenharmony_ci int headroom = skb_headroom(skb); 14162306a36Sopenharmony_ci int tailroom = skb_tailroom(skb); 14262306a36Sopenharmony_ci __le32 *packet_count; 14362306a36Sopenharmony_ci __le32 *packet_len; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci // FIXME: magic numbers, bleech 14662306a36Sopenharmony_ci padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if ((!skb_cloned(skb)) 14962306a36Sopenharmony_ci && ((headroom + tailroom) >= (padlen + (4 + 4*1)))) { 15062306a36Sopenharmony_ci if ((headroom < (4 + 4*1)) || (tailroom < padlen)) { 15162306a36Sopenharmony_ci skb->data = memmove(skb->head + (4 + 4*1), 15262306a36Sopenharmony_ci skb->data, skb->len); 15362306a36Sopenharmony_ci skb_set_tail_pointer(skb, skb->len); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci } else { 15662306a36Sopenharmony_ci struct sk_buff *skb2; 15762306a36Sopenharmony_ci skb2 = skb_copy_expand(skb, (4 + 4*1) , padlen, flags); 15862306a36Sopenharmony_ci dev_kfree_skb_any(skb); 15962306a36Sopenharmony_ci skb = skb2; 16062306a36Sopenharmony_ci if (!skb) 16162306a36Sopenharmony_ci return NULL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci // attach the packet count to the header 16562306a36Sopenharmony_ci packet_count = skb_push(skb, (4 + 4 * 1)); 16662306a36Sopenharmony_ci packet_len = packet_count + 1; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci *packet_count = cpu_to_le32(1); 16962306a36Sopenharmony_ci *packet_len = cpu_to_le32(length); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci // add padding byte 17262306a36Sopenharmony_ci if ((skb->len % dev->maxpacket) == 0) 17362306a36Sopenharmony_ci skb_put(skb, 1); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return skb; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int genelink_bind(struct usbnet *dev, struct usb_interface *intf) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci dev->hard_mtu = GL_RCV_BUF_SIZE; 18162306a36Sopenharmony_ci dev->net->hard_header_len += 4; 18262306a36Sopenharmony_ci dev->in = usb_rcvbulkpipe(dev->udev, dev->driver_info->in); 18362306a36Sopenharmony_ci dev->out = usb_sndbulkpipe(dev->udev, dev->driver_info->out); 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic const struct driver_info genelink_info = { 18862306a36Sopenharmony_ci .description = "Genesys GeneLink", 18962306a36Sopenharmony_ci .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_GL | FLAG_NO_SETINT, 19062306a36Sopenharmony_ci .bind = genelink_bind, 19162306a36Sopenharmony_ci .rx_fixup = genelink_rx_fixup, 19262306a36Sopenharmony_ci .tx_fixup = genelink_tx_fixup, 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci .in = 1, .out = 2, 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci#ifdef GENELINK_ACK 19762306a36Sopenharmony_ci .check_connect =genelink_check_connect, 19862306a36Sopenharmony_ci#endif 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic const struct usb_device_id products [] = { 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci USB_DEVICE(0x05e3, 0x0502), // GL620USB-A 20562306a36Sopenharmony_ci .driver_info = (unsigned long) &genelink_info, 20662306a36Sopenharmony_ci}, 20762306a36Sopenharmony_ci /* NOT: USB_DEVICE(0x05e3, 0x0501), // GL620USB 20862306a36Sopenharmony_ci * that's half duplex, not currently supported 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci { }, // END 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic struct usb_driver gl620a_driver = { 21562306a36Sopenharmony_ci .name = "gl620a", 21662306a36Sopenharmony_ci .id_table = products, 21762306a36Sopenharmony_ci .probe = usbnet_probe, 21862306a36Sopenharmony_ci .disconnect = usbnet_disconnect, 21962306a36Sopenharmony_ci .suspend = usbnet_suspend, 22062306a36Sopenharmony_ci .resume = usbnet_resume, 22162306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cimodule_usb_driver(gl620a_driver); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ciMODULE_AUTHOR("Jiun-Jie Huang"); 22762306a36Sopenharmony_ciMODULE_DESCRIPTION("GL620-USB-A Host-to-Host Link cables"); 22862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 22962306a36Sopenharmony_ci 230