| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * acm_ms.c -- Composite driver, with ACM and mass storage support | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2008 David Brownell | 
					
						
							|  |  |  |  * Copyright (C) 2008 Nokia Corporation | 
					
						
							|  |  |  |  * Author: David Brownell | 
					
						
							|  |  |  |  * Modified: Klaus Schwarzkopf <schwarzkopf@sensortherm.de> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Heavily based on multi.c and cdc2.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; either version 2 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:27 +02:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "u_serial.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DRIVER_DESC		"Composite Gadget (ACM + MS)"
 | 
					
						
							|  |  |  | #define DRIVER_VERSION		"2011/10/10"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*-------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!! | 
					
						
							|  |  |  |  * Instead:  allocate your own, using normal USB-IF procedures. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define ACM_MS_VENDOR_NUM	0x1d6b	/* Linux Foundation */
 | 
					
						
							|  |  |  | #define ACM_MS_PRODUCT_NUM	0x0106	/* Composite Gadget: ACM + MS*/
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | #include "f_mass_storage.h"
 | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*-------------------------------------------------------------------------*/ | 
					
						
							| 
									
										
										
										
											2012-09-10 15:01:53 +02:00
										 |  |  | USB_GADGET_COMPOSITE_OPTIONS(); | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | static struct usb_device_descriptor device_desc = { | 
					
						
							|  |  |  | 	.bLength =		sizeof device_desc, | 
					
						
							|  |  |  | 	.bDescriptorType =	USB_DT_DEVICE, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.bcdUSB =		cpu_to_le16(0x0200), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.bDeviceClass =		USB_CLASS_MISC /* 0xEF */, | 
					
						
							|  |  |  | 	.bDeviceSubClass =	2, | 
					
						
							|  |  |  | 	.bDeviceProtocol =	1, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* .bMaxPacketSize0 = f(hardware) */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Vendor and product id can be overridden by module parameters.  */ | 
					
						
							|  |  |  | 	.idVendor =		cpu_to_le16(ACM_MS_VENDOR_NUM), | 
					
						
							|  |  |  | 	.idProduct =		cpu_to_le16(ACM_MS_PRODUCT_NUM), | 
					
						
							|  |  |  | 	/* .bcdDevice = f(hardware) */ | 
					
						
							|  |  |  | 	/* .iManufacturer = DYNAMIC */ | 
					
						
							|  |  |  | 	/* .iProduct = DYNAMIC */ | 
					
						
							|  |  |  | 	/* NO SERIAL NUMBER */ | 
					
						
							|  |  |  | 	/*.bNumConfigurations =	DYNAMIC*/ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct usb_otg_descriptor otg_descriptor = { | 
					
						
							|  |  |  | 	.bLength =		sizeof otg_descriptor, | 
					
						
							|  |  |  | 	.bDescriptorType =	USB_DT_OTG, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * REVISIT SRP-only hardware is possible, although | 
					
						
							|  |  |  | 	 * it would not be called "OTG" ... | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	.bmAttributes =		USB_OTG_SRP | USB_OTG_HNP, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct usb_descriptor_header *otg_desc[] = { | 
					
						
							|  |  |  | 	(struct usb_descriptor_header *) &otg_descriptor, | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* string IDs are assigned dynamically */ | 
					
						
							|  |  |  | static struct usb_string strings_dev[] = { | 
					
						
							| 
									
										
										
										
											2012-09-10 15:01:58 +02:00
										 |  |  | 	[USB_GADGET_MANUFACTURER_IDX].s = "", | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:21 +02:00
										 |  |  | 	[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, | 
					
						
							|  |  |  | 	[USB_GADGET_SERIAL_IDX].s = "", | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	{  } /* end of list */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct usb_gadget_strings stringtab_dev = { | 
					
						
							|  |  |  | 	.language	= 0x0409,	/* en-us */ | 
					
						
							|  |  |  | 	.strings	= strings_dev, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct usb_gadget_strings *dev_strings[] = { | 
					
						
							|  |  |  | 	&stringtab_dev, | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /****************************** Configurations ******************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; | 
					
						
							| 
									
										
										
										
											2013-09-26 14:38:16 +02:00
										 |  |  | #ifdef CONFIG_USB_GADGET_DEBUG_FILES
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Number of buffers we will use. | 
					
						
							|  |  |  |  * 2 is usually enough for good buffering pipeline | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define fsg_num_buffers	CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif /* CONFIG_USB_DEBUG */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*-------------------------------------------------------------------------*/ | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:08 +01:00
										 |  |  | static struct usb_function *f_acm; | 
					
						
							|  |  |  | static struct usb_function_instance *f_acm_inst; | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | static struct usb_function_instance *fi_msg; | 
					
						
							|  |  |  | static struct usb_function *f_msg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * We _always_ have both ACM and mass storage functions. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int __init acm_ms_do_config(struct usb_configuration *c) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 	struct fsg_opts *opts; | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	int	status; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (gadget_is_otg(c->cdev->gadget)) { | 
					
						
							|  |  |  | 		c->descriptors = otg_desc; | 
					
						
							|  |  |  | 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 	opts = fsg_opts_from_func_inst(fi_msg); | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	f_acm = usb_get_function(f_acm_inst); | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 	if (IS_ERR(f_acm)) | 
					
						
							|  |  |  | 		return PTR_ERR(f_acm); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	f_msg = usb_get_function(fi_msg); | 
					
						
							|  |  |  | 	if (IS_ERR(f_msg)) { | 
					
						
							|  |  |  | 		status = PTR_ERR(f_msg); | 
					
						
							|  |  |  | 		goto put_acm; | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:08 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:08 +01:00
										 |  |  | 	status = usb_add_function(c, f_acm); | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	if (status < 0) | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 		goto put_msg; | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 	status = fsg_common_run_thread(opts->common); | 
					
						
							|  |  |  | 	if (status) | 
					
						
							|  |  |  | 		goto remove_acm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	status = usb_add_function(c, f_msg); | 
					
						
							|  |  |  | 	if (status) | 
					
						
							|  |  |  | 		goto remove_acm; | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | remove_acm: | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:08 +01:00
										 |  |  | 	usb_remove_function(c, f_acm); | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | put_msg: | 
					
						
							|  |  |  | 	usb_put_function(f_msg); | 
					
						
							|  |  |  | put_acm: | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:08 +01:00
										 |  |  | 	usb_put_function(f_acm); | 
					
						
							|  |  |  | 	return status; | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct usb_configuration acm_ms_config_driver = { | 
					
						
							|  |  |  | 	.label			= DRIVER_DESC, | 
					
						
							|  |  |  | 	.bConfigurationValue	= 1, | 
					
						
							|  |  |  | 	/* .iConfiguration = DYNAMIC */ | 
					
						
							|  |  |  | 	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*-------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init acm_ms_bind(struct usb_composite_dev *cdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct usb_gadget	*gadget = cdev->gadget; | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 	struct fsg_opts		*opts; | 
					
						
							|  |  |  | 	struct fsg_config	config; | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	int			status; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 	f_acm_inst = usb_get_function_instance("acm"); | 
					
						
							|  |  |  | 	if (IS_ERR(f_acm_inst)) | 
					
						
							|  |  |  | 		return PTR_ERR(f_acm_inst); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fi_msg = usb_get_function_instance("mass_storage"); | 
					
						
							|  |  |  | 	if (IS_ERR(fi_msg)) { | 
					
						
							|  |  |  | 		status = PTR_ERR(fi_msg); | 
					
						
							|  |  |  | 		goto fail_get_msg; | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 	/* set up mass storage function */ | 
					
						
							|  |  |  | 	fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers); | 
					
						
							|  |  |  | 	opts = fsg_opts_from_func_inst(fi_msg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts->no_configfs = true; | 
					
						
							|  |  |  | 	status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers); | 
					
						
							|  |  |  | 	if (status) | 
					
						
							|  |  |  | 		goto fail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	status = fsg_common_set_nluns(opts->common, config.nluns); | 
					
						
							|  |  |  | 	if (status) | 
					
						
							|  |  |  | 		goto fail_set_nluns; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	status = fsg_common_set_cdev(opts->common, cdev, config.can_stall); | 
					
						
							|  |  |  | 	if (status) | 
					
						
							|  |  |  | 		goto fail_set_cdev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fsg_common_set_sysfs(opts->common, true); | 
					
						
							|  |  |  | 	status = fsg_common_create_luns(opts->common, &config); | 
					
						
							|  |  |  | 	if (status) | 
					
						
							|  |  |  | 		goto fail_set_cdev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fsg_common_set_inquiry_string(opts->common, config.vendor_name, | 
					
						
							|  |  |  | 				      config.product_name); | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Allocate string descriptor numbers ... note that string | 
					
						
							|  |  |  | 	 * contents can be overridden by the composite_dev glue. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:16 +02:00
										 |  |  | 	status = usb_string_ids_tab(cdev, strings_dev); | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	if (status < 0) | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 		goto fail_string_ids; | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:21 +02:00
										 |  |  | 	device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; | 
					
						
							|  |  |  | 	device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* register our configuration */ | 
					
						
							|  |  |  | 	status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config); | 
					
						
							|  |  |  | 	if (status < 0) | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 		goto fail_string_ids; | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-10 15:01:53 +02:00
										 |  |  | 	usb_composite_overwrite_options(cdev, &coverwrite); | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", | 
					
						
							|  |  |  | 			DRIVER_DESC); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* error recovery */ | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | fail_string_ids: | 
					
						
							|  |  |  | 	fsg_common_remove_luns(opts->common); | 
					
						
							|  |  |  | fail_set_cdev: | 
					
						
							|  |  |  | 	fsg_common_free_luns(opts->common); | 
					
						
							|  |  |  | fail_set_nluns: | 
					
						
							|  |  |  | 	fsg_common_free_buffers(opts->common); | 
					
						
							|  |  |  | fail: | 
					
						
							|  |  |  | 	usb_put_function_instance(fi_msg); | 
					
						
							|  |  |  | fail_get_msg: | 
					
						
							|  |  |  | 	usb_put_function_instance(f_acm_inst); | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	return status; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-10-09 10:07:29 +02:00
										 |  |  | 	usb_put_function(f_msg); | 
					
						
							|  |  |  | 	usb_put_function_instance(fi_msg); | 
					
						
							| 
									
										
										
										
											2012-12-23 21:10:08 +01:00
										 |  |  | 	usb_put_function(f_acm); | 
					
						
							|  |  |  | 	usb_put_function_instance(f_acm_inst); | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:03 +02:00
										 |  |  | static __refdata struct usb_composite_driver acm_ms_driver = { | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	.name		= "g_acm_ms", | 
					
						
							|  |  |  | 	.dev		= &device_desc, | 
					
						
							| 
									
										
										
										
											2012-06-22 14:16:19 +10:00
										 |  |  | 	.max_speed	= USB_SPEED_SUPER, | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	.strings	= dev_strings, | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:04 +02:00
										 |  |  | 	.bind		= acm_ms_bind, | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | 	.unbind		= __exit_p(acm_ms_unbind), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_DESCRIPTION(DRIVER_DESC); | 
					
						
							|  |  |  | MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL v2"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-09-06 20:11:04 +02:00
										 |  |  | 	return usb_composite_probe(&acm_ms_driver); | 
					
						
							| 
									
										
										
										
											2011-10-10 10:32:23 +02:00
										 |  |  | } | 
					
						
							|  |  |  | module_init(init); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __exit cleanup(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	usb_composite_unregister(&acm_ms_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | module_exit(cleanup); |