aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/message.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/message.c')
-rw-r--r--drivers/usb/core/message.c153
1 files changed, 153 insertions, 0 deletions
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index ea681f157368..0406a59f0551 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -12,6 +12,7 @@
12#include <linux/nls.h> 12#include <linux/nls.h>
13#include <linux/device.h> 13#include <linux/device.h>
14#include <linux/scatterlist.h> 14#include <linux/scatterlist.h>
15#include <linux/usb/cdc.h>
15#include <linux/usb/quirks.h> 16#include <linux/usb/quirks.h>
16#include <linux/usb/hcd.h> /* for usbcore internals */ 17#include <linux/usb/hcd.h> /* for usbcore internals */
17#include <asm/byteorder.h> 18#include <asm/byteorder.h>
@@ -2023,3 +2024,155 @@ int usb_driver_set_configuration(struct usb_device *udev, int config)
2023 return 0; 2024 return 0;
2024} 2025}
2025EXPORT_SYMBOL_GPL(usb_driver_set_configuration); 2026EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
2027
2028/**
2029 * cdc_parse_cdc_header - parse the extra headers present in CDC devices
2030 * @hdr: the place to put the results of the parsing
2031 * @intf: the interface for which parsing is requested
2032 * @buffer: pointer to the extra headers to be parsed
2033 * @buflen: length of the extra headers
2034 *
2035 * This evaluates the extra headers present in CDC devices which
2036 * bind the interfaces for data and control and provide details
2037 * about the capabilities of the device.
2038 *
2039 * Return: number of descriptors parsed or -EINVAL
2040 * if the header is contradictory beyond salvage
2041 */
2042
2043int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr,
2044 struct usb_interface *intf,
2045 u8 *buffer,
2046 int buflen)
2047{
2048 /* duplicates are ignored */
2049 struct usb_cdc_union_desc *union_header = NULL;
2050
2051 /* duplicates are not tolerated */
2052 struct usb_cdc_header_desc *header = NULL;
2053 struct usb_cdc_ether_desc *ether = NULL;
2054 struct usb_cdc_mdlm_detail_desc *detail = NULL;
2055 struct usb_cdc_mdlm_desc *desc = NULL;
2056
2057 unsigned int elength;
2058 int cnt = 0;
2059
2060 memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header));
2061 hdr->phonet_magic_present = false;
2062 while (buflen > 0) {
2063 elength = buffer[0];
2064 if (!elength) {
2065 dev_err(&intf->dev, "skipping garbage byte\n");
2066 elength = 1;
2067 goto next_desc;
2068 }
2069 if (buffer[1] != USB_DT_CS_INTERFACE) {
2070 dev_err(&intf->dev, "skipping garbage\n");
2071 goto next_desc;
2072 }
2073
2074 switch (buffer[2]) {
2075 case USB_CDC_UNION_TYPE: /* we've found it */
2076 if (elength < sizeof(struct usb_cdc_union_desc))
2077 goto next_desc;
2078 if (union_header) {
2079 dev_err(&intf->dev, "More than one union descriptor, skipping ...\n");
2080 goto next_desc;
2081 }
2082 union_header = (struct usb_cdc_union_desc *)buffer;
2083 break;
2084 case USB_CDC_COUNTRY_TYPE:
2085 if (elength < sizeof(struct usb_cdc_country_functional_desc))
2086 goto next_desc;
2087 hdr->usb_cdc_country_functional_desc =
2088 (struct usb_cdc_country_functional_desc *)buffer;
2089 break;
2090 case USB_CDC_HEADER_TYPE:
2091 if (elength != sizeof(struct usb_cdc_header_desc))
2092 goto next_desc;
2093 if (header)
2094 return -EINVAL;
2095 header = (struct usb_cdc_header_desc *)buffer;
2096 break;
2097 case USB_CDC_ACM_TYPE:
2098 if (elength < sizeof(struct usb_cdc_acm_descriptor))
2099 goto next_desc;
2100 hdr->usb_cdc_acm_descriptor =
2101 (struct usb_cdc_acm_descriptor *)buffer;
2102 break;
2103 case USB_CDC_ETHERNET_TYPE:
2104 if (elength != sizeof(struct usb_cdc_ether_desc))
2105 goto next_desc;
2106 if (ether)
2107 return -EINVAL;
2108 ether = (struct usb_cdc_ether_desc *)buffer;
2109 break;
2110 case USB_CDC_CALL_MANAGEMENT_TYPE:
2111 if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor))
2112 goto next_desc;
2113 hdr->usb_cdc_call_mgmt_descriptor =
2114 (struct usb_cdc_call_mgmt_descriptor *)buffer;
2115 break;
2116 case USB_CDC_DMM_TYPE:
2117 if (elength < sizeof(struct usb_cdc_dmm_desc))
2118 goto next_desc;
2119 hdr->usb_cdc_dmm_desc =
2120 (struct usb_cdc_dmm_desc *)buffer;
2121 break;
2122 case USB_CDC_MDLM_TYPE:
2123 if (elength < sizeof(struct usb_cdc_mdlm_desc *))
2124 goto next_desc;
2125 if (desc)
2126 return -EINVAL;
2127 desc = (struct usb_cdc_mdlm_desc *)buffer;
2128 break;
2129 case USB_CDC_MDLM_DETAIL_TYPE:
2130 if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *))
2131 goto next_desc;
2132 if (detail)
2133 return -EINVAL;
2134 detail = (struct usb_cdc_mdlm_detail_desc *)buffer;
2135 break;
2136 case USB_CDC_NCM_TYPE:
2137 if (elength < sizeof(struct usb_cdc_ncm_desc))
2138 goto next_desc;
2139 hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer;
2140 break;
2141 case USB_CDC_MBIM_TYPE:
2142 if (elength < sizeof(struct usb_cdc_mbim_desc))
2143 goto next_desc;
2144
2145 hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer;
2146 break;
2147 case USB_CDC_MBIM_EXTENDED_TYPE:
2148 if (elength < sizeof(struct usb_cdc_mbim_extended_desc))
2149 break;
2150 hdr->usb_cdc_mbim_extended_desc =
2151 (struct usb_cdc_mbim_extended_desc *)buffer;
2152 break;
2153 case CDC_PHONET_MAGIC_NUMBER:
2154 hdr->phonet_magic_present = true;
2155 break;
2156 default:
2157 /*
2158 * there are LOTS more CDC descriptors that
2159 * could legitimately be found here.
2160 */
2161 dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n",
2162 buffer[2], elength);
2163 goto next_desc;
2164 }
2165 cnt++;
2166next_desc:
2167 buflen -= elength;
2168 buffer += elength;
2169 }
2170 hdr->usb_cdc_union_desc = union_header;
2171 hdr->usb_cdc_header_desc = header;
2172 hdr->usb_cdc_mdlm_detail_desc = detail;
2173 hdr->usb_cdc_mdlm_desc = desc;
2174 hdr->usb_cdc_ether_desc = ether;
2175 return cnt;
2176}
2177
2178EXPORT_SYMBOL(cdc_parse_cdc_header);