Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^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");