backports: add cdc_parse_cdc_header()
authorJohannes Berg <johannes.berg@intel.com>
Tue, 7 Feb 2017 20:12:16 +0000 (21:12 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 7 Feb 2017 20:30:54 +0000 (21:30 +0100)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
backport/backport-include/linux/usb/cdc.h [new file with mode: 0644]
backport/compat/Makefile
backport/compat/backport-4.8.c [new file with mode: 0644]

diff --git a/backport/backport-include/linux/usb/cdc.h b/backport/backport-include/linux/usb/cdc.h
new file mode 100644 (file)
index 0000000..89ebcf4
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef __BP_USB_CDC_H
+#define __BP_USB_CDC_H
+#include <linux/version.h>
+
+#if LINUX_VERSION_IS_GEQ(4,8,0)
+#include_next <linux/usb/cdc.h>
+#else
+#include <uapi/linux/usb/cdc.h>
+
+/*
+ * inofficial magic numbers
+ */
+
+#define CDC_PHONET_MAGIC_NUMBER                0xAB
+
+/*
+ * parsing CDC headers
+ */
+
+struct usb_cdc_parsed_header {
+       struct usb_cdc_union_desc *usb_cdc_union_desc;
+       struct usb_cdc_header_desc *usb_cdc_header_desc;
+
+       struct usb_cdc_call_mgmt_descriptor *usb_cdc_call_mgmt_descriptor;
+       struct usb_cdc_acm_descriptor *usb_cdc_acm_descriptor;
+       struct usb_cdc_country_functional_desc *usb_cdc_country_functional_desc;
+       struct usb_cdc_network_terminal_desc *usb_cdc_network_terminal_desc;
+       struct usb_cdc_ether_desc *usb_cdc_ether_desc;
+       struct usb_cdc_dmm_desc *usb_cdc_dmm_desc;
+       struct usb_cdc_mdlm_desc *usb_cdc_mdlm_desc;
+       struct usb_cdc_mdlm_detail_desc *usb_cdc_mdlm_detail_desc;
+       struct usb_cdc_obex_desc *usb_cdc_obex_desc;
+       struct usb_cdc_ncm_desc *usb_cdc_ncm_desc;
+       struct usb_cdc_mbim_desc *usb_cdc_mbim_desc;
+       struct usb_cdc_mbim_extended_desc *usb_cdc_mbim_extended_desc;
+
+       bool phonet_magic_present;
+};
+
+#define cdc_parse_cdc_header LINUX_BACKPORT(cdc_parse_cdc_header)
+int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
+                        struct usb_interface *intf,
+                        u8 *buffer, int buflen);
+#endif
+
+#endif /* __BP_USB_CDC_H */
index 8cf1a7b83c8fd63be985eac7198db0acccd3a897..95d1bd2fa35dc1b11e9005aed7b9fe6022ba74de 100644 (file)
@@ -34,6 +34,7 @@ compat-$(CPTCFG_KERNEL_4_4) += backport-4.4.o
 compat-$(CPTCFG_KERNEL_4_5) += backport-4.5.o
 compat-$(CPTCFG_KERNEL_4_6) += backport-4.6.o
 compat-$(CPTCFG_KERNEL_4_7) += backport-4.7.o
+compat-$(CPTCFG_KERNEL_4_8) += backport-4.8.o
 
 compat-$(CPTCFG_BPAUTO_BUILD_CRYPTO_CCM) += crypto-ccm.o
 compat-$(CPTCFG_BPAUTO_CRYPTO_SKCIPHER) += crypto-skcipher.o
