xref: /linux/drivers/net/usb/int51x1.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2009 Peter Holik
4  *
5  * Intellon usb PLC (Powerline Communications) usb net driver
6  *
7  * https://web.archive.org/web/20101025091240id_/http://www.tandel.be/downloads/INT51X1_Datasheet.pdf
8  *
9  * Based on the work of Jan 'RedBully' Seiffert
10  */
11 
12 #include <linux/module.h>
13 #include <linux/ctype.h>
14 #include <linux/netdevice.h>
15 #include <linux/etherdevice.h>
16 #include <linux/ethtool.h>
17 #include <linux/slab.h>
18 #include <linux/mii.h>
19 #include <linux/usb.h>
20 #include <linux/usb/usbnet.h>
21 
22 #define INT51X1_VENDOR_ID	0x09e1
23 #define INT51X1_PRODUCT_ID	0x5121
24 
25 #define INT51X1_HEADER_SIZE	2	/* 2 byte header */
26 
27 static int int51x1_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
28 {
29 	int len;
30 
31 	if (!(pskb_may_pull(skb, INT51X1_HEADER_SIZE))) {
32 		netdev_err(dev->net, "unexpected tiny rx frame\n");
33 		return 0;
34 	}
35 
36 	len = le16_to_cpu(*(__le16 *)&skb->data[skb->len - 2]);
37 
38 	skb_trim(skb, len);
39 
40 	return 1;
41 }
42 
43 static struct sk_buff *int51x1_tx_fixup(struct usbnet *dev,
44 		struct sk_buff *skb, gfp_t flags)
45 {
46 	int pack_len = skb->len;
47 	int pack_with_header_len = pack_len + INT51X1_HEADER_SIZE;
48 	int headroom = skb_headroom(skb);
49 	int tailroom = skb_tailroom(skb);
50 	int need_tail = 0;
51 	__le16 *len;
52 
53 	/* if packet and our header is smaller than 64 pad to 64 (+ ZLP) */
54 	if ((pack_with_header_len) < dev->maxpacket)
55 		need_tail = dev->maxpacket - pack_with_header_len + 1;
56 	/*
57 	 * usbnet would send a ZLP if packetlength mod urbsize == 0 for us,
58 	 * but we need to know ourself, because this would add to the length
59 	 * we send down to the device...
60 	 */
61 	else if (!(pack_with_header_len % dev->maxpacket))
62 		need_tail = 1;
63 
64 	if (!skb_cloned(skb) &&
65 			(headroom + tailroom >= need_tail + INT51X1_HEADER_SIZE)) {
66 		if (headroom < INT51X1_HEADER_SIZE || tailroom < need_tail) {
67 			skb->data = memmove(skb->head + INT51X1_HEADER_SIZE,
68 					skb->data, skb->len);
69 			skb_set_tail_pointer(skb, skb->len);
70 		}
71 	} else {
72 		struct sk_buff *skb2;
73 
74 		skb2 = skb_copy_expand(skb,
75 				INT51X1_HEADER_SIZE,
76 				need_tail,
77 				flags);
78 		dev_kfree_skb_any(skb);
79 		if (!skb2)
80 			return NULL;
81 		skb = skb2;
82 	}
83 
84 	pack_len += need_tail;
85 	pack_len &= 0x07ff;
86 
87 	len = __skb_push(skb, INT51X1_HEADER_SIZE);
88 	*len = cpu_to_le16(pack_len);
89 
90 	if(need_tail)
91 		__skb_put_zero(skb, need_tail);
92 
93 	return skb;
94 }
95 
96 static const struct net_device_ops int51x1_netdev_ops = {
97 	.ndo_open		= usbnet_open,
98 	.ndo_stop		= usbnet_stop,
99 	.ndo_start_xmit		= usbnet_start_xmit,
100 	.ndo_tx_timeout		= usbnet_tx_timeout,
101 	.ndo_change_mtu		= usbnet_change_mtu,
102 	.ndo_get_stats64	= dev_get_tstats64,
103 	.ndo_set_mac_address	= eth_mac_addr,
104 	.ndo_validate_addr	= eth_validate_addr,
105 	.ndo_set_rx_mode	= usbnet_set_rx_mode,
106 };
107 
108 static int int51x1_bind(struct usbnet *dev, struct usb_interface *intf)
109 {
110 	int status = usbnet_get_ethernet_addr(dev, 3);
111 
112 	if (status)
113 		return status;
114 
115 	dev->net->hard_header_len += INT51X1_HEADER_SIZE;
116 	dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
117 	dev->net->netdev_ops = &int51x1_netdev_ops;
118 
119 	return usbnet_get_endpoints(dev, intf);
120 }
121 
122 static const struct driver_info int51x1_info = {
123 	.description = "Intellon usb powerline adapter",
124 	.bind        = int51x1_bind,
125 	.rx_fixup    = int51x1_rx_fixup,
126 	.tx_fixup    = int51x1_tx_fixup,
127 	.set_rx_mode = usbnet_cdc_update_filter,
128 	.in          = 1,
129 	.out         = 2,
130 	.flags       = FLAG_ETHER,
131 };
132 
133 static const struct usb_device_id products[] = {
134 	{
135 	USB_DEVICE(INT51X1_VENDOR_ID, INT51X1_PRODUCT_ID),
136 		.driver_info = (unsigned long) &int51x1_info,
137 	},
138 	{},
139 };
140 MODULE_DEVICE_TABLE(usb, products);
141 
142 static struct usb_driver int51x1_driver = {
143 	.name       = "int51x1",
144 	.id_table   = products,
145 	.probe      = usbnet_probe,
146 	.disconnect = usbnet_disconnect,
147 	.suspend    = usbnet_suspend,
148 	.resume     = usbnet_resume,
149 	.disable_hub_initiated_lpm = 1,
150 };
151 
152 module_usb_driver(int51x1_driver);
153 
154 MODULE_AUTHOR("Peter Holik");
155 MODULE_DESCRIPTION("Intellon usb powerline adapter");
156 MODULE_LICENSE("GPL");
157