Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (c) 2009 Peter Holik
0004  *
0005  * Intellon usb PLC (Powerline Communications) usb net driver
0006  *
0007  * http://www.tandel.be/downloads/INT51X1_Datasheet.pdf
0008  *
0009  * Based on the work of Jan 'RedBully' Seiffert
0010  */
0011 
0012 /*
0013  */
0014 
0015 #include <linux/module.h>
0016 #include <linux/ctype.h>
0017 #include <linux/netdevice.h>
0018 #include <linux/etherdevice.h>
0019 #include <linux/ethtool.h>
0020 #include <linux/slab.h>
0021 #include <linux/mii.h>
0022 #include <linux/usb.h>
0023 #include <linux/usb/usbnet.h>
0024 
0025 #define INT51X1_VENDOR_ID   0x09e1
0026 #define INT51X1_PRODUCT_ID  0x5121
0027 
0028 #define INT51X1_HEADER_SIZE 2   /* 2 byte header */
0029 
0030 #define PACKET_TYPE_PROMISCUOUS     (1 << 0)
0031 #define PACKET_TYPE_ALL_MULTICAST   (1 << 1) /* no filter */
0032 #define PACKET_TYPE_DIRECTED        (1 << 2)
0033 #define PACKET_TYPE_BROADCAST       (1 << 3)
0034 #define PACKET_TYPE_MULTICAST       (1 << 4) /* filtered */
0035 
0036 #define SET_ETHERNET_PACKET_FILTER  0x43
0037 
0038 static int int51x1_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
0039 {
0040     int len;
0041 
0042     if (!(pskb_may_pull(skb, INT51X1_HEADER_SIZE))) {
0043         netdev_err(dev->net, "unexpected tiny rx frame\n");
0044         return 0;
0045     }
0046 
0047     len = le16_to_cpu(*(__le16 *)&skb->data[skb->len - 2]);
0048 
0049     skb_trim(skb, len);
0050 
0051     return 1;
0052 }
0053 
0054 static struct sk_buff *int51x1_tx_fixup(struct usbnet *dev,
0055         struct sk_buff *skb, gfp_t flags)
0056 {
0057     int pack_len = skb->len;
0058     int pack_with_header_len = pack_len + INT51X1_HEADER_SIZE;
0059     int headroom = skb_headroom(skb);
0060     int tailroom = skb_tailroom(skb);
0061     int need_tail = 0;
0062     __le16 *len;
0063 
0064     /* if packet and our header is smaller than 64 pad to 64 (+ ZLP) */
0065     if ((pack_with_header_len) < dev->maxpacket)
0066         need_tail = dev->maxpacket - pack_with_header_len + 1;
0067     /*
0068      * usbnet would send a ZLP if packetlength mod urbsize == 0 for us,
0069      * but we need to know ourself, because this would add to the length
0070      * we send down to the device...
0071      */
0072     else if (!(pack_with_header_len % dev->maxpacket))
0073         need_tail = 1;
0074 
0075     if (!skb_cloned(skb) &&
0076             (headroom + tailroom >= need_tail + INT51X1_HEADER_SIZE)) {
0077         if (headroom < INT51X1_HEADER_SIZE || tailroom < need_tail) {
0078             skb->data = memmove(skb->head + INT51X1_HEADER_SIZE,
0079                     skb->data, skb->len);
0080             skb_set_tail_pointer(skb, skb->len);
0081         }
0082     } else {
0083         struct sk_buff *skb2;
0084 
0085         skb2 = skb_copy_expand(skb,
0086                 INT51X1_HEADER_SIZE,
0087                 need_tail,
0088                 flags);
0089         dev_kfree_skb_any(skb);
0090         if (!skb2)
0091             return NULL;
0092         skb = skb2;
0093     }
0094 
0095     pack_len += need_tail;
0096     pack_len &= 0x07ff;
0097 
0098     len = __skb_push(skb, INT51X1_HEADER_SIZE);
0099     *len = cpu_to_le16(pack_len);
0100 
0101     if(need_tail)
0102         __skb_put_zero(skb, need_tail);
0103 
0104     return skb;
0105 }
0106 
0107 static void int51x1_set_multicast(struct net_device *netdev)
0108 {
0109     struct usbnet *dev = netdev_priv(netdev);
0110     u16 filter = PACKET_TYPE_DIRECTED | PACKET_TYPE_BROADCAST;
0111 
0112     if (netdev->flags & IFF_PROMISC) {
0113         /* do not expect to see traffic of other PLCs */
0114         filter |= PACKET_TYPE_PROMISCUOUS;
0115         netdev_info(dev->net, "promiscuous mode enabled\n");
0116     } else if (!netdev_mc_empty(netdev) ||
0117           (netdev->flags & IFF_ALLMULTI)) {
0118         filter |= PACKET_TYPE_ALL_MULTICAST;
0119         netdev_dbg(dev->net, "receive all multicast enabled\n");
0120     } else {
0121         /* ~PROMISCUOUS, ~MULTICAST */
0122         netdev_dbg(dev->net, "receive own packets only\n");
0123     }
0124 
0125     usbnet_write_cmd_async(dev, SET_ETHERNET_PACKET_FILTER,
0126                    USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0127                    filter, 0, NULL, 0);
0128 }
0129 
0130 static const struct net_device_ops int51x1_netdev_ops = {
0131     .ndo_open       = usbnet_open,
0132     .ndo_stop       = usbnet_stop,
0133     .ndo_start_xmit     = usbnet_start_xmit,
0134     .ndo_tx_timeout     = usbnet_tx_timeout,
0135     .ndo_change_mtu     = usbnet_change_mtu,
0136     .ndo_get_stats64    = dev_get_tstats64,
0137     .ndo_set_mac_address    = eth_mac_addr,
0138     .ndo_validate_addr  = eth_validate_addr,
0139     .ndo_set_rx_mode    = int51x1_set_multicast,
0140 };
0141 
0142 static int int51x1_bind(struct usbnet *dev, struct usb_interface *intf)
0143 {
0144     int status = usbnet_get_ethernet_addr(dev, 3);
0145 
0146     if (status)
0147         return status;
0148 
0149     dev->net->hard_header_len += INT51X1_HEADER_SIZE;
0150     dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
0151     dev->net->netdev_ops = &int51x1_netdev_ops;
0152 
0153     return usbnet_get_endpoints(dev, intf);
0154 }
0155 
0156 static const struct driver_info int51x1_info = {
0157     .description = "Intellon usb powerline adapter",
0158     .bind        = int51x1_bind,
0159     .rx_fixup    = int51x1_rx_fixup,
0160     .tx_fixup    = int51x1_tx_fixup,
0161     .in          = 1,
0162     .out         = 2,
0163     .flags       = FLAG_ETHER,
0164 };
0165 
0166 static const struct usb_device_id products[] = {
0167     {
0168     USB_DEVICE(INT51X1_VENDOR_ID, INT51X1_PRODUCT_ID),
0169         .driver_info = (unsigned long) &int51x1_info,
0170     },
0171     {},
0172 };
0173 MODULE_DEVICE_TABLE(usb, products);
0174 
0175 static struct usb_driver int51x1_driver = {
0176     .name       = "int51x1",
0177     .id_table   = products,
0178     .probe      = usbnet_probe,
0179     .disconnect = usbnet_disconnect,
0180     .suspend    = usbnet_suspend,
0181     .resume     = usbnet_resume,
0182     .disable_hub_initiated_lpm = 1,
0183 };
0184 
0185 module_usb_driver(int51x1_driver);
0186 
0187 MODULE_AUTHOR("Peter Holik");
0188 MODULE_DESCRIPTION("Intellon usb powerline adapter");
0189 MODULE_LICENSE("GPL");