diff --git a/backport/compat/backport-4.8.c b/backport/compat/backport-4.8.c
new file mode 100644 (file)
index 0000000..7d05644
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright(c) 2017 Intel Deutschland GmbH
+ *
+ * Backport functionality introduced in Linux 4.8.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+
+int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
+                        struct usb_interface *intf,
+                        u8 *buffer, int buflen)
+{
+       /* duplicates are ignored */
+       struct usb_cdc_union_desc *union_header = NULL;
+
+       /* duplicates are not tolerated */
+       struct usb_cdc_header_desc *header = NULL;
+       struct usb_cdc_ether_desc *ether = NULL;
+       struct usb_cdc_mdlm_detail_desc *detail = NULL;
+       struct usb_cdc_mdlm_desc *desc = NULL;
+
+       unsigned int elength;
+       int cnt = 0;
+
+       memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header));
+       hdr->phonet_magic_present = false;
+       while (buflen > 0) {
+               elength = buffer[0];
+               if (!elength) {
+                       dev_err(&intf->dev, "skipping garbage byte\n");
+                       elength = 1;
+                       goto next_desc;
+               }
+               if (buffer[1] != USB_DT_CS_INTERFACE) {
+                       dev_err(&intf->dev, "skipping garbage\n");
+                       goto next_desc;
+               }
+
+               switch (buffer[2]) {
+               case USB_CDC_UNION_TYPE: /* we've found it */
+                       if (elength < sizeof(struct usb_cdc_union_desc))
+                               goto next_desc;
+                       if (union_header) {
+                               dev_err(&intf->dev, "More than one union descriptor, skipping ...\n");
+                               goto next_desc;
+                       }
+                       union_header = (struct usb_cdc_union_desc *)buffer;
+                       break;
+               case USB_CDC_COUNTRY_TYPE:
+                       if (elength < sizeof(struct usb_cdc_country_functional_desc))
+                               goto next_desc;
+                       hdr->usb_cdc_country_functional_desc =
+                               (struct usb_cdc_country_functional_desc *)buffer;
+                       break;
+               case USB_CDC_HEADER_TYPE:
+                       if (elength != sizeof(struct usb_cdc_header_desc))
+                               goto next_desc;
+                       if (header)
+                               return -EINVAL;
+                       header = (struct usb_cdc_header_desc *)buffer;
+                       break;
+               case USB_CDC_ACM_TYPE:
+                       if (elength < sizeof(struct usb_cdc_acm_descriptor))
+                               goto next_desc;
+                       hdr->usb_cdc_acm_descriptor =
+                               (struct usb_cdc_acm_descriptor *)buffer;
+                       break;
+               case USB_CDC_ETHERNET_TYPE:
+                       if (elength != sizeof(struct usb_cdc_ether_desc))
+                               goto next_desc;
+                       if (ether)
+                               return -EINVAL;
+                       ether = (struct usb_cdc_ether_desc *)buffer;
+                       break;
+               case USB_CDC_CALL_MANAGEMENT_TYPE:
+                       if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor))
+                               goto next_desc;
+                       hdr->usb_cdc_call_mgmt_descriptor =
+                               (struct usb_cdc_call_mgmt_descriptor *)buffer;
+                       break;
+               case USB_CDC_DMM_TYPE:
+                       if (elength < sizeof(struct usb_cdc_dmm_desc))
+                               goto next_desc;
+                       hdr->usb_cdc_dmm_desc =
+                               (struct usb_cdc_dmm_desc *)buffer;
+                       break;
+               case USB_CDC_MDLM_TYPE:
+                       if (elength < sizeof(struct usb_cdc_mdlm_desc *))
+                               goto next_desc;
+                       if (desc)
+                               return -EINVAL;
+                       desc = (struct usb_cdc_mdlm_desc *)buffer;
+                       break;
+               case USB_CDC_MDLM_DETAIL_TYPE:
+                       if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *))
+                               goto next_desc;
+                       if (detail)
+                               return -EINVAL;
+                       detail = (struct usb_cdc_mdlm_detail_desc *)buffer;
+                       break;
+               case USB_CDC_NCM_TYPE:
+                       if (elength < sizeof(struct usb_cdc_ncm_desc))
+                               goto next_desc;
+                       hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer;
+                       break;
+               case USB_CDC_MBIM_TYPE:
+                       if (elength < sizeof(struct usb_cdc_mbim_desc))
+                               goto next_desc;
+
+                       hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer;
+                       break;
+               case USB_CDC_MBIM_EXTENDED_TYPE:
+                       if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
+                               break;
+                       hdr->usb_cdc_mbim_extended_desc =
+                               (struct usb_cdc_mbim_extended_desc *)buffer;
+                       break;
+               case CDC_PHONET_MAGIC_NUMBER:
+                       hdr->phonet_magic_present = true;
+                       break;
+               default:
+                       /*
+                        * there are LOTS more CDC descriptors that
+                        * could legitimately be found here.
+                        */
+                       dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n",
+                                       buffer[2], elength);
+                       goto next_desc;
+               }
+               cnt++;
+next_desc:
+               buflen -= elength;
+               buffer += elength;
+       }
+       hdr->usb_cdc_union_desc = union_header;
+       hdr->usb_cdc_header_desc = header;
+       hdr->usb_cdc_mdlm_detail_desc = detail;
+       hdr->usb_cdc_mdlm_desc = desc;
+       hdr->usb_cdc_ether_desc = ether;
+       return cnt;
+}
+EXPORT_SYMBOL_GPL(cdc_parse_cdc_header);