1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * nokia.c -- Nokia Composite Gadget Driver |
4 | * |
5 | * Copyright (C) 2008-2010 Nokia Corporation |
6 | * Contact: Felipe Balbi <felipe.balbi@nokia.com> |
7 | * |
8 | * This gadget driver borrows from serial.c which is: |
9 | * |
10 | * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) |
11 | * Copyright (C) 2008 by David Brownell |
12 | * Copyright (C) 2008 by Nokia Corporation |
13 | */ |
14 | |
15 | #include <linux/kernel.h> |
16 | #include <linux/module.h> |
17 | #include <linux/device.h> |
18 | |
19 | #include "u_serial.h" |
20 | #include "u_ether.h" |
21 | #include "u_phonet.h" |
22 | #include "u_ecm.h" |
23 | #include "f_mass_storage.h" |
24 | |
25 | /* Defines */ |
26 | |
27 | #define NOKIA_VERSION_NUM 0x0211 |
28 | #define NOKIA_LONG_NAME "N900 (PC-Suite Mode)" |
29 | |
30 | USB_GADGET_COMPOSITE_OPTIONS(); |
31 | |
32 | USB_ETHERNET_MODULE_PARAMETERS(); |
33 | |
34 | static struct fsg_module_parameters fsg_mod_data = { |
35 | .stall = 0, |
36 | .luns = 2, |
37 | .removable_count = 2, |
38 | .removable = { 1, 1, }, |
39 | }; |
40 | |
41 | #ifdef CONFIG_USB_GADGET_DEBUG_FILES |
42 | |
43 | static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; |
44 | |
45 | #else |
46 | |
47 | /* |
48 | * Number of buffers we will use. |
49 | * 2 is usually enough for good buffering pipeline |
50 | */ |
51 | #define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS |
52 | |
53 | #endif /* CONFIG_USB_DEBUG */ |
54 | |
55 | FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); |
56 | |
57 | #define NOKIA_VENDOR_ID 0x0421 /* Nokia */ |
58 | #define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */ |
59 | |
60 | /* string IDs are assigned dynamically */ |
61 | |
62 | #define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX |
63 | |
64 | static char manufacturer_nokia[] = "Nokia" ; |
65 | static const char description_nokia[] = "PC-Suite Configuration" ; |
66 | |
67 | static struct usb_string strings_dev[] = { |
68 | [USB_GADGET_MANUFACTURER_IDX].s = manufacturer_nokia, |
69 | [USB_GADGET_PRODUCT_IDX].s = NOKIA_LONG_NAME, |
70 | [USB_GADGET_SERIAL_IDX].s = "" , |
71 | [STRING_DESCRIPTION_IDX].s = description_nokia, |
72 | { } /* end of list */ |
73 | }; |
74 | |
75 | static struct usb_gadget_strings stringtab_dev = { |
76 | .language = 0x0409, /* en-us */ |
77 | .strings = strings_dev, |
78 | }; |
79 | |
80 | static struct usb_gadget_strings *dev_strings[] = { |
81 | &stringtab_dev, |
82 | NULL, |
83 | }; |
84 | |
85 | static struct usb_device_descriptor device_desc = { |
86 | .bLength = USB_DT_DEVICE_SIZE, |
87 | .bDescriptorType = USB_DT_DEVICE, |
88 | /* .bcdUSB = DYNAMIC */ |
89 | .bDeviceClass = USB_CLASS_COMM, |
90 | .idVendor = cpu_to_le16(NOKIA_VENDOR_ID), |
91 | .idProduct = cpu_to_le16(NOKIA_PRODUCT_ID), |
92 | .bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM), |
93 | /* .iManufacturer = DYNAMIC */ |
94 | /* .iProduct = DYNAMIC */ |
95 | .bNumConfigurations = 1, |
96 | }; |
97 | |
98 | /*-------------------------------------------------------------------------*/ |
99 | |
100 | /* Module */ |
101 | MODULE_DESCRIPTION("Nokia composite gadget driver for N900" ); |
102 | MODULE_AUTHOR("Felipe Balbi" ); |
103 | MODULE_LICENSE("GPL" ); |
104 | |
105 | /*-------------------------------------------------------------------------*/ |
106 | static struct usb_function *f_acm_cfg1; |
107 | static struct usb_function *f_acm_cfg2; |
108 | static struct usb_function *f_ecm_cfg1; |
109 | static struct usb_function *f_ecm_cfg2; |
110 | static struct usb_function *f_obex1_cfg1; |
111 | static struct usb_function *f_obex2_cfg1; |
112 | static struct usb_function *f_obex1_cfg2; |
113 | static struct usb_function *f_obex2_cfg2; |
114 | static struct usb_function *f_phonet_cfg1; |
115 | static struct usb_function *f_phonet_cfg2; |
116 | static struct usb_function *f_msg_cfg1; |
117 | static struct usb_function *f_msg_cfg2; |
118 | |
119 | |
120 | static struct usb_configuration nokia_config_500ma_driver = { |
121 | .label = "Bus Powered" , |
122 | .bConfigurationValue = 1, |
123 | /* .iConfiguration = DYNAMIC */ |
124 | .bmAttributes = USB_CONFIG_ATT_ONE, |
125 | .MaxPower = 500, |
126 | }; |
127 | |
128 | static struct usb_configuration nokia_config_100ma_driver = { |
129 | .label = "Self Powered" , |
130 | .bConfigurationValue = 2, |
131 | /* .iConfiguration = DYNAMIC */ |
132 | .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, |
133 | .MaxPower = 100, |
134 | }; |
135 | |
136 | static struct usb_function_instance *fi_acm; |
137 | static struct usb_function_instance *fi_ecm; |
138 | static struct usb_function_instance *fi_obex1; |
139 | static struct usb_function_instance *fi_obex2; |
140 | static struct usb_function_instance *fi_phonet; |
141 | static struct usb_function_instance *fi_msg; |
142 | |
143 | static int nokia_bind_config(struct usb_configuration *c) |
144 | { |
145 | struct usb_function *f_acm; |
146 | struct usb_function *f_phonet = NULL; |
147 | struct usb_function *f_obex1 = NULL; |
148 | struct usb_function *f_ecm; |
149 | struct usb_function *f_obex2 = NULL; |
150 | struct usb_function *f_msg; |
151 | int status = 0; |
152 | int obex1_stat = -1; |
153 | int obex2_stat = -1; |
154 | int phonet_stat = -1; |
155 | |
156 | if (!IS_ERR(ptr: fi_phonet)) { |
157 | f_phonet = usb_get_function(fi: fi_phonet); |
158 | if (IS_ERR(ptr: f_phonet)) |
159 | pr_debug("could not get phonet function\n" ); |
160 | } |
161 | |
162 | if (!IS_ERR(ptr: fi_obex1)) { |
163 | f_obex1 = usb_get_function(fi: fi_obex1); |
164 | if (IS_ERR(ptr: f_obex1)) |
165 | pr_debug("could not get obex function 0\n" ); |
166 | } |
167 | |
168 | if (!IS_ERR(ptr: fi_obex2)) { |
169 | f_obex2 = usb_get_function(fi: fi_obex2); |
170 | if (IS_ERR(ptr: f_obex2)) |
171 | pr_debug("could not get obex function 1\n" ); |
172 | } |
173 | |
174 | f_acm = usb_get_function(fi: fi_acm); |
175 | if (IS_ERR(ptr: f_acm)) { |
176 | status = PTR_ERR(ptr: f_acm); |
177 | goto err_get_acm; |
178 | } |
179 | |
180 | f_ecm = usb_get_function(fi: fi_ecm); |
181 | if (IS_ERR(ptr: f_ecm)) { |
182 | status = PTR_ERR(ptr: f_ecm); |
183 | goto err_get_ecm; |
184 | } |
185 | |
186 | f_msg = usb_get_function(fi: fi_msg); |
187 | if (IS_ERR(ptr: f_msg)) { |
188 | status = PTR_ERR(ptr: f_msg); |
189 | goto err_get_msg; |
190 | } |
191 | |
192 | if (!IS_ERR_OR_NULL(ptr: f_phonet)) { |
193 | phonet_stat = usb_add_function(c, f_phonet); |
194 | if (phonet_stat) |
195 | pr_debug("could not add phonet function\n" ); |
196 | } |
197 | |
198 | if (!IS_ERR_OR_NULL(ptr: f_obex1)) { |
199 | obex1_stat = usb_add_function(c, f_obex1); |
200 | if (obex1_stat) |
201 | pr_debug("could not add obex function 0\n" ); |
202 | } |
203 | |
204 | if (!IS_ERR_OR_NULL(ptr: f_obex2)) { |
205 | obex2_stat = usb_add_function(c, f_obex2); |
206 | if (obex2_stat) |
207 | pr_debug("could not add obex function 1\n" ); |
208 | } |
209 | |
210 | status = usb_add_function(c, f_acm); |
211 | if (status) |
212 | goto err_conf; |
213 | |
214 | status = usb_add_function(c, f_ecm); |
215 | if (status) { |
216 | pr_debug("could not bind ecm config %d\n" , status); |
217 | goto err_ecm; |
218 | } |
219 | |
220 | status = usb_add_function(c, f_msg); |
221 | if (status) |
222 | goto err_msg; |
223 | |
224 | if (c == &nokia_config_500ma_driver) { |
225 | f_acm_cfg1 = f_acm; |
226 | f_ecm_cfg1 = f_ecm; |
227 | f_phonet_cfg1 = f_phonet; |
228 | f_obex1_cfg1 = f_obex1; |
229 | f_obex2_cfg1 = f_obex2; |
230 | f_msg_cfg1 = f_msg; |
231 | } else { |
232 | f_acm_cfg2 = f_acm; |
233 | f_ecm_cfg2 = f_ecm; |
234 | f_phonet_cfg2 = f_phonet; |
235 | f_obex1_cfg2 = f_obex1; |
236 | f_obex2_cfg2 = f_obex2; |
237 | f_msg_cfg2 = f_msg; |
238 | } |
239 | |
240 | return status; |
241 | err_msg: |
242 | usb_remove_function(c, f: f_ecm); |
243 | err_ecm: |
244 | usb_remove_function(c, f: f_acm); |
245 | err_conf: |
246 | if (!obex2_stat) |
247 | usb_remove_function(c, f: f_obex2); |
248 | if (!obex1_stat) |
249 | usb_remove_function(c, f: f_obex1); |
250 | if (!phonet_stat) |
251 | usb_remove_function(c, f: f_phonet); |
252 | usb_put_function(f: f_msg); |
253 | err_get_msg: |
254 | usb_put_function(f: f_ecm); |
255 | err_get_ecm: |
256 | usb_put_function(f: f_acm); |
257 | err_get_acm: |
258 | if (!IS_ERR_OR_NULL(ptr: f_obex2)) |
259 | usb_put_function(f: f_obex2); |
260 | if (!IS_ERR_OR_NULL(ptr: f_obex1)) |
261 | usb_put_function(f: f_obex1); |
262 | if (!IS_ERR_OR_NULL(ptr: f_phonet)) |
263 | usb_put_function(f: f_phonet); |
264 | return status; |
265 | } |
266 | |
267 | static int nokia_bind(struct usb_composite_dev *cdev) |
268 | { |
269 | struct usb_gadget *gadget = cdev->gadget; |
270 | struct fsg_opts *fsg_opts; |
271 | struct fsg_config fsg_config; |
272 | int status; |
273 | |
274 | status = usb_string_ids_tab(c: cdev, str: strings_dev); |
275 | if (status < 0) |
276 | goto err_usb; |
277 | device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; |
278 | device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; |
279 | status = strings_dev[STRING_DESCRIPTION_IDX].id; |
280 | nokia_config_500ma_driver.iConfiguration = status; |
281 | nokia_config_100ma_driver.iConfiguration = status; |
282 | |
283 | if (!gadget_is_altset_supported(g: gadget)) { |
284 | status = -ENODEV; |
285 | goto err_usb; |
286 | } |
287 | |
288 | fi_phonet = usb_get_function_instance(name: "phonet" ); |
289 | if (IS_ERR(ptr: fi_phonet)) |
290 | pr_debug("could not find phonet function\n" ); |
291 | |
292 | fi_obex1 = usb_get_function_instance(name: "obex" ); |
293 | if (IS_ERR(ptr: fi_obex1)) |
294 | pr_debug("could not find obex function 1\n" ); |
295 | |
296 | fi_obex2 = usb_get_function_instance(name: "obex" ); |
297 | if (IS_ERR(ptr: fi_obex2)) |
298 | pr_debug("could not find obex function 2\n" ); |
299 | |
300 | fi_acm = usb_get_function_instance(name: "acm" ); |
301 | if (IS_ERR(ptr: fi_acm)) { |
302 | status = PTR_ERR(ptr: fi_acm); |
303 | goto err_obex2_inst; |
304 | } |
305 | |
306 | fi_ecm = usb_get_function_instance(name: "ecm" ); |
307 | if (IS_ERR(ptr: fi_ecm)) { |
308 | status = PTR_ERR(ptr: fi_ecm); |
309 | goto err_acm_inst; |
310 | } |
311 | |
312 | fi_msg = usb_get_function_instance(name: "mass_storage" ); |
313 | if (IS_ERR(ptr: fi_msg)) { |
314 | status = PTR_ERR(ptr: fi_msg); |
315 | goto err_ecm_inst; |
316 | } |
317 | |
318 | /* set up mass storage function */ |
319 | fsg_config_from_params(cfg: &fsg_config, params: &fsg_mod_data, fsg_num_buffers); |
320 | fsg_config.vendor_name = "Nokia" ; |
321 | fsg_config.product_name = "N900" ; |
322 | |
323 | fsg_opts = fsg_opts_from_func_inst(fi: fi_msg); |
324 | fsg_opts->no_configfs = true; |
325 | |
326 | status = fsg_common_set_num_buffers(common: fsg_opts->common, n: fsg_num_buffers); |
327 | if (status) |
328 | goto err_msg_inst; |
329 | |
330 | status = fsg_common_set_cdev(common: fsg_opts->common, cdev, can_stall: fsg_config.can_stall); |
331 | if (status) |
332 | goto err_msg_buf; |
333 | |
334 | fsg_common_set_sysfs(common: fsg_opts->common, sysfs: true); |
335 | |
336 | status = fsg_common_create_luns(common: fsg_opts->common, cfg: &fsg_config); |
337 | if (status) |
338 | goto err_msg_buf; |
339 | |
340 | fsg_common_set_inquiry_string(common: fsg_opts->common, vn: fsg_config.vendor_name, |
341 | pn: fsg_config.product_name); |
342 | |
343 | /* finally register the configuration */ |
344 | status = usb_add_config(cdev, &nokia_config_500ma_driver, |
345 | nokia_bind_config); |
346 | if (status < 0) |
347 | goto err_msg_luns; |
348 | |
349 | status = usb_add_config(cdev, &nokia_config_100ma_driver, |
350 | nokia_bind_config); |
351 | if (status < 0) |
352 | goto err_put_cfg1; |
353 | |
354 | usb_composite_overwrite_options(cdev, covr: &coverwrite); |
355 | dev_info(&gadget->dev, "%s\n" , NOKIA_LONG_NAME); |
356 | |
357 | return 0; |
358 | |
359 | err_put_cfg1: |
360 | usb_put_function(f: f_acm_cfg1); |
361 | if (!IS_ERR_OR_NULL(ptr: f_obex1_cfg1)) |
362 | usb_put_function(f: f_obex1_cfg1); |
363 | if (!IS_ERR_OR_NULL(ptr: f_obex2_cfg1)) |
364 | usb_put_function(f: f_obex2_cfg1); |
365 | if (!IS_ERR_OR_NULL(ptr: f_phonet_cfg1)) |
366 | usb_put_function(f: f_phonet_cfg1); |
367 | usb_put_function(f: f_ecm_cfg1); |
368 | err_msg_luns: |
369 | fsg_common_remove_luns(common: fsg_opts->common); |
370 | err_msg_buf: |
371 | fsg_common_free_buffers(common: fsg_opts->common); |
372 | err_msg_inst: |
373 | usb_put_function_instance(fi: fi_msg); |
374 | err_ecm_inst: |
375 | usb_put_function_instance(fi: fi_ecm); |
376 | err_acm_inst: |
377 | usb_put_function_instance(fi: fi_acm); |
378 | err_obex2_inst: |
379 | if (!IS_ERR(ptr: fi_obex2)) |
380 | usb_put_function_instance(fi: fi_obex2); |
381 | if (!IS_ERR(ptr: fi_obex1)) |
382 | usb_put_function_instance(fi: fi_obex1); |
383 | if (!IS_ERR(ptr: fi_phonet)) |
384 | usb_put_function_instance(fi: fi_phonet); |
385 | err_usb: |
386 | return status; |
387 | } |
388 | |
389 | static int nokia_unbind(struct usb_composite_dev *cdev) |
390 | { |
391 | if (!IS_ERR_OR_NULL(ptr: f_obex1_cfg2)) |
392 | usb_put_function(f: f_obex1_cfg2); |
393 | if (!IS_ERR_OR_NULL(ptr: f_obex2_cfg2)) |
394 | usb_put_function(f: f_obex2_cfg2); |
395 | if (!IS_ERR_OR_NULL(ptr: f_obex1_cfg1)) |
396 | usb_put_function(f: f_obex1_cfg1); |
397 | if (!IS_ERR_OR_NULL(ptr: f_obex2_cfg1)) |
398 | usb_put_function(f: f_obex2_cfg1); |
399 | if (!IS_ERR_OR_NULL(ptr: f_phonet_cfg1)) |
400 | usb_put_function(f: f_phonet_cfg1); |
401 | if (!IS_ERR_OR_NULL(ptr: f_phonet_cfg2)) |
402 | usb_put_function(f: f_phonet_cfg2); |
403 | usb_put_function(f: f_acm_cfg1); |
404 | usb_put_function(f: f_acm_cfg2); |
405 | usb_put_function(f: f_ecm_cfg1); |
406 | usb_put_function(f: f_ecm_cfg2); |
407 | usb_put_function(f: f_msg_cfg1); |
408 | usb_put_function(f: f_msg_cfg2); |
409 | |
410 | usb_put_function_instance(fi: fi_msg); |
411 | usb_put_function_instance(fi: fi_ecm); |
412 | if (!IS_ERR(ptr: fi_obex2)) |
413 | usb_put_function_instance(fi: fi_obex2); |
414 | if (!IS_ERR(ptr: fi_obex1)) |
415 | usb_put_function_instance(fi: fi_obex1); |
416 | if (!IS_ERR(ptr: fi_phonet)) |
417 | usb_put_function_instance(fi: fi_phonet); |
418 | usb_put_function_instance(fi: fi_acm); |
419 | |
420 | return 0; |
421 | } |
422 | |
423 | static struct usb_composite_driver nokia_driver = { |
424 | .name = "g_nokia" , |
425 | .dev = &device_desc, |
426 | .strings = dev_strings, |
427 | .max_speed = USB_SPEED_HIGH, |
428 | .bind = nokia_bind, |
429 | .unbind = nokia_unbind, |
430 | }; |
431 | |
432 | module_usb_composite_driver(nokia_driver); |
433 | |