^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * USB network interface driver for Samsung Kalmia based LTE USB modem like the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Samsung GT-B3730 and GT-B3710.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (C) 2011 Marius Bjoernstad Kotsbak <marius@kotsbak.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Sponsored by Quicklink Video Distribution Services Ltd.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Based on the cdc_eem module.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/netdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/etherdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/ctype.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/ethtool.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/workqueue.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/mii.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/usb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/crc32.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/usb/cdc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/usb/usbnet.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/gfp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * The Samsung Kalmia based LTE USB modems have a CDC ACM port for modem control
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * handled by the "option" module and an ethernet data port handled by this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * module.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * The stick must first be switched into modem mode by usb_modeswitch
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * or similar tool. Then the modem gets sent two initialization packets by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * this module, which gives the MAC address of the device. User space can then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * connect the modem using AT commands through the ACM port and then use
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * DHCP on the network interface exposed by this module. Network packets are
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * sent to and from the modem in a proprietary format discovered after watching
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * the behavior of the windows driver for the modem.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) * More information about the use of the modem is available in usb_modeswitch
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) * forum and the project page:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * http://www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?t=465
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) * https://github.com/mkotsbak/Samsung-GT-B3730-linux-driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) /* #define DEBUG */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) /* #define VERBOSE */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #define KALMIA_HEADER_LENGTH 6
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) #define KALMIA_ALIGN_SIZE 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) #define KALMIA_USB_TIMEOUT 10000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) /*-------------------------------------------------------------------------*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) static int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) kalmia_send_init_packet(struct usbnet *dev, u8 *init_msg, u8 init_msg_len,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) u8 *buffer, u8 expected_len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) int act_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) int status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) netdev_dbg(dev->net, "Sending init packet");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) status = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 0x02),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) init_msg, init_msg_len, &act_len, KALMIA_USB_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) if (status != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) netdev_err(dev->net,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) "Error sending init packet. Status %i, length %i\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) status, act_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) return status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) else if (act_len != init_msg_len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) netdev_err(dev->net,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) "Did not send all of init packet. Bytes sent: %i",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) act_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) netdev_dbg(dev->net, "Successfully sent init packet.");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) status = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, 0x81),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) buffer, expected_len, &act_len, KALMIA_USB_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) if (status != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) netdev_err(dev->net,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) "Error receiving init result. Status %i, length %i\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) status, act_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) else if (act_len != expected_len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) netdev_err(dev->net, "Unexpected init result length: %i\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) act_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) return status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) static int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) kalmia_init_and_get_ethernet_addr(struct usbnet *dev, u8 *ethernet_addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) static const char init_msg_1[] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) { 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 0x00, 0x00 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) static const char init_msg_2[] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) { 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xf4,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) 0x00, 0x00 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) static const int buflen = 28;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) char *usb_buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) int status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) usb_buf = kmalloc(buflen, GFP_DMA | GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (!usb_buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) memcpy(usb_buf, init_msg_1, 12);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) status = kalmia_send_init_packet(dev, usb_buf, ARRAY_SIZE(init_msg_1),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) usb_buf, 24);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) if (status != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) memcpy(usb_buf, init_msg_2, 12);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) status = kalmia_send_init_packet(dev, usb_buf, ARRAY_SIZE(init_msg_2),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) usb_buf, 28);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) if (status != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) memcpy(ethernet_addr, usb_buf + 10, ETH_ALEN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) kfree(usb_buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) return status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) static int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) kalmia_bind(struct usbnet *dev, struct usb_interface *intf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) int status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) u8 ethernet_addr[ETH_ALEN];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) /* Don't bind to AT command interface */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) dev->in = usb_rcvbulkpipe(dev->udev, 0x81 & USB_ENDPOINT_NUMBER_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) dev->out = usb_sndbulkpipe(dev->udev, 0x02 & USB_ENDPOINT_NUMBER_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) dev->status = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) dev->net->hard_header_len += KALMIA_HEADER_LENGTH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) dev->hard_mtu = 1400;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) dev->rx_urb_size = dev->hard_mtu * 10; // Found as optimal after testing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) status = kalmia_init_and_get_ethernet_addr(dev, ethernet_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) if (status)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) return status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) memcpy(dev->net->dev_addr, ethernet_addr, ETH_ALEN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) return status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) static struct sk_buff *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) kalmia_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) struct sk_buff *skb2 = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) u16 content_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) unsigned char *header_start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) unsigned char ether_type_1, ether_type_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) u8 remainder, padlen = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) if (!skb_cloned(skb)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) int headroom = skb_headroom(skb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) int tailroom = skb_tailroom(skb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) if ((tailroom >= KALMIA_ALIGN_SIZE) && (headroom
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) >= KALMIA_HEADER_LENGTH))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) if ((headroom + tailroom) > (KALMIA_HEADER_LENGTH
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) + KALMIA_ALIGN_SIZE)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) skb->data = memmove(skb->head + KALMIA_HEADER_LENGTH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) skb->data, skb->len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) skb_set_tail_pointer(skb, skb->len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) goto done;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) skb2 = skb_copy_expand(skb, KALMIA_HEADER_LENGTH,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) KALMIA_ALIGN_SIZE, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) if (!skb2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) dev_kfree_skb_any(skb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) skb = skb2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) done:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) header_start = skb_push(skb, KALMIA_HEADER_LENGTH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) ether_type_1 = header_start[KALMIA_HEADER_LENGTH + 12];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) ether_type_2 = header_start[KALMIA_HEADER_LENGTH + 13];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) netdev_dbg(dev->net, "Sending etherType: %02x%02x", ether_type_1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) ether_type_2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) /* According to empiric data for data packages */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) header_start[0] = 0x57;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) header_start[1] = 0x44;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) content_len = skb->len - KALMIA_HEADER_LENGTH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) put_unaligned_le16(content_len, &header_start[2]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) header_start[4] = ether_type_1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) header_start[5] = ether_type_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) /* Align to 4 bytes by padding with zeros */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) remainder = skb->len % KALMIA_ALIGN_SIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) if (remainder > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) padlen = KALMIA_ALIGN_SIZE - remainder;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) skb_put_zero(skb, padlen);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) netdev_dbg(dev->net,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) "Sending package with length %i and padding %i. Header: %6phC.",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) content_len, padlen, header_start);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) return skb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) static int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) * Our task here is to strip off framing, leaving skb with one
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) * data frame for the usbnet framework code to process.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) static const u8 HEADER_END_OF_USB_PACKET[] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) { 0x57, 0x5a, 0x00, 0x00, 0x08, 0x00 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) static const u8 EXPECTED_UNKNOWN_HEADER_1[] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) { 0x57, 0x43, 0x1e, 0x00, 0x15, 0x02 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) static const u8 EXPECTED_UNKNOWN_HEADER_2[] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) { 0x57, 0x50, 0x0e, 0x00, 0x00, 0x00 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) int i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) /* incomplete header? */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) if (skb->len < KALMIA_HEADER_LENGTH)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) do {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) struct sk_buff *skb2 = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) u8 *header_start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) u16 usb_packet_length, ether_packet_length;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) int is_last;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) header_start = skb->data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) if (unlikely(header_start[0] != 0x57 || header_start[1] != 0x44)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) if (!memcmp(header_start, EXPECTED_UNKNOWN_HEADER_1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) sizeof(EXPECTED_UNKNOWN_HEADER_1)) || !memcmp(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) header_start, EXPECTED_UNKNOWN_HEADER_2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) sizeof(EXPECTED_UNKNOWN_HEADER_2))) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) netdev_dbg(dev->net,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) "Received expected unknown frame header: %6phC. Package length: %i\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) header_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) skb->len - KALMIA_HEADER_LENGTH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) netdev_err(dev->net,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) "Received unknown frame header: %6phC. Package length: %i\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) header_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) skb->len - KALMIA_HEADER_LENGTH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) netdev_dbg(dev->net,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) "Received header: %6phC. Package length: %i\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) header_start, skb->len - KALMIA_HEADER_LENGTH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) /* subtract start header and end header */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) ether_packet_length = get_unaligned_le16(&header_start[2]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) skb_pull(skb, KALMIA_HEADER_LENGTH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) /* Some small packets misses end marker */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) if (usb_packet_length < ether_packet_length) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) ether_packet_length = usb_packet_length
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) + KALMIA_HEADER_LENGTH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) is_last = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) netdev_dbg(dev->net, "Correct package length #%i", i
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) is_last = (memcmp(skb->data + ether_packet_length,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) HEADER_END_OF_USB_PACKET,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) sizeof(HEADER_END_OF_USB_PACKET)) == 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) if (!is_last) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) header_start = skb->data + ether_packet_length;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) netdev_dbg(dev->net,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) "End header: %6phC. Package length: %i\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) header_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) skb->len - KALMIA_HEADER_LENGTH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) if (is_last) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) skb2 = skb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) skb2 = skb_clone(skb, GFP_ATOMIC);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) if (unlikely(!skb2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) skb_trim(skb2, ether_packet_length);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) if (is_last) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) usbnet_skb_return(dev, skb2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) skb_pull(skb, ether_packet_length);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) i++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) while (skb->len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) static const struct driver_info kalmia_info = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) .description = "Samsung Kalmia LTE USB dongle",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) .flags = FLAG_WWAN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) .bind = kalmia_bind,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) .rx_fixup = kalmia_rx_fixup,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) .tx_fixup = kalmia_tx_fixup
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) /*-------------------------------------------------------------------------*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) static const struct usb_device_id products[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) /* The unswitched USB ID, to get the module auto loaded: */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) { USB_DEVICE(0x04e8, 0x689a) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) /* The stick switched into modem (by e.g. usb_modeswitch): */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) { USB_DEVICE(0x04e8, 0x6889),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) .driver_info = (unsigned long) &kalmia_info, },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) { /* EMPTY == end of list */} };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) MODULE_DEVICE_TABLE( usb, products);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) static struct usb_driver kalmia_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) .name = "kalmia",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) .id_table = products,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) .probe = usbnet_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) .disconnect = usbnet_disconnect,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) .suspend = usbnet_suspend,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) .resume = usbnet_resume,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) .disable_hub_initiated_lpm = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) module_usb_driver(kalmia_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) MODULE_AUTHOR("Marius Bjoernstad Kotsbak <marius@kotsbak.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) MODULE_DESCRIPTION("Samsung Kalmia USB network driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) MODULE_LICENSE("GPL");