18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GeneSys GL620USB-A based links 48c2ecf20Sopenharmony_ci * Copyright (C) 2001 by Jiun-Jie Huang <huangjj@genesyslogic.com.tw> 58c2ecf20Sopenharmony_ci * Copyright (C) 2001 by Stanislav Brabec <utx@penguin.cz> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci// #define DEBUG // error path messages, extra info 98c2ecf20Sopenharmony_ci// #define VERBOSE // more; success messages 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 158c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 168c2ecf20Sopenharmony_ci#include <linux/mii.h> 178c2ecf20Sopenharmony_ci#include <linux/usb.h> 188c2ecf20Sopenharmony_ci#include <linux/usb/usbnet.h> 198c2ecf20Sopenharmony_ci#include <linux/gfp.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * GeneSys GL620USB-A (www.genesyslogic.com.tw) 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * ... should partially interop with the Win32 driver for this hardware. 268c2ecf20Sopenharmony_ci * The GeneSys docs imply there's some NDIS issue motivating this framing. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * Some info from GeneSys: 298c2ecf20Sopenharmony_ci * - GL620USB-A is full duplex; GL620USB is only half duplex for bulk. 308c2ecf20Sopenharmony_ci * (Some cables, like the BAFO-100c, use the half duplex version.) 318c2ecf20Sopenharmony_ci * - For the full duplex model, the low bit of the version code says 328c2ecf20Sopenharmony_ci * which side is which ("left/right"). 338c2ecf20Sopenharmony_ci * - For the half duplex type, a control/interrupt handshake settles 348c2ecf20Sopenharmony_ci * the transfer direction. (That's disabled here, partially coded.) 358c2ecf20Sopenharmony_ci * A control URB would block until other side writes an interrupt. 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * Original code from Jiun-Jie Huang <huangjj@genesyslogic.com.tw> 388c2ecf20Sopenharmony_ci * and merged into "usbnet" by Stanislav Brabec <utx@penguin.cz>. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci// control msg write command 428c2ecf20Sopenharmony_ci#define GENELINK_CONNECT_WRITE 0xF0 438c2ecf20Sopenharmony_ci// interrupt pipe index 448c2ecf20Sopenharmony_ci#define GENELINK_INTERRUPT_PIPE 0x03 458c2ecf20Sopenharmony_ci// interrupt read buffer size 468c2ecf20Sopenharmony_ci#define INTERRUPT_BUFSIZE 0x08 478c2ecf20Sopenharmony_ci// interrupt pipe interval value 488c2ecf20Sopenharmony_ci#define GENELINK_INTERRUPT_INTERVAL 0x10 498c2ecf20Sopenharmony_ci// max transmit packet number per transmit 508c2ecf20Sopenharmony_ci#define GL_MAX_TRANSMIT_PACKETS 32 518c2ecf20Sopenharmony_ci// max packet length 528c2ecf20Sopenharmony_ci#define GL_MAX_PACKET_LEN 1514 538c2ecf20Sopenharmony_ci// max receive buffer size 548c2ecf20Sopenharmony_ci#define GL_RCV_BUF_SIZE \ 558c2ecf20Sopenharmony_ci (((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct gl_packet { 588c2ecf20Sopenharmony_ci __le32 packet_length; 598c2ecf20Sopenharmony_ci char packet_data [1]; 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistruct gl_header { 638c2ecf20Sopenharmony_ci __le32 packet_count; 648c2ecf20Sopenharmony_ci struct gl_packet packets; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct gl_header *header; 708c2ecf20Sopenharmony_ci struct gl_packet *packet; 718c2ecf20Sopenharmony_ci struct sk_buff *gl_skb; 728c2ecf20Sopenharmony_ci u32 size; 738c2ecf20Sopenharmony_ci u32 count; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* This check is no longer done by usbnet */ 768c2ecf20Sopenharmony_ci if (skb->len < dev->net->hard_header_len) 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci header = (struct gl_header *) skb->data; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci // get the packet count of the received skb 828c2ecf20Sopenharmony_ci count = le32_to_cpu(header->packet_count); 838c2ecf20Sopenharmony_ci if (count > GL_MAX_TRANSMIT_PACKETS) { 848c2ecf20Sopenharmony_ci netdev_dbg(dev->net, 858c2ecf20Sopenharmony_ci "genelink: invalid received packet count %u\n", 868c2ecf20Sopenharmony_ci count); 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci // set the current packet pointer to the first packet 918c2ecf20Sopenharmony_ci packet = &header->packets; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci // decrement the length for the packet count size 4 bytes 948c2ecf20Sopenharmony_ci skb_pull(skb, 4); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci while (count > 1) { 978c2ecf20Sopenharmony_ci // get the packet length 988c2ecf20Sopenharmony_ci size = le32_to_cpu(packet->packet_length); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci // this may be a broken packet 1018c2ecf20Sopenharmony_ci if (size > GL_MAX_PACKET_LEN) { 1028c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "genelink: invalid rx length %d\n", 1038c2ecf20Sopenharmony_ci size); 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci // allocate the skb for the individual packet 1088c2ecf20Sopenharmony_ci gl_skb = alloc_skb(size, GFP_ATOMIC); 1098c2ecf20Sopenharmony_ci if (gl_skb) { 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci // copy the packet data to the new skb 1128c2ecf20Sopenharmony_ci skb_put_data(gl_skb, packet->packet_data, size); 1138c2ecf20Sopenharmony_ci usbnet_skb_return(dev, gl_skb); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci // advance to the next packet 1178c2ecf20Sopenharmony_ci packet = (struct gl_packet *)&packet->packet_data[size]; 1188c2ecf20Sopenharmony_ci count--; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci // shift the data pointer to the next gl_packet 1218c2ecf20Sopenharmony_ci skb_pull(skb, size + 4); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci // skip the packet length field 4 bytes 1258c2ecf20Sopenharmony_ci skb_pull(skb, 4); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (skb->len > GL_MAX_PACKET_LEN) { 1288c2ecf20Sopenharmony_ci netdev_dbg(dev->net, "genelink: invalid rx length %d\n", 1298c2ecf20Sopenharmony_ci skb->len); 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci return 1; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic struct sk_buff * 1368c2ecf20Sopenharmony_cigenelink_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci int padlen; 1398c2ecf20Sopenharmony_ci int length = skb->len; 1408c2ecf20Sopenharmony_ci int headroom = skb_headroom(skb); 1418c2ecf20Sopenharmony_ci int tailroom = skb_tailroom(skb); 1428c2ecf20Sopenharmony_ci __le32 *packet_count; 1438c2ecf20Sopenharmony_ci __le32 *packet_len; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci // FIXME: magic numbers, bleech 1468c2ecf20Sopenharmony_ci padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if ((!skb_cloned(skb)) 1498c2ecf20Sopenharmony_ci && ((headroom + tailroom) >= (padlen + (4 + 4*1)))) { 1508c2ecf20Sopenharmony_ci if ((headroom < (4 + 4*1)) || (tailroom < padlen)) { 1518c2ecf20Sopenharmony_ci skb->data = memmove(skb->head + (4 + 4*1), 1528c2ecf20Sopenharmony_ci skb->data, skb->len); 1538c2ecf20Sopenharmony_ci skb_set_tail_pointer(skb, skb->len); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } else { 1568c2ecf20Sopenharmony_ci struct sk_buff *skb2; 1578c2ecf20Sopenharmony_ci skb2 = skb_copy_expand(skb, (4 + 4*1) , padlen, flags); 1588c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1598c2ecf20Sopenharmony_ci skb = skb2; 1608c2ecf20Sopenharmony_ci if (!skb) 1618c2ecf20Sopenharmony_ci return NULL; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci // attach the packet count to the header 1658c2ecf20Sopenharmony_ci packet_count = skb_push(skb, (4 + 4 * 1)); 1668c2ecf20Sopenharmony_ci packet_len = packet_count + 1; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci *packet_count = cpu_to_le32(1); 1698c2ecf20Sopenharmony_ci *packet_len = cpu_to_le32(length); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci // add padding byte 1728c2ecf20Sopenharmony_ci if ((skb->len % dev->maxpacket) == 0) 1738c2ecf20Sopenharmony_ci skb_put(skb, 1); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return skb; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int genelink_bind(struct usbnet *dev, struct usb_interface *intf) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci dev->hard_mtu = GL_RCV_BUF_SIZE; 1818c2ecf20Sopenharmony_ci dev->net->hard_header_len += 4; 1828c2ecf20Sopenharmony_ci dev->in = usb_rcvbulkpipe(dev->udev, dev->driver_info->in); 1838c2ecf20Sopenharmony_ci dev->out = usb_sndbulkpipe(dev->udev, dev->driver_info->out); 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const struct driver_info genelink_info = { 1888c2ecf20Sopenharmony_ci .description = "Genesys GeneLink", 1898c2ecf20Sopenharmony_ci .flags = FLAG_POINTTOPOINT | FLAG_FRAMING_GL | FLAG_NO_SETINT, 1908c2ecf20Sopenharmony_ci .bind = genelink_bind, 1918c2ecf20Sopenharmony_ci .rx_fixup = genelink_rx_fixup, 1928c2ecf20Sopenharmony_ci .tx_fixup = genelink_tx_fixup, 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci .in = 1, .out = 2, 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci#ifdef GENELINK_ACK 1978c2ecf20Sopenharmony_ci .check_connect =genelink_check_connect, 1988c2ecf20Sopenharmony_ci#endif 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic const struct usb_device_id products [] = { 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci USB_DEVICE(0x05e3, 0x0502), // GL620USB-A 2058c2ecf20Sopenharmony_ci .driver_info = (unsigned long) &genelink_info, 2068c2ecf20Sopenharmony_ci}, 2078c2ecf20Sopenharmony_ci /* NOT: USB_DEVICE(0x05e3, 0x0501), // GL620USB 2088c2ecf20Sopenharmony_ci * that's half duplex, not currently supported 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci { }, // END 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic struct usb_driver gl620a_driver = { 2158c2ecf20Sopenharmony_ci .name = "gl620a", 2168c2ecf20Sopenharmony_ci .id_table = products, 2178c2ecf20Sopenharmony_ci .probe = usbnet_probe, 2188c2ecf20Sopenharmony_ci .disconnect = usbnet_disconnect, 2198c2ecf20Sopenharmony_ci .suspend = usbnet_suspend, 2208c2ecf20Sopenharmony_ci .resume = usbnet_resume, 2218c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cimodule_usb_driver(gl620a_driver); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jiun-Jie Huang"); 2278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GL620-USB-A Host-to-Host Link cables"); 2288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2298c2ecf20Sopenharmony_ci 230