0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/module.h>
0014 #include <linux/netdevice.h>
0015 #include <linux/etherdevice.h>
0016 #include <linux/ctype.h>
0017 #include <linux/ethtool.h>
0018 #include <linux/workqueue.h>
0019 #include <linux/mii.h>
0020 #include <linux/usb.h>
0021 #include <linux/crc32.h>
0022 #include <linux/usb/cdc.h>
0023 #include <linux/usb/usbnet.h>
0024 #include <linux/gfp.h>
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049 #define KALMIA_HEADER_LENGTH 6
0050 #define KALMIA_ALIGN_SIZE 4
0051 #define KALMIA_USB_TIMEOUT 10000
0052
0053
0054
0055 static int
0056 kalmia_send_init_packet(struct usbnet *dev, u8 *init_msg, u8 init_msg_len,
0057 u8 *buffer, u8 expected_len)
0058 {
0059 int act_len;
0060 int status;
0061
0062 netdev_dbg(dev->net, "Sending init packet");
0063
0064 status = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 0x02),
0065 init_msg, init_msg_len, &act_len, KALMIA_USB_TIMEOUT);
0066 if (status != 0) {
0067 netdev_err(dev->net,
0068 "Error sending init packet. Status %i, length %i\n",
0069 status, act_len);
0070 return status;
0071 }
0072 else if (act_len != init_msg_len) {
0073 netdev_err(dev->net,
0074 "Did not send all of init packet. Bytes sent: %i",
0075 act_len);
0076 }
0077 else {
0078 netdev_dbg(dev->net, "Successfully sent init packet.");
0079 }
0080
0081 status = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, 0x81),
0082 buffer, expected_len, &act_len, KALMIA_USB_TIMEOUT);
0083
0084 if (status != 0)
0085 netdev_err(dev->net,
0086 "Error receiving init result. Status %i, length %i\n",
0087 status, act_len);
0088 else if (act_len != expected_len)
0089 netdev_err(dev->net, "Unexpected init result length: %i\n",
0090 act_len);
0091
0092 return status;
0093 }
0094
0095 static int
0096 kalmia_init_and_get_ethernet_addr(struct usbnet *dev, u8 *ethernet_addr)
0097 {
0098 static const char init_msg_1[] =
0099 { 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
0100 0x00, 0x00 };
0101 static const char init_msg_2[] =
0102 { 0x57, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xf4,
0103 0x00, 0x00 };
0104 static const int buflen = 28;
0105 char *usb_buf;
0106 int status;
0107
0108 usb_buf = kmalloc(buflen, GFP_DMA | GFP_KERNEL);
0109 if (!usb_buf)
0110 return -ENOMEM;
0111
0112 memcpy(usb_buf, init_msg_1, 12);
0113 status = kalmia_send_init_packet(dev, usb_buf, ARRAY_SIZE(init_msg_1),
0114 usb_buf, 24);
0115 if (status != 0)
0116 goto out;
0117
0118 memcpy(usb_buf, init_msg_2, 12);
0119 status = kalmia_send_init_packet(dev, usb_buf, ARRAY_SIZE(init_msg_2),
0120 usb_buf, 28);
0121 if (status != 0)
0122 goto out;
0123
0124 memcpy(ethernet_addr, usb_buf + 10, ETH_ALEN);
0125 out:
0126 kfree(usb_buf);
0127 return status;
0128 }
0129
0130 static int
0131 kalmia_bind(struct usbnet *dev, struct usb_interface *intf)
0132 {
0133 int status;
0134 u8 ethernet_addr[ETH_ALEN];
0135
0136
0137 if (intf->cur_altsetting->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC)
0138 return -EINVAL;
0139
0140 dev->in = usb_rcvbulkpipe(dev->udev, 0x81 & USB_ENDPOINT_NUMBER_MASK);
0141 dev->out = usb_sndbulkpipe(dev->udev, 0x02 & USB_ENDPOINT_NUMBER_MASK);
0142 dev->status = NULL;
0143
0144 dev->net->hard_header_len += KALMIA_HEADER_LENGTH;
0145 dev->hard_mtu = 1400;
0146 dev->rx_urb_size = dev->hard_mtu * 10;
0147
0148 status = kalmia_init_and_get_ethernet_addr(dev, ethernet_addr);
0149 if (status)
0150 return status;
0151
0152 eth_hw_addr_set(dev->net, ethernet_addr);
0153
0154 return status;
0155 }
0156
0157 static struct sk_buff *
0158 kalmia_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
0159 {
0160 struct sk_buff *skb2 = NULL;
0161 u16 content_len;
0162 unsigned char *header_start;
0163 unsigned char ether_type_1, ether_type_2;
0164 u8 remainder, padlen = 0;
0165
0166 if (!skb_cloned(skb)) {
0167 int headroom = skb_headroom(skb);
0168 int tailroom = skb_tailroom(skb);
0169
0170 if ((tailroom >= KALMIA_ALIGN_SIZE) && (headroom
0171 >= KALMIA_HEADER_LENGTH))
0172 goto done;
0173
0174 if ((headroom + tailroom) > (KALMIA_HEADER_LENGTH
0175 + KALMIA_ALIGN_SIZE)) {
0176 skb->data = memmove(skb->head + KALMIA_HEADER_LENGTH,
0177 skb->data, skb->len);
0178 skb_set_tail_pointer(skb, skb->len);
0179 goto done;
0180 }
0181 }
0182
0183 skb2 = skb_copy_expand(skb, KALMIA_HEADER_LENGTH,
0184 KALMIA_ALIGN_SIZE, flags);
0185 if (!skb2)
0186 return NULL;
0187
0188 dev_kfree_skb_any(skb);
0189 skb = skb2;
0190
0191 done:
0192 header_start = skb_push(skb, KALMIA_HEADER_LENGTH);
0193 ether_type_1 = header_start[KALMIA_HEADER_LENGTH + 12];
0194 ether_type_2 = header_start[KALMIA_HEADER_LENGTH + 13];
0195
0196 netdev_dbg(dev->net, "Sending etherType: %02x%02x", ether_type_1,
0197 ether_type_2);
0198
0199
0200 header_start[0] = 0x57;
0201 header_start[1] = 0x44;
0202 content_len = skb->len - KALMIA_HEADER_LENGTH;
0203
0204 put_unaligned_le16(content_len, &header_start[2]);
0205 header_start[4] = ether_type_1;
0206 header_start[5] = ether_type_2;
0207
0208
0209 remainder = skb->len % KALMIA_ALIGN_SIZE;
0210 if (remainder > 0) {
0211 padlen = KALMIA_ALIGN_SIZE - remainder;
0212 skb_put_zero(skb, padlen);
0213 }
0214
0215 netdev_dbg(dev->net,
0216 "Sending package with length %i and padding %i. Header: %6phC.",
0217 content_len, padlen, header_start);
0218
0219 return skb;
0220 }
0221
0222 static int
0223 kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
0224 {
0225
0226
0227
0228
0229 static const u8 HEADER_END_OF_USB_PACKET[] =
0230 { 0x57, 0x5a, 0x00, 0x00, 0x08, 0x00 };
0231 static const u8 EXPECTED_UNKNOWN_HEADER_1[] =
0232 { 0x57, 0x43, 0x1e, 0x00, 0x15, 0x02 };
0233 static const u8 EXPECTED_UNKNOWN_HEADER_2[] =
0234 { 0x57, 0x50, 0x0e, 0x00, 0x00, 0x00 };
0235 int i = 0;
0236
0237
0238 if (skb->len < KALMIA_HEADER_LENGTH)
0239 return 0;
0240
0241 do {
0242 struct sk_buff *skb2 = NULL;
0243 u8 *header_start;
0244 u16 usb_packet_length, ether_packet_length;
0245 int is_last;
0246
0247 header_start = skb->data;
0248
0249 if (unlikely(header_start[0] != 0x57 || header_start[1] != 0x44)) {
0250 if (!memcmp(header_start, EXPECTED_UNKNOWN_HEADER_1,
0251 sizeof(EXPECTED_UNKNOWN_HEADER_1)) || !memcmp(
0252 header_start, EXPECTED_UNKNOWN_HEADER_2,
0253 sizeof(EXPECTED_UNKNOWN_HEADER_2))) {
0254 netdev_dbg(dev->net,
0255 "Received expected unknown frame header: %6phC. Package length: %i\n",
0256 header_start,
0257 skb->len - KALMIA_HEADER_LENGTH);
0258 }
0259 else {
0260 netdev_err(dev->net,
0261 "Received unknown frame header: %6phC. Package length: %i\n",
0262 header_start,
0263 skb->len - KALMIA_HEADER_LENGTH);
0264 return 0;
0265 }
0266 }
0267 else
0268 netdev_dbg(dev->net,
0269 "Received header: %6phC. Package length: %i\n",
0270 header_start, skb->len - KALMIA_HEADER_LENGTH);
0271
0272
0273 usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH);
0274 ether_packet_length = get_unaligned_le16(&header_start[2]);
0275 skb_pull(skb, KALMIA_HEADER_LENGTH);
0276
0277
0278 if (usb_packet_length < ether_packet_length) {
0279 ether_packet_length = usb_packet_length
0280 + KALMIA_HEADER_LENGTH;
0281 is_last = true;
0282 }
0283 else {
0284 netdev_dbg(dev->net, "Correct package length #%i", i
0285 + 1);
0286
0287 is_last = (memcmp(skb->data + ether_packet_length,
0288 HEADER_END_OF_USB_PACKET,
0289 sizeof(HEADER_END_OF_USB_PACKET)) == 0);
0290 if (!is_last) {
0291 header_start = skb->data + ether_packet_length;
0292 netdev_dbg(dev->net,
0293 "End header: %6phC. Package length: %i\n",
0294 header_start,
0295 skb->len - KALMIA_HEADER_LENGTH);
0296 }
0297 }
0298
0299 if (is_last) {
0300 skb2 = skb;
0301 }
0302 else {
0303 skb2 = skb_clone(skb, GFP_ATOMIC);
0304 if (unlikely(!skb2))
0305 return 0;
0306 }
0307
0308 skb_trim(skb2, ether_packet_length);
0309
0310 if (is_last) {
0311 return 1;
0312 }
0313 else {
0314 usbnet_skb_return(dev, skb2);
0315 skb_pull(skb, ether_packet_length);
0316 }
0317
0318 i++;
0319 }
0320 while (skb->len);
0321
0322 return 1;
0323 }
0324
0325 static const struct driver_info kalmia_info = {
0326 .description = "Samsung Kalmia LTE USB dongle",
0327 .flags = FLAG_WWAN,
0328 .bind = kalmia_bind,
0329 .rx_fixup = kalmia_rx_fixup,
0330 .tx_fixup = kalmia_tx_fixup
0331 };
0332
0333
0334
0335 static const struct usb_device_id products[] = {
0336
0337 { USB_DEVICE(0x04e8, 0x689a) },
0338
0339 { USB_DEVICE(0x04e8, 0x6889),
0340 .driver_info = (unsigned long) &kalmia_info, },
0341 { } };
0342 MODULE_DEVICE_TABLE( usb, products);
0343
0344 static struct usb_driver kalmia_driver = {
0345 .name = "kalmia",
0346 .id_table = products,
0347 .probe = usbnet_probe,
0348 .disconnect = usbnet_disconnect,
0349 .suspend = usbnet_suspend,
0350 .resume = usbnet_resume,
0351 .disable_hub_initiated_lpm = 1,
0352 };
0353
0354 module_usb_driver(kalmia_driver);
0355
0356 MODULE_AUTHOR("Marius Bjoernstad Kotsbak <marius@kotsbak.com>");
0357 MODULE_DESCRIPTION("Samsung Kalmia USB network driver");
0358 MODULE_LICENSE("GPL");