xref: /openbmc/linux/drivers/net/usb/gl620a.c (revision 0f9b4c3ca5fdf3e177266ef994071b1a03f07318)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25b2fc499SJeff Garzik /*
35b2fc499SJeff Garzik  * GeneSys GL620USB-A based links
45b2fc499SJeff Garzik  * Copyright (C) 2001 by Jiun-Jie Huang <huangjj@genesyslogic.com.tw>
55b2fc499SJeff Garzik  * Copyright (C) 2001 by Stanislav Brabec <utx@penguin.cz>
65b2fc499SJeff Garzik  */
75b2fc499SJeff Garzik 
85b2fc499SJeff Garzik // #define	DEBUG			// error path messages, extra info
95b2fc499SJeff Garzik // #define	VERBOSE			// more; success messages
105b2fc499SJeff Garzik 
115b2fc499SJeff Garzik #include <linux/module.h>
125b2fc499SJeff Garzik #include <linux/netdevice.h>
135b2fc499SJeff Garzik #include <linux/etherdevice.h>
145b2fc499SJeff Garzik #include <linux/ethtool.h>
155b2fc499SJeff Garzik #include <linux/workqueue.h>
165b2fc499SJeff Garzik #include <linux/mii.h>
175b2fc499SJeff Garzik #include <linux/usb.h>
183692e94fSJussi Kivilinna #include <linux/usb/usbnet.h>
195a0e3ad6STejun Heo #include <linux/gfp.h>
205b2fc499SJeff Garzik 
215b2fc499SJeff Garzik 
225b2fc499SJeff Garzik /*
235b2fc499SJeff Garzik  * GeneSys GL620USB-A (www.genesyslogic.com.tw)
245b2fc499SJeff Garzik  *
255b2fc499SJeff Garzik  * ... should partially interop with the Win32 driver for this hardware.
265b2fc499SJeff Garzik  * The GeneSys docs imply there's some NDIS issue motivating this framing.
275b2fc499SJeff Garzik  *
285b2fc499SJeff Garzik  * Some info from GeneSys:
295b2fc499SJeff Garzik  *  - GL620USB-A is full duplex; GL620USB is only half duplex for bulk.
305b2fc499SJeff Garzik  *    (Some cables, like the BAFO-100c, use the half duplex version.)
315b2fc499SJeff Garzik  *  - For the full duplex model, the low bit of the version code says
325b2fc499SJeff Garzik  *    which side is which ("left/right").
335b2fc499SJeff Garzik  *  - For the half duplex type, a control/interrupt handshake settles
345b2fc499SJeff Garzik  *    the transfer direction.  (That's disabled here, partially coded.)
355b2fc499SJeff Garzik  *    A control URB would block until other side writes an interrupt.
365b2fc499SJeff Garzik  *
375b2fc499SJeff Garzik  * Original code from Jiun-Jie Huang <huangjj@genesyslogic.com.tw>
385b2fc499SJeff Garzik  * and merged into "usbnet" by Stanislav Brabec <utx@penguin.cz>.
395b2fc499SJeff Garzik  */
405b2fc499SJeff Garzik 
415b2fc499SJeff Garzik // control msg write command
425b2fc499SJeff Garzik #define GENELINK_CONNECT_WRITE			0xF0
435b2fc499SJeff Garzik // interrupt pipe index
445b2fc499SJeff Garzik #define GENELINK_INTERRUPT_PIPE			0x03
455b2fc499SJeff Garzik // interrupt read buffer size
465b2fc499SJeff Garzik #define INTERRUPT_BUFSIZE			0x08
475b2fc499SJeff Garzik // interrupt pipe interval value
485b2fc499SJeff Garzik #define GENELINK_INTERRUPT_INTERVAL		0x10
495b2fc499SJeff Garzik // max transmit packet number per transmit
505b2fc499SJeff Garzik #define GL_MAX_TRANSMIT_PACKETS			32
515b2fc499SJeff Garzik // max packet length
525b2fc499SJeff Garzik #define GL_MAX_PACKET_LEN			1514
535b2fc499SJeff Garzik // max receive buffer size
545b2fc499SJeff Garzik #define GL_RCV_BUF_SIZE		\
555b2fc499SJeff Garzik 	(((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4)
565b2fc499SJeff Garzik 
575b2fc499SJeff Garzik struct gl_packet {
585b2fc499SJeff Garzik 	__le32		packet_length;
59cc727b64SGustavo A. R. Silva 	char		packet_data[];
605b2fc499SJeff Garzik };
615b2fc499SJeff Garzik 
625b2fc499SJeff Garzik struct gl_header {
635b2fc499SJeff Garzik 	__le32			packet_count;
645b2fc499SJeff Garzik 	struct gl_packet	packets;
655b2fc499SJeff Garzik };
665b2fc499SJeff Garzik 
genelink_rx_fixup(struct usbnet * dev,struct sk_buff * skb)675b2fc499SJeff Garzik static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
685b2fc499SJeff Garzik {
695b2fc499SJeff Garzik 	struct gl_header	*header;
705b2fc499SJeff Garzik 	struct gl_packet	*packet;
715b2fc499SJeff Garzik 	struct sk_buff		*gl_skb;
725b2fc499SJeff Garzik 	u32			size;
735b2fc499SJeff Garzik 	u32			count;
745b2fc499SJeff Garzik 
75eb85569fSEmil Goode 	/* This check is no longer done by usbnet */
76eb85569fSEmil Goode 	if (skb->len < dev->net->hard_header_len)
77eb85569fSEmil Goode 		return 0;
78eb85569fSEmil Goode 
795b2fc499SJeff Garzik 	header = (struct gl_header *) skb->data;
805b2fc499SJeff Garzik 
815b2fc499SJeff Garzik 	// get the packet count of the received skb
825b2fc499SJeff Garzik 	count = le32_to_cpu(header->packet_count);
835b2fc499SJeff Garzik 	if (count > GL_MAX_TRANSMIT_PACKETS) {
8449ae25b0SGreg Kroah-Hartman 		netdev_dbg(dev->net,
8549ae25b0SGreg Kroah-Hartman 			   "genelink: invalid received packet count %u\n",
8649ae25b0SGreg Kroah-Hartman 			   count);
875b2fc499SJeff Garzik 		return 0;
885b2fc499SJeff Garzik 	}
895b2fc499SJeff Garzik 
905b2fc499SJeff Garzik 	// set the current packet pointer to the first packet
915b2fc499SJeff Garzik 	packet = &header->packets;
925b2fc499SJeff Garzik 
935b2fc499SJeff Garzik 	// decrement the length for the packet count size 4 bytes
945b2fc499SJeff Garzik 	skb_pull(skb, 4);
955b2fc499SJeff Garzik 
965b2fc499SJeff Garzik 	while (count > 1) {
975b2fc499SJeff Garzik 		// get the packet length
985b2fc499SJeff Garzik 		size = le32_to_cpu(packet->packet_length);
995b2fc499SJeff Garzik 
1005b2fc499SJeff Garzik 		// this may be a broken packet
1015b2fc499SJeff Garzik 		if (size > GL_MAX_PACKET_LEN) {
10249ae25b0SGreg Kroah-Hartman 			netdev_dbg(dev->net, "genelink: invalid rx length %d\n",
10349ae25b0SGreg Kroah-Hartman 				   size);
1045b2fc499SJeff Garzik 			return 0;
1055b2fc499SJeff Garzik 		}
1065b2fc499SJeff Garzik 
1075b2fc499SJeff Garzik 		// allocate the skb for the individual packet
1085b2fc499SJeff Garzik 		gl_skb = alloc_skb(size, GFP_ATOMIC);
1095b2fc499SJeff Garzik 		if (gl_skb) {
1105b2fc499SJeff Garzik 
1115b2fc499SJeff Garzik 			// copy the packet data to the new skb
11259ae1d12SJohannes Berg 			skb_put_data(gl_skb, packet->packet_data, size);
1135b2fc499SJeff Garzik 			usbnet_skb_return(dev, gl_skb);
1145b2fc499SJeff Garzik 		}
1155b2fc499SJeff Garzik 
1165b2fc499SJeff Garzik 		// advance to the next packet
1175b2fc499SJeff Garzik 		packet = (struct gl_packet *)&packet->packet_data[size];
1185b2fc499SJeff Garzik 		count--;
1195b2fc499SJeff Garzik 
1205b2fc499SJeff Garzik 		// shift the data pointer to the next gl_packet
1215b2fc499SJeff Garzik 		skb_pull(skb, size + 4);
1225b2fc499SJeff Garzik 	}
1235b2fc499SJeff Garzik 
1245b2fc499SJeff Garzik 	// skip the packet length field 4 bytes
1255b2fc499SJeff Garzik 	skb_pull(skb, 4);
1265b2fc499SJeff Garzik 
1275b2fc499SJeff Garzik 	if (skb->len > GL_MAX_PACKET_LEN) {
12849ae25b0SGreg Kroah-Hartman 		netdev_dbg(dev->net, "genelink: invalid rx length %d\n",
12949ae25b0SGreg Kroah-Hartman 			   skb->len);
1305b2fc499SJeff Garzik 		return 0;
1315b2fc499SJeff Garzik 	}
1325b2fc499SJeff Garzik 	return 1;
1335b2fc499SJeff Garzik }
1345b2fc499SJeff Garzik 
1355b2fc499SJeff Garzik static struct sk_buff *
genelink_tx_fixup(struct usbnet * dev,struct sk_buff * skb,gfp_t flags)1365b2fc499SJeff Garzik genelink_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
1375b2fc499SJeff Garzik {
1385b2fc499SJeff Garzik 	int 	padlen;
1395b2fc499SJeff Garzik 	int	length = skb->len;
1405b2fc499SJeff Garzik 	int	headroom = skb_headroom(skb);
1415b2fc499SJeff Garzik 	int	tailroom = skb_tailroom(skb);
1425b2fc499SJeff Garzik 	__le32	*packet_count;
1435b2fc499SJeff Garzik 	__le32	*packet_len;
1445b2fc499SJeff Garzik 
1455b2fc499SJeff Garzik 	// FIXME:  magic numbers, bleech
1465b2fc499SJeff Garzik 	padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1;
1475b2fc499SJeff Garzik 
1485b2fc499SJeff Garzik 	if ((!skb_cloned(skb))
1495b2fc499SJeff Garzik 			&& ((headroom + tailroom) >= (padlen + (4 + 4*1)))) {
1505b2fc499SJeff Garzik 		if ((headroom < (4 + 4*1)) || (tailroom < padlen)) {
1515b2fc499SJeff Garzik 			skb->data = memmove(skb->head + (4 + 4*1),
1525b2fc499SJeff Garzik 					     skb->data, skb->len);
1535b2fc499SJeff Garzik 			skb_set_tail_pointer(skb, skb->len);
1545b2fc499SJeff Garzik 		}
1555b2fc499SJeff Garzik 	} else {
1565b2fc499SJeff Garzik 		struct sk_buff	*skb2;
1575b2fc499SJeff Garzik 		skb2 = skb_copy_expand(skb, (4 + 4*1) , padlen, flags);
1585b2fc499SJeff Garzik 		dev_kfree_skb_any(skb);
1595b2fc499SJeff Garzik 		skb = skb2;
1605b2fc499SJeff Garzik 		if (!skb)
1615b2fc499SJeff Garzik 			return NULL;
1625b2fc499SJeff Garzik 	}
1635b2fc499SJeff Garzik 
1645b2fc499SJeff Garzik 	// attach the packet count to the header
165d58ff351SJohannes Berg 	packet_count = skb_push(skb, (4 + 4 * 1));
1665b2fc499SJeff Garzik 	packet_len = packet_count + 1;
1675b2fc499SJeff Garzik 
1685b2fc499SJeff Garzik 	*packet_count = cpu_to_le32(1);
1695b2fc499SJeff Garzik 	*packet_len = cpu_to_le32(length);
1705b2fc499SJeff Garzik 
1715b2fc499SJeff Garzik 	// add padding byte
1725b2fc499SJeff Garzik 	if ((skb->len % dev->maxpacket) == 0)
1735b2fc499SJeff Garzik 		skb_put(skb, 1);
1745b2fc499SJeff Garzik 
1755b2fc499SJeff Garzik 	return skb;
1765b2fc499SJeff Garzik }
1775b2fc499SJeff Garzik 
genelink_bind(struct usbnet * dev,struct usb_interface * intf)1785b2fc499SJeff Garzik static int genelink_bind(struct usbnet *dev, struct usb_interface *intf)
1795b2fc499SJeff Garzik {
1805b2fc499SJeff Garzik 	dev->hard_mtu = GL_RCV_BUF_SIZE;
1815b2fc499SJeff Garzik 	dev->net->hard_header_len += 4;
182*a2ee5e55SNikita Zhandarovich 	return usbnet_get_endpoints(dev, intf);
1835b2fc499SJeff Garzik }
1845b2fc499SJeff Garzik 
1855b2fc499SJeff Garzik static const struct driver_info	genelink_info = {
1865b2fc499SJeff Garzik 	.description =	"Genesys GeneLink",
187c261344dSArnd Bergmann 	.flags =	FLAG_POINTTOPOINT | FLAG_FRAMING_GL | FLAG_NO_SETINT,
1885b2fc499SJeff Garzik 	.bind =		genelink_bind,
1895b2fc499SJeff Garzik 	.rx_fixup =	genelink_rx_fixup,
1905b2fc499SJeff Garzik 	.tx_fixup =	genelink_tx_fixup,
1915b2fc499SJeff Garzik 
1925b2fc499SJeff Garzik 	.in = 1, .out = 2,
1935b2fc499SJeff Garzik 
1945b2fc499SJeff Garzik #ifdef	GENELINK_ACK
1955b2fc499SJeff Garzik 	.check_connect =genelink_check_connect,
1965b2fc499SJeff Garzik #endif
1975b2fc499SJeff Garzik };
1985b2fc499SJeff Garzik 
1995b2fc499SJeff Garzik static const struct usb_device_id	products [] = {
2005b2fc499SJeff Garzik 
2015b2fc499SJeff Garzik {
2025b2fc499SJeff Garzik 	USB_DEVICE(0x05e3, 0x0502),	// GL620USB-A
2035b2fc499SJeff Garzik 	.driver_info =	(unsigned long) &genelink_info,
2045b2fc499SJeff Garzik },
2055b2fc499SJeff Garzik 	/* NOT: USB_DEVICE(0x05e3, 0x0501),	// GL620USB
2065b2fc499SJeff Garzik 	 * that's half duplex, not currently supported
2075b2fc499SJeff Garzik 	 */
2085b2fc499SJeff Garzik 	{ },		// END
2095b2fc499SJeff Garzik };
2105b2fc499SJeff Garzik MODULE_DEVICE_TABLE(usb, products);
2115b2fc499SJeff Garzik 
2125b2fc499SJeff Garzik static struct usb_driver gl620a_driver = {
2135b2fc499SJeff Garzik 	.name =		"gl620a",
2145b2fc499SJeff Garzik 	.id_table =	products,
2155b2fc499SJeff Garzik 	.probe =	usbnet_probe,
2165b2fc499SJeff Garzik 	.disconnect =	usbnet_disconnect,
2175b2fc499SJeff Garzik 	.suspend =	usbnet_suspend,
2185b2fc499SJeff Garzik 	.resume =	usbnet_resume,
219e1f12eb6SSarah Sharp 	.disable_hub_initiated_lpm = 1,
2205b2fc499SJeff Garzik };
2215b2fc499SJeff Garzik 
222d632eb1bSGreg Kroah-Hartman module_usb_driver(gl620a_driver);
2235b2fc499SJeff Garzik 
2245b2fc499SJeff Garzik MODULE_AUTHOR("Jiun-Jie Huang");
2255b2fc499SJeff Garzik MODULE_DESCRIPTION("GL620-USB-A Host-to-Host Link cables");
2265b2fc499SJeff Garzik MODULE_LICENSE("GPL");
2275b2fc499SJeff Garzik 
228