diff options
Diffstat (limited to 'drivers/usb/gadget/multi.c')
-rw-r--r-- | drivers/usb/gadget/multi.c | 262 |
1 files changed, 146 insertions, 116 deletions
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index a930d7fd7e7a..795d76232167 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
26 | #include <linux/utsname.h> | 26 | #include <linux/utsname.h> |
27 | #include <linux/module.h> | ||
27 | 28 | ||
28 | 29 | ||
29 | #if defined USB_ETH_RNDIS | 30 | #if defined USB_ETH_RNDIS |
@@ -35,14 +36,13 @@ | |||
35 | 36 | ||
36 | 37 | ||
37 | #define DRIVER_DESC "Multifunction Composite Gadget" | 38 | #define DRIVER_DESC "Multifunction Composite Gadget" |
38 | #define DRIVER_VERSION "2009/07/21" | ||
39 | 39 | ||
40 | /*-------------------------------------------------------------------------*/ | 40 | MODULE_DESCRIPTION(DRIVER_DESC); |
41 | MODULE_AUTHOR("Michal Nazarewicz"); | ||
42 | MODULE_LICENSE("GPL"); | ||
41 | 43 | ||
42 | #define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */ | ||
43 | #define MULTI_PRODUCT_NUM 0xa4ab /* XXX */ | ||
44 | 44 | ||
45 | /*-------------------------------------------------------------------------*/ | 45 | /***************************** All the files... *****************************/ |
46 | 46 | ||
47 | /* | 47 | /* |
48 | * kbuild is not very cooperative with respect to linking separately | 48 | * kbuild is not very cooperative with respect to linking separately |
@@ -57,6 +57,8 @@ | |||
57 | #include "config.c" | 57 | #include "config.c" |
58 | #include "epautoconf.c" | 58 | #include "epautoconf.c" |
59 | 59 | ||
60 | #include "f_mass_storage.c" | ||
61 | |||
60 | #include "u_serial.c" | 62 | #include "u_serial.c" |
61 | #include "f_acm.c" | 63 | #include "f_acm.c" |
62 | 64 | ||
@@ -68,13 +70,24 @@ | |||
68 | #endif | 70 | #endif |
69 | #include "u_ether.c" | 71 | #include "u_ether.c" |
70 | 72 | ||
71 | #undef DBG /* u_ether.c has broken idea about macros */ | ||
72 | #undef VDBG /* so clean up after it */ | ||
73 | #undef ERROR | ||
74 | #undef INFO | ||
75 | #include "f_mass_storage.c" | ||
76 | 73 | ||
77 | /*-------------------------------------------------------------------------*/ | 74 | |
75 | /***************************** Device Descriptor ****************************/ | ||
76 | |||
77 | #define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */ | ||
78 | #define MULTI_PRODUCT_NUM 0xa4ab /* XXX */ | ||
79 | |||
80 | |||
81 | enum { | ||
82 | __MULTI_NO_CONFIG, | ||
83 | #ifdef CONFIG_USB_G_MULTI_RNDIS | ||
84 | MULTI_RNDIS_CONFIG_NUM, | ||
85 | #endif | ||
86 | #ifdef CONFIG_USB_G_MULTI_CDC | ||
87 | MULTI_CDC_CONFIG_NUM, | ||
88 | #endif | ||
89 | }; | ||
90 | |||
78 | 91 | ||
79 | static struct usb_device_descriptor device_desc = { | 92 | static struct usb_device_descriptor device_desc = { |
80 | .bLength = sizeof device_desc, | 93 | .bLength = sizeof device_desc, |
@@ -82,80 +95,82 @@ static struct usb_device_descriptor device_desc = { | |||
82 | 95 | ||
83 | .bcdUSB = cpu_to_le16(0x0200), | 96 | .bcdUSB = cpu_to_le16(0x0200), |
84 | 97 | ||
85 | /* .bDeviceClass = USB_CLASS_COMM, */ | 98 | .bDeviceClass = USB_CLASS_MISC /* 0xEF */, |
86 | /* .bDeviceSubClass = 0, */ | ||
87 | /* .bDeviceProtocol = 0, */ | ||
88 | .bDeviceClass = 0xEF, | ||
89 | .bDeviceSubClass = 2, | 99 | .bDeviceSubClass = 2, |
90 | .bDeviceProtocol = 1, | 100 | .bDeviceProtocol = 1, |
91 | /* .bMaxPacketSize0 = f(hardware) */ | ||
92 | 101 | ||
93 | /* Vendor and product id can be overridden by module parameters. */ | 102 | /* Vendor and product id can be overridden by module parameters. */ |
94 | .idVendor = cpu_to_le16(MULTI_VENDOR_NUM), | 103 | .idVendor = cpu_to_le16(MULTI_VENDOR_NUM), |
95 | .idProduct = cpu_to_le16(MULTI_PRODUCT_NUM), | 104 | .idProduct = cpu_to_le16(MULTI_PRODUCT_NUM), |
96 | /* .bcdDevice = f(hardware) */ | ||
97 | /* .iManufacturer = DYNAMIC */ | ||
98 | /* .iProduct = DYNAMIC */ | ||
99 | /* NO SERIAL NUMBER */ | ||
100 | .bNumConfigurations = 1, | ||
101 | }; | 105 | }; |
102 | 106 | ||
103 | static struct usb_otg_descriptor otg_descriptor = { | ||
104 | .bLength = sizeof otg_descriptor, | ||
105 | .bDescriptorType = USB_DT_OTG, | ||
106 | |||
107 | /* REVISIT SRP-only hardware is possible, although | ||
108 | * it would not be called "OTG" ... | ||
109 | */ | ||
110 | .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, | ||
111 | }; | ||
112 | 107 | ||
113 | static const struct usb_descriptor_header *otg_desc[] = { | 108 | static const struct usb_descriptor_header *otg_desc[] = { |
114 | (struct usb_descriptor_header *) &otg_descriptor, | 109 | (struct usb_descriptor_header *) &(struct usb_otg_descriptor){ |
110 | .bLength = sizeof(struct usb_otg_descriptor), | ||
111 | .bDescriptorType = USB_DT_OTG, | ||
112 | |||
113 | /* | ||
114 | * REVISIT SRP-only hardware is possible, although | ||
115 | * it would not be called "OTG" ... | ||
116 | */ | ||
117 | .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, | ||
118 | }, | ||
115 | NULL, | 119 | NULL, |
116 | }; | 120 | }; |
117 | 121 | ||
118 | 122 | ||
119 | /* string IDs are assigned dynamically */ | 123 | enum { |
120 | 124 | MULTI_STRING_MANUFACTURER_IDX, | |
121 | #define STRING_MANUFACTURER_IDX 0 | 125 | MULTI_STRING_PRODUCT_IDX, |
122 | #define STRING_PRODUCT_IDX 1 | 126 | #ifdef CONFIG_USB_G_MULTI_RNDIS |
127 | MULTI_STRING_RNDIS_CONFIG_IDX, | ||
128 | #endif | ||
129 | #ifdef CONFIG_USB_G_MULTI_CDC | ||
130 | MULTI_STRING_CDC_CONFIG_IDX, | ||
131 | #endif | ||
132 | }; | ||
123 | 133 | ||
124 | static char manufacturer[50]; | 134 | static char manufacturer[50]; |
125 | 135 | ||
126 | static struct usb_string strings_dev[] = { | 136 | static struct usb_string strings_dev[] = { |
127 | [STRING_MANUFACTURER_IDX].s = manufacturer, | 137 | [MULTI_STRING_MANUFACTURER_IDX].s = manufacturer, |
128 | [STRING_PRODUCT_IDX].s = DRIVER_DESC, | 138 | [MULTI_STRING_PRODUCT_IDX].s = DRIVER_DESC, |
139 | #ifdef CONFIG_USB_G_MULTI_RNDIS | ||
140 | [MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS", | ||
141 | #endif | ||
142 | #ifdef CONFIG_USB_G_MULTI_CDC | ||
143 | [MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM", | ||
144 | #endif | ||
129 | { } /* end of list */ | 145 | { } /* end of list */ |
130 | }; | 146 | }; |
131 | 147 | ||
132 | static struct usb_gadget_strings stringtab_dev = { | ||
133 | .language = 0x0409, /* en-us */ | ||
134 | .strings = strings_dev, | ||
135 | }; | ||
136 | |||
137 | static struct usb_gadget_strings *dev_strings[] = { | 148 | static struct usb_gadget_strings *dev_strings[] = { |
138 | &stringtab_dev, | 149 | &(struct usb_gadget_strings){ |
150 | .language = 0x0409, /* en-us */ | ||
151 | .strings = strings_dev, | ||
152 | }, | ||
139 | NULL, | 153 | NULL, |
140 | }; | 154 | }; |
141 | 155 | ||
142 | static u8 hostaddr[ETH_ALEN]; | ||
143 | 156 | ||
144 | 157 | ||
145 | 158 | ||
146 | /****************************** Configurations ******************************/ | 159 | /****************************** Configurations ******************************/ |
147 | 160 | ||
148 | static struct fsg_module_parameters mod_data = { | 161 | static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; |
149 | .stall = 1 | 162 | FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); |
150 | }; | 163 | |
151 | FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); | 164 | static struct fsg_common fsg_common; |
165 | |||
166 | static u8 hostaddr[ETH_ALEN]; | ||
152 | 167 | ||
153 | static struct fsg_common *fsg_common; | ||
154 | 168 | ||
169 | /********** RNDIS **********/ | ||
155 | 170 | ||
156 | #ifdef USB_ETH_RNDIS | 171 | #ifdef USB_ETH_RNDIS |
157 | 172 | ||
158 | static int __init rndis_do_config(struct usb_configuration *c) | 173 | static __ref int rndis_do_config(struct usb_configuration *c) |
159 | { | 174 | { |
160 | int ret; | 175 | int ret; |
161 | 176 | ||
@@ -172,26 +187,42 @@ static int __init rndis_do_config(struct usb_configuration *c) | |||
172 | if (ret < 0) | 187 | if (ret < 0) |
173 | return ret; | 188 | return ret; |
174 | 189 | ||
175 | ret = fsg_add(c->cdev, c, fsg_common); | 190 | ret = fsg_bind_config(c->cdev, c, &fsg_common); |
176 | if (ret < 0) | 191 | if (ret < 0) |
177 | return ret; | 192 | return ret; |
178 | 193 | ||
179 | return 0; | 194 | return 0; |
180 | } | 195 | } |
181 | 196 | ||
182 | static struct usb_configuration rndis_config_driver = { | 197 | static int rndis_config_register(struct usb_composite_dev *cdev) |
183 | .label = "Multifunction Composite (RNDIS + MS + ACM)", | 198 | { |
184 | .bind = rndis_do_config, | 199 | static struct usb_configuration config = { |
185 | .bConfigurationValue = 2, | 200 | .bind = rndis_do_config, |
186 | /* .iConfiguration = DYNAMIC */ | 201 | .bConfigurationValue = MULTI_RNDIS_CONFIG_NUM, |
187 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, | 202 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, |
188 | }; | 203 | }; |
204 | |||
205 | config.label = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s; | ||
206 | config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id; | ||
207 | |||
208 | return usb_add_config(cdev, &config); | ||
209 | } | ||
210 | |||
211 | #else | ||
212 | |||
213 | static int rndis_config_register(struct usb_composite_dev *cdev) | ||
214 | { | ||
215 | return 0; | ||
216 | } | ||
189 | 217 | ||
190 | #endif | 218 | #endif |
191 | 219 | ||
220 | |||
221 | /********** CDC ECM **********/ | ||
222 | |||
192 | #ifdef CONFIG_USB_G_MULTI_CDC | 223 | #ifdef CONFIG_USB_G_MULTI_CDC |
193 | 224 | ||
194 | static int __init cdc_do_config(struct usb_configuration *c) | 225 | static __ref int cdc_do_config(struct usb_configuration *c) |
195 | { | 226 | { |
196 | int ret; | 227 | int ret; |
197 | 228 | ||
@@ -208,20 +239,33 @@ static int __init cdc_do_config(struct usb_configuration *c) | |||
208 | if (ret < 0) | 239 | if (ret < 0) |
209 | return ret; | 240 | return ret; |
210 | 241 | ||
211 | ret = fsg_add(c->cdev, c, fsg_common); | 242 | ret = fsg_bind_config(c->cdev, c, &fsg_common); |
212 | if (ret < 0) | 243 | if (ret < 0) |
213 | return ret; | 244 | return ret; |
214 | 245 | ||
215 | return 0; | 246 | return 0; |
216 | } | 247 | } |
217 | 248 | ||
218 | static struct usb_configuration cdc_config_driver = { | 249 | static int cdc_config_register(struct usb_composite_dev *cdev) |
219 | .label = "Multifunction Composite (CDC + MS + ACM)", | 250 | { |
220 | .bind = cdc_do_config, | 251 | static struct usb_configuration config = { |
221 | .bConfigurationValue = 1, | 252 | .bind = cdc_do_config, |
222 | /* .iConfiguration = DYNAMIC */ | 253 | .bConfigurationValue = MULTI_CDC_CONFIG_NUM, |
223 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, | 254 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, |
224 | }; | 255 | }; |
256 | |||
257 | config.label = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s; | ||
258 | config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id; | ||
259 | |||
260 | return usb_add_config(cdev, &config); | ||
261 | } | ||
262 | |||
263 | #else | ||
264 | |||
265 | static int cdc_config_register(struct usb_composite_dev *cdev) | ||
266 | { | ||
267 | return 0; | ||
268 | } | ||
225 | 269 | ||
226 | #endif | 270 | #endif |
227 | 271 | ||
@@ -230,7 +274,7 @@ static struct usb_configuration cdc_config_driver = { | |||
230 | /****************************** Gadget Bind ******************************/ | 274 | /****************************** Gadget Bind ******************************/ |
231 | 275 | ||
232 | 276 | ||
233 | static int __init multi_bind(struct usb_composite_dev *cdev) | 277 | static int __ref multi_bind(struct usb_composite_dev *cdev) |
234 | { | 278 | { |
235 | struct usb_gadget *gadget = cdev->gadget; | 279 | struct usb_gadget *gadget = cdev->gadget; |
236 | int status, gcnum; | 280 | int status, gcnum; |
@@ -252,67 +296,56 @@ static int __init multi_bind(struct usb_composite_dev *cdev) | |||
252 | goto fail0; | 296 | goto fail0; |
253 | 297 | ||
254 | /* set up mass storage function */ | 298 | /* set up mass storage function */ |
255 | fsg_common = fsg_common_from_params(0, cdev, &mod_data); | 299 | { |
256 | if (IS_ERR(fsg_common)) { | 300 | void *retp; |
257 | status = PTR_ERR(fsg_common); | 301 | retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); |
258 | goto fail1; | 302 | if (IS_ERR(retp)) { |
303 | status = PTR_ERR(retp); | ||
304 | goto fail1; | ||
305 | } | ||
259 | } | 306 | } |
260 | 307 | ||
261 | 308 | /* set bcdDevice */ | |
262 | gcnum = usb_gadget_controller_number(gadget); | 309 | gcnum = usb_gadget_controller_number(gadget); |
263 | if (gcnum >= 0) | 310 | if (gcnum >= 0) { |
264 | device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); | 311 | device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); |
265 | else { | 312 | } else { |
266 | /* We assume that can_support_ecm() tells the truth; | 313 | WARNING(cdev, "controller '%s' not recognized\n", gadget->name); |
267 | * but if the controller isn't recognized at all then | ||
268 | * that assumption is a bit more likely to be wrong. | ||
269 | */ | ||
270 | WARNING(cdev, "controller '%s' not recognized\n", | ||
271 | gadget->name); | ||
272 | device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099); | 314 | device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099); |
273 | } | 315 | } |
274 | 316 | ||
275 | 317 | /* allocate string descriptor numbers */ | |
276 | /* Allocate string descriptor numbers ... note that string | ||
277 | * contents can be overridden by the composite_dev glue. | ||
278 | */ | ||
279 | |||
280 | /* device descriptor strings: manufacturer, product */ | ||
281 | snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", | 318 | snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", |
282 | init_utsname()->sysname, init_utsname()->release, | 319 | init_utsname()->sysname, init_utsname()->release, |
283 | gadget->name); | 320 | gadget->name); |
284 | status = usb_string_id(cdev); | ||
285 | if (status < 0) | ||
286 | goto fail2; | ||
287 | strings_dev[STRING_MANUFACTURER_IDX].id = status; | ||
288 | device_desc.iManufacturer = status; | ||
289 | 321 | ||
290 | status = usb_string_id(cdev); | 322 | status = usb_string_ids_tab(cdev, strings_dev); |
291 | if (status < 0) | 323 | if (unlikely(status < 0)) |
292 | goto fail2; | 324 | goto fail2; |
293 | strings_dev[STRING_PRODUCT_IDX].id = status; | ||
294 | device_desc.iProduct = status; | ||
295 | 325 | ||
296 | #ifdef USB_ETH_RNDIS | 326 | device_desc.iManufacturer = |
297 | /* register our first configuration */ | 327 | strings_dev[MULTI_STRING_MANUFACTURER_IDX].id; |
298 | status = usb_add_config(cdev, &rndis_config_driver); | 328 | device_desc.iProduct = |
299 | if (status < 0) | 329 | strings_dev[MULTI_STRING_PRODUCT_IDX].id; |
330 | |||
331 | /* register configurations */ | ||
332 | status = rndis_config_register(cdev); | ||
333 | if (unlikely(status < 0)) | ||
300 | goto fail2; | 334 | goto fail2; |
301 | #endif | ||
302 | 335 | ||
303 | #ifdef CONFIG_USB_G_MULTI_CDC | 336 | status = cdc_config_register(cdev); |
304 | /* register our second configuration */ | 337 | if (unlikely(status < 0)) |
305 | status = usb_add_config(cdev, &cdc_config_driver); | ||
306 | if (status < 0) | ||
307 | goto fail2; | 338 | goto fail2; |
308 | #endif | ||
309 | 339 | ||
310 | dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); | 340 | /* we're done */ |
311 | fsg_common_put(fsg_common); | 341 | dev_info(&gadget->dev, DRIVER_DESC "\n"); |
342 | fsg_common_put(&fsg_common); | ||
312 | return 0; | 343 | return 0; |
313 | 344 | ||
345 | |||
346 | /* error recovery */ | ||
314 | fail2: | 347 | fail2: |
315 | fsg_common_put(fsg_common); | 348 | fsg_common_put(&fsg_common); |
316 | fail1: | 349 | fail1: |
317 | gserial_cleanup(); | 350 | gserial_cleanup(); |
318 | fail0: | 351 | fail0: |
@@ -339,18 +372,15 @@ static struct usb_composite_driver multi_driver = { | |||
339 | .unbind = __exit_p(multi_unbind), | 372 | .unbind = __exit_p(multi_unbind), |
340 | }; | 373 | }; |
341 | 374 | ||
342 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
343 | MODULE_AUTHOR("Michal Nazarewicz"); | ||
344 | MODULE_LICENSE("GPL"); | ||
345 | 375 | ||
346 | static int __init g_multi_init(void) | 376 | static int __init multi_init(void) |
347 | { | 377 | { |
348 | return usb_composite_register(&multi_driver); | 378 | return usb_composite_register(&multi_driver); |
349 | } | 379 | } |
350 | module_init(g_multi_init); | 380 | module_init(multi_init); |
351 | 381 | ||
352 | static void __exit g_multi_cleanup(void) | 382 | static void __exit multi_exit(void) |
353 | { | 383 | { |
354 | usb_composite_unregister(&multi_driver); | 384 | usb_composite_unregister(&multi_driver); |
355 | } | 385 | } |
356 | module_exit(g_multi_cleanup); | 386 | module_exit(multi_exit); |