mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	usbnet: move the CDC parser into USB core
The dependencies were impossible to handle preventing drivers for CDC devices not which are not network drivers from using the common parser. Signed-off-by: Oliver Neukum <ONeukum@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									a1ca2c6b29
								
							
						
					
					
						commit
						e4c6fb7794
					
				
					 2 changed files with 153 additions and 138 deletions
				
			
		| 
						 | 
					@ -42,7 +42,6 @@
 | 
				
			||||||
#include <linux/mii.h>
 | 
					#include <linux/mii.h>
 | 
				
			||||||
#include <linux/usb.h>
 | 
					#include <linux/usb.h>
 | 
				
			||||||
#include <linux/usb/usbnet.h>
 | 
					#include <linux/usb/usbnet.h>
 | 
				
			||||||
#include <linux/usb/cdc.h>
 | 
					 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
#include <linux/kernel.h>
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
#include <linux/pm_runtime.h>
 | 
					#include <linux/pm_runtime.h>
 | 
				
			||||||
| 
						 | 
					@ -1968,143 +1967,6 @@ static int __usbnet_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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(cdc_parse_cdc_header);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * The function can't be called inside suspend/resume callback,
 | 
					 * The function can't be called inside suspend/resume callback,
 | 
				
			||||||
 * otherwise deadlock will be caused.
 | 
					 * otherwise deadlock will be caused.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@
 | 
				
			||||||
#include <linux/nls.h>
 | 
					#include <linux/nls.h>
 | 
				
			||||||
#include <linux/device.h>
 | 
					#include <linux/device.h>
 | 
				
			||||||
#include <linux/scatterlist.h>
 | 
					#include <linux/scatterlist.h>
 | 
				
			||||||
 | 
					#include <linux/usb/cdc.h>
 | 
				
			||||||
#include <linux/usb/quirks.h>
 | 
					#include <linux/usb/quirks.h>
 | 
				
			||||||
#include <linux/usb/hcd.h>	/* for usbcore internals */
 | 
					#include <linux/usb/hcd.h>	/* for usbcore internals */
 | 
				
			||||||
#include <asm/byteorder.h>
 | 
					#include <asm/byteorder.h>
 | 
				
			||||||
| 
						 | 
					@ -2023,3 +2024,155 @@ int usb_driver_set_configuration(struct usb_device *udev, int config)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
 | 
					EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * cdc_parse_cdc_header - parse the extra headers present in CDC devices
 | 
				
			||||||
 | 
					 * @hdr: the place to put the results of the parsing
 | 
				
			||||||
 | 
					 * @intf: the interface for which parsing is requested
 | 
				
			||||||
 | 
					 * @buffer: pointer to the extra headers to be parsed
 | 
				
			||||||
 | 
					 * @buflen: length of the extra headers
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This evaluates the extra headers present in CDC devices which
 | 
				
			||||||
 | 
					 * bind the interfaces for data and control and provide details
 | 
				
			||||||
 | 
					 * about the capabilities of the device.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: number of descriptors parsed or -EINVAL
 | 
				
			||||||
 | 
					 * if the header is contradictory beyond salvage
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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(cdc_parse_cdc_header);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue