diff options
Diffstat (limited to 'drivers/usb/core/message.c')
-rw-r--r-- | drivers/usb/core/message.c | 153 |
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 | } |
2025 | EXPORT_SYMBOL_GPL(usb_driver_set_configuration); | 2026 | EXPORT_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 | |||
2043 | int 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++; | ||
2166 | next_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 | |||
2178 | EXPORT_SYMBOL(cdc_parse_cdc_header); | ||