| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Gadget Driver for Android | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2008 Google, Inc. | 
|  | 5 | * Author: Mike Lockwood <lockwood@android.com> | 
|  | 6 | *         Benoit Goby <benoit@android.com> | 
|  | 7 | * | 
|  | 8 | * This software is licensed under the terms of the GNU General Public | 
|  | 9 | * License version 2, as published by the Free Software Foundation, and | 
|  | 10 | * may be copied, distributed, and modified under those terms. | 
|  | 11 | * | 
|  | 12 | * This program is distributed in the hope that it will be useful, | 
|  | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 15 | * GNU General Public License for more details. | 
|  | 16 | * | 
|  | 17 | */ | 
|  | 18 |  | 
|  | 19 | #include <linux/init.h> | 
|  | 20 | #include <linux/module.h> | 
|  | 21 | #include <linux/fs.h> | 
|  | 22 | #include <linux/delay.h> | 
|  | 23 | #include <linux/kernel.h> | 
|  | 24 | #include <linux/utsname.h> | 
|  | 25 | #include <linux/platform_device.h> | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 26 | #include <linux/pm_qos.h> | 
| Vijayavardhan Vennapusa | 8ceade8 | 2012-11-01 15:11:21 +0530 | [diff] [blame] | 27 | #include <linux/of.h> | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 28 |  | 
|  | 29 | #include <linux/usb/ch9.h> | 
|  | 30 | #include <linux/usb/composite.h> | 
|  | 31 | #include <linux/usb/gadget.h> | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 32 | #include <linux/usb/android.h> | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 33 |  | 
| Manu Gautam | 43c61a1 | 2012-08-22 17:09:37 -0700 | [diff] [blame] | 34 | #include <mach/diag_dload.h> | 
|  | 35 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 36 | #include "gadget_chips.h" | 
|  | 37 |  | 
|  | 38 | /* | 
|  | 39 | * Kbuild is not very cooperative with respect to linking separately | 
|  | 40 | * compiled library objects into one module.  So for now we won't use | 
|  | 41 | * separate compilation ... ensuring init/exit sections work to shrink | 
|  | 42 | * the runtime footprint, and giving us at least some parts of what | 
|  | 43 | * a "gcc --combine ... part1.c part2.c part3.c ... " build would. | 
|  | 44 | */ | 
|  | 45 | #include "usbstring.c" | 
|  | 46 | #include "config.c" | 
|  | 47 | #include "epautoconf.c" | 
|  | 48 | #include "composite.c" | 
|  | 49 |  | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 50 | #include "f_diag.c" | 
| Shimrit Malichi | a00d732 | 2012-08-05 13:56:28 +0300 | [diff] [blame] | 51 | #include "f_qdss.c" | 
| Manu Gautam | 1c8ffd7 | 2011-09-02 16:00:49 +0530 | [diff] [blame] | 52 | #include "f_rmnet_smd.c" | 
| Manu Gautam | 8e0719b | 2011-09-26 14:47:55 +0530 | [diff] [blame] | 53 | #include "f_rmnet_sdio.c" | 
|  | 54 | #include "f_rmnet_smd_sdio.c" | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 55 | #include "f_rmnet.c" | 
| Anna Perel | 3ee23dd | 2013-02-26 16:06:40 +0200 | [diff] [blame] | 56 | #ifdef CONFIG_SND_PCM | 
| Mike Lockwood | 1187482 | 2012-08-27 16:43:53 +0530 | [diff] [blame] | 57 | #include "f_audio_source.c" | 
| Anna Perel | 3ee23dd | 2013-02-26 16:06:40 +0200 | [diff] [blame] | 58 | #endif | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 59 | #include "f_mass_storage.c" | 
|  | 60 | #include "u_serial.c" | 
| Manu Gautam | a4d993f | 2011-08-30 18:25:55 +0530 | [diff] [blame] | 61 | #include "u_sdio.c" | 
|  | 62 | #include "u_smd.c" | 
|  | 63 | #include "u_bam.c" | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 64 | #include "u_rmnet_ctrl_smd.c" | 
| Bar Weiner | 0dae81b | 2013-02-14 13:53:54 +0200 | [diff] [blame] | 65 | #include "u_rmnet_ctrl_qti.c" | 
| Jack Pham | 427f692 | 2011-11-23 19:42:00 -0800 | [diff] [blame] | 66 | #include "u_ctrl_hsic.c" | 
|  | 67 | #include "u_data_hsic.c" | 
| Vijayavardhan Vennapusa | eb8d239 | 2012-04-03 18:58:49 +0530 | [diff] [blame] | 68 | #include "u_ctrl_hsuart.c" | 
|  | 69 | #include "u_data_hsuart.c" | 
| Manu Gautam | a4d993f | 2011-08-30 18:25:55 +0530 | [diff] [blame] | 70 | #include "f_serial.c" | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 71 | #include "f_acm.c" | 
| Benoit Goby | 2b6862d | 2011-12-19 14:38:41 -0800 | [diff] [blame] | 72 | #include "f_adb.c" | 
| Chiranjeevi Velempati | e130fd0 | 2011-11-29 05:06:13 +0530 | [diff] [blame] | 73 | #include "f_ccid.c" | 
| Benoit Goby | f0fbc48 | 2011-12-19 14:37:50 -0800 | [diff] [blame] | 74 | #include "f_mtp.c" | 
| Benoit Goby | cf3fc06 | 2011-12-19 14:39:37 -0800 | [diff] [blame] | 75 | #include "f_accessory.c" | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 76 | #define USB_ETH_RNDIS y | 
|  | 77 | #include "f_rndis.c" | 
|  | 78 | #include "rndis.c" | 
| Amit Blay | f9b352b | 2013-03-04 15:01:40 +0200 | [diff] [blame] | 79 | #include "f_qc_ecm.c" | 
| Anna Perel | a8c991d | 2012-04-09 16:44:46 +0300 | [diff] [blame] | 80 | #include "u_bam_data.c" | 
|  | 81 | #include "f_mbim.c" | 
| Anna Perel | f9d0155 | 2012-11-20 15:56:32 +0200 | [diff] [blame] | 82 | #include "f_ecm.c" | 
| Ofir Cohen | aef90b7 | 2012-07-31 12:37:04 +0200 | [diff] [blame] | 83 | #include "f_qc_rndis.c" | 
| Jack Pham | 0ad82e6 | 2012-09-27 17:31:08 -0700 | [diff] [blame] | 84 | #include "u_ether.c" | 
| Ofir Cohen | 7b15542 | 2012-07-31 13:02:49 +0300 | [diff] [blame] | 85 | #include "u_qc_ether.c" | 
| Pavankumar Kondeti | 8f6ca4f | 2012-06-26 09:44:36 +0530 | [diff] [blame] | 86 | #ifdef CONFIG_TARGET_CORE | 
|  | 87 | #include "f_tcm.c" | 
|  | 88 | #endif | 
| Jack Pham | 2ec5fdc | 2012-09-26 10:13:48 -0700 | [diff] [blame] | 89 | #ifdef CONFIG_SND_PCM | 
| Anna Perel | 432367a | 2012-09-20 10:55:32 +0300 | [diff] [blame] | 90 | #include "u_uac1.c" | 
|  | 91 | #include "f_uac1.c" | 
| Jack Pham | 2ec5fdc | 2012-09-26 10:13:48 -0700 | [diff] [blame] | 92 | #endif | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 93 |  | 
|  | 94 | MODULE_AUTHOR("Mike Lockwood"); | 
|  | 95 | MODULE_DESCRIPTION("Android Composite USB Driver"); | 
|  | 96 | MODULE_LICENSE("GPL"); | 
|  | 97 | MODULE_VERSION("1.0"); | 
|  | 98 |  | 
|  | 99 | static const char longname[] = "Gadget Android"; | 
|  | 100 |  | 
|  | 101 | /* Default vendor and product IDs, overridden by userspace */ | 
|  | 102 | #define VENDOR_ID		0x18D1 | 
|  | 103 | #define PRODUCT_ID		0x0001 | 
|  | 104 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 105 | #define ANDROID_DEVICE_NODE_NAME_LENGTH 11 | 
|  | 106 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 107 | struct android_usb_function { | 
|  | 108 | char *name; | 
|  | 109 | void *config; | 
|  | 110 |  | 
|  | 111 | struct device *dev; | 
|  | 112 | char *dev_name; | 
|  | 113 | struct device_attribute **attributes; | 
|  | 114 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 115 | struct android_dev *android_dev; | 
|  | 116 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 117 | /* Optional: initialization during gadget bind */ | 
|  | 118 | int (*init)(struct android_usb_function *, struct usb_composite_dev *); | 
|  | 119 | /* Optional: cleanup during gadget unbind */ | 
|  | 120 | void (*cleanup)(struct android_usb_function *); | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 121 | /* Optional: called when the function is added the list of | 
|  | 122 | *		enabled functions */ | 
|  | 123 | void (*enable)(struct android_usb_function *); | 
|  | 124 | /* Optional: called when it is removed */ | 
|  | 125 | void (*disable)(struct android_usb_function *); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 126 |  | 
|  | 127 | int (*bind_config)(struct android_usb_function *, | 
|  | 128 | struct usb_configuration *); | 
|  | 129 |  | 
|  | 130 | /* Optional: called when the configuration is removed */ | 
|  | 131 | void (*unbind_config)(struct android_usb_function *, | 
|  | 132 | struct usb_configuration *); | 
|  | 133 | /* Optional: handle ctrl requests before the device is configured */ | 
|  | 134 | int (*ctrlrequest)(struct android_usb_function *, | 
|  | 135 | struct usb_composite_dev *, | 
|  | 136 | const struct usb_ctrlrequest *); | 
|  | 137 | }; | 
|  | 138 |  | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 139 | struct android_usb_function_holder { | 
|  | 140 |  | 
|  | 141 | struct android_usb_function *f; | 
|  | 142 |  | 
|  | 143 | /* for android_conf.enabled_functions */ | 
|  | 144 | struct list_head enabled_list; | 
|  | 145 | }; | 
|  | 146 |  | 
| Ido Shayevitz | fb5edfe | 2012-12-26 14:26:37 +0200 | [diff] [blame] | 147 | /** | 
|  | 148 | * struct android_dev - represents android USB gadget device | 
|  | 149 | * @name: device name. | 
|  | 150 | * @functions: an array of all the supported USB function | 
|  | 151 | *    drivers that this gadget support but not necessarily | 
|  | 152 | *    added to one of the gadget configurations. | 
|  | 153 | * @cdev: The internal composite device. Android gadget device | 
|  | 154 | *    is a composite device, such that it can support configurations | 
|  | 155 | *    with more than one function driver. | 
|  | 156 | * @dev: The kernel device that represents this android device. | 
|  | 157 | * @enabled: True if the android gadget is enabled, means all | 
|  | 158 | *    the configurations were set and all function drivers were | 
|  | 159 | *    bind and ready for USB enumeration. | 
|  | 160 | * @disable_depth: Number of times the device was disabled, after | 
|  | 161 | *    symmetrical number of enables the device willl be enabled. | 
|  | 162 | *    Used for controlling ADB userspace disable/enable requests. | 
|  | 163 | * @mutex: Internal mutex for protecting device member fields. | 
|  | 164 | * @pdata: Platform data fetched from the kernel device platfrom data. | 
|  | 165 | * @connected: True if got connect notification from the gadget UDC. | 
|  | 166 | *    False if got disconnect notification from the gadget UDC. | 
|  | 167 | * @sw_connected: Equal to 'connected' only after the connect | 
|  | 168 | *    notification was handled by the android gadget work function. | 
|  | 169 | * @suspended: True if got suspend notification from the gadget UDC. | 
|  | 170 | *    False if got resume notification from the gadget UDC. | 
|  | 171 | * @sw_suspended: Equal to 'suspended' only after the susped | 
|  | 172 | *    notification was handled by the android gadget work function. | 
|  | 173 | * @pm_qos: An attribute string that can be set by user space in order to | 
|  | 174 | *    determine pm_qos policy. Set to 'high' for always demand pm_qos | 
|  | 175 | *    when USB bus is connected and resumed. Set to 'low' for disable | 
|  | 176 | *    any setting of pm_qos by this driver. Default = 'high'. | 
|  | 177 | * @work: workqueue used for handling notifications from the gadget UDC. | 
|  | 178 | * @configs: List of configurations currently configured into the device. | 
|  | 179 | *    The android gadget supports more than one configuration. The host | 
|  | 180 | *    may choose one configuration from the suggested. | 
|  | 181 | * @configs_num: Number of configurations currently configured and existing | 
|  | 182 | *    in the configs list. | 
|  | 183 | * @list_item: This driver supports more than one android gadget device (for | 
|  | 184 | *    example in order to support multiple USB cores), therefore this is | 
|  | 185 | *    a item in a linked list of android devices. | 
|  | 186 | */ | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 187 | struct android_dev { | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 188 | const char *name; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 189 | struct android_usb_function **functions; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 190 | struct usb_composite_dev *cdev; | 
|  | 191 | struct device *dev; | 
|  | 192 |  | 
|  | 193 | bool enabled; | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 194 | int disable_depth; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 195 | struct mutex mutex; | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 196 | struct android_usb_platform_data *pdata; | 
|  | 197 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 198 | bool connected; | 
|  | 199 | bool sw_connected; | 
| Ido Shayevitz | fb5edfe | 2012-12-26 14:26:37 +0200 | [diff] [blame] | 200 | bool suspended; | 
|  | 201 | bool sw_suspended; | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 202 | char pm_qos[5]; | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 203 | struct pm_qos_request pm_qos_req_dma; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 204 | struct work_struct work; | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 205 |  | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 206 | /* A list of struct android_configuration */ | 
|  | 207 | struct list_head configs; | 
|  | 208 | int configs_num; | 
|  | 209 |  | 
|  | 210 | /* A list node inside the android_dev_list */ | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 211 | struct list_head list_item; | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 212 | }; | 
|  | 213 |  | 
|  | 214 | struct android_configuration { | 
|  | 215 | struct usb_configuration usb_config; | 
|  | 216 |  | 
|  | 217 | /* A list of the functions supported by this config */ | 
|  | 218 | struct list_head enabled_functions; | 
|  | 219 |  | 
|  | 220 | /* A list node inside the struct android_dev.configs list */ | 
|  | 221 | struct list_head list_item; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 222 | }; | 
|  | 223 |  | 
| Manu Gautam | 43c61a1 | 2012-08-22 17:09:37 -0700 | [diff] [blame] | 224 | struct dload_struct __iomem *diag_dload; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 225 | static struct class *android_class; | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 226 | static struct list_head android_dev_list; | 
|  | 227 | static int android_dev_count; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 228 | static int android_bind_config(struct usb_configuration *c); | 
|  | 229 | static void android_unbind_config(struct usb_configuration *c); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 230 | static struct android_dev *cdev_to_android_dev(struct usb_composite_dev *cdev); | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 231 | static struct android_configuration *alloc_android_config | 
|  | 232 | (struct android_dev *dev); | 
|  | 233 | static void free_android_config(struct android_dev *dev, | 
|  | 234 | struct android_configuration *conf); | 
| Manu Gautam | 43c61a1 | 2012-08-22 17:09:37 -0700 | [diff] [blame] | 235 | static int usb_diag_update_pid_and_serial_num(uint32_t pid, const char *snum); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 236 |  | 
|  | 237 | /* string IDs are assigned dynamically */ | 
|  | 238 | #define STRING_MANUFACTURER_IDX		0 | 
|  | 239 | #define STRING_PRODUCT_IDX		1 | 
|  | 240 | #define STRING_SERIAL_IDX		2 | 
|  | 241 |  | 
|  | 242 | static char manufacturer_string[256]; | 
|  | 243 | static char product_string[256]; | 
|  | 244 | static char serial_string[256]; | 
|  | 245 |  | 
|  | 246 | /* String Table */ | 
|  | 247 | static struct usb_string strings_dev[] = { | 
|  | 248 | [STRING_MANUFACTURER_IDX].s = manufacturer_string, | 
|  | 249 | [STRING_PRODUCT_IDX].s = product_string, | 
|  | 250 | [STRING_SERIAL_IDX].s = serial_string, | 
|  | 251 | {  }			/* end of list */ | 
|  | 252 | }; | 
|  | 253 |  | 
|  | 254 | static struct usb_gadget_strings stringtab_dev = { | 
|  | 255 | .language	= 0x0409,	/* en-us */ | 
|  | 256 | .strings	= strings_dev, | 
|  | 257 | }; | 
|  | 258 |  | 
|  | 259 | static struct usb_gadget_strings *dev_strings[] = { | 
|  | 260 | &stringtab_dev, | 
|  | 261 | NULL, | 
|  | 262 | }; | 
|  | 263 |  | 
|  | 264 | static struct usb_device_descriptor device_desc = { | 
|  | 265 | .bLength              = sizeof(device_desc), | 
|  | 266 | .bDescriptorType      = USB_DT_DEVICE, | 
|  | 267 | .bcdUSB               = __constant_cpu_to_le16(0x0200), | 
|  | 268 | .bDeviceClass         = USB_CLASS_PER_INTERFACE, | 
|  | 269 | .idVendor             = __constant_cpu_to_le16(VENDOR_ID), | 
|  | 270 | .idProduct            = __constant_cpu_to_le16(PRODUCT_ID), | 
|  | 271 | .bcdDevice            = __constant_cpu_to_le16(0xffff), | 
|  | 272 | .bNumConfigurations   = 1, | 
|  | 273 | }; | 
|  | 274 |  | 
| Vijayavardhan Vennapusa | 56e6052 | 2012-02-16 15:40:16 +0530 | [diff] [blame] | 275 | static struct usb_otg_descriptor otg_descriptor = { | 
|  | 276 | .bLength =		sizeof otg_descriptor, | 
|  | 277 | .bDescriptorType =	USB_DT_OTG, | 
|  | 278 | .bmAttributes =		USB_OTG_SRP | USB_OTG_HNP, | 
|  | 279 | .bcdOTG               = __constant_cpu_to_le16(0x0200), | 
|  | 280 | }; | 
|  | 281 |  | 
|  | 282 | static const struct usb_descriptor_header *otg_desc[] = { | 
|  | 283 | (struct usb_descriptor_header *) &otg_descriptor, | 
|  | 284 | NULL, | 
|  | 285 | }; | 
|  | 286 |  | 
| Manu Gautam | a2b5414 | 2012-04-03 14:34:32 +0530 | [diff] [blame] | 287 | enum android_device_state { | 
|  | 288 | USB_DISCONNECTED, | 
|  | 289 | USB_CONNECTED, | 
|  | 290 | USB_CONFIGURED, | 
| Ido Shayevitz | fb5edfe | 2012-12-26 14:26:37 +0200 | [diff] [blame] | 291 | USB_SUSPENDED, | 
|  | 292 | USB_RESUMED | 
| Manu Gautam | a2b5414 | 2012-04-03 14:34:32 +0530 | [diff] [blame] | 293 | }; | 
|  | 294 |  | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 295 | static void android_pm_qos_update_latency(struct android_dev *dev, int vote) | 
|  | 296 | { | 
|  | 297 | struct android_usb_platform_data *pdata = dev->pdata; | 
|  | 298 | u32 swfi_latency = 0; | 
|  | 299 | static int last_vote = -1; | 
|  | 300 |  | 
| Ofir Cohen | 56eb707 | 2012-05-20 11:41:39 +0300 | [diff] [blame] | 301 | if (!pdata || vote == last_vote | 
|  | 302 | || !pdata->swfi_latency) | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 303 | return; | 
|  | 304 |  | 
|  | 305 | swfi_latency = pdata->swfi_latency + 1; | 
|  | 306 | if (vote) | 
|  | 307 | pm_qos_update_request(&dev->pm_qos_req_dma, | 
|  | 308 | swfi_latency); | 
|  | 309 | else | 
|  | 310 | pm_qos_update_request(&dev->pm_qos_req_dma, | 
|  | 311 | PM_QOS_DEFAULT_VALUE); | 
|  | 312 | last_vote = vote; | 
|  | 313 | } | 
|  | 314 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 315 | static void android_work(struct work_struct *data) | 
|  | 316 | { | 
|  | 317 | struct android_dev *dev = container_of(data, struct android_dev, work); | 
|  | 318 | struct usb_composite_dev *cdev = dev->cdev; | 
|  | 319 | char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; | 
|  | 320 | char *connected[2]    = { "USB_STATE=CONNECTED", NULL }; | 
|  | 321 | char *configured[2]   = { "USB_STATE=CONFIGURED", NULL }; | 
| Ido Shayevitz | fb5edfe | 2012-12-26 14:26:37 +0200 | [diff] [blame] | 322 | char *suspended[2]   = { "USB_STATE=SUSPENDED", NULL }; | 
|  | 323 | char *resumed[2]   = { "USB_STATE=RESUMED", NULL }; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 324 | char **uevent_envp = NULL; | 
| Manu Gautam | a2b5414 | 2012-04-03 14:34:32 +0530 | [diff] [blame] | 325 | static enum android_device_state last_uevent, next_state; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 326 | unsigned long flags; | 
| Ofir Cohen | bcbb1a7 | 2012-05-20 16:28:15 +0300 | [diff] [blame] | 327 | int pm_qos_vote = -1; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 328 |  | 
|  | 329 | spin_lock_irqsave(&cdev->lock, flags); | 
| Ido Shayevitz | fb5edfe | 2012-12-26 14:26:37 +0200 | [diff] [blame] | 330 | if (dev->suspended != dev->sw_suspended && cdev->config) { | 
|  | 331 | if (strncmp(dev->pm_qos, "low", 3)) | 
|  | 332 | pm_qos_vote = dev->suspended ? 0 : 1; | 
|  | 333 | next_state = dev->suspended ? USB_SUSPENDED : USB_RESUMED; | 
|  | 334 | uevent_envp = dev->suspended ? suspended : resumed; | 
|  | 335 | } else if (cdev->config) { | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 336 | uevent_envp = configured; | 
| Manu Gautam | a2b5414 | 2012-04-03 14:34:32 +0530 | [diff] [blame] | 337 | next_state = USB_CONFIGURED; | 
|  | 338 | } else if (dev->connected != dev->sw_connected) { | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 339 | uevent_envp = dev->connected ? connected : disconnected; | 
| Manu Gautam | a2b5414 | 2012-04-03 14:34:32 +0530 | [diff] [blame] | 340 | next_state = dev->connected ? USB_CONNECTED : USB_DISCONNECTED; | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 341 | if (dev->connected && strncmp(dev->pm_qos, "low", 3)) | 
| Ofir Cohen | bcbb1a7 | 2012-05-20 16:28:15 +0300 | [diff] [blame] | 342 | pm_qos_vote = 1; | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 343 | else if (!dev->connected || !strncmp(dev->pm_qos, "low", 3)) | 
| Ofir Cohen | bcbb1a7 | 2012-05-20 16:28:15 +0300 | [diff] [blame] | 344 | pm_qos_vote = 0; | 
| Manu Gautam | a2b5414 | 2012-04-03 14:34:32 +0530 | [diff] [blame] | 345 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 346 | dev->sw_connected = dev->connected; | 
| Ido Shayevitz | fb5edfe | 2012-12-26 14:26:37 +0200 | [diff] [blame] | 347 | dev->sw_suspended = dev->suspended; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 348 | spin_unlock_irqrestore(&cdev->lock, flags); | 
|  | 349 |  | 
| Ofir Cohen | bcbb1a7 | 2012-05-20 16:28:15 +0300 | [diff] [blame] | 350 | if (pm_qos_vote != -1) | 
|  | 351 | android_pm_qos_update_latency(dev, pm_qos_vote); | 
|  | 352 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 353 | if (uevent_envp) { | 
| Manu Gautam | a2b5414 | 2012-04-03 14:34:32 +0530 | [diff] [blame] | 354 | /* | 
|  | 355 | * Some userspace modules, e.g. MTP, work correctly only if | 
|  | 356 | * CONFIGURED uevent is preceded by DISCONNECT uevent. | 
|  | 357 | * Check if we missed sending out a DISCONNECT uevent. This can | 
|  | 358 | * happen if host PC resets and configures device really quick. | 
|  | 359 | */ | 
|  | 360 | if (((uevent_envp == connected) && | 
|  | 361 | (last_uevent != USB_DISCONNECTED)) || | 
|  | 362 | ((uevent_envp == configured) && | 
|  | 363 | (last_uevent == USB_CONFIGURED))) { | 
|  | 364 | pr_info("%s: sent missed DISCONNECT event\n", __func__); | 
|  | 365 | kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, | 
|  | 366 | disconnected); | 
|  | 367 | msleep(20); | 
|  | 368 | } | 
|  | 369 | /* | 
|  | 370 | * Before sending out CONFIGURED uevent give function drivers | 
|  | 371 | * a chance to wakeup userspace threads and notify disconnect | 
|  | 372 | */ | 
|  | 373 | if (uevent_envp == configured) | 
|  | 374 | msleep(50); | 
|  | 375 |  | 
| Ido Shayevitz | fb5edfe | 2012-12-26 14:26:37 +0200 | [diff] [blame] | 376 | /* Do not notify on suspend / resume */ | 
|  | 377 | if (next_state != USB_SUSPENDED && next_state != USB_RESUMED) { | 
|  | 378 | kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, | 
|  | 379 | uevent_envp); | 
|  | 380 | last_uevent = next_state; | 
|  | 381 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 382 | pr_info("%s: sent uevent %s\n", __func__, uevent_envp[0]); | 
|  | 383 | } else { | 
|  | 384 | pr_info("%s: did not send uevent (%d %d %p)\n", __func__, | 
|  | 385 | dev->connected, dev->sw_connected, cdev->config); | 
|  | 386 | } | 
|  | 387 | } | 
|  | 388 |  | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 389 | static void android_enable(struct android_dev *dev) | 
|  | 390 | { | 
|  | 391 | struct usb_composite_dev *cdev = dev->cdev; | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 392 | struct android_configuration *conf; | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 393 |  | 
|  | 394 | if (WARN_ON(!dev->disable_depth)) | 
|  | 395 | return; | 
|  | 396 |  | 
|  | 397 | if (--dev->disable_depth == 0) { | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 398 |  | 
|  | 399 | list_for_each_entry(conf, &dev->configs, list_item) | 
|  | 400 | usb_add_config(cdev, &conf->usb_config, | 
|  | 401 | android_bind_config); | 
|  | 402 |  | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 403 | usb_gadget_connect(cdev->gadget); | 
|  | 404 | } | 
|  | 405 | } | 
|  | 406 |  | 
|  | 407 | static void android_disable(struct android_dev *dev) | 
|  | 408 | { | 
|  | 409 | struct usb_composite_dev *cdev = dev->cdev; | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 410 | struct android_configuration *conf; | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 411 |  | 
|  | 412 | if (dev->disable_depth++ == 0) { | 
|  | 413 | usb_gadget_disconnect(cdev->gadget); | 
|  | 414 | /* Cancel pending control requests */ | 
|  | 415 | usb_ep_dequeue(cdev->gadget->ep0, cdev->req); | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 416 |  | 
|  | 417 | list_for_each_entry(conf, &dev->configs, list_item) | 
|  | 418 | usb_remove_config(cdev, &conf->usb_config); | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 419 | } | 
|  | 420 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 421 |  | 
|  | 422 | /*-------------------------------------------------------------------------*/ | 
|  | 423 | /* Supported functions initialization */ | 
|  | 424 |  | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 425 | struct adb_data { | 
|  | 426 | bool opened; | 
|  | 427 | bool enabled; | 
|  | 428 | }; | 
|  | 429 |  | 
| Benoit Goby | 2b6862d | 2011-12-19 14:38:41 -0800 | [diff] [blame] | 430 | static int | 
|  | 431 | adb_function_init(struct android_usb_function *f, | 
|  | 432 | struct usb_composite_dev *cdev) | 
|  | 433 | { | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 434 | f->config = kzalloc(sizeof(struct adb_data), GFP_KERNEL); | 
|  | 435 | if (!f->config) | 
|  | 436 | return -ENOMEM; | 
|  | 437 |  | 
| Benoit Goby | 2b6862d | 2011-12-19 14:38:41 -0800 | [diff] [blame] | 438 | return adb_setup(); | 
|  | 439 | } | 
|  | 440 |  | 
|  | 441 | static void adb_function_cleanup(struct android_usb_function *f) | 
|  | 442 | { | 
|  | 443 | adb_cleanup(); | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 444 | kfree(f->config); | 
| Benoit Goby | 2b6862d | 2011-12-19 14:38:41 -0800 | [diff] [blame] | 445 | } | 
|  | 446 |  | 
|  | 447 | static int | 
|  | 448 | adb_function_bind_config(struct android_usb_function *f, | 
|  | 449 | struct usb_configuration *c) | 
|  | 450 | { | 
|  | 451 | return adb_bind_config(c); | 
|  | 452 | } | 
|  | 453 |  | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 454 | static void adb_android_function_enable(struct android_usb_function *f) | 
|  | 455 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 456 | struct android_dev *dev = f->android_dev; | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 457 | struct adb_data *data = f->config; | 
|  | 458 |  | 
|  | 459 | data->enabled = true; | 
|  | 460 |  | 
|  | 461 | /* Disable the gadget until adbd is ready */ | 
|  | 462 | if (!data->opened) | 
|  | 463 | android_disable(dev); | 
|  | 464 | } | 
|  | 465 |  | 
|  | 466 | static void adb_android_function_disable(struct android_usb_function *f) | 
|  | 467 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 468 | struct android_dev *dev = f->android_dev; | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 469 | struct adb_data *data = f->config; | 
|  | 470 |  | 
|  | 471 | data->enabled = false; | 
|  | 472 |  | 
|  | 473 | /* Balance the disable that was called in closed_callback */ | 
|  | 474 | if (!data->opened) | 
|  | 475 | android_enable(dev); | 
|  | 476 | } | 
|  | 477 |  | 
| Benoit Goby | 2b6862d | 2011-12-19 14:38:41 -0800 | [diff] [blame] | 478 | static struct android_usb_function adb_function = { | 
|  | 479 | .name		= "adb", | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 480 | .enable		= adb_android_function_enable, | 
|  | 481 | .disable	= adb_android_function_disable, | 
| Benoit Goby | 2b6862d | 2011-12-19 14:38:41 -0800 | [diff] [blame] | 482 | .init		= adb_function_init, | 
|  | 483 | .cleanup	= adb_function_cleanup, | 
|  | 484 | .bind_config	= adb_function_bind_config, | 
|  | 485 | }; | 
|  | 486 |  | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 487 | static void adb_ready_callback(void) | 
|  | 488 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 489 | struct android_dev *dev = adb_function.android_dev; | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 490 | struct adb_data *data = adb_function.config; | 
|  | 491 |  | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 492 | data->opened = true; | 
|  | 493 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 494 | if (data->enabled && dev) { | 
|  | 495 | mutex_lock(&dev->mutex); | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 496 | android_enable(dev); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 497 | mutex_unlock(&dev->mutex); | 
|  | 498 | } | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 499 | } | 
|  | 500 |  | 
|  | 501 | static void adb_closed_callback(void) | 
|  | 502 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 503 | struct android_dev *dev = adb_function.android_dev; | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 504 | struct adb_data *data = adb_function.config; | 
|  | 505 |  | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 506 | data->opened = false; | 
|  | 507 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 508 | if (data->enabled) { | 
|  | 509 | mutex_lock(&dev->mutex); | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 510 | android_disable(dev); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 511 | mutex_unlock(&dev->mutex); | 
|  | 512 | } | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 513 | } | 
|  | 514 |  | 
| Benoit Goby | 2b6862d | 2011-12-19 14:38:41 -0800 | [diff] [blame] | 515 |  | 
| Benoit Goby | aab9681 | 2011-04-19 20:37:33 -0700 | [diff] [blame] | 516 | /*-------------------------------------------------------------------------*/ | 
|  | 517 | /* Supported functions initialization */ | 
|  | 518 |  | 
| Manu Gautam | 8e0719b | 2011-09-26 14:47:55 +0530 | [diff] [blame] | 519 | /* RMNET_SMD */ | 
| Manu Gautam | 1c8ffd7 | 2011-09-02 16:00:49 +0530 | [diff] [blame] | 520 | static int rmnet_smd_function_bind_config(struct android_usb_function *f, | 
|  | 521 | struct usb_configuration *c) | 
|  | 522 | { | 
|  | 523 | return rmnet_smd_bind_config(c); | 
|  | 524 | } | 
|  | 525 |  | 
|  | 526 | static struct android_usb_function rmnet_smd_function = { | 
|  | 527 | .name		= "rmnet_smd", | 
|  | 528 | .bind_config	= rmnet_smd_function_bind_config, | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 529 | }; | 
|  | 530 |  | 
| Manu Gautam | 8e0719b | 2011-09-26 14:47:55 +0530 | [diff] [blame] | 531 | /* RMNET_SDIO */ | 
|  | 532 | static int rmnet_sdio_function_bind_config(struct android_usb_function *f, | 
|  | 533 | struct usb_configuration *c) | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 534 | { | 
| Manu Gautam | 8e0719b | 2011-09-26 14:47:55 +0530 | [diff] [blame] | 535 | return rmnet_sdio_function_add(c); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 536 | } | 
|  | 537 |  | 
| Manu Gautam | 8e0719b | 2011-09-26 14:47:55 +0530 | [diff] [blame] | 538 | static struct android_usb_function rmnet_sdio_function = { | 
|  | 539 | .name		= "rmnet_sdio", | 
|  | 540 | .bind_config	= rmnet_sdio_function_bind_config, | 
|  | 541 | }; | 
|  | 542 |  | 
|  | 543 | /* RMNET_SMD_SDIO */ | 
|  | 544 | static int rmnet_smd_sdio_function_init(struct android_usb_function *f, | 
|  | 545 | struct usb_composite_dev *cdev) | 
|  | 546 | { | 
|  | 547 | return rmnet_smd_sdio_init(); | 
|  | 548 | } | 
|  | 549 |  | 
|  | 550 | static void rmnet_smd_sdio_function_cleanup(struct android_usb_function *f) | 
|  | 551 | { | 
|  | 552 | rmnet_smd_sdio_cleanup(); | 
|  | 553 | } | 
|  | 554 |  | 
|  | 555 | static int rmnet_smd_sdio_bind_config(struct android_usb_function *f, | 
|  | 556 | struct usb_configuration *c) | 
|  | 557 | { | 
|  | 558 | return rmnet_smd_sdio_function_add(c); | 
|  | 559 | } | 
|  | 560 |  | 
|  | 561 | static struct device_attribute *rmnet_smd_sdio_attributes[] = { | 
|  | 562 | &dev_attr_transport, NULL }; | 
|  | 563 |  | 
|  | 564 | static struct android_usb_function rmnet_smd_sdio_function = { | 
|  | 565 | .name		= "rmnet_smd_sdio", | 
|  | 566 | .init		= rmnet_smd_sdio_function_init, | 
|  | 567 | .cleanup	= rmnet_smd_sdio_function_cleanup, | 
|  | 568 | .bind_config	= rmnet_smd_sdio_bind_config, | 
|  | 569 | .attributes	= rmnet_smd_sdio_attributes, | 
|  | 570 | }; | 
|  | 571 |  | 
| Hemant Kumar | 1b820d5 | 2011-11-03 15:08:28 -0700 | [diff] [blame] | 572 | /*rmnet transport string format(per port):"ctrl0,data0,ctrl1,data1..." */ | 
|  | 573 | #define MAX_XPORT_STR_LEN 50 | 
|  | 574 | static char rmnet_transports[MAX_XPORT_STR_LEN]; | 
| Manu Gautam | 1c8ffd7 | 2011-09-02 16:00:49 +0530 | [diff] [blame] | 575 |  | 
| Manu Gautam | e3e897c | 2011-09-12 17:18:46 +0530 | [diff] [blame] | 576 | static void rmnet_function_cleanup(struct android_usb_function *f) | 
|  | 577 | { | 
|  | 578 | frmnet_cleanup(); | 
|  | 579 | } | 
|  | 580 |  | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 581 | static int rmnet_function_bind_config(struct android_usb_function *f, | 
|  | 582 | struct usb_configuration *c) | 
|  | 583 | { | 
|  | 584 | int i; | 
| Hemant Kumar | 1b820d5 | 2011-11-03 15:08:28 -0700 | [diff] [blame] | 585 | int err = 0; | 
|  | 586 | char *ctrl_name; | 
|  | 587 | char *data_name; | 
|  | 588 | char buf[MAX_XPORT_STR_LEN], *b; | 
|  | 589 | static int rmnet_initialized, ports; | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 590 |  | 
| Hemant Kumar | 1b820d5 | 2011-11-03 15:08:28 -0700 | [diff] [blame] | 591 | if (!rmnet_initialized) { | 
|  | 592 | rmnet_initialized = 1; | 
|  | 593 | strlcpy(buf, rmnet_transports, sizeof(buf)); | 
|  | 594 | b = strim(buf); | 
|  | 595 | while (b) { | 
|  | 596 | ctrl_name = strsep(&b, ","); | 
|  | 597 | data_name = strsep(&b, ","); | 
|  | 598 | if (ctrl_name && data_name) { | 
|  | 599 | err = frmnet_init_port(ctrl_name, data_name); | 
|  | 600 | if (err) { | 
|  | 601 | pr_err("rmnet: Cannot open ctrl port:" | 
|  | 602 | "'%s' data port:'%s'\n", | 
|  | 603 | ctrl_name, data_name); | 
|  | 604 | goto out; | 
|  | 605 | } | 
|  | 606 | ports++; | 
|  | 607 | } | 
|  | 608 | } | 
|  | 609 |  | 
|  | 610 | err = rmnet_gport_setup(); | 
|  | 611 | if (err) { | 
|  | 612 | pr_err("rmnet: Cannot setup transports"); | 
|  | 613 | goto out; | 
|  | 614 | } | 
|  | 615 | } | 
|  | 616 |  | 
|  | 617 | for (i = 0; i < ports; i++) { | 
|  | 618 | err = frmnet_bind_config(c, i); | 
|  | 619 | if (err) { | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 620 | pr_err("Could not bind rmnet%u config\n", i); | 
|  | 621 | break; | 
|  | 622 | } | 
|  | 623 | } | 
| Hemant Kumar | 1b820d5 | 2011-11-03 15:08:28 -0700 | [diff] [blame] | 624 | out: | 
|  | 625 | return err; | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 626 | } | 
|  | 627 |  | 
| Hemant Kumar | 1b820d5 | 2011-11-03 15:08:28 -0700 | [diff] [blame] | 628 | static ssize_t rmnet_transports_show(struct device *dev, | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 629 | struct device_attribute *attr, char *buf) | 
|  | 630 | { | 
| Hemant Kumar | 1b820d5 | 2011-11-03 15:08:28 -0700 | [diff] [blame] | 631 | return snprintf(buf, PAGE_SIZE, "%s\n", rmnet_transports); | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 632 | } | 
|  | 633 |  | 
| Hemant Kumar | 1b820d5 | 2011-11-03 15:08:28 -0700 | [diff] [blame] | 634 | static ssize_t rmnet_transports_store( | 
|  | 635 | struct device *device, struct device_attribute *attr, | 
|  | 636 | const char *buff, size_t size) | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 637 | { | 
| Hemant Kumar | 1b820d5 | 2011-11-03 15:08:28 -0700 | [diff] [blame] | 638 | strlcpy(rmnet_transports, buff, sizeof(rmnet_transports)); | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 639 |  | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 640 | return size; | 
|  | 641 | } | 
|  | 642 |  | 
| Hemant Kumar | 1b820d5 | 2011-11-03 15:08:28 -0700 | [diff] [blame] | 643 | static struct device_attribute dev_attr_rmnet_transports = | 
|  | 644 | __ATTR(transports, S_IRUGO | S_IWUSR, | 
|  | 645 | rmnet_transports_show, | 
|  | 646 | rmnet_transports_store); | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 647 | static struct device_attribute *rmnet_function_attributes[] = { | 
| Hemant Kumar | 1b820d5 | 2011-11-03 15:08:28 -0700 | [diff] [blame] | 648 | &dev_attr_rmnet_transports, | 
|  | 649 | NULL }; | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 650 |  | 
|  | 651 | static struct android_usb_function rmnet_function = { | 
|  | 652 | .name		= "rmnet", | 
| Manu Gautam | e3e897c | 2011-09-12 17:18:46 +0530 | [diff] [blame] | 653 | .cleanup	= rmnet_function_cleanup, | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 654 | .bind_config	= rmnet_function_bind_config, | 
|  | 655 | .attributes	= rmnet_function_attributes, | 
|  | 656 | }; | 
|  | 657 |  | 
| Amit Blay | f9b352b | 2013-03-04 15:01:40 +0200 | [diff] [blame] | 658 | /* ecm transport string */ | 
|  | 659 | static char ecm_transports[MAX_XPORT_STR_LEN]; | 
|  | 660 |  | 
| Ofir Cohen | 7b15542 | 2012-07-31 13:02:49 +0300 | [diff] [blame] | 661 | struct ecm_function_config { | 
|  | 662 | u8      ethaddr[ETH_ALEN]; | 
|  | 663 | }; | 
|  | 664 |  | 
|  | 665 | static int ecm_function_init(struct android_usb_function *f, | 
|  | 666 | struct usb_composite_dev *cdev) | 
|  | 667 | { | 
|  | 668 | f->config = kzalloc(sizeof(struct ecm_function_config), GFP_KERNEL); | 
|  | 669 | if (!f->config) | 
|  | 670 | return -ENOMEM; | 
|  | 671 | return 0; | 
|  | 672 | } | 
|  | 673 |  | 
|  | 674 | static void ecm_function_cleanup(struct android_usb_function *f) | 
|  | 675 | { | 
|  | 676 | kfree(f->config); | 
|  | 677 | f->config = NULL; | 
|  | 678 | } | 
|  | 679 |  | 
|  | 680 | static int ecm_qc_function_bind_config(struct android_usb_function *f, | 
|  | 681 | struct usb_configuration *c) | 
|  | 682 | { | 
|  | 683 | int ret; | 
| Amit Blay | f9b352b | 2013-03-04 15:01:40 +0200 | [diff] [blame] | 684 | char *trans; | 
| Ofir Cohen | 7b15542 | 2012-07-31 13:02:49 +0300 | [diff] [blame] | 685 | struct ecm_function_config *ecm = f->config; | 
|  | 686 |  | 
|  | 687 | if (!ecm) { | 
|  | 688 | pr_err("%s: ecm_pdata\n", __func__); | 
|  | 689 | return -EINVAL; | 
|  | 690 | } | 
|  | 691 |  | 
|  | 692 | pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__, | 
|  | 693 | ecm->ethaddr[0], ecm->ethaddr[1], ecm->ethaddr[2], | 
|  | 694 | ecm->ethaddr[3], ecm->ethaddr[4], ecm->ethaddr[5]); | 
|  | 695 |  | 
| Amit Blay | f9b352b | 2013-03-04 15:01:40 +0200 | [diff] [blame] | 696 | pr_debug("%s: ecm_transport is %s", __func__, ecm_transports); | 
|  | 697 |  | 
|  | 698 | trans = strim(ecm_transports); | 
|  | 699 | if (strcmp("BAM2BAM_IPA", trans)) { | 
|  | 700 | ret = gether_qc_setup_name(c->cdev->gadget, | 
|  | 701 | ecm->ethaddr, "ecm"); | 
|  | 702 | if (ret) { | 
|  | 703 | pr_err("%s: gether_setup failed\n", __func__); | 
|  | 704 | return ret; | 
|  | 705 | } | 
| Ofir Cohen | 7b15542 | 2012-07-31 13:02:49 +0300 | [diff] [blame] | 706 | } | 
|  | 707 |  | 
| Amit Blay | f9b352b | 2013-03-04 15:01:40 +0200 | [diff] [blame] | 708 | return ecm_qc_bind_config(c, ecm->ethaddr, trans); | 
| Ofir Cohen | 7b15542 | 2012-07-31 13:02:49 +0300 | [diff] [blame] | 709 | } | 
|  | 710 |  | 
|  | 711 | static void ecm_qc_function_unbind_config(struct android_usb_function *f, | 
|  | 712 | struct usb_configuration *c) | 
|  | 713 | { | 
| Amit Blay | f9b352b | 2013-03-04 15:01:40 +0200 | [diff] [blame] | 714 | char *trans = strim(ecm_transports); | 
|  | 715 |  | 
|  | 716 | if (strcmp("BAM2BAM_IPA", trans)) | 
|  | 717 | gether_qc_cleanup_name("ecm0"); | 
| Ofir Cohen | 7b15542 | 2012-07-31 13:02:49 +0300 | [diff] [blame] | 718 | } | 
|  | 719 |  | 
|  | 720 | static ssize_t ecm_ethaddr_show(struct device *dev, | 
|  | 721 | struct device_attribute *attr, char *buf) | 
|  | 722 | { | 
|  | 723 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 724 | struct ecm_function_config *ecm = f->config; | 
|  | 725 | return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n", | 
|  | 726 | ecm->ethaddr[0], ecm->ethaddr[1], ecm->ethaddr[2], | 
|  | 727 | ecm->ethaddr[3], ecm->ethaddr[4], ecm->ethaddr[5]); | 
|  | 728 | } | 
|  | 729 |  | 
|  | 730 | static ssize_t ecm_ethaddr_store(struct device *dev, | 
|  | 731 | struct device_attribute *attr, const char *buf, size_t size) | 
|  | 732 | { | 
|  | 733 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 734 | struct ecm_function_config *ecm = f->config; | 
|  | 735 |  | 
|  | 736 | if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", | 
|  | 737 | (int *)&ecm->ethaddr[0], (int *)&ecm->ethaddr[1], | 
|  | 738 | (int *)&ecm->ethaddr[2], (int *)&ecm->ethaddr[3], | 
|  | 739 | (int *)&ecm->ethaddr[4], (int *)&ecm->ethaddr[5]) == 6) | 
|  | 740 | return size; | 
|  | 741 | return -EINVAL; | 
|  | 742 | } | 
|  | 743 |  | 
|  | 744 | static DEVICE_ATTR(ecm_ethaddr, S_IRUGO | S_IWUSR, ecm_ethaddr_show, | 
|  | 745 | ecm_ethaddr_store); | 
|  | 746 |  | 
| Amit Blay | f9b352b | 2013-03-04 15:01:40 +0200 | [diff] [blame] | 747 | static ssize_t ecm_transports_show(struct device *dev, | 
|  | 748 | struct device_attribute *attr, char *buf) | 
|  | 749 | { | 
|  | 750 | return snprintf(buf, PAGE_SIZE, "%s\n", ecm_transports); | 
|  | 751 | } | 
|  | 752 |  | 
|  | 753 | static ssize_t ecm_transports_store(struct device *dev, | 
|  | 754 | struct device_attribute *attr, const char *buf, size_t size) | 
|  | 755 | { | 
|  | 756 | strlcpy(ecm_transports, buf, sizeof(ecm_transports)); | 
|  | 757 | return size; | 
|  | 758 | } | 
|  | 759 |  | 
|  | 760 | static DEVICE_ATTR(ecm_transports, S_IRUGO | S_IWUSR, ecm_transports_show, | 
|  | 761 | ecm_transports_store); | 
|  | 762 |  | 
| Ofir Cohen | 7b15542 | 2012-07-31 13:02:49 +0300 | [diff] [blame] | 763 | static struct device_attribute *ecm_function_attributes[] = { | 
| Amit Blay | f9b352b | 2013-03-04 15:01:40 +0200 | [diff] [blame] | 764 | &dev_attr_ecm_transports, | 
| Ofir Cohen | 7b15542 | 2012-07-31 13:02:49 +0300 | [diff] [blame] | 765 | &dev_attr_ecm_ethaddr, | 
|  | 766 | NULL | 
|  | 767 | }; | 
|  | 768 |  | 
|  | 769 | static struct android_usb_function ecm_qc_function = { | 
|  | 770 | .name		= "ecm_qc", | 
|  | 771 | .init		= ecm_function_init, | 
|  | 772 | .cleanup	= ecm_function_cleanup, | 
|  | 773 | .bind_config	= ecm_qc_function_bind_config, | 
|  | 774 | .unbind_config	= ecm_qc_function_unbind_config, | 
|  | 775 | .attributes	= ecm_function_attributes, | 
|  | 776 | }; | 
| Anna Perel | a8c991d | 2012-04-09 16:44:46 +0300 | [diff] [blame] | 777 |  | 
|  | 778 | /* MBIM - used with BAM */ | 
|  | 779 | #define MAX_MBIM_INSTANCES 1 | 
|  | 780 |  | 
|  | 781 | static int mbim_function_init(struct android_usb_function *f, | 
|  | 782 | struct usb_composite_dev *cdev) | 
|  | 783 | { | 
|  | 784 | return mbim_init(MAX_MBIM_INSTANCES); | 
|  | 785 | } | 
|  | 786 |  | 
|  | 787 | static void mbim_function_cleanup(struct android_usb_function *f) | 
|  | 788 | { | 
|  | 789 | fmbim_cleanup(); | 
|  | 790 | } | 
|  | 791 |  | 
|  | 792 | static int mbim_function_bind_config(struct android_usb_function *f, | 
|  | 793 | struct usb_configuration *c) | 
|  | 794 | { | 
|  | 795 | return mbim_bind_config(c, 0); | 
|  | 796 | } | 
|  | 797 |  | 
| Jack Pham | 2df2f70 | 2012-10-11 19:08:24 -0700 | [diff] [blame] | 798 | static int mbim_function_ctrlrequest(struct android_usb_function *f, | 
|  | 799 | struct usb_composite_dev *cdev, | 
|  | 800 | const struct usb_ctrlrequest *c) | 
|  | 801 | { | 
|  | 802 | return mbim_ctrlrequest(cdev, c); | 
|  | 803 | } | 
|  | 804 |  | 
| Anna Perel | a8c991d | 2012-04-09 16:44:46 +0300 | [diff] [blame] | 805 | static struct android_usb_function mbim_function = { | 
|  | 806 | .name		= "usb_mbim", | 
|  | 807 | .cleanup	= mbim_function_cleanup, | 
|  | 808 | .bind_config	= mbim_function_bind_config, | 
|  | 809 | .init		= mbim_function_init, | 
| Jack Pham | 2df2f70 | 2012-10-11 19:08:24 -0700 | [diff] [blame] | 810 | .ctrlrequest	= mbim_function_ctrlrequest, | 
| Anna Perel | a8c991d | 2012-04-09 16:44:46 +0300 | [diff] [blame] | 811 | }; | 
|  | 812 |  | 
| Jack Pham | 2ec5fdc | 2012-09-26 10:13:48 -0700 | [diff] [blame] | 813 | #ifdef CONFIG_SND_PCM | 
| Anna Perel | 432367a | 2012-09-20 10:55:32 +0300 | [diff] [blame] | 814 | /* PERIPHERAL AUDIO */ | 
|  | 815 | static int audio_function_bind_config(struct android_usb_function *f, | 
|  | 816 | struct usb_configuration *c) | 
|  | 817 | { | 
|  | 818 | return audio_bind_config(c); | 
|  | 819 | } | 
|  | 820 |  | 
|  | 821 | static struct android_usb_function audio_function = { | 
|  | 822 | .name		= "audio", | 
|  | 823 | .bind_config	= audio_function_bind_config, | 
|  | 824 | }; | 
| Jack Pham | 2ec5fdc | 2012-09-26 10:13:48 -0700 | [diff] [blame] | 825 | #endif | 
| Anna Perel | 432367a | 2012-09-20 10:55:32 +0300 | [diff] [blame] | 826 |  | 
| Anna Perel | a8c991d | 2012-04-09 16:44:46 +0300 | [diff] [blame] | 827 |  | 
| Manu Gautam | 8e0719b | 2011-09-26 14:47:55 +0530 | [diff] [blame] | 828 | /* DIAG */ | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 829 | static char diag_clients[32];	    /*enabled DIAG clients- "diag[,diag_mdm]" */ | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 830 | static ssize_t clients_store( | 
|  | 831 | struct device *device, struct device_attribute *attr, | 
|  | 832 | const char *buff, size_t size) | 
|  | 833 | { | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 834 | strlcpy(diag_clients, buff, sizeof(diag_clients)); | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 835 |  | 
|  | 836 | return size; | 
|  | 837 | } | 
|  | 838 |  | 
|  | 839 | static DEVICE_ATTR(clients, S_IWUSR, NULL, clients_store); | 
|  | 840 | static struct device_attribute *diag_function_attributes[] = | 
|  | 841 | { &dev_attr_clients, NULL }; | 
|  | 842 |  | 
|  | 843 | static int diag_function_init(struct android_usb_function *f, | 
|  | 844 | struct usb_composite_dev *cdev) | 
|  | 845 | { | 
|  | 846 | return diag_setup(); | 
|  | 847 | } | 
|  | 848 |  | 
|  | 849 | static void diag_function_cleanup(struct android_usb_function *f) | 
|  | 850 | { | 
|  | 851 | diag_cleanup(); | 
|  | 852 | } | 
|  | 853 |  | 
|  | 854 | static int diag_function_bind_config(struct android_usb_function *f, | 
|  | 855 | struct usb_configuration *c) | 
|  | 856 | { | 
|  | 857 | char *name; | 
|  | 858 | char buf[32], *b; | 
| Manu Gautam | c576030 | 2011-08-25 14:30:24 +0530 | [diff] [blame] | 859 | int once = 0, err = -1; | 
| Jack Pham | b830a6c | 2011-12-12 22:35:27 -0800 | [diff] [blame] | 860 | int (*notify)(uint32_t, const char *); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 861 | struct android_dev *dev = cdev_to_android_dev(c->cdev); | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 862 |  | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 863 | strlcpy(buf, diag_clients, sizeof(buf)); | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 864 | b = strim(buf); | 
|  | 865 |  | 
|  | 866 | while (b) { | 
| Jack Pham | b830a6c | 2011-12-12 22:35:27 -0800 | [diff] [blame] | 867 | notify = NULL; | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 868 | name = strsep(&b, ","); | 
| Manu Gautam | c576030 | 2011-08-25 14:30:24 +0530 | [diff] [blame] | 869 | /* Allow only first diag channel to update pid and serial no */ | 
| Manu Gautam | 43c61a1 | 2012-08-22 17:09:37 -0700 | [diff] [blame] | 870 | if (!once++) { | 
|  | 871 | if (dev->pdata && dev->pdata->update_pid_and_serial_num) | 
|  | 872 | notify = dev->pdata->update_pid_and_serial_num; | 
|  | 873 | else | 
|  | 874 | notify = usb_diag_update_pid_and_serial_num; | 
|  | 875 | } | 
| Manu Gautam | c576030 | 2011-08-25 14:30:24 +0530 | [diff] [blame] | 876 |  | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 877 | if (name) { | 
| Manu Gautam | c576030 | 2011-08-25 14:30:24 +0530 | [diff] [blame] | 878 | err = diag_function_add(c, name, notify); | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 879 | if (err) | 
|  | 880 | pr_err("diag: Cannot open channel '%s'", name); | 
|  | 881 | } | 
|  | 882 | } | 
|  | 883 |  | 
|  | 884 | return err; | 
|  | 885 | } | 
|  | 886 |  | 
|  | 887 | static struct android_usb_function diag_function = { | 
|  | 888 | .name		= "diag", | 
|  | 889 | .init		= diag_function_init, | 
|  | 890 | .cleanup	= diag_function_cleanup, | 
|  | 891 | .bind_config	= diag_function_bind_config, | 
|  | 892 | .attributes	= diag_function_attributes, | 
|  | 893 | }; | 
|  | 894 |  | 
| Shimrit Malichi | a00d732 | 2012-08-05 13:56:28 +0300 | [diff] [blame] | 895 | /* DEBUG */ | 
|  | 896 | static int qdss_function_init(struct android_usb_function *f, | 
|  | 897 | struct usb_composite_dev *cdev) | 
|  | 898 | { | 
|  | 899 | return qdss_setup(); | 
|  | 900 | } | 
|  | 901 |  | 
|  | 902 | static void qdss_function_cleanup(struct android_usb_function *f) | 
|  | 903 | { | 
|  | 904 | qdss_cleanup(); | 
|  | 905 | } | 
|  | 906 |  | 
|  | 907 | static int qdss_function_bind_config(struct android_usb_function *f, | 
|  | 908 | struct usb_configuration *c) | 
|  | 909 | { | 
|  | 910 | int  err = -1; | 
|  | 911 |  | 
|  | 912 | err = qdss_bind_config(c, "qdss"); | 
|  | 913 | if (err) | 
|  | 914 | pr_err("qdss: Cannot open channel qdss"); | 
|  | 915 |  | 
|  | 916 | return err; | 
|  | 917 | } | 
|  | 918 |  | 
|  | 919 | static struct android_usb_function qdss_function = { | 
|  | 920 | .name		= "qdss", | 
|  | 921 | .init		= qdss_function_init, | 
|  | 922 | .cleanup	= qdss_function_cleanup, | 
|  | 923 | .bind_config	= qdss_function_bind_config, | 
|  | 924 | }; | 
|  | 925 |  | 
| Manu Gautam | 8e0719b | 2011-09-26 14:47:55 +0530 | [diff] [blame] | 926 | /* SERIAL */ | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 927 | static char serial_transports[32];	/*enabled FSERIAL ports - "tty[,sdio]"*/ | 
| Manu Gautam | a4d993f | 2011-08-30 18:25:55 +0530 | [diff] [blame] | 928 | static ssize_t serial_transports_store( | 
|  | 929 | struct device *device, struct device_attribute *attr, | 
|  | 930 | const char *buff, size_t size) | 
|  | 931 | { | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 932 | strlcpy(serial_transports, buff, sizeof(serial_transports)); | 
| Manu Gautam | a4d993f | 2011-08-30 18:25:55 +0530 | [diff] [blame] | 933 |  | 
|  | 934 | return size; | 
|  | 935 | } | 
|  | 936 |  | 
|  | 937 | static DEVICE_ATTR(transports, S_IWUSR, NULL, serial_transports_store); | 
|  | 938 | static struct device_attribute *serial_function_attributes[] = | 
|  | 939 | { &dev_attr_transports, NULL }; | 
|  | 940 |  | 
|  | 941 | static void serial_function_cleanup(struct android_usb_function *f) | 
|  | 942 | { | 
|  | 943 | gserial_cleanup(); | 
|  | 944 | } | 
|  | 945 |  | 
|  | 946 | static int serial_function_bind_config(struct android_usb_function *f, | 
|  | 947 | struct usb_configuration *c) | 
|  | 948 | { | 
|  | 949 | char *name; | 
|  | 950 | char buf[32], *b; | 
|  | 951 | int err = -1, i; | 
|  | 952 | static int serial_initialized = 0, ports = 0; | 
|  | 953 |  | 
|  | 954 | if (serial_initialized) | 
|  | 955 | goto bind_config; | 
|  | 956 |  | 
|  | 957 | serial_initialized = 1; | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 958 | strlcpy(buf, serial_transports, sizeof(buf)); | 
| Manu Gautam | a4d993f | 2011-08-30 18:25:55 +0530 | [diff] [blame] | 959 | b = strim(buf); | 
|  | 960 |  | 
|  | 961 | while (b) { | 
|  | 962 | name = strsep(&b, ","); | 
|  | 963 |  | 
|  | 964 | if (name) { | 
|  | 965 | err = gserial_init_port(ports, name); | 
|  | 966 | if (err) { | 
|  | 967 | pr_err("serial: Cannot open port '%s'", name); | 
|  | 968 | goto out; | 
|  | 969 | } | 
|  | 970 | ports++; | 
|  | 971 | } | 
|  | 972 | } | 
|  | 973 | err = gport_setup(c); | 
|  | 974 | if (err) { | 
|  | 975 | pr_err("serial: Cannot setup transports"); | 
|  | 976 | goto out; | 
|  | 977 | } | 
|  | 978 |  | 
|  | 979 | bind_config: | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 980 | for (i = 0; i < ports; i++) { | 
| Manu Gautam | a4d993f | 2011-08-30 18:25:55 +0530 | [diff] [blame] | 981 | err = gser_bind_config(c, i); | 
|  | 982 | if (err) { | 
|  | 983 | pr_err("serial: bind_config failed for port %d", i); | 
|  | 984 | goto out; | 
|  | 985 | } | 
|  | 986 | } | 
|  | 987 |  | 
|  | 988 | out: | 
|  | 989 | return err; | 
|  | 990 | } | 
|  | 991 |  | 
|  | 992 | static struct android_usb_function serial_function = { | 
|  | 993 | .name		= "serial", | 
|  | 994 | .cleanup	= serial_function_cleanup, | 
|  | 995 | .bind_config	= serial_function_bind_config, | 
|  | 996 | .attributes	= serial_function_attributes, | 
|  | 997 | }; | 
|  | 998 |  | 
| Anji jonnala | 92be1b4 | 2011-12-19 09:44:41 +0530 | [diff] [blame] | 999 | /* ACM */ | 
|  | 1000 | static char acm_transports[32];	/*enabled ACM ports - "tty[,sdio]"*/ | 
|  | 1001 | static ssize_t acm_transports_store( | 
|  | 1002 | struct device *device, struct device_attribute *attr, | 
|  | 1003 | const char *buff, size_t size) | 
|  | 1004 | { | 
|  | 1005 | strlcpy(acm_transports, buff, sizeof(acm_transports)); | 
|  | 1006 |  | 
|  | 1007 | return size; | 
|  | 1008 | } | 
|  | 1009 |  | 
|  | 1010 | static DEVICE_ATTR(acm_transports, S_IWUSR, NULL, acm_transports_store); | 
|  | 1011 | static struct device_attribute *acm_function_attributes[] = { | 
|  | 1012 | &dev_attr_acm_transports, NULL }; | 
|  | 1013 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1014 | static void acm_function_cleanup(struct android_usb_function *f) | 
|  | 1015 | { | 
|  | 1016 | gserial_cleanup(); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1017 | } | 
|  | 1018 |  | 
| Anji jonnala | 92be1b4 | 2011-12-19 09:44:41 +0530 | [diff] [blame] | 1019 | static int acm_function_bind_config(struct android_usb_function *f, | 
|  | 1020 | struct usb_configuration *c) | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1021 | { | 
| Anji jonnala | 92be1b4 | 2011-12-19 09:44:41 +0530 | [diff] [blame] | 1022 | char *name; | 
|  | 1023 | char buf[32], *b; | 
|  | 1024 | int err = -1, i; | 
|  | 1025 | static int acm_initialized, ports; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1026 |  | 
| Anji jonnala | 92be1b4 | 2011-12-19 09:44:41 +0530 | [diff] [blame] | 1027 | if (acm_initialized) | 
|  | 1028 | goto bind_config; | 
|  | 1029 |  | 
|  | 1030 | acm_initialized = 1; | 
|  | 1031 | strlcpy(buf, acm_transports, sizeof(buf)); | 
|  | 1032 | b = strim(buf); | 
|  | 1033 |  | 
|  | 1034 | while (b) { | 
|  | 1035 | name = strsep(&b, ","); | 
|  | 1036 |  | 
|  | 1037 | if (name) { | 
|  | 1038 | err = acm_init_port(ports, name); | 
|  | 1039 | if (err) { | 
|  | 1040 | pr_err("acm: Cannot open port '%s'", name); | 
|  | 1041 | goto out; | 
|  | 1042 | } | 
|  | 1043 | ports++; | 
|  | 1044 | } | 
|  | 1045 | } | 
|  | 1046 | err = acm_port_setup(c); | 
|  | 1047 | if (err) { | 
|  | 1048 | pr_err("acm: Cannot setup transports"); | 
|  | 1049 | goto out; | 
|  | 1050 | } | 
|  | 1051 |  | 
|  | 1052 | bind_config: | 
|  | 1053 | for (i = 0; i < ports; i++) { | 
|  | 1054 | err = acm_bind_config(c, i); | 
|  | 1055 | if (err) { | 
|  | 1056 | pr_err("acm: bind_config failed for port %d", i); | 
|  | 1057 | goto out; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1058 | } | 
|  | 1059 | } | 
|  | 1060 |  | 
| Anji jonnala | 92be1b4 | 2011-12-19 09:44:41 +0530 | [diff] [blame] | 1061 | out: | 
|  | 1062 | return err; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1063 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1064 | static struct android_usb_function acm_function = { | 
|  | 1065 | .name		= "acm", | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1066 | .cleanup	= acm_function_cleanup, | 
|  | 1067 | .bind_config	= acm_function_bind_config, | 
|  | 1068 | .attributes	= acm_function_attributes, | 
|  | 1069 | }; | 
|  | 1070 |  | 
| Chiranjeevi Velempati | e130fd0 | 2011-11-29 05:06:13 +0530 | [diff] [blame] | 1071 | /* CCID */ | 
|  | 1072 | static int ccid_function_init(struct android_usb_function *f, | 
|  | 1073 | struct usb_composite_dev *cdev) | 
|  | 1074 | { | 
|  | 1075 | return ccid_setup(); | 
|  | 1076 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1077 |  | 
| Chiranjeevi Velempati | e130fd0 | 2011-11-29 05:06:13 +0530 | [diff] [blame] | 1078 | static void ccid_function_cleanup(struct android_usb_function *f) | 
|  | 1079 | { | 
|  | 1080 | ccid_cleanup(); | 
|  | 1081 | } | 
|  | 1082 |  | 
|  | 1083 | static int ccid_function_bind_config(struct android_usb_function *f, | 
|  | 1084 | struct usb_configuration *c) | 
|  | 1085 | { | 
|  | 1086 | return ccid_bind_config(c); | 
|  | 1087 | } | 
|  | 1088 |  | 
|  | 1089 | static struct android_usb_function ccid_function = { | 
|  | 1090 | .name		= "ccid", | 
|  | 1091 | .init		= ccid_function_init, | 
|  | 1092 | .cleanup	= ccid_function_cleanup, | 
|  | 1093 | .bind_config	= ccid_function_bind_config, | 
|  | 1094 | }; | 
|  | 1095 |  | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 1096 | static int mtp_function_init(struct android_usb_function *f, | 
| Benoit Goby | f0fbc48 | 2011-12-19 14:37:50 -0800 | [diff] [blame] | 1097 | struct usb_composite_dev *cdev) | 
|  | 1098 | { | 
|  | 1099 | return mtp_setup(); | 
|  | 1100 | } | 
|  | 1101 |  | 
|  | 1102 | static void mtp_function_cleanup(struct android_usb_function *f) | 
|  | 1103 | { | 
|  | 1104 | mtp_cleanup(); | 
|  | 1105 | } | 
|  | 1106 |  | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 1107 | static int mtp_function_bind_config(struct android_usb_function *f, | 
| Benoit Goby | f0fbc48 | 2011-12-19 14:37:50 -0800 | [diff] [blame] | 1108 | struct usb_configuration *c) | 
|  | 1109 | { | 
|  | 1110 | return mtp_bind_config(c, false); | 
|  | 1111 | } | 
|  | 1112 |  | 
| Mike Lockwood | cf7addf | 2011-06-01 22:17:36 -0400 | [diff] [blame] | 1113 | static int ptp_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) | 
| Benoit Goby | f0fbc48 | 2011-12-19 14:37:50 -0800 | [diff] [blame] | 1114 | { | 
|  | 1115 | /* nothing to do - initialization is handled by mtp_function_init */ | 
|  | 1116 | return 0; | 
|  | 1117 | } | 
|  | 1118 |  | 
|  | 1119 | static void ptp_function_cleanup(struct android_usb_function *f) | 
|  | 1120 | { | 
|  | 1121 | /* nothing to do - cleanup is handled by mtp_function_cleanup */ | 
|  | 1122 | } | 
|  | 1123 |  | 
| Mike Lockwood | cf7addf | 2011-06-01 22:17:36 -0400 | [diff] [blame] | 1124 | static int ptp_function_bind_config(struct android_usb_function *f, struct usb_configuration *c) | 
| Benoit Goby | f0fbc48 | 2011-12-19 14:37:50 -0800 | [diff] [blame] | 1125 | { | 
|  | 1126 | return mtp_bind_config(c, true); | 
|  | 1127 | } | 
|  | 1128 |  | 
|  | 1129 | static int mtp_function_ctrlrequest(struct android_usb_function *f, | 
|  | 1130 | struct usb_composite_dev *cdev, | 
|  | 1131 | const struct usb_ctrlrequest *c) | 
|  | 1132 | { | 
|  | 1133 | return mtp_ctrlrequest(cdev, c); | 
|  | 1134 | } | 
|  | 1135 |  | 
|  | 1136 | static struct android_usb_function mtp_function = { | 
|  | 1137 | .name		= "mtp", | 
|  | 1138 | .init		= mtp_function_init, | 
|  | 1139 | .cleanup	= mtp_function_cleanup, | 
|  | 1140 | .bind_config	= mtp_function_bind_config, | 
|  | 1141 | .ctrlrequest	= mtp_function_ctrlrequest, | 
|  | 1142 | }; | 
|  | 1143 |  | 
|  | 1144 | /* PTP function is same as MTP with slightly different interface descriptor */ | 
|  | 1145 | static struct android_usb_function ptp_function = { | 
|  | 1146 | .name		= "ptp", | 
|  | 1147 | .init		= ptp_function_init, | 
|  | 1148 | .cleanup	= ptp_function_cleanup, | 
|  | 1149 | .bind_config	= ptp_function_bind_config, | 
|  | 1150 | }; | 
|  | 1151 |  | 
|  | 1152 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1153 | struct rndis_function_config { | 
|  | 1154 | u8      ethaddr[ETH_ALEN]; | 
|  | 1155 | u32     vendorID; | 
| Ofir Cohen | aef90b7 | 2012-07-31 12:37:04 +0200 | [diff] [blame] | 1156 | u8      max_pkt_per_xfer; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1157 | char	manufacturer[256]; | 
|  | 1158 | /* "Wireless" RNDIS; auto-detected by Windows */ | 
|  | 1159 | bool	wceis; | 
|  | 1160 | }; | 
|  | 1161 |  | 
|  | 1162 | static int | 
|  | 1163 | rndis_function_init(struct android_usb_function *f, | 
|  | 1164 | struct usb_composite_dev *cdev) | 
|  | 1165 | { | 
|  | 1166 | f->config = kzalloc(sizeof(struct rndis_function_config), GFP_KERNEL); | 
|  | 1167 | if (!f->config) | 
|  | 1168 | return -ENOMEM; | 
|  | 1169 | return 0; | 
|  | 1170 | } | 
|  | 1171 |  | 
|  | 1172 | static void rndis_function_cleanup(struct android_usb_function *f) | 
|  | 1173 | { | 
|  | 1174 | kfree(f->config); | 
|  | 1175 | f->config = NULL; | 
|  | 1176 | } | 
|  | 1177 |  | 
| Ofir Cohen | aef90b7 | 2012-07-31 12:37:04 +0200 | [diff] [blame] | 1178 | static int rndis_qc_function_init(struct android_usb_function *f, | 
|  | 1179 | struct usb_composite_dev *cdev) | 
|  | 1180 | { | 
|  | 1181 | f->config = kzalloc(sizeof(struct rndis_function_config), GFP_KERNEL); | 
|  | 1182 | if (!f->config) | 
|  | 1183 | return -ENOMEM; | 
|  | 1184 |  | 
|  | 1185 | return rndis_qc_init(); | 
|  | 1186 | } | 
|  | 1187 |  | 
|  | 1188 | static void rndis_qc_function_cleanup(struct android_usb_function *f) | 
|  | 1189 | { | 
|  | 1190 | rndis_qc_cleanup(); | 
|  | 1191 | kfree(f->config); | 
|  | 1192 | } | 
|  | 1193 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1194 | static int | 
|  | 1195 | rndis_function_bind_config(struct android_usb_function *f, | 
|  | 1196 | struct usb_configuration *c) | 
|  | 1197 | { | 
|  | 1198 | int ret; | 
|  | 1199 | struct rndis_function_config *rndis = f->config; | 
|  | 1200 |  | 
|  | 1201 | if (!rndis) { | 
|  | 1202 | pr_err("%s: rndis_pdata\n", __func__); | 
|  | 1203 | return -1; | 
|  | 1204 | } | 
|  | 1205 |  | 
|  | 1206 | pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__, | 
|  | 1207 | rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], | 
|  | 1208 | rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); | 
|  | 1209 |  | 
|  | 1210 | ret = gether_setup_name(c->cdev->gadget, rndis->ethaddr, "rndis"); | 
|  | 1211 | if (ret) { | 
|  | 1212 | pr_err("%s: gether_setup failed\n", __func__); | 
|  | 1213 | return ret; | 
|  | 1214 | } | 
|  | 1215 |  | 
|  | 1216 | if (rndis->wceis) { | 
|  | 1217 | /* "Wireless" RNDIS; auto-detected by Windows */ | 
|  | 1218 | rndis_iad_descriptor.bFunctionClass = | 
|  | 1219 | USB_CLASS_WIRELESS_CONTROLLER; | 
|  | 1220 | rndis_iad_descriptor.bFunctionSubClass = 0x01; | 
|  | 1221 | rndis_iad_descriptor.bFunctionProtocol = 0x03; | 
|  | 1222 | rndis_control_intf.bInterfaceClass = | 
|  | 1223 | USB_CLASS_WIRELESS_CONTROLLER; | 
|  | 1224 | rndis_control_intf.bInterfaceSubClass =	 0x01; | 
|  | 1225 | rndis_control_intf.bInterfaceProtocol =	 0x03; | 
|  | 1226 | } | 
|  | 1227 |  | 
|  | 1228 | return rndis_bind_config_vendor(c, rndis->ethaddr, rndis->vendorID, | 
|  | 1229 | rndis->manufacturer); | 
|  | 1230 | } | 
|  | 1231 |  | 
| Ofir Cohen | aef90b7 | 2012-07-31 12:37:04 +0200 | [diff] [blame] | 1232 | static int rndis_qc_function_bind_config(struct android_usb_function *f, | 
|  | 1233 | struct usb_configuration *c) | 
|  | 1234 | { | 
|  | 1235 | int ret; | 
|  | 1236 | struct rndis_function_config *rndis = f->config; | 
|  | 1237 |  | 
|  | 1238 | if (!rndis) { | 
|  | 1239 | pr_err("%s: rndis_pdata\n", __func__); | 
|  | 1240 | return -EINVAL; | 
|  | 1241 | } | 
|  | 1242 |  | 
|  | 1243 | pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__, | 
|  | 1244 | rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], | 
|  | 1245 | rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); | 
|  | 1246 |  | 
|  | 1247 | ret = gether_qc_setup_name(c->cdev->gadget, rndis->ethaddr, "rndis"); | 
|  | 1248 | if (ret) { | 
|  | 1249 | pr_err("%s: gether_setup failed\n", __func__); | 
|  | 1250 | return ret; | 
|  | 1251 | } | 
|  | 1252 |  | 
|  | 1253 | if (rndis->wceis) { | 
|  | 1254 | /* "Wireless" RNDIS; auto-detected by Windows */ | 
|  | 1255 | rndis_qc_iad_descriptor.bFunctionClass = | 
|  | 1256 | USB_CLASS_WIRELESS_CONTROLLER; | 
|  | 1257 | rndis_qc_iad_descriptor.bFunctionSubClass = 0x01; | 
|  | 1258 | rndis_qc_iad_descriptor.bFunctionProtocol = 0x03; | 
|  | 1259 | rndis_qc_control_intf.bInterfaceClass = | 
|  | 1260 | USB_CLASS_WIRELESS_CONTROLLER; | 
|  | 1261 | rndis_qc_control_intf.bInterfaceSubClass =	 0x01; | 
|  | 1262 | rndis_qc_control_intf.bInterfaceProtocol =	 0x03; | 
|  | 1263 | } | 
|  | 1264 |  | 
|  | 1265 | return rndis_qc_bind_config_vendor(c, rndis->ethaddr, rndis->vendorID, | 
|  | 1266 | rndis->manufacturer, | 
|  | 1267 | rndis->max_pkt_per_xfer); | 
|  | 1268 | } | 
|  | 1269 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1270 | static void rndis_function_unbind_config(struct android_usb_function *f, | 
|  | 1271 | struct usb_configuration *c) | 
|  | 1272 | { | 
|  | 1273 | gether_cleanup(); | 
|  | 1274 | } | 
|  | 1275 |  | 
| Ofir Cohen | aef90b7 | 2012-07-31 12:37:04 +0200 | [diff] [blame] | 1276 | static void rndis_qc_function_unbind_config(struct android_usb_function *f, | 
|  | 1277 | struct usb_configuration *c) | 
|  | 1278 | { | 
| Amit Blay | d6d690a | 2012-10-16 13:37:42 +0200 | [diff] [blame] | 1279 | gether_qc_cleanup_name("rndis0"); | 
| Ofir Cohen | aef90b7 | 2012-07-31 12:37:04 +0200 | [diff] [blame] | 1280 | } | 
|  | 1281 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1282 | static ssize_t rndis_manufacturer_show(struct device *dev, | 
|  | 1283 | struct device_attribute *attr, char *buf) | 
|  | 1284 | { | 
|  | 1285 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1286 | struct rndis_function_config *config = f->config; | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 1287 |  | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 1288 | return snprintf(buf, PAGE_SIZE, "%s\n", config->manufacturer); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1289 | } | 
|  | 1290 |  | 
|  | 1291 | static ssize_t rndis_manufacturer_store(struct device *dev, | 
|  | 1292 | struct device_attribute *attr, const char *buf, size_t size) | 
|  | 1293 | { | 
|  | 1294 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1295 | struct rndis_function_config *config = f->config; | 
|  | 1296 |  | 
|  | 1297 | if (size >= sizeof(config->manufacturer)) | 
|  | 1298 | return -EINVAL; | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 1299 |  | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 1300 | if (sscanf(buf, "%255s", config->manufacturer) == 1) | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1301 | return size; | 
|  | 1302 | return -1; | 
|  | 1303 | } | 
|  | 1304 |  | 
|  | 1305 | static DEVICE_ATTR(manufacturer, S_IRUGO | S_IWUSR, rndis_manufacturer_show, | 
|  | 1306 | rndis_manufacturer_store); | 
|  | 1307 |  | 
|  | 1308 | static ssize_t rndis_wceis_show(struct device *dev, | 
|  | 1309 | struct device_attribute *attr, char *buf) | 
|  | 1310 | { | 
|  | 1311 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1312 | struct rndis_function_config *config = f->config; | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 1313 |  | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 1314 | return snprintf(buf, PAGE_SIZE, "%d\n", config->wceis); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1315 | } | 
|  | 1316 |  | 
|  | 1317 | static ssize_t rndis_wceis_store(struct device *dev, | 
|  | 1318 | struct device_attribute *attr, const char *buf, size_t size) | 
|  | 1319 | { | 
|  | 1320 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1321 | struct rndis_function_config *config = f->config; | 
|  | 1322 | int value; | 
|  | 1323 |  | 
|  | 1324 | if (sscanf(buf, "%d", &value) == 1) { | 
|  | 1325 | config->wceis = value; | 
|  | 1326 | return size; | 
|  | 1327 | } | 
|  | 1328 | return -EINVAL; | 
|  | 1329 | } | 
|  | 1330 |  | 
|  | 1331 | static DEVICE_ATTR(wceis, S_IRUGO | S_IWUSR, rndis_wceis_show, | 
|  | 1332 | rndis_wceis_store); | 
|  | 1333 |  | 
|  | 1334 | static ssize_t rndis_ethaddr_show(struct device *dev, | 
|  | 1335 | struct device_attribute *attr, char *buf) | 
|  | 1336 | { | 
|  | 1337 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1338 | struct rndis_function_config *rndis = f->config; | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 1339 |  | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 1340 | return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n", | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1341 | rndis->ethaddr[0], rndis->ethaddr[1], rndis->ethaddr[2], | 
|  | 1342 | rndis->ethaddr[3], rndis->ethaddr[4], rndis->ethaddr[5]); | 
|  | 1343 | } | 
|  | 1344 |  | 
|  | 1345 | static ssize_t rndis_ethaddr_store(struct device *dev, | 
|  | 1346 | struct device_attribute *attr, const char *buf, size_t size) | 
|  | 1347 | { | 
|  | 1348 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1349 | struct rndis_function_config *rndis = f->config; | 
|  | 1350 |  | 
|  | 1351 | if (sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", | 
|  | 1352 | (int *)&rndis->ethaddr[0], (int *)&rndis->ethaddr[1], | 
|  | 1353 | (int *)&rndis->ethaddr[2], (int *)&rndis->ethaddr[3], | 
|  | 1354 | (int *)&rndis->ethaddr[4], (int *)&rndis->ethaddr[5]) == 6) | 
|  | 1355 | return size; | 
|  | 1356 | return -EINVAL; | 
|  | 1357 | } | 
|  | 1358 |  | 
|  | 1359 | static DEVICE_ATTR(ethaddr, S_IRUGO | S_IWUSR, rndis_ethaddr_show, | 
|  | 1360 | rndis_ethaddr_store); | 
|  | 1361 |  | 
|  | 1362 | static ssize_t rndis_vendorID_show(struct device *dev, | 
|  | 1363 | struct device_attribute *attr, char *buf) | 
|  | 1364 | { | 
|  | 1365 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1366 | struct rndis_function_config *config = f->config; | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 1367 |  | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 1368 | return snprintf(buf, PAGE_SIZE, "%04x\n", config->vendorID); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1369 | } | 
|  | 1370 |  | 
|  | 1371 | static ssize_t rndis_vendorID_store(struct device *dev, | 
|  | 1372 | struct device_attribute *attr, const char *buf, size_t size) | 
|  | 1373 | { | 
|  | 1374 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1375 | struct rndis_function_config *config = f->config; | 
|  | 1376 | int value; | 
|  | 1377 |  | 
|  | 1378 | if (sscanf(buf, "%04x", &value) == 1) { | 
|  | 1379 | config->vendorID = value; | 
|  | 1380 | return size; | 
|  | 1381 | } | 
|  | 1382 | return -EINVAL; | 
|  | 1383 | } | 
|  | 1384 |  | 
|  | 1385 | static DEVICE_ATTR(vendorID, S_IRUGO | S_IWUSR, rndis_vendorID_show, | 
|  | 1386 | rndis_vendorID_store); | 
|  | 1387 |  | 
| Ofir Cohen | aef90b7 | 2012-07-31 12:37:04 +0200 | [diff] [blame] | 1388 | static ssize_t rndis_max_pkt_per_xfer_show(struct device *dev, | 
|  | 1389 | struct device_attribute *attr, char *buf) | 
|  | 1390 | { | 
|  | 1391 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1392 | struct rndis_function_config *config = f->config; | 
|  | 1393 | return snprintf(buf, PAGE_SIZE, "%d\n", config->max_pkt_per_xfer); | 
|  | 1394 | } | 
|  | 1395 |  | 
|  | 1396 | static ssize_t rndis_max_pkt_per_xfer_store(struct device *dev, | 
|  | 1397 | struct device_attribute *attr, const char *buf, size_t size) | 
|  | 1398 | { | 
|  | 1399 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1400 | struct rndis_function_config *config = f->config; | 
|  | 1401 | int value; | 
|  | 1402 |  | 
|  | 1403 | if (sscanf(buf, "%d", &value) == 1) { | 
|  | 1404 | config->max_pkt_per_xfer = value; | 
|  | 1405 | return size; | 
|  | 1406 | } | 
|  | 1407 | return -EINVAL; | 
|  | 1408 | } | 
|  | 1409 |  | 
|  | 1410 | static DEVICE_ATTR(max_pkt_per_xfer, S_IRUGO | S_IWUSR, | 
|  | 1411 | rndis_max_pkt_per_xfer_show, | 
|  | 1412 | rndis_max_pkt_per_xfer_store); | 
|  | 1413 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1414 | static struct device_attribute *rndis_function_attributes[] = { | 
|  | 1415 | &dev_attr_manufacturer, | 
|  | 1416 | &dev_attr_wceis, | 
|  | 1417 | &dev_attr_ethaddr, | 
|  | 1418 | &dev_attr_vendorID, | 
| Ofir Cohen | aef90b7 | 2012-07-31 12:37:04 +0200 | [diff] [blame] | 1419 | &dev_attr_max_pkt_per_xfer, | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1420 | NULL | 
|  | 1421 | }; | 
|  | 1422 |  | 
|  | 1423 | static struct android_usb_function rndis_function = { | 
|  | 1424 | .name		= "rndis", | 
|  | 1425 | .init		= rndis_function_init, | 
|  | 1426 | .cleanup	= rndis_function_cleanup, | 
|  | 1427 | .bind_config	= rndis_function_bind_config, | 
|  | 1428 | .unbind_config	= rndis_function_unbind_config, | 
|  | 1429 | .attributes	= rndis_function_attributes, | 
|  | 1430 | }; | 
|  | 1431 |  | 
| Ofir Cohen | aef90b7 | 2012-07-31 12:37:04 +0200 | [diff] [blame] | 1432 | static struct android_usb_function rndis_qc_function = { | 
|  | 1433 | .name		= "rndis_qc", | 
|  | 1434 | .init		= rndis_qc_function_init, | 
|  | 1435 | .cleanup	= rndis_qc_function_cleanup, | 
|  | 1436 | .bind_config	= rndis_qc_function_bind_config, | 
|  | 1437 | .unbind_config	= rndis_qc_function_unbind_config, | 
|  | 1438 | .attributes	= rndis_function_attributes, | 
|  | 1439 | }; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1440 |  | 
| Anna Perel | f9d0155 | 2012-11-20 15:56:32 +0200 | [diff] [blame] | 1441 | static int ecm_function_bind_config(struct android_usb_function *f, | 
|  | 1442 | struct usb_configuration *c) | 
|  | 1443 | { | 
|  | 1444 | int ret; | 
|  | 1445 | struct ecm_function_config *ecm = f->config; | 
|  | 1446 |  | 
|  | 1447 | if (!ecm) { | 
|  | 1448 | pr_err("%s: ecm_pdata\n", __func__); | 
|  | 1449 | return -EINVAL; | 
|  | 1450 | } | 
|  | 1451 |  | 
|  | 1452 | pr_info("%s MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", __func__, | 
|  | 1453 | ecm->ethaddr[0], ecm->ethaddr[1], ecm->ethaddr[2], | 
|  | 1454 | ecm->ethaddr[3], ecm->ethaddr[4], ecm->ethaddr[5]); | 
|  | 1455 |  | 
|  | 1456 | ret = gether_setup_name(c->cdev->gadget, ecm->ethaddr, "ecm"); | 
|  | 1457 | if (ret) { | 
|  | 1458 | pr_err("%s: gether_setup failed\n", __func__); | 
|  | 1459 | return ret; | 
|  | 1460 | } | 
|  | 1461 |  | 
|  | 1462 | ret = ecm_bind_config(c, ecm->ethaddr); | 
|  | 1463 | if (ret) { | 
|  | 1464 | pr_err("%s: ecm_bind_config failed\n", __func__); | 
|  | 1465 | gether_cleanup(); | 
|  | 1466 | } | 
|  | 1467 | return ret; | 
|  | 1468 | } | 
|  | 1469 |  | 
|  | 1470 | static void ecm_function_unbind_config(struct android_usb_function *f, | 
|  | 1471 | struct usb_configuration *c) | 
|  | 1472 | { | 
|  | 1473 | gether_cleanup(); | 
|  | 1474 | } | 
|  | 1475 |  | 
|  | 1476 | static struct android_usb_function ecm_function = { | 
|  | 1477 | .name		= "ecm", | 
|  | 1478 | .init		= ecm_function_init, | 
|  | 1479 | .cleanup	= ecm_function_cleanup, | 
|  | 1480 | .bind_config	= ecm_function_bind_config, | 
|  | 1481 | .unbind_config	= ecm_function_unbind_config, | 
|  | 1482 | .attributes	= ecm_function_attributes, | 
|  | 1483 | }; | 
|  | 1484 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1485 | struct mass_storage_function_config { | 
|  | 1486 | struct fsg_config fsg; | 
|  | 1487 | struct fsg_common *common; | 
|  | 1488 | }; | 
|  | 1489 |  | 
|  | 1490 | static int mass_storage_function_init(struct android_usb_function *f, | 
|  | 1491 | struct usb_composite_dev *cdev) | 
|  | 1492 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 1493 | struct android_dev *dev = cdev_to_android_dev(cdev); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1494 | struct mass_storage_function_config *config; | 
|  | 1495 | struct fsg_common *common; | 
|  | 1496 | int err; | 
| Rajkumar Raghupathy | c9cb205 | 2012-04-26 13:14:10 +0530 | [diff] [blame] | 1497 | int i; | 
|  | 1498 | const char *name[2]; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1499 |  | 
|  | 1500 | config = kzalloc(sizeof(struct mass_storage_function_config), | 
|  | 1501 | GFP_KERNEL); | 
|  | 1502 | if (!config) | 
|  | 1503 | return -ENOMEM; | 
|  | 1504 |  | 
|  | 1505 | config->fsg.nluns = 1; | 
| Rajkumar Raghupathy | c9cb205 | 2012-04-26 13:14:10 +0530 | [diff] [blame] | 1506 | name[0] = "lun"; | 
| Pavankumar Kondeti | 2043e30 | 2012-07-19 08:54:04 +0530 | [diff] [blame] | 1507 | if (dev->pdata && dev->pdata->cdrom) { | 
| Rajkumar Raghupathy | c9cb205 | 2012-04-26 13:14:10 +0530 | [diff] [blame] | 1508 | config->fsg.nluns = 2; | 
|  | 1509 | config->fsg.luns[1].cdrom = 1; | 
|  | 1510 | config->fsg.luns[1].ro = 1; | 
| Rajkumar Raghupathy | 3959500 | 2012-08-24 16:34:03 +0530 | [diff] [blame] | 1511 | config->fsg.luns[1].removable = 0; | 
| Rajkumar Raghupathy | c9cb205 | 2012-04-26 13:14:10 +0530 | [diff] [blame] | 1512 | name[1] = "lun0"; | 
|  | 1513 | } | 
|  | 1514 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1515 | config->fsg.luns[0].removable = 1; | 
|  | 1516 |  | 
|  | 1517 | common = fsg_common_init(NULL, cdev, &config->fsg); | 
|  | 1518 | if (IS_ERR(common)) { | 
|  | 1519 | kfree(config); | 
|  | 1520 | return PTR_ERR(common); | 
|  | 1521 | } | 
|  | 1522 |  | 
| Rajkumar Raghupathy | c9cb205 | 2012-04-26 13:14:10 +0530 | [diff] [blame] | 1523 | for (i = 0; i < config->fsg.nluns; i++) { | 
|  | 1524 | err = sysfs_create_link(&f->dev->kobj, | 
|  | 1525 | &common->luns[i].dev.kobj, | 
|  | 1526 | name[i]); | 
|  | 1527 | if (err) | 
|  | 1528 | goto error; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1529 | } | 
|  | 1530 |  | 
|  | 1531 | config->common = common; | 
|  | 1532 | f->config = config; | 
|  | 1533 | return 0; | 
| Rajkumar Raghupathy | c9cb205 | 2012-04-26 13:14:10 +0530 | [diff] [blame] | 1534 | error: | 
|  | 1535 | for (; i > 0 ; i--) | 
|  | 1536 | sysfs_remove_link(&f->dev->kobj, name[i-1]); | 
|  | 1537 |  | 
|  | 1538 | fsg_common_release(&common->ref); | 
|  | 1539 | kfree(config); | 
|  | 1540 | return err; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1541 | } | 
|  | 1542 |  | 
|  | 1543 | static void mass_storage_function_cleanup(struct android_usb_function *f) | 
|  | 1544 | { | 
|  | 1545 | kfree(f->config); | 
|  | 1546 | f->config = NULL; | 
|  | 1547 | } | 
|  | 1548 |  | 
|  | 1549 | static int mass_storage_function_bind_config(struct android_usb_function *f, | 
|  | 1550 | struct usb_configuration *c) | 
|  | 1551 | { | 
|  | 1552 | struct mass_storage_function_config *config = f->config; | 
|  | 1553 | return fsg_bind_config(c->cdev, c, config->common); | 
|  | 1554 | } | 
|  | 1555 |  | 
|  | 1556 | static ssize_t mass_storage_inquiry_show(struct device *dev, | 
|  | 1557 | struct device_attribute *attr, char *buf) | 
|  | 1558 | { | 
|  | 1559 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1560 | struct mass_storage_function_config *config = f->config; | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 1561 | return snprintf(buf, PAGE_SIZE, "%s\n", config->common->inquiry_string); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1562 | } | 
|  | 1563 |  | 
|  | 1564 | static ssize_t mass_storage_inquiry_store(struct device *dev, | 
|  | 1565 | struct device_attribute *attr, const char *buf, size_t size) | 
|  | 1566 | { | 
|  | 1567 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1568 | struct mass_storage_function_config *config = f->config; | 
|  | 1569 | if (size >= sizeof(config->common->inquiry_string)) | 
|  | 1570 | return -EINVAL; | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 1571 | if (sscanf(buf, "%28s", config->common->inquiry_string) != 1) | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1572 | return -EINVAL; | 
|  | 1573 | return size; | 
|  | 1574 | } | 
|  | 1575 |  | 
|  | 1576 | static DEVICE_ATTR(inquiry_string, S_IRUGO | S_IWUSR, | 
|  | 1577 | mass_storage_inquiry_show, | 
|  | 1578 | mass_storage_inquiry_store); | 
|  | 1579 |  | 
|  | 1580 | static struct device_attribute *mass_storage_function_attributes[] = { | 
|  | 1581 | &dev_attr_inquiry_string, | 
|  | 1582 | NULL | 
|  | 1583 | }; | 
|  | 1584 |  | 
|  | 1585 | static struct android_usb_function mass_storage_function = { | 
|  | 1586 | .name		= "mass_storage", | 
|  | 1587 | .init		= mass_storage_function_init, | 
|  | 1588 | .cleanup	= mass_storage_function_cleanup, | 
|  | 1589 | .bind_config	= mass_storage_function_bind_config, | 
|  | 1590 | .attributes	= mass_storage_function_attributes, | 
|  | 1591 | }; | 
|  | 1592 |  | 
|  | 1593 |  | 
| Benoit Goby | cf3fc06 | 2011-12-19 14:39:37 -0800 | [diff] [blame] | 1594 | static int accessory_function_init(struct android_usb_function *f, | 
|  | 1595 | struct usb_composite_dev *cdev) | 
|  | 1596 | { | 
|  | 1597 | return acc_setup(); | 
|  | 1598 | } | 
|  | 1599 |  | 
|  | 1600 | static void accessory_function_cleanup(struct android_usb_function *f) | 
|  | 1601 | { | 
|  | 1602 | acc_cleanup(); | 
|  | 1603 | } | 
|  | 1604 |  | 
|  | 1605 | static int accessory_function_bind_config(struct android_usb_function *f, | 
|  | 1606 | struct usb_configuration *c) | 
|  | 1607 | { | 
|  | 1608 | return acc_bind_config(c); | 
|  | 1609 | } | 
|  | 1610 |  | 
|  | 1611 | static int accessory_function_ctrlrequest(struct android_usb_function *f, | 
|  | 1612 | struct usb_composite_dev *cdev, | 
|  | 1613 | const struct usb_ctrlrequest *c) | 
|  | 1614 | { | 
|  | 1615 | return acc_ctrlrequest(cdev, c); | 
|  | 1616 | } | 
|  | 1617 |  | 
|  | 1618 | static struct android_usb_function accessory_function = { | 
|  | 1619 | .name		= "accessory", | 
|  | 1620 | .init		= accessory_function_init, | 
|  | 1621 | .cleanup	= accessory_function_cleanup, | 
|  | 1622 | .bind_config	= accessory_function_bind_config, | 
|  | 1623 | .ctrlrequest	= accessory_function_ctrlrequest, | 
|  | 1624 | }; | 
|  | 1625 |  | 
| Anna Perel | 3ee23dd | 2013-02-26 16:06:40 +0200 | [diff] [blame] | 1626 | #ifdef CONFIG_SND_PCM | 
| Mike Lockwood | 1187482 | 2012-08-27 16:43:53 +0530 | [diff] [blame] | 1627 | static int audio_source_function_init(struct android_usb_function *f, | 
|  | 1628 | struct usb_composite_dev *cdev) | 
|  | 1629 | { | 
|  | 1630 | struct audio_source_config *config; | 
|  | 1631 |  | 
|  | 1632 | config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL); | 
|  | 1633 | if (!config) | 
|  | 1634 | return -ENOMEM; | 
|  | 1635 | config->card = -1; | 
|  | 1636 | config->device = -1; | 
|  | 1637 | f->config = config; | 
|  | 1638 | return 0; | 
|  | 1639 | } | 
|  | 1640 |  | 
|  | 1641 | static void audio_source_function_cleanup(struct android_usb_function *f) | 
|  | 1642 | { | 
|  | 1643 | kfree(f->config); | 
|  | 1644 | } | 
|  | 1645 |  | 
|  | 1646 | static int audio_source_function_bind_config(struct android_usb_function *f, | 
|  | 1647 | struct usb_configuration *c) | 
|  | 1648 | { | 
|  | 1649 | struct audio_source_config *config = f->config; | 
|  | 1650 |  | 
|  | 1651 | return audio_source_bind_config(c, config); | 
|  | 1652 | } | 
|  | 1653 |  | 
|  | 1654 | static void audio_source_function_unbind_config(struct android_usb_function *f, | 
|  | 1655 | struct usb_configuration *c) | 
|  | 1656 | { | 
|  | 1657 | struct audio_source_config *config = f->config; | 
|  | 1658 |  | 
|  | 1659 | config->card = -1; | 
|  | 1660 | config->device = -1; | 
|  | 1661 | } | 
|  | 1662 |  | 
|  | 1663 | static ssize_t audio_source_pcm_show(struct device *dev, | 
|  | 1664 | struct device_attribute *attr, char *buf) | 
|  | 1665 | { | 
|  | 1666 | struct android_usb_function *f = dev_get_drvdata(dev); | 
|  | 1667 | struct audio_source_config *config = f->config; | 
|  | 1668 |  | 
|  | 1669 | /* print PCM card and device numbers */ | 
|  | 1670 | return sprintf(buf, "%d %d\n", config->card, config->device); | 
|  | 1671 | } | 
|  | 1672 |  | 
|  | 1673 | static DEVICE_ATTR(pcm, S_IRUGO | S_IWUSR, audio_source_pcm_show, NULL); | 
|  | 1674 |  | 
|  | 1675 | static struct device_attribute *audio_source_function_attributes[] = { | 
|  | 1676 | &dev_attr_pcm, | 
|  | 1677 | NULL | 
|  | 1678 | }; | 
|  | 1679 |  | 
|  | 1680 | static struct android_usb_function audio_source_function = { | 
|  | 1681 | .name		= "audio_source", | 
|  | 1682 | .init		= audio_source_function_init, | 
|  | 1683 | .cleanup	= audio_source_function_cleanup, | 
|  | 1684 | .bind_config	= audio_source_function_bind_config, | 
|  | 1685 | .unbind_config	= audio_source_function_unbind_config, | 
|  | 1686 | .attributes	= audio_source_function_attributes, | 
|  | 1687 | }; | 
| Anna Perel | 3ee23dd | 2013-02-26 16:06:40 +0200 | [diff] [blame] | 1688 | #endif | 
| Mike Lockwood | 1187482 | 2012-08-27 16:43:53 +0530 | [diff] [blame] | 1689 |  | 
| Pavankumar Kondeti | 8f6ca4f | 2012-06-26 09:44:36 +0530 | [diff] [blame] | 1690 | static int android_uasp_connect_cb(bool connect) | 
|  | 1691 | { | 
|  | 1692 | /* | 
|  | 1693 | * TODO | 
|  | 1694 | * We may have to disable gadget till UASP configfs nodes | 
|  | 1695 | * are configured which includes mapping LUN with the | 
|  | 1696 | * backing file. It is a fundamental difference between | 
|  | 1697 | * f_mass_storage and f_tcp. That means UASP can not be | 
|  | 1698 | * in default composition. | 
|  | 1699 | * | 
|  | 1700 | * For now, assume that UASP configfs nodes are configured | 
|  | 1701 | * before enabling android gadget. Or cable should be | 
|  | 1702 | * reconnected after mapping the LUN. | 
|  | 1703 | * | 
|  | 1704 | * Also consider making UASP to respond to Host requests when | 
|  | 1705 | * Lun is not mapped. | 
|  | 1706 | */ | 
|  | 1707 | pr_debug("UASP %s\n", connect ? "connect" : "disconnect"); | 
|  | 1708 |  | 
|  | 1709 | return 0; | 
|  | 1710 | } | 
|  | 1711 |  | 
|  | 1712 | static int uasp_function_init(struct android_usb_function *f, | 
|  | 1713 | struct usb_composite_dev *cdev) | 
|  | 1714 | { | 
|  | 1715 | return f_tcm_init(&android_uasp_connect_cb); | 
|  | 1716 | } | 
|  | 1717 |  | 
|  | 1718 | static void uasp_function_cleanup(struct android_usb_function *f) | 
|  | 1719 | { | 
|  | 1720 | f_tcm_exit(); | 
|  | 1721 | } | 
|  | 1722 |  | 
|  | 1723 | static int uasp_function_bind_config(struct android_usb_function *f, | 
|  | 1724 | struct usb_configuration *c) | 
|  | 1725 | { | 
|  | 1726 | return tcm_bind_config(c); | 
|  | 1727 | } | 
|  | 1728 |  | 
|  | 1729 | static struct android_usb_function uasp_function = { | 
|  | 1730 | .name		= "uasp", | 
|  | 1731 | .init		= uasp_function_init, | 
|  | 1732 | .cleanup	= uasp_function_cleanup, | 
|  | 1733 | .bind_config	= uasp_function_bind_config, | 
|  | 1734 | }; | 
| Benoit Goby | cf3fc06 | 2011-12-19 14:39:37 -0800 | [diff] [blame] | 1735 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1736 | static struct android_usb_function *supported_functions[] = { | 
| Anna Perel | a8c991d | 2012-04-09 16:44:46 +0300 | [diff] [blame] | 1737 | &mbim_function, | 
| Ofir Cohen | 7b15542 | 2012-07-31 13:02:49 +0300 | [diff] [blame] | 1738 | &ecm_qc_function, | 
| Jack Pham | 2ec5fdc | 2012-09-26 10:13:48 -0700 | [diff] [blame] | 1739 | #ifdef CONFIG_SND_PCM | 
| Anna Perel | 432367a | 2012-09-20 10:55:32 +0300 | [diff] [blame] | 1740 | &audio_function, | 
| Jack Pham | 2ec5fdc | 2012-09-26 10:13:48 -0700 | [diff] [blame] | 1741 | #endif | 
| Manu Gautam | 1c8ffd7 | 2011-09-02 16:00:49 +0530 | [diff] [blame] | 1742 | &rmnet_smd_function, | 
| Manu Gautam | 8e0719b | 2011-09-26 14:47:55 +0530 | [diff] [blame] | 1743 | &rmnet_sdio_function, | 
|  | 1744 | &rmnet_smd_sdio_function, | 
| Manu Gautam | 2b0234a | 2011-09-07 16:47:52 +0530 | [diff] [blame] | 1745 | &rmnet_function, | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 1746 | &diag_function, | 
| Shimrit Malichi | a00d732 | 2012-08-05 13:56:28 +0300 | [diff] [blame] | 1747 | &qdss_function, | 
| Manu Gautam | a4d993f | 2011-08-30 18:25:55 +0530 | [diff] [blame] | 1748 | &serial_function, | 
| Benoit Goby | 2b6862d | 2011-12-19 14:38:41 -0800 | [diff] [blame] | 1749 | &adb_function, | 
| Chiranjeevi Velempati | e130fd0 | 2011-11-29 05:06:13 +0530 | [diff] [blame] | 1750 | &ccid_function, | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1751 | &acm_function, | 
| Benoit Goby | f0fbc48 | 2011-12-19 14:37:50 -0800 | [diff] [blame] | 1752 | &mtp_function, | 
|  | 1753 | &ptp_function, | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1754 | &rndis_function, | 
| Ofir Cohen | aef90b7 | 2012-07-31 12:37:04 +0200 | [diff] [blame] | 1755 | &rndis_qc_function, | 
| Anna Perel | f9d0155 | 2012-11-20 15:56:32 +0200 | [diff] [blame] | 1756 | &ecm_function, | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1757 | &mass_storage_function, | 
| Benoit Goby | cf3fc06 | 2011-12-19 14:39:37 -0800 | [diff] [blame] | 1758 | &accessory_function, | 
| Anna Perel | 3ee23dd | 2013-02-26 16:06:40 +0200 | [diff] [blame] | 1759 | #ifdef CONFIG_SND_PCM | 
| Mike Lockwood | 1187482 | 2012-08-27 16:43:53 +0530 | [diff] [blame] | 1760 | &audio_source_function, | 
| Anna Perel | 3ee23dd | 2013-02-26 16:06:40 +0200 | [diff] [blame] | 1761 | #endif | 
| Pavankumar Kondeti | 8f6ca4f | 2012-06-26 09:44:36 +0530 | [diff] [blame] | 1762 | &uasp_function, | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1763 | NULL | 
|  | 1764 | }; | 
|  | 1765 |  | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 1766 | static void android_cleanup_functions(struct android_usb_function **functions) | 
|  | 1767 | { | 
|  | 1768 | struct android_usb_function *f; | 
|  | 1769 | struct device_attribute **attrs; | 
|  | 1770 | struct device_attribute *attr; | 
|  | 1771 |  | 
|  | 1772 | while (*functions) { | 
|  | 1773 | f = *functions++; | 
|  | 1774 |  | 
|  | 1775 | if (f->dev) { | 
|  | 1776 | device_destroy(android_class, f->dev->devt); | 
|  | 1777 | kfree(f->dev_name); | 
|  | 1778 | } else | 
|  | 1779 | continue; | 
|  | 1780 |  | 
|  | 1781 | if (f->cleanup) | 
|  | 1782 | f->cleanup(f); | 
|  | 1783 |  | 
|  | 1784 | attrs = f->attributes; | 
|  | 1785 | if (attrs) { | 
|  | 1786 | while ((attr = *attrs++)) | 
|  | 1787 | device_remove_file(f->dev, attr); | 
|  | 1788 | } | 
|  | 1789 | } | 
|  | 1790 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1791 |  | 
|  | 1792 | static int android_init_functions(struct android_usb_function **functions, | 
|  | 1793 | struct usb_composite_dev *cdev) | 
|  | 1794 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 1795 | struct android_dev *dev = cdev_to_android_dev(cdev); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1796 | struct android_usb_function *f; | 
|  | 1797 | struct device_attribute **attrs; | 
|  | 1798 | struct device_attribute *attr; | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 1799 | int err = 0; | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 1800 | int index = 1; /* index 0 is for android0 device */ | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1801 |  | 
|  | 1802 | for (; (f = *functions++); index++) { | 
|  | 1803 | f->dev_name = kasprintf(GFP_KERNEL, "f_%s", f->name); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 1804 | f->android_dev = NULL; | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 1805 | if (!f->dev_name) { | 
|  | 1806 | err = -ENOMEM; | 
|  | 1807 | goto err_out; | 
|  | 1808 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1809 | f->dev = device_create(android_class, dev->dev, | 
|  | 1810 | MKDEV(0, index), f, f->dev_name); | 
|  | 1811 | if (IS_ERR(f->dev)) { | 
|  | 1812 | pr_err("%s: Failed to create dev %s", __func__, | 
|  | 1813 | f->dev_name); | 
|  | 1814 | err = PTR_ERR(f->dev); | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 1815 | f->dev = NULL; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1816 | goto err_create; | 
|  | 1817 | } | 
|  | 1818 |  | 
|  | 1819 | if (f->init) { | 
|  | 1820 | err = f->init(f, cdev); | 
|  | 1821 | if (err) { | 
|  | 1822 | pr_err("%s: Failed to init %s", __func__, | 
|  | 1823 | f->name); | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 1824 | goto err_init; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1825 | } | 
|  | 1826 | } | 
|  | 1827 |  | 
|  | 1828 | attrs = f->attributes; | 
|  | 1829 | if (attrs) { | 
|  | 1830 | while ((attr = *attrs++) && !err) | 
|  | 1831 | err = device_create_file(f->dev, attr); | 
|  | 1832 | } | 
|  | 1833 | if (err) { | 
|  | 1834 | pr_err("%s: Failed to create function %s attributes", | 
|  | 1835 | __func__, f->name); | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 1836 | goto err_attrs; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1837 | } | 
|  | 1838 | } | 
|  | 1839 | return 0; | 
|  | 1840 |  | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 1841 | err_attrs: | 
|  | 1842 | for (attr = *(attrs -= 2); attrs != f->attributes; attr = *(attrs--)) | 
|  | 1843 | device_remove_file(f->dev, attr); | 
|  | 1844 | if (f->cleanup) | 
|  | 1845 | f->cleanup(f); | 
|  | 1846 | err_init: | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1847 | device_destroy(android_class, f->dev->devt); | 
|  | 1848 | err_create: | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 1849 | f->dev = NULL; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1850 | kfree(f->dev_name); | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 1851 | err_out: | 
|  | 1852 | android_cleanup_functions(dev->functions); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1853 | return err; | 
|  | 1854 | } | 
|  | 1855 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1856 | static int | 
|  | 1857 | android_bind_enabled_functions(struct android_dev *dev, | 
|  | 1858 | struct usb_configuration *c) | 
|  | 1859 | { | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1860 | struct android_usb_function_holder *f_holder; | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1861 | struct android_configuration *conf = | 
|  | 1862 | container_of(c, struct android_configuration, usb_config); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1863 | int ret; | 
|  | 1864 |  | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1865 | list_for_each_entry(f_holder, &conf->enabled_functions, enabled_list) { | 
|  | 1866 | ret = f_holder->f->bind_config(f_holder->f, c); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1867 | if (ret) { | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1868 | pr_err("%s: %s failed", __func__, f_holder->f->name); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1869 | return ret; | 
|  | 1870 | } | 
|  | 1871 | } | 
|  | 1872 | return 0; | 
|  | 1873 | } | 
|  | 1874 |  | 
|  | 1875 | static void | 
|  | 1876 | android_unbind_enabled_functions(struct android_dev *dev, | 
|  | 1877 | struct usb_configuration *c) | 
|  | 1878 | { | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1879 | struct android_usb_function_holder *f_holder; | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1880 | struct android_configuration *conf = | 
|  | 1881 | container_of(c, struct android_configuration, usb_config); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1882 |  | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1883 | list_for_each_entry(f_holder, &conf->enabled_functions, enabled_list) { | 
|  | 1884 | if (f_holder->f->unbind_config) | 
|  | 1885 | f_holder->f->unbind_config(f_holder->f, c); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1886 | } | 
|  | 1887 | } | 
|  | 1888 |  | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1889 | static int android_enable_function(struct android_dev *dev, | 
|  | 1890 | struct android_configuration *conf, | 
|  | 1891 | char *name) | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1892 | { | 
|  | 1893 | struct android_usb_function **functions = dev->functions; | 
|  | 1894 | struct android_usb_function *f; | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1895 | struct android_usb_function_holder *f_holder; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1896 | while ((f = *functions++)) { | 
|  | 1897 | if (!strcmp(name, f->name)) { | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1898 | if (f->android_dev && f->android_dev != dev) | 
|  | 1899 | pr_err("%s is enabled in other device\n", | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 1900 | f->name); | 
|  | 1901 | else { | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1902 | f_holder = kzalloc(sizeof(*f_holder), | 
|  | 1903 | GFP_KERNEL); | 
|  | 1904 | if (!f_holder) { | 
|  | 1905 | pr_err("Failed to alloc f_holder\n"); | 
|  | 1906 | return -ENOMEM; | 
|  | 1907 | } | 
|  | 1908 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 1909 | f->android_dev = dev; | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1910 | f_holder->f = f; | 
|  | 1911 | list_add_tail(&f_holder->enabled_list, | 
|  | 1912 | &conf->enabled_functions); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 1913 | return 0; | 
|  | 1914 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1915 | } | 
|  | 1916 | } | 
|  | 1917 | return -EINVAL; | 
|  | 1918 | } | 
|  | 1919 |  | 
|  | 1920 | /*-------------------------------------------------------------------------*/ | 
|  | 1921 | /* /sys/class/android_usb/android%d/ interface */ | 
|  | 1922 |  | 
| Pavankumar Kondeti | 352396c | 2011-12-07 13:32:40 +0530 | [diff] [blame] | 1923 | static ssize_t remote_wakeup_show(struct device *pdev, | 
|  | 1924 | struct device_attribute *attr, char *buf) | 
|  | 1925 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 1926 | struct android_dev *dev = dev_get_drvdata(pdev); | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1927 | struct android_configuration *conf; | 
|  | 1928 |  | 
|  | 1929 | /* | 
|  | 1930 | * Show the wakeup attribute of the first configuration, | 
|  | 1931 | * since all configurations have the same wakeup attribute | 
|  | 1932 | */ | 
|  | 1933 | if (dev->configs_num == 0) | 
|  | 1934 | return 0; | 
|  | 1935 | conf = list_entry(dev->configs.next, | 
|  | 1936 | struct android_configuration, | 
|  | 1937 | list_item); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 1938 |  | 
| Pavankumar Kondeti | 352396c | 2011-12-07 13:32:40 +0530 | [diff] [blame] | 1939 | return snprintf(buf, PAGE_SIZE, "%d\n", | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1940 | !!(conf->usb_config.bmAttributes & | 
| Pavankumar Kondeti | 352396c | 2011-12-07 13:32:40 +0530 | [diff] [blame] | 1941 | USB_CONFIG_ATT_WAKEUP)); | 
|  | 1942 | } | 
|  | 1943 |  | 
|  | 1944 | static ssize_t remote_wakeup_store(struct device *pdev, | 
|  | 1945 | struct device_attribute *attr, const char *buff, size_t size) | 
|  | 1946 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 1947 | struct android_dev *dev = dev_get_drvdata(pdev); | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1948 | struct android_configuration *conf; | 
| Pavankumar Kondeti | 352396c | 2011-12-07 13:32:40 +0530 | [diff] [blame] | 1949 | int enable = 0; | 
|  | 1950 |  | 
|  | 1951 | sscanf(buff, "%d", &enable); | 
|  | 1952 |  | 
|  | 1953 | pr_debug("android_usb: %s remote wakeup\n", | 
|  | 1954 | enable ? "enabling" : "disabling"); | 
|  | 1955 |  | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1956 | list_for_each_entry(conf, &dev->configs, list_item) | 
|  | 1957 | if (enable) | 
|  | 1958 | conf->usb_config.bmAttributes |= | 
|  | 1959 | USB_CONFIG_ATT_WAKEUP; | 
|  | 1960 | else | 
|  | 1961 | conf->usb_config.bmAttributes &= | 
|  | 1962 | ~USB_CONFIG_ATT_WAKEUP; | 
| Pavankumar Kondeti | 352396c | 2011-12-07 13:32:40 +0530 | [diff] [blame] | 1963 |  | 
|  | 1964 | return size; | 
|  | 1965 | } | 
|  | 1966 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1967 | static ssize_t | 
|  | 1968 | functions_show(struct device *pdev, struct device_attribute *attr, char *buf) | 
|  | 1969 | { | 
|  | 1970 | struct android_dev *dev = dev_get_drvdata(pdev); | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1971 | struct android_configuration *conf; | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1972 | struct android_usb_function_holder *f_holder; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1973 | char *buff = buf; | 
|  | 1974 |  | 
|  | 1975 | mutex_lock(&dev->mutex); | 
|  | 1976 |  | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1977 | list_for_each_entry(conf, &dev->configs, list_item) { | 
|  | 1978 | if (buff != buf) | 
|  | 1979 | *(buff-1) = ':'; | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 1980 | list_for_each_entry(f_holder, &conf->enabled_functions, | 
|  | 1981 | enabled_list) | 
|  | 1982 | buff += snprintf(buff, PAGE_SIZE, "%s,", | 
|  | 1983 | f_holder->f->name); | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1984 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 1985 |  | 
|  | 1986 | mutex_unlock(&dev->mutex); | 
|  | 1987 |  | 
|  | 1988 | if (buff != buf) | 
|  | 1989 | *(buff-1) = '\n'; | 
|  | 1990 | return buff - buf; | 
|  | 1991 | } | 
|  | 1992 |  | 
|  | 1993 | static ssize_t | 
|  | 1994 | functions_store(struct device *pdev, struct device_attribute *attr, | 
|  | 1995 | const char *buff, size_t size) | 
|  | 1996 | { | 
|  | 1997 | struct android_dev *dev = dev_get_drvdata(pdev); | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 1998 | struct list_head *curr_conf = &dev->configs; | 
|  | 1999 | struct android_configuration *conf; | 
|  | 2000 | char *conf_str; | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 2001 | struct android_usb_function_holder *f_holder; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2002 | char *name; | 
|  | 2003 | char buf[256], *b; | 
|  | 2004 | int err; | 
|  | 2005 |  | 
|  | 2006 | mutex_lock(&dev->mutex); | 
|  | 2007 |  | 
|  | 2008 | if (dev->enabled) { | 
|  | 2009 | mutex_unlock(&dev->mutex); | 
|  | 2010 | return -EBUSY; | 
|  | 2011 | } | 
|  | 2012 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2013 | /* Clear previous enabled list */ | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2014 | list_for_each_entry(conf, &dev->configs, list_item) { | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 2015 | while (conf->enabled_functions.next != | 
|  | 2016 | &conf->enabled_functions) { | 
|  | 2017 | f_holder = list_entry(conf->enabled_functions.next, | 
|  | 2018 | typeof(*f_holder), | 
|  | 2019 | enabled_list); | 
|  | 2020 | f_holder->f->android_dev = NULL; | 
|  | 2021 | list_del(&f_holder->enabled_list); | 
|  | 2022 | kfree(f_holder); | 
|  | 2023 | } | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2024 | INIT_LIST_HEAD(&conf->enabled_functions); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2025 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2026 |  | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 2027 | strlcpy(buf, buff, sizeof(buf)); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2028 | b = strim(buf); | 
|  | 2029 |  | 
|  | 2030 | while (b) { | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2031 | conf_str = strsep(&b, ":"); | 
|  | 2032 | if (conf_str) { | 
|  | 2033 | /* If the next not equal to the head, take it */ | 
|  | 2034 | if (curr_conf->next != &dev->configs) | 
|  | 2035 | conf = list_entry(curr_conf->next, | 
|  | 2036 | struct android_configuration, | 
|  | 2037 | list_item); | 
|  | 2038 | else | 
|  | 2039 | conf = alloc_android_config(dev); | 
|  | 2040 |  | 
|  | 2041 | curr_conf = curr_conf->next; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2042 | } | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2043 |  | 
|  | 2044 | while (conf_str) { | 
|  | 2045 | name = strsep(&conf_str, ","); | 
|  | 2046 | if (name) { | 
|  | 2047 | err = android_enable_function(dev, conf, name); | 
|  | 2048 | if (err) | 
|  | 2049 | pr_err("android_usb: Cannot enable %s", | 
|  | 2050 | name); | 
|  | 2051 | } | 
|  | 2052 | } | 
|  | 2053 | } | 
|  | 2054 |  | 
|  | 2055 | /* Free uneeded configurations if exists */ | 
|  | 2056 | while (curr_conf->next != &dev->configs) { | 
|  | 2057 | conf = list_entry(curr_conf->next, | 
|  | 2058 | struct android_configuration, list_item); | 
|  | 2059 | free_android_config(dev, conf); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2060 | } | 
|  | 2061 |  | 
|  | 2062 | mutex_unlock(&dev->mutex); | 
|  | 2063 |  | 
|  | 2064 | return size; | 
|  | 2065 | } | 
|  | 2066 |  | 
|  | 2067 | static ssize_t enable_show(struct device *pdev, struct device_attribute *attr, | 
|  | 2068 | char *buf) | 
|  | 2069 | { | 
|  | 2070 | struct android_dev *dev = dev_get_drvdata(pdev); | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 2071 |  | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 2072 | return snprintf(buf, PAGE_SIZE, "%d\n", dev->enabled); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2073 | } | 
|  | 2074 |  | 
|  | 2075 | static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, | 
|  | 2076 | const char *buff, size_t size) | 
|  | 2077 | { | 
|  | 2078 | struct android_dev *dev = dev_get_drvdata(pdev); | 
|  | 2079 | struct usb_composite_dev *cdev = dev->cdev; | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 2080 | struct android_usb_function_holder *f_holder; | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2081 | struct android_configuration *conf; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2082 | int enabled = 0; | 
| Rajkumar Raghupathy | a79363b | 2013-01-02 19:08:49 +0530 | [diff] [blame] | 2083 | bool audio_enabled = false; | 
| Pavankumar Kondeti | 19d8bc6 | 2013-02-28 10:19:40 +0530 | [diff] [blame] | 2084 | static DEFINE_RATELIMIT_STATE(rl, 10*HZ, 1); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2085 |  | 
| Benoit Goby | cf3fc06 | 2011-12-19 14:39:37 -0800 | [diff] [blame] | 2086 | if (!cdev) | 
|  | 2087 | return -ENODEV; | 
|  | 2088 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2089 | mutex_lock(&dev->mutex); | 
|  | 2090 |  | 
|  | 2091 | sscanf(buff, "%d", &enabled); | 
|  | 2092 | if (enabled && !dev->enabled) { | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2093 | /* | 
|  | 2094 | * Update values in composite driver's copy of | 
|  | 2095 | * device descriptor. | 
|  | 2096 | */ | 
|  | 2097 | cdev->desc.idVendor = device_desc.idVendor; | 
|  | 2098 | cdev->desc.idProduct = device_desc.idProduct; | 
|  | 2099 | cdev->desc.bcdDevice = device_desc.bcdDevice; | 
|  | 2100 | cdev->desc.bDeviceClass = device_desc.bDeviceClass; | 
|  | 2101 | cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass; | 
|  | 2102 | cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol; | 
| Rajkumar Raghupathy | a79363b | 2013-01-02 19:08:49 +0530 | [diff] [blame] | 2103 |  | 
|  | 2104 | /* Audio dock accessory is unable to enumerate device if | 
|  | 2105 | * pull-up is enabled immediately. The enumeration is | 
|  | 2106 | * reliable with 100 msec delay. | 
|  | 2107 | */ | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2108 | list_for_each_entry(conf, &dev->configs, list_item) | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 2109 | list_for_each_entry(f_holder, &conf->enabled_functions, | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2110 | enabled_list) { | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 2111 | if (f_holder->f->enable) | 
|  | 2112 | f_holder->f->enable(f_holder->f); | 
| Rajkumar Raghupathy | a79363b | 2013-01-02 19:08:49 +0530 | [diff] [blame] | 2113 | if (!strncmp(f_holder->f->name, | 
|  | 2114 | "audio_source", 12)) | 
|  | 2115 | audio_enabled = true; | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2116 | } | 
| Rajkumar Raghupathy | a79363b | 2013-01-02 19:08:49 +0530 | [diff] [blame] | 2117 | if (audio_enabled) | 
|  | 2118 | msleep(100); | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 2119 | android_enable(dev); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2120 | dev->enabled = true; | 
|  | 2121 | } else if (!enabled && dev->enabled) { | 
| Benoit Goby | 80ba14d | 2012-03-19 18:56:52 -0700 | [diff] [blame] | 2122 | android_disable(dev); | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2123 | list_for_each_entry(conf, &dev->configs, list_item) | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 2124 | list_for_each_entry(f_holder, &conf->enabled_functions, | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2125 | enabled_list) { | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 2126 | if (f_holder->f->disable) | 
|  | 2127 | f_holder->f->disable(f_holder->f); | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2128 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2129 | dev->enabled = false; | 
| Pavankumar Kondeti | 19d8bc6 | 2013-02-28 10:19:40 +0530 | [diff] [blame] | 2130 | } else if (__ratelimit(&rl)) { | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2131 | pr_err("android_usb: already %s\n", | 
|  | 2132 | dev->enabled ? "enabled" : "disabled"); | 
|  | 2133 | } | 
|  | 2134 |  | 
|  | 2135 | mutex_unlock(&dev->mutex); | 
| Steve Muckle | f132c6c | 2012-06-06 18:30:57 -0700 | [diff] [blame] | 2136 |  | 
| Benoit Goby | aab9681 | 2011-04-19 20:37:33 -0700 | [diff] [blame] | 2137 | return size; | 
|  | 2138 | } | 
|  | 2139 |  | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 2140 | static ssize_t pm_qos_show(struct device *pdev, | 
|  | 2141 | struct device_attribute *attr, char *buf) | 
|  | 2142 | { | 
|  | 2143 | struct android_dev *dev = dev_get_drvdata(pdev); | 
|  | 2144 |  | 
|  | 2145 | return snprintf(buf, PAGE_SIZE, "%s\n", dev->pm_qos); | 
|  | 2146 | } | 
|  | 2147 |  | 
|  | 2148 | static ssize_t pm_qos_store(struct device *pdev, | 
|  | 2149 | struct device_attribute *attr, | 
|  | 2150 | const char *buff, size_t size) | 
|  | 2151 | { | 
|  | 2152 | struct android_dev *dev = dev_get_drvdata(pdev); | 
|  | 2153 |  | 
|  | 2154 | strlcpy(dev->pm_qos, buff, sizeof(dev->pm_qos)); | 
|  | 2155 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2156 | return size; | 
|  | 2157 | } | 
|  | 2158 |  | 
|  | 2159 | static ssize_t state_show(struct device *pdev, struct device_attribute *attr, | 
|  | 2160 | char *buf) | 
|  | 2161 | { | 
|  | 2162 | struct android_dev *dev = dev_get_drvdata(pdev); | 
|  | 2163 | struct usb_composite_dev *cdev = dev->cdev; | 
|  | 2164 | char *state = "DISCONNECTED"; | 
|  | 2165 | unsigned long flags; | 
|  | 2166 |  | 
|  | 2167 | if (!cdev) | 
|  | 2168 | goto out; | 
|  | 2169 |  | 
|  | 2170 | spin_lock_irqsave(&cdev->lock, flags); | 
|  | 2171 | if (cdev->config) | 
|  | 2172 | state = "CONFIGURED"; | 
|  | 2173 | else if (dev->connected) | 
|  | 2174 | state = "CONNECTED"; | 
|  | 2175 | spin_unlock_irqrestore(&cdev->lock, flags); | 
|  | 2176 | out: | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 2177 | return snprintf(buf, PAGE_SIZE, "%s\n", state); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2178 | } | 
|  | 2179 |  | 
|  | 2180 | #define DESCRIPTOR_ATTR(field, format_string)				\ | 
|  | 2181 | static ssize_t								\ | 
|  | 2182 | field ## _show(struct device *dev, struct device_attribute *attr,	\ | 
|  | 2183 | char *buf)						\ | 
|  | 2184 | {									\ | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 2185 | return snprintf(buf, PAGE_SIZE,					\ | 
|  | 2186 | format_string, device_desc.field);		\ | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2187 | }									\ | 
|  | 2188 | static ssize_t								\ | 
|  | 2189 | field ## _store(struct device *dev, struct device_attribute *attr,	\ | 
|  | 2190 | const char *buf, size_t size)				\ | 
|  | 2191 | {									\ | 
|  | 2192 | int value;							\ | 
|  | 2193 | if (sscanf(buf, format_string, &value) == 1) {			\ | 
|  | 2194 | device_desc.field = value;				\ | 
|  | 2195 | return size;						\ | 
|  | 2196 | }								\ | 
|  | 2197 | return -1;							\ | 
|  | 2198 | }									\ | 
|  | 2199 | static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); | 
|  | 2200 |  | 
|  | 2201 | #define DESCRIPTOR_STRING_ATTR(field, buffer)				\ | 
|  | 2202 | static ssize_t								\ | 
|  | 2203 | field ## _show(struct device *dev, struct device_attribute *attr,	\ | 
|  | 2204 | char *buf)						\ | 
|  | 2205 | {									\ | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 2206 | return snprintf(buf, PAGE_SIZE, "%s", buffer);			\ | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2207 | }									\ | 
|  | 2208 | static ssize_t								\ | 
|  | 2209 | field ## _store(struct device *dev, struct device_attribute *attr,	\ | 
|  | 2210 | const char *buf, size_t size)				\ | 
|  | 2211 | {									\ | 
|  | 2212 | if (size >= sizeof(buffer))					\ | 
|  | 2213 | return -EINVAL;						\ | 
| Pavankumar Kondeti | e02a51a | 2012-06-20 08:52:37 +0530 | [diff] [blame] | 2214 | strlcpy(buffer, buf, sizeof(buffer));				\ | 
|  | 2215 | strim(buffer);							\ | 
| Pavankumar Kondeti | 4c22c10 | 2012-06-15 10:59:05 +0530 | [diff] [blame] | 2216 | return size;							\ | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2217 | }									\ | 
|  | 2218 | static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); | 
|  | 2219 |  | 
|  | 2220 |  | 
|  | 2221 | DESCRIPTOR_ATTR(idVendor, "%04x\n") | 
|  | 2222 | DESCRIPTOR_ATTR(idProduct, "%04x\n") | 
|  | 2223 | DESCRIPTOR_ATTR(bcdDevice, "%04x\n") | 
|  | 2224 | DESCRIPTOR_ATTR(bDeviceClass, "%d\n") | 
|  | 2225 | DESCRIPTOR_ATTR(bDeviceSubClass, "%d\n") | 
|  | 2226 | DESCRIPTOR_ATTR(bDeviceProtocol, "%d\n") | 
|  | 2227 | DESCRIPTOR_STRING_ATTR(iManufacturer, manufacturer_string) | 
|  | 2228 | DESCRIPTOR_STRING_ATTR(iProduct, product_string) | 
|  | 2229 | DESCRIPTOR_STRING_ATTR(iSerial, serial_string) | 
|  | 2230 |  | 
|  | 2231 | static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show, | 
|  | 2232 | functions_store); | 
|  | 2233 | static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 2234 | static DEVICE_ATTR(pm_qos, S_IRUGO | S_IWUSR, | 
|  | 2235 | pm_qos_show, pm_qos_store); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2236 | static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); | 
| Pavankumar Kondeti | 352396c | 2011-12-07 13:32:40 +0530 | [diff] [blame] | 2237 | static DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR, | 
|  | 2238 | remote_wakeup_show, remote_wakeup_store); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2239 |  | 
|  | 2240 | static struct device_attribute *android_usb_attributes[] = { | 
|  | 2241 | &dev_attr_idVendor, | 
|  | 2242 | &dev_attr_idProduct, | 
|  | 2243 | &dev_attr_bcdDevice, | 
|  | 2244 | &dev_attr_bDeviceClass, | 
|  | 2245 | &dev_attr_bDeviceSubClass, | 
|  | 2246 | &dev_attr_bDeviceProtocol, | 
|  | 2247 | &dev_attr_iManufacturer, | 
|  | 2248 | &dev_attr_iProduct, | 
|  | 2249 | &dev_attr_iSerial, | 
|  | 2250 | &dev_attr_functions, | 
|  | 2251 | &dev_attr_enable, | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 2252 | &dev_attr_pm_qos, | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2253 | &dev_attr_state, | 
| Pavankumar Kondeti | 352396c | 2011-12-07 13:32:40 +0530 | [diff] [blame] | 2254 | &dev_attr_remote_wakeup, | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2255 | NULL | 
|  | 2256 | }; | 
|  | 2257 |  | 
|  | 2258 | /*-------------------------------------------------------------------------*/ | 
|  | 2259 | /* Composite driver */ | 
|  | 2260 |  | 
|  | 2261 | static int android_bind_config(struct usb_configuration *c) | 
|  | 2262 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2263 | struct android_dev *dev = cdev_to_android_dev(c->cdev); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2264 | int ret = 0; | 
|  | 2265 |  | 
|  | 2266 | ret = android_bind_enabled_functions(dev, c); | 
|  | 2267 | if (ret) | 
|  | 2268 | return ret; | 
|  | 2269 |  | 
|  | 2270 | return 0; | 
|  | 2271 | } | 
|  | 2272 |  | 
|  | 2273 | static void android_unbind_config(struct usb_configuration *c) | 
|  | 2274 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2275 | struct android_dev *dev = cdev_to_android_dev(c->cdev); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2276 |  | 
|  | 2277 | android_unbind_enabled_functions(dev, c); | 
|  | 2278 | } | 
|  | 2279 |  | 
|  | 2280 | static int android_bind(struct usb_composite_dev *cdev) | 
|  | 2281 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2282 | struct android_dev *dev; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2283 | struct usb_gadget	*gadget = cdev->gadget; | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2284 | struct android_configuration *conf; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2285 | int			gcnum, id, ret; | 
|  | 2286 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2287 | /* Bind to the last android_dev that was probed */ | 
|  | 2288 | dev = list_entry(android_dev_list.prev, struct android_dev, list_item); | 
|  | 2289 |  | 
|  | 2290 | dev->cdev = cdev; | 
|  | 2291 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2292 | /* | 
|  | 2293 | * Start disconnected. Userspace will connect the gadget once | 
|  | 2294 | * it is done configuring the functions. | 
|  | 2295 | */ | 
|  | 2296 | usb_gadget_disconnect(gadget); | 
|  | 2297 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2298 | /* Init the supported functions only once, on the first android_dev */ | 
|  | 2299 | if (android_dev_count == 1) { | 
|  | 2300 | ret = android_init_functions(dev->functions, cdev); | 
|  | 2301 | if (ret) | 
|  | 2302 | return ret; | 
|  | 2303 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2304 |  | 
|  | 2305 | /* Allocate string descriptor numbers ... note that string | 
|  | 2306 | * contents can be overridden by the composite_dev glue. | 
|  | 2307 | */ | 
|  | 2308 | id = usb_string_id(cdev); | 
|  | 2309 | if (id < 0) | 
|  | 2310 | return id; | 
|  | 2311 | strings_dev[STRING_MANUFACTURER_IDX].id = id; | 
|  | 2312 | device_desc.iManufacturer = id; | 
|  | 2313 |  | 
|  | 2314 | id = usb_string_id(cdev); | 
|  | 2315 | if (id < 0) | 
|  | 2316 | return id; | 
|  | 2317 | strings_dev[STRING_PRODUCT_IDX].id = id; | 
|  | 2318 | device_desc.iProduct = id; | 
|  | 2319 |  | 
|  | 2320 | /* Default strings - should be updated by userspace */ | 
| Rajkumar Raghupathy | 42ec8da | 2011-10-21 18:58:53 +0530 | [diff] [blame] | 2321 | strlcpy(manufacturer_string, "Android", | 
|  | 2322 | sizeof(manufacturer_string) - 1); | 
|  | 2323 | strlcpy(product_string, "Android", sizeof(product_string) - 1); | 
|  | 2324 | strlcpy(serial_string, "0123456789ABCDEF", sizeof(serial_string) - 1); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2325 |  | 
|  | 2326 | id = usb_string_id(cdev); | 
|  | 2327 | if (id < 0) | 
|  | 2328 | return id; | 
|  | 2329 | strings_dev[STRING_SERIAL_IDX].id = id; | 
|  | 2330 | device_desc.iSerialNumber = id; | 
|  | 2331 |  | 
| Vijayavardhan Vennapusa | 56e6052 | 2012-02-16 15:40:16 +0530 | [diff] [blame] | 2332 | if (gadget_is_otg(cdev->gadget)) | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2333 | list_for_each_entry(conf, &dev->configs, list_item) | 
|  | 2334 | conf->usb_config.descriptors = otg_desc; | 
| Vijayavardhan Vennapusa | 56e6052 | 2012-02-16 15:40:16 +0530 | [diff] [blame] | 2335 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2336 | gcnum = usb_gadget_controller_number(gadget); | 
|  | 2337 | if (gcnum >= 0) | 
|  | 2338 | device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); | 
|  | 2339 | else { | 
|  | 2340 | pr_warning("%s: controller '%s' not recognized\n", | 
|  | 2341 | longname, gadget->name); | 
|  | 2342 | device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); | 
|  | 2343 | } | 
|  | 2344 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2345 | return 0; | 
|  | 2346 | } | 
|  | 2347 |  | 
|  | 2348 | static int android_usb_unbind(struct usb_composite_dev *cdev) | 
|  | 2349 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2350 | struct android_dev *dev = cdev_to_android_dev(cdev); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2351 |  | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 2352 | manufacturer_string[0] = '\0'; | 
|  | 2353 | product_string[0] = '\0'; | 
|  | 2354 | serial_string[0] = '0'; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2355 | cancel_work_sync(&dev->work); | 
|  | 2356 | android_cleanup_functions(dev->functions); | 
|  | 2357 | return 0; | 
|  | 2358 | } | 
|  | 2359 |  | 
|  | 2360 | static struct usb_composite_driver android_usb_driver = { | 
|  | 2361 | .name		= "android_usb", | 
|  | 2362 | .dev		= &device_desc, | 
|  | 2363 | .strings	= dev_strings, | 
|  | 2364 | .unbind		= android_usb_unbind, | 
| Tatyana Brokhman | 3ba2890 | 2011-06-29 16:41:49 +0300 | [diff] [blame] | 2365 | .max_speed	= USB_SPEED_SUPER | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2366 | }; | 
|  | 2367 |  | 
|  | 2368 | static int | 
|  | 2369 | android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) | 
|  | 2370 | { | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2371 | struct usb_composite_dev	*cdev = get_gadget_data(gadget); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2372 | struct android_dev		*dev = cdev_to_android_dev(cdev); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2373 | struct usb_request		*req = cdev->req; | 
|  | 2374 | struct android_usb_function	*f; | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 2375 | struct android_usb_function_holder *f_holder; | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2376 | struct android_configuration	*conf; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2377 | int value = -EOPNOTSUPP; | 
|  | 2378 | unsigned long flags; | 
|  | 2379 |  | 
|  | 2380 | req->zero = 0; | 
|  | 2381 | req->complete = composite_setup_complete; | 
|  | 2382 | req->length = 0; | 
|  | 2383 | gadget->ep0->driver_data = cdev; | 
|  | 2384 |  | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2385 | list_for_each_entry(conf, &dev->configs, list_item) | 
| Ido Shayevitz | 68557e3 | 2012-11-06 12:40:37 +0200 | [diff] [blame] | 2386 | list_for_each_entry(f_holder, | 
|  | 2387 | &conf->enabled_functions, | 
|  | 2388 | enabled_list) { | 
|  | 2389 | f = f_holder->f; | 
|  | 2390 | if (f->ctrlrequest) { | 
|  | 2391 | value = f->ctrlrequest(f, cdev, c); | 
|  | 2392 | if (value >= 0) | 
|  | 2393 | break; | 
|  | 2394 | } | 
|  | 2395 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2396 |  | 
| Benoit Goby | cf3fc06 | 2011-12-19 14:39:37 -0800 | [diff] [blame] | 2397 | /* Special case the accessory function. | 
|  | 2398 | * It needs to handle control requests before it is enabled. | 
|  | 2399 | */ | 
|  | 2400 | if (value < 0) | 
|  | 2401 | value = acc_ctrlrequest(cdev, c); | 
|  | 2402 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2403 | if (value < 0) | 
|  | 2404 | value = composite_setup(gadget, c); | 
|  | 2405 |  | 
|  | 2406 | spin_lock_irqsave(&cdev->lock, flags); | 
|  | 2407 | if (!dev->connected) { | 
|  | 2408 | dev->connected = 1; | 
|  | 2409 | schedule_work(&dev->work); | 
|  | 2410 | } else if (c->bRequest == USB_REQ_SET_CONFIGURATION && | 
|  | 2411 | cdev->config) { | 
|  | 2412 | schedule_work(&dev->work); | 
|  | 2413 | } | 
|  | 2414 | spin_unlock_irqrestore(&cdev->lock, flags); | 
|  | 2415 |  | 
|  | 2416 | return value; | 
|  | 2417 | } | 
|  | 2418 |  | 
|  | 2419 | static void android_disconnect(struct usb_gadget *gadget) | 
|  | 2420 | { | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2421 | struct usb_composite_dev *cdev = get_gadget_data(gadget); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2422 | struct android_dev *dev = cdev_to_android_dev(cdev); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2423 | unsigned long flags; | 
|  | 2424 |  | 
|  | 2425 | composite_disconnect(gadget); | 
| Mike Lockwood | e7558bb | 2012-08-27 16:23:48 +0530 | [diff] [blame] | 2426 | /* accessory HID support can be active while the | 
|  | 2427 | accessory function is not actually enabled, | 
|  | 2428 | so we need to inform it when we are disconnected. | 
|  | 2429 | */ | 
|  | 2430 | acc_disconnect(); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2431 |  | 
|  | 2432 | spin_lock_irqsave(&cdev->lock, flags); | 
|  | 2433 | dev->connected = 0; | 
|  | 2434 | schedule_work(&dev->work); | 
|  | 2435 | spin_unlock_irqrestore(&cdev->lock, flags); | 
|  | 2436 | } | 
|  | 2437 |  | 
| Ido Shayevitz | fb5edfe | 2012-12-26 14:26:37 +0200 | [diff] [blame] | 2438 | static void android_suspend(struct usb_gadget *gadget) | 
|  | 2439 | { | 
|  | 2440 | struct usb_composite_dev *cdev = get_gadget_data(gadget); | 
|  | 2441 | struct android_dev *dev = cdev_to_android_dev(cdev); | 
|  | 2442 | unsigned long flags; | 
|  | 2443 |  | 
|  | 2444 | spin_lock_irqsave(&cdev->lock, flags); | 
|  | 2445 | dev->suspended = 1; | 
|  | 2446 | schedule_work(&dev->work); | 
|  | 2447 | spin_unlock_irqrestore(&cdev->lock, flags); | 
|  | 2448 |  | 
|  | 2449 | composite_suspend(gadget); | 
|  | 2450 | } | 
|  | 2451 |  | 
|  | 2452 | static void android_resume(struct usb_gadget *gadget) | 
|  | 2453 | { | 
|  | 2454 | struct usb_composite_dev *cdev = get_gadget_data(gadget); | 
|  | 2455 | struct android_dev *dev = cdev_to_android_dev(cdev); | 
|  | 2456 | unsigned long flags; | 
|  | 2457 |  | 
|  | 2458 | spin_lock_irqsave(&cdev->lock, flags); | 
|  | 2459 | dev->suspended = 0; | 
|  | 2460 | schedule_work(&dev->work); | 
|  | 2461 | spin_unlock_irqrestore(&cdev->lock, flags); | 
|  | 2462 |  | 
|  | 2463 | composite_resume(gadget); | 
|  | 2464 | } | 
|  | 2465 |  | 
|  | 2466 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2467 | static int android_create_device(struct android_dev *dev, u8 usb_core_id) | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2468 | { | 
|  | 2469 | struct device_attribute **attrs = android_usb_attributes; | 
|  | 2470 | struct device_attribute *attr; | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2471 | char device_node_name[ANDROID_DEVICE_NODE_NAME_LENGTH]; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2472 | int err; | 
|  | 2473 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2474 | /* | 
|  | 2475 | * The primary usb core should always have usb_core_id=0, since | 
|  | 2476 | * Android user space is currently interested in android0 events. | 
|  | 2477 | */ | 
|  | 2478 | snprintf(device_node_name, ANDROID_DEVICE_NODE_NAME_LENGTH, | 
|  | 2479 | "android%d", usb_core_id); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2480 | dev->dev = device_create(android_class, NULL, | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2481 | MKDEV(0, 0), NULL, device_node_name); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2482 | if (IS_ERR(dev->dev)) | 
|  | 2483 | return PTR_ERR(dev->dev); | 
|  | 2484 |  | 
|  | 2485 | dev_set_drvdata(dev->dev, dev); | 
|  | 2486 |  | 
|  | 2487 | while ((attr = *attrs++)) { | 
|  | 2488 | err = device_create_file(dev->dev, attr); | 
|  | 2489 | if (err) { | 
|  | 2490 | device_destroy(android_class, dev->dev->devt); | 
|  | 2491 | return err; | 
|  | 2492 | } | 
|  | 2493 | } | 
|  | 2494 | return 0; | 
|  | 2495 | } | 
|  | 2496 |  | 
| Rajkumar Raghupathy | a1df77e | 2012-01-19 17:45:55 +0530 | [diff] [blame] | 2497 | static void android_destroy_device(struct android_dev *dev) | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2498 | { | 
| Rajkumar Raghupathy | a1df77e | 2012-01-19 17:45:55 +0530 | [diff] [blame] | 2499 | struct device_attribute **attrs = android_usb_attributes; | 
|  | 2500 | struct device_attribute *attr; | 
|  | 2501 |  | 
|  | 2502 | while ((attr = *attrs++)) | 
|  | 2503 | device_remove_file(dev->dev, attr); | 
|  | 2504 | device_destroy(android_class, dev->dev->devt); | 
|  | 2505 | } | 
|  | 2506 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2507 | static struct android_dev *cdev_to_android_dev(struct usb_composite_dev *cdev) | 
|  | 2508 | { | 
|  | 2509 | struct android_dev *dev = NULL; | 
|  | 2510 |  | 
|  | 2511 | /* Find the android dev from the list */ | 
|  | 2512 | list_for_each_entry(dev, &android_dev_list, list_item) { | 
|  | 2513 | if (dev->cdev == cdev) | 
|  | 2514 | break; | 
|  | 2515 | } | 
|  | 2516 |  | 
|  | 2517 | return dev; | 
|  | 2518 | } | 
|  | 2519 |  | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2520 | static struct android_configuration *alloc_android_config | 
|  | 2521 | (struct android_dev *dev) | 
|  | 2522 | { | 
|  | 2523 | struct android_configuration *conf; | 
|  | 2524 |  | 
|  | 2525 | conf = kzalloc(sizeof(*conf), GFP_KERNEL); | 
|  | 2526 | if (!conf) { | 
|  | 2527 | pr_err("%s(): Failed to alloc memory for android conf\n", | 
|  | 2528 | __func__); | 
|  | 2529 | return ERR_PTR(-ENOMEM); | 
|  | 2530 | } | 
|  | 2531 |  | 
|  | 2532 | dev->configs_num++; | 
|  | 2533 | conf->usb_config.label = dev->name; | 
|  | 2534 | conf->usb_config.unbind = android_unbind_config; | 
|  | 2535 | conf->usb_config.bConfigurationValue = dev->configs_num; | 
|  | 2536 |  | 
|  | 2537 | INIT_LIST_HEAD(&conf->enabled_functions); | 
|  | 2538 |  | 
|  | 2539 | list_add_tail(&conf->list_item, &dev->configs); | 
|  | 2540 |  | 
|  | 2541 | return conf; | 
|  | 2542 | } | 
|  | 2543 |  | 
|  | 2544 | static void free_android_config(struct android_dev *dev, | 
|  | 2545 | struct android_configuration *conf) | 
|  | 2546 | { | 
|  | 2547 | list_del(&conf->list_item); | 
|  | 2548 | dev->configs_num--; | 
|  | 2549 | kfree(conf); | 
|  | 2550 | } | 
|  | 2551 |  | 
| Manu Gautam | 43c61a1 | 2012-08-22 17:09:37 -0700 | [diff] [blame] | 2552 | static int usb_diag_update_pid_and_serial_num(u32 pid, const char *snum) | 
|  | 2553 | { | 
|  | 2554 | struct dload_struct local_diag_dload = { 0 }; | 
|  | 2555 | int *src, *dst, i; | 
|  | 2556 |  | 
|  | 2557 | if (!diag_dload) { | 
|  | 2558 | pr_debug("%s: unable to update PID and serial_no\n", __func__); | 
|  | 2559 | return -ENODEV; | 
|  | 2560 | } | 
|  | 2561 |  | 
|  | 2562 | pr_debug("%s: dload:%p pid:%x serial_num:%s\n", | 
|  | 2563 | __func__, diag_dload, pid, snum); | 
|  | 2564 |  | 
|  | 2565 | /* update pid */ | 
|  | 2566 | local_diag_dload.magic_struct.pid = PID_MAGIC_ID; | 
|  | 2567 | local_diag_dload.pid = pid; | 
|  | 2568 |  | 
|  | 2569 | /* update serial number */ | 
|  | 2570 | if (!snum) { | 
|  | 2571 | local_diag_dload.magic_struct.serial_num = 0; | 
|  | 2572 | memset(&local_diag_dload.serial_number, 0, | 
|  | 2573 | SERIAL_NUMBER_LENGTH); | 
|  | 2574 | } else { | 
|  | 2575 | local_diag_dload.magic_struct.serial_num = SERIAL_NUM_MAGIC_ID; | 
|  | 2576 | strlcpy((char *)&local_diag_dload.serial_number, snum, | 
|  | 2577 | SERIAL_NUMBER_LENGTH); | 
|  | 2578 | } | 
|  | 2579 |  | 
|  | 2580 | /* Copy to shared struct (accesses need to be 32 bit aligned) */ | 
|  | 2581 | src = (int *)&local_diag_dload; | 
|  | 2582 | dst = (int *)diag_dload; | 
|  | 2583 |  | 
|  | 2584 | for (i = 0; i < sizeof(*diag_dload) / 4; i++) | 
|  | 2585 | *dst++ = *src++; | 
|  | 2586 |  | 
|  | 2587 | return 0; | 
|  | 2588 | } | 
|  | 2589 |  | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 2590 | static int __devinit android_probe(struct platform_device *pdev) | 
|  | 2591 | { | 
| Vijayavardhan Vennapusa | 8ceade8 | 2012-11-01 15:11:21 +0530 | [diff] [blame] | 2592 | struct android_usb_platform_data *pdata; | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2593 | struct android_dev *android_dev; | 
| Manu Gautam | 43c61a1 | 2012-08-22 17:09:37 -0700 | [diff] [blame] | 2594 | struct resource *res; | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 2595 | int ret = 0; | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 2596 |  | 
| Vijayavardhan Vennapusa | 8ceade8 | 2012-11-01 15:11:21 +0530 | [diff] [blame] | 2597 | if (pdev->dev.of_node) { | 
|  | 2598 | dev_dbg(&pdev->dev, "device tree enabled\n"); | 
|  | 2599 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | 
|  | 2600 | if (!pdata) { | 
|  | 2601 | pr_err("unable to allocate platform data\n"); | 
|  | 2602 | return -ENOMEM; | 
|  | 2603 | } | 
|  | 2604 |  | 
|  | 2605 | of_property_read_u32(pdev->dev.of_node, | 
|  | 2606 | "qcom,android-usb-swfi-latency", | 
|  | 2607 | &pdata->swfi_latency); | 
|  | 2608 | } else { | 
|  | 2609 | pdata = pdev->dev.platform_data; | 
|  | 2610 | } | 
|  | 2611 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2612 | if (!android_class) { | 
|  | 2613 | android_class = class_create(THIS_MODULE, "android_usb"); | 
|  | 2614 | if (IS_ERR(android_class)) | 
|  | 2615 | return PTR_ERR(android_class); | 
|  | 2616 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2617 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2618 | android_dev = kzalloc(sizeof(*android_dev), GFP_KERNEL); | 
|  | 2619 | if (!android_dev) { | 
|  | 2620 | pr_err("%s(): Failed to alloc memory for android_dev\n", | 
|  | 2621 | __func__); | 
|  | 2622 | ret = -ENOMEM; | 
|  | 2623 | goto err_alloc; | 
|  | 2624 | } | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2625 |  | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2626 | android_dev->name = pdev->name; | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2627 | android_dev->disable_depth = 1; | 
|  | 2628 | android_dev->functions = supported_functions; | 
| Ido Shayevitz | 2a65e7c | 2012-08-02 13:34:18 +0300 | [diff] [blame] | 2629 | android_dev->configs_num = 0; | 
|  | 2630 | INIT_LIST_HEAD(&android_dev->configs); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2631 | INIT_WORK(&android_dev->work, android_work); | 
|  | 2632 | mutex_init(&android_dev->mutex); | 
|  | 2633 |  | 
|  | 2634 | android_dev->pdata = pdata; | 
|  | 2635 |  | 
|  | 2636 | list_add_tail(&android_dev->list_item, &android_dev_list); | 
|  | 2637 | android_dev_count++; | 
|  | 2638 |  | 
|  | 2639 | if (pdata) | 
|  | 2640 | composite_driver.usb_core_id = pdata->usb_core_id; | 
|  | 2641 | else | 
|  | 2642 | composite_driver.usb_core_id = 0; /*To backward compatibility*/ | 
|  | 2643 |  | 
| Manu Gautam | 43c61a1 | 2012-08-22 17:09:37 -0700 | [diff] [blame] | 2644 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 2645 | if (res) { | 
|  | 2646 | diag_dload = devm_ioremap(&pdev->dev, res->start, | 
|  | 2647 | resource_size(res)); | 
|  | 2648 | if (!diag_dload) { | 
|  | 2649 | dev_err(&pdev->dev, "ioremap failed\n"); | 
|  | 2650 | ret = -ENOMEM; | 
|  | 2651 | goto err_dev; | 
|  | 2652 | } | 
|  | 2653 | } else { | 
|  | 2654 | dev_dbg(&pdev->dev, "failed to get mem resource\n"); | 
|  | 2655 | } | 
|  | 2656 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2657 | ret = android_create_device(android_dev, composite_driver.usb_core_id); | 
| Rajkumar Raghupathy | 5d3fc39 | 2012-04-11 16:53:19 +0530 | [diff] [blame] | 2658 | if (ret) { | 
|  | 2659 | pr_err("%s(): android_create_device failed\n", __func__); | 
|  | 2660 | goto err_dev; | 
|  | 2661 | } | 
|  | 2662 |  | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 2663 | ret = usb_composite_probe(&android_usb_driver, android_bind); | 
|  | 2664 | if (ret) { | 
|  | 2665 | pr_err("%s(): Failed to register android " | 
|  | 2666 | "composite driver\n", __func__); | 
| Rajkumar Raghupathy | 5d3fc39 | 2012-04-11 16:53:19 +0530 | [diff] [blame] | 2667 | goto err_probe; | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 2668 | } | 
|  | 2669 |  | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 2670 | /* pm qos request to prevent apps idle power collapse */ | 
| Manu Gautam | 94dc614 | 2012-05-08 14:35:24 +0530 | [diff] [blame] | 2671 | if (pdata && pdata->swfi_latency) | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2672 | pm_qos_add_request(&android_dev->pm_qos_req_dma, | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 2673 | PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2674 | strlcpy(android_dev->pm_qos, "high", sizeof(android_dev->pm_qos)); | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 2675 |  | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 2676 | return ret; | 
| Rajkumar Raghupathy | 5d3fc39 | 2012-04-11 16:53:19 +0530 | [diff] [blame] | 2677 | err_probe: | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2678 | android_destroy_device(android_dev); | 
| Rajkumar Raghupathy | 5d3fc39 | 2012-04-11 16:53:19 +0530 | [diff] [blame] | 2679 | err_dev: | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2680 | list_del(&android_dev->list_item); | 
|  | 2681 | android_dev_count--; | 
|  | 2682 | kfree(android_dev); | 
|  | 2683 | err_alloc: | 
|  | 2684 | if (list_empty(&android_dev_list)) { | 
|  | 2685 | class_destroy(android_class); | 
|  | 2686 | android_class = NULL; | 
|  | 2687 | } | 
| Rajkumar Raghupathy | 5d3fc39 | 2012-04-11 16:53:19 +0530 | [diff] [blame] | 2688 | return ret; | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 2689 | } | 
|  | 2690 |  | 
|  | 2691 | static int android_remove(struct platform_device *pdev) | 
|  | 2692 | { | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2693 | struct android_dev *dev = NULL; | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 2694 | struct android_usb_platform_data *pdata = pdev->dev.platform_data; | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2695 | int usb_core_id = 0; | 
| Rajkumar Raghupathy | 5d3fc39 | 2012-04-11 16:53:19 +0530 | [diff] [blame] | 2696 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2697 | if (pdata) | 
|  | 2698 | usb_core_id = pdata->usb_core_id; | 
|  | 2699 |  | 
|  | 2700 | /* Find the android dev from the list */ | 
|  | 2701 | list_for_each_entry(dev, &android_dev_list, list_item) { | 
|  | 2702 | if (!dev->pdata) | 
|  | 2703 | break; /*To backward compatibility*/ | 
|  | 2704 | if (dev->pdata->usb_core_id == usb_core_id) | 
|  | 2705 | break; | 
|  | 2706 | } | 
|  | 2707 |  | 
|  | 2708 | if (dev) { | 
|  | 2709 | android_destroy_device(dev); | 
|  | 2710 | if (pdata && pdata->swfi_latency) | 
|  | 2711 | pm_qos_remove_request(&dev->pm_qos_req_dma); | 
|  | 2712 | list_del(&dev->list_item); | 
|  | 2713 | android_dev_count--; | 
|  | 2714 | kfree(dev); | 
|  | 2715 | } | 
|  | 2716 |  | 
|  | 2717 | if (list_empty(&android_dev_list)) { | 
|  | 2718 | class_destroy(android_class); | 
|  | 2719 | android_class = NULL; | 
|  | 2720 | usb_composite_unregister(&android_usb_driver); | 
|  | 2721 | } | 
| Ofir Cohen | 94213a7 | 2012-05-03 14:26:32 +0300 | [diff] [blame] | 2722 |  | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 2723 | return 0; | 
|  | 2724 | } | 
|  | 2725 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2726 | static const struct platform_device_id android_id_table[] __devinitconst = { | 
|  | 2727 | { | 
|  | 2728 | .name = "android_usb", | 
|  | 2729 | }, | 
|  | 2730 | { | 
|  | 2731 | .name = "android_usb_hsic", | 
|  | 2732 | }, | 
|  | 2733 | }; | 
|  | 2734 |  | 
| Manu Gautam | 43c61a1 | 2012-08-22 17:09:37 -0700 | [diff] [blame] | 2735 | static struct of_device_id usb_android_dt_match[] = { | 
|  | 2736 | {	.compatible = "qcom,android-usb", | 
|  | 2737 | }, | 
|  | 2738 | {} | 
|  | 2739 | }; | 
|  | 2740 |  | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 2741 | static struct platform_driver android_platform_driver = { | 
| Manu Gautam | 43c61a1 | 2012-08-22 17:09:37 -0700 | [diff] [blame] | 2742 | .driver = { | 
|  | 2743 | .name = "android_usb", | 
|  | 2744 | .of_match_table = usb_android_dt_match, | 
|  | 2745 | }, | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 2746 | .probe = android_probe, | 
|  | 2747 | .remove = android_remove, | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2748 | .id_table = android_id_table, | 
| Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 2749 | }; | 
| Mike Lockwood | 7f0d7bd | 2008-12-02 22:01:33 -0500 | [diff] [blame] | 2750 |  | 
|  | 2751 | static int __init init(void) | 
|  | 2752 | { | 
| Rajkumar Raghupathy | a1df77e | 2012-01-19 17:45:55 +0530 | [diff] [blame] | 2753 | int ret; | 
| Mike Lockwood | 7f0d7bd | 2008-12-02 22:01:33 -0500 | [diff] [blame] | 2754 |  | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2755 | /* Override composite driver functions */ | 
|  | 2756 | composite_driver.setup = android_setup; | 
|  | 2757 | composite_driver.disconnect = android_disconnect; | 
| Ido Shayevitz | fb5edfe | 2012-12-26 14:26:37 +0200 | [diff] [blame] | 2758 | composite_driver.suspend = android_suspend; | 
|  | 2759 | composite_driver.resume = android_resume; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2760 |  | 
| Ido Shayevitz | 23dc77c | 2012-07-18 16:16:06 +0300 | [diff] [blame] | 2761 | INIT_LIST_HEAD(&android_dev_list); | 
|  | 2762 | android_dev_count = 0; | 
|  | 2763 |  | 
| Pavankumar Kondeti | 044914d | 2012-01-31 12:56:13 +0530 | [diff] [blame] | 2764 | ret = platform_driver_register(&android_platform_driver); | 
| Rajkumar Raghupathy | a1df77e | 2012-01-19 17:45:55 +0530 | [diff] [blame] | 2765 | if (ret) { | 
|  | 2766 | pr_err("%s(): Failed to register android" | 
|  | 2767 | "platform driver\n", __func__); | 
| Rajkumar Raghupathy | a1df77e | 2012-01-19 17:45:55 +0530 | [diff] [blame] | 2768 | } | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 2769 |  | 
| Rajkumar Raghupathy | a1df77e | 2012-01-19 17:45:55 +0530 | [diff] [blame] | 2770 | return ret; | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2771 | } | 
|  | 2772 | module_init(init); | 
|  | 2773 |  | 
|  | 2774 | static void __exit cleanup(void) | 
|  | 2775 | { | 
| Lena Salman | d092f2d | 2012-03-12 17:27:24 +0200 | [diff] [blame] | 2776 | platform_driver_unregister(&android_platform_driver); | 
| Benoit Goby | 1e8ce15 | 2011-12-12 13:01:23 -0800 | [diff] [blame] | 2777 | } | 
|  | 2778 | module_exit(cleanup); |