linux/drivers/net/usb/int51x1.c
<<
>>
Prefs
   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 * http://www.tandel.be/downloads/INT51X1_Datasheet.pdf
   8 *
   9 * Based on the work of Jan 'RedBully' Seiffert
  10 */
  11
  12/*
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/ctype.h>
  17#include <linux/netdevice.h>
  18#include <linux/etherdevice.h>
  19#include <linux/ethtool.h>
  20#include <linux/slab.h>
  21#include <linux/mii.h>
  22#include <linux/usb.h>
  23#include <linux/usb/usbnet.h>
  24
  25#define INT51X1_VENDOR_ID       0x09e1
  26#define INT51X1_PRODUCT_ID      0x5121
  27
  28#define INT51X1_HEADER_SIZE     2       /* 2 byte header */
  29
  30#define PACKET_TYPE_PROMISCUOUS         (1 << 0)
  31#define PACKET_TYPE_ALL_MULTICAST       (1 << 1) /* no filter */
  32#define PACKET_TYPE_DIRECTED            (1 << 2)
  33#define PACKET_TYPE_BROADCAST           (1 << 3)
  34#define PACKET_TYPE_MULTICAST           (1 << 4) /* filtered */
  35
  36#define SET_ETHERNET_PACKET_FILTER      0x43
  37
  38static int int51x1_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
  39{
  40        int len;
  41
  42        if (!(pskb_may_pull(skb, INT51X1_HEADER_SIZE))) {
  43                netdev_err(dev->net, "unexpected tiny rx frame\n");
  44                return 0;
  45        }
  46
  47        len = le16_to_cpu(*(__le16 *)&skb->data[skb->len - 2]);
  48
  49        skb_trim(skb, len);
  50
  51        return 1;
  52}
  53
  54static struct sk_buff *int51x1_tx_fixup(struct usbnet *dev,
  55                struct sk_buff *skb, gfp_t flags)
  56{
  57        int pack_len = skb->len;
  58        int pack_with_header_len = pack_len + INT51X1_HEADER_SIZE;
  59        int headroom = skb_headroom(skb);
  60        int tailroom = skb_tailroom(skb);
  61        int need_tail = 0;
  62        __le16 *len;
  63
  64        /* if packet and our header is smaler than 64 pad to 64 (+ ZLP) */
  65        if ((pack_with_header_len) < dev->maxpacket)
  66                need_tail = dev->maxpacket - pack_with_header_len + 1;
  67        /*
  68         * usbnet would send a ZLP if packetlength mod urbsize == 0 for us,
  69         * but we need to know ourself, because this would add to the length
  70         * we send down to the device...
  71         */
  72        else if (!(pack_with_header_len % dev->maxpacket))
  73                need_tail = 1;
  74
  75        if (!skb_cloned(skb) &&
  76                        (headroom + tailroom >= need_tail + INT51X1_HEADER_SIZE)) {
  77                if (headroom < INT51X1_HEADER_SIZE || tailroom < need_tail) {
  78                        skb->data = memmove(skb->head + INT51X1_HEADER_SIZE,
  79                                        skb->data, skb->len);
  80                        skb_set_tail_pointer(skb, skb->len);
  81                }
  82        } else {
  83                struct sk_buff *skb2;
  84
  85                skb2 = skb_copy_expand(skb,
  86                                INT51X1_HEADER_SIZE,
  87                                need_tail,
  88                                flags);
  89                dev_kfree_skb_any(skb);
  90                if (!skb2)
  91                        return NULL;
  92                skb = skb2;
  93        }
  94
  95        pack_len += need_tail;
  96        pack_len &= 0x07ff;
  97
  98        len = __skb_push(skb, INT51X1_HEADER_SIZE);
  99        *len = cpu_to_le16(pack_len);
 100
 101        if(need_tail)
 102                __skb_put_zero(skb, need_tail);
 103
 104        return skb;
 105}
 106
 107static void int51x1_set_multicast(struct net_device *netdev)
 108{
 109        struct usbnet *dev = netdev_priv(netdev);
 110        u16 filter = PACKET_TYPE_DIRECTED | PACKET_TYPE_BROADCAST;
 111
 112        if (netdev->flags & IFF_PROMISC) {
 113                /* do not expect to see traffic of other PLCs */
 114                filter |= PACKET_TYPE_PROMISCUOUS;
 115                netdev_info(dev->net, "promiscuous mode enabled\n");
 116        } else if (!netdev_mc_empty(netdev) ||
 117                  (netdev->flags & IFF_ALLMULTI)) {
 118                filter |= PACKET_TYPE_ALL_MULTICAST;
 119                netdev_dbg(dev->net, "receive all multicast enabled\n");
 120        } else {
 121                /* ~PROMISCUOUS, ~MULTICAST */
 122                netdev_dbg(dev->net, "receive own packets only\n");
 123        }
 124
 125        usbnet_write_cmd_async(dev, SET_ETHERNET_PACKET_FILTER,
 126                               USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
 127                               filter, 0, NULL, 0);
 128}
 129
 130static const struct net_device_ops int51x1_netdev_ops = {
 131        .ndo_open               = usbnet_open,
 132        .ndo_stop               = usbnet_stop,
 133        .ndo_start_xmit         = usbnet_start_xmit,
 134        .ndo_tx_timeout         = usbnet_tx_timeout,
 135        .ndo_change_mtu         = usbnet_change_mtu,
 136        .ndo_get_stats64        = usbnet_get_stats64,
 137        .ndo_set_mac_address    = eth_mac_addr,
 138        .ndo_validate_addr      = eth_validate_addr,
 139        .ndo_set_rx_mode        = int51x1_set_multicast,
 140};
 141
 142static int int51x1_bind(struct usbnet *dev, struct usb_interface *intf)
 143{
 144        int status = usbnet_get_ethernet_addr(dev, 3);
 145
 146        if (status)
 147                return status;
 148
 149        dev->net->hard_header_len += INT51X1_HEADER_SIZE;
 150        dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
 151        dev->net->netdev_ops = &int51x1_netdev_ops;
 152
 153        return usbnet_get_endpoints(dev, intf);
 154}
 155
 156static const struct driver_info int51x1_info = {
 157        .description = "Intellon usb powerline adapter",
 158        .bind        = int51x1_bind,
 159        .rx_fixup    = int51x1_rx_fixup,
 160        .tx_fixup    = int51x1_tx_fixup,
 161        .in          = 1,
 162        .out         = 2,
 163        .flags       = FLAG_ETHER,
 164};
 165
 166static const struct usb_device_id products[] = {
 167        {
 168        USB_DEVICE(INT51X1_VENDOR_ID, INT51X1_PRODUCT_ID),
 169                .driver_info = (unsigned long) &int51x1_info,
 170        },
 171        {},
 172};
 173MODULE_DEVICE_TABLE(usb, products);
 174
 175static struct usb_driver int51x1_driver = {
 176        .name       = "int51x1",
 177        .id_table   = products,
 178        .probe      = usbnet_probe,
 179        .disconnect = usbnet_disconnect,
 180        .suspend    = usbnet_suspend,
 181        .resume     = usbnet_resume,
 182        .disable_hub_initiated_lpm = 1,
 183};
 184
 185module_usb_driver(int51x1_driver);
 186
 187MODULE_AUTHOR("Peter Holik");
 188MODULE_DESCRIPTION("Intellon usb powerline adapter");
 189MODULE_LICENSE("GPL");
 190