1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * f_obex.c -- USB CDC OBEX function driver |
4 | * |
5 | * Copyright (C) 2008 Nokia Corporation |
6 | * Contact: Felipe Balbi <felipe.balbi@nokia.com> |
7 | * |
8 | * Based on f_acm.c by Al Borchers and David Brownell. |
9 | */ |
10 | |
11 | /* #define VERBOSE_DEBUG */ |
12 | |
13 | #include <linux/slab.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/device.h> |
16 | #include <linux/module.h> |
17 | |
18 | #include "u_serial.h" |
19 | |
20 | |
21 | /* |
22 | * This CDC OBEX function support just packages a TTY-ish byte stream. |
23 | * A user mode server will put it into "raw" mode and handle all the |
24 | * relevant protocol details ... this is just a kernel passthrough. |
25 | * When possible, we prevent gadget enumeration until that server is |
26 | * ready to handle the commands. |
27 | */ |
28 | |
29 | struct f_obex { |
30 | struct gserial port; |
31 | u8 ctrl_id; |
32 | u8 data_id; |
33 | u8 cur_alt; |
34 | u8 port_num; |
35 | }; |
36 | |
37 | static inline struct f_obex *func_to_obex(struct usb_function *f) |
38 | { |
39 | return container_of(f, struct f_obex, port.func); |
40 | } |
41 | |
42 | static inline struct f_obex *port_to_obex(struct gserial *p) |
43 | { |
44 | return container_of(p, struct f_obex, port); |
45 | } |
46 | |
47 | /*-------------------------------------------------------------------------*/ |
48 | |
49 | #define OBEX_CTRL_IDX 0 |
50 | #define OBEX_DATA_IDX 1 |
51 | |
52 | static struct usb_string obex_string_defs[] = { |
53 | [OBEX_CTRL_IDX].s = "CDC Object Exchange (OBEX)" , |
54 | [OBEX_DATA_IDX].s = "CDC OBEX Data" , |
55 | { }, /* end of list */ |
56 | }; |
57 | |
58 | static struct usb_gadget_strings obex_string_table = { |
59 | .language = 0x0409, /* en-US */ |
60 | .strings = obex_string_defs, |
61 | }; |
62 | |
63 | static struct usb_gadget_strings *obex_strings[] = { |
64 | &obex_string_table, |
65 | NULL, |
66 | }; |
67 | |
68 | /*-------------------------------------------------------------------------*/ |
69 | |
70 | static struct usb_interface_descriptor obex_control_intf = { |
71 | .bLength = sizeof(obex_control_intf), |
72 | .bDescriptorType = USB_DT_INTERFACE, |
73 | .bInterfaceNumber = 0, |
74 | |
75 | .bAlternateSetting = 0, |
76 | .bNumEndpoints = 0, |
77 | .bInterfaceClass = USB_CLASS_COMM, |
78 | .bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX, |
79 | }; |
80 | |
81 | static struct usb_interface_descriptor obex_data_nop_intf = { |
82 | .bLength = sizeof(obex_data_nop_intf), |
83 | .bDescriptorType = USB_DT_INTERFACE, |
84 | .bInterfaceNumber = 1, |
85 | |
86 | .bAlternateSetting = 0, |
87 | .bNumEndpoints = 0, |
88 | .bInterfaceClass = USB_CLASS_CDC_DATA, |
89 | }; |
90 | |
91 | static struct usb_interface_descriptor obex_data_intf = { |
92 | .bLength = sizeof(obex_data_intf), |
93 | .bDescriptorType = USB_DT_INTERFACE, |
94 | .bInterfaceNumber = 2, |
95 | |
96 | .bAlternateSetting = 1, |
97 | .bNumEndpoints = 2, |
98 | .bInterfaceClass = USB_CLASS_CDC_DATA, |
99 | }; |
100 | |
101 | static struct usb_cdc_header_desc = { |
102 | .bLength = sizeof(obex_cdc_header_desc), |
103 | .bDescriptorType = USB_DT_CS_INTERFACE, |
104 | .bDescriptorSubType = USB_CDC_HEADER_TYPE, |
105 | .bcdCDC = cpu_to_le16(0x0120), |
106 | }; |
107 | |
108 | static struct usb_cdc_union_desc obex_cdc_union_desc = { |
109 | .bLength = sizeof(obex_cdc_union_desc), |
110 | .bDescriptorType = USB_DT_CS_INTERFACE, |
111 | .bDescriptorSubType = USB_CDC_UNION_TYPE, |
112 | .bMasterInterface0 = 1, |
113 | .bSlaveInterface0 = 2, |
114 | }; |
115 | |
116 | static struct usb_cdc_obex_desc obex_desc = { |
117 | .bLength = sizeof(obex_desc), |
118 | .bDescriptorType = USB_DT_CS_INTERFACE, |
119 | .bDescriptorSubType = USB_CDC_OBEX_TYPE, |
120 | .bcdVersion = cpu_to_le16(0x0100), |
121 | }; |
122 | |
123 | /* High-Speed Support */ |
124 | |
125 | static struct usb_endpoint_descriptor obex_hs_ep_out_desc = { |
126 | .bLength = USB_DT_ENDPOINT_SIZE, |
127 | .bDescriptorType = USB_DT_ENDPOINT, |
128 | |
129 | .bEndpointAddress = USB_DIR_OUT, |
130 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
131 | .wMaxPacketSize = cpu_to_le16(512), |
132 | }; |
133 | |
134 | static struct usb_endpoint_descriptor obex_hs_ep_in_desc = { |
135 | .bLength = USB_DT_ENDPOINT_SIZE, |
136 | .bDescriptorType = USB_DT_ENDPOINT, |
137 | |
138 | .bEndpointAddress = USB_DIR_IN, |
139 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
140 | .wMaxPacketSize = cpu_to_le16(512), |
141 | }; |
142 | |
143 | static struct usb_descriptor_header *hs_function[] = { |
144 | (struct usb_descriptor_header *) &obex_control_intf, |
145 | (struct usb_descriptor_header *) &obex_cdc_header_desc, |
146 | (struct usb_descriptor_header *) &obex_desc, |
147 | (struct usb_descriptor_header *) &obex_cdc_union_desc, |
148 | |
149 | (struct usb_descriptor_header *) &obex_data_nop_intf, |
150 | (struct usb_descriptor_header *) &obex_data_intf, |
151 | (struct usb_descriptor_header *) &obex_hs_ep_in_desc, |
152 | (struct usb_descriptor_header *) &obex_hs_ep_out_desc, |
153 | NULL, |
154 | }; |
155 | |
156 | /* Full-Speed Support */ |
157 | |
158 | static struct usb_endpoint_descriptor obex_fs_ep_in_desc = { |
159 | .bLength = USB_DT_ENDPOINT_SIZE, |
160 | .bDescriptorType = USB_DT_ENDPOINT, |
161 | |
162 | .bEndpointAddress = USB_DIR_IN, |
163 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
164 | }; |
165 | |
166 | static struct usb_endpoint_descriptor obex_fs_ep_out_desc = { |
167 | .bLength = USB_DT_ENDPOINT_SIZE, |
168 | .bDescriptorType = USB_DT_ENDPOINT, |
169 | |
170 | .bEndpointAddress = USB_DIR_OUT, |
171 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
172 | }; |
173 | |
174 | static struct usb_descriptor_header *fs_function[] = { |
175 | (struct usb_descriptor_header *) &obex_control_intf, |
176 | (struct usb_descriptor_header *) &obex_cdc_header_desc, |
177 | (struct usb_descriptor_header *) &obex_desc, |
178 | (struct usb_descriptor_header *) &obex_cdc_union_desc, |
179 | |
180 | (struct usb_descriptor_header *) &obex_data_nop_intf, |
181 | (struct usb_descriptor_header *) &obex_data_intf, |
182 | (struct usb_descriptor_header *) &obex_fs_ep_in_desc, |
183 | (struct usb_descriptor_header *) &obex_fs_ep_out_desc, |
184 | NULL, |
185 | }; |
186 | |
187 | /*-------------------------------------------------------------------------*/ |
188 | |
189 | static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) |
190 | { |
191 | struct f_obex *obex = func_to_obex(f); |
192 | struct usb_composite_dev *cdev = f->config->cdev; |
193 | |
194 | if (intf == obex->ctrl_id) { |
195 | if (alt != 0) |
196 | goto fail; |
197 | /* NOP */ |
198 | dev_dbg(&cdev->gadget->dev, |
199 | "reset obex ttyGS%d control\n" , obex->port_num); |
200 | |
201 | } else if (intf == obex->data_id) { |
202 | if (alt > 1) |
203 | goto fail; |
204 | |
205 | if (obex->port.in->enabled) { |
206 | dev_dbg(&cdev->gadget->dev, |
207 | "reset obex ttyGS%d\n" , obex->port_num); |
208 | gserial_disconnect(&obex->port); |
209 | } |
210 | |
211 | if (!obex->port.in->desc || !obex->port.out->desc) { |
212 | dev_dbg(&cdev->gadget->dev, |
213 | "init obex ttyGS%d\n" , obex->port_num); |
214 | if (config_ep_by_speed(g: cdev->gadget, f, |
215 | ep: obex->port.in) || |
216 | config_ep_by_speed(g: cdev->gadget, f, |
217 | ep: obex->port.out)) { |
218 | obex->port.out->desc = NULL; |
219 | obex->port.in->desc = NULL; |
220 | goto fail; |
221 | } |
222 | } |
223 | |
224 | if (alt == 1) { |
225 | dev_dbg(&cdev->gadget->dev, |
226 | "activate obex ttyGS%d\n" , obex->port_num); |
227 | gserial_connect(&obex->port, port_num: obex->port_num); |
228 | } |
229 | |
230 | } else |
231 | goto fail; |
232 | |
233 | obex->cur_alt = alt; |
234 | |
235 | return 0; |
236 | |
237 | fail: |
238 | return -EINVAL; |
239 | } |
240 | |
241 | static int obex_get_alt(struct usb_function *f, unsigned intf) |
242 | { |
243 | struct f_obex *obex = func_to_obex(f); |
244 | |
245 | return obex->cur_alt; |
246 | } |
247 | |
248 | static void obex_disable(struct usb_function *f) |
249 | { |
250 | struct f_obex *obex = func_to_obex(f); |
251 | struct usb_composite_dev *cdev = f->config->cdev; |
252 | |
253 | dev_dbg(&cdev->gadget->dev, "obex ttyGS%d disable\n" , obex->port_num); |
254 | gserial_disconnect(&obex->port); |
255 | } |
256 | |
257 | /*-------------------------------------------------------------------------*/ |
258 | |
259 | static void obex_connect(struct gserial *g) |
260 | { |
261 | struct f_obex *obex = port_to_obex(p: g); |
262 | struct usb_composite_dev *cdev = g->func.config->cdev; |
263 | int status; |
264 | |
265 | status = usb_function_activate(&g->func); |
266 | if (status) |
267 | dev_dbg(&cdev->gadget->dev, |
268 | "obex ttyGS%d function activate --> %d\n" , |
269 | obex->port_num, status); |
270 | } |
271 | |
272 | static void obex_disconnect(struct gserial *g) |
273 | { |
274 | struct f_obex *obex = port_to_obex(p: g); |
275 | struct usb_composite_dev *cdev = g->func.config->cdev; |
276 | int status; |
277 | |
278 | status = usb_function_deactivate(&g->func); |
279 | if (status) |
280 | dev_dbg(&cdev->gadget->dev, |
281 | "obex ttyGS%d function deactivate --> %d\n" , |
282 | obex->port_num, status); |
283 | } |
284 | |
285 | /*-------------------------------------------------------------------------*/ |
286 | |
287 | /* Some controllers can't support CDC OBEX ... */ |
288 | static inline bool can_support_obex(struct usb_configuration *c) |
289 | { |
290 | /* Since the first interface is a NOP, we can ignore the |
291 | * issue of multi-interface support on most controllers. |
292 | * |
293 | * Altsettings are mandatory, however... |
294 | */ |
295 | if (!gadget_is_altset_supported(g: c->cdev->gadget)) |
296 | return false; |
297 | |
298 | /* everything else is *probably* fine ... */ |
299 | return true; |
300 | } |
301 | |
302 | static int obex_bind(struct usb_configuration *c, struct usb_function *f) |
303 | { |
304 | struct usb_composite_dev *cdev = c->cdev; |
305 | struct f_obex *obex = func_to_obex(f); |
306 | struct usb_string *us; |
307 | int status; |
308 | struct usb_ep *ep; |
309 | |
310 | if (!can_support_obex(c)) |
311 | return -EINVAL; |
312 | |
313 | us = usb_gstrings_attach(cdev, sp: obex_strings, |
314 | ARRAY_SIZE(obex_string_defs)); |
315 | if (IS_ERR(ptr: us)) |
316 | return PTR_ERR(ptr: us); |
317 | obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id; |
318 | obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id; |
319 | obex_data_intf.iInterface = us[OBEX_DATA_IDX].id; |
320 | |
321 | /* allocate instance-specific interface IDs, and patch descriptors */ |
322 | |
323 | status = usb_interface_id(c, f); |
324 | if (status < 0) |
325 | goto fail; |
326 | obex->ctrl_id = status; |
327 | |
328 | obex_control_intf.bInterfaceNumber = status; |
329 | obex_cdc_union_desc.bMasterInterface0 = status; |
330 | |
331 | status = usb_interface_id(c, f); |
332 | if (status < 0) |
333 | goto fail; |
334 | obex->data_id = status; |
335 | |
336 | obex_data_nop_intf.bInterfaceNumber = status; |
337 | obex_data_intf.bInterfaceNumber = status; |
338 | obex_cdc_union_desc.bSlaveInterface0 = status; |
339 | |
340 | /* allocate instance-specific endpoints */ |
341 | |
342 | status = -ENODEV; |
343 | ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc); |
344 | if (!ep) |
345 | goto fail; |
346 | obex->port.in = ep; |
347 | |
348 | ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc); |
349 | if (!ep) |
350 | goto fail; |
351 | obex->port.out = ep; |
352 | |
353 | /* support all relevant hardware speeds... we expect that when |
354 | * hardware is dual speed, all bulk-capable endpoints work at |
355 | * both speeds |
356 | */ |
357 | |
358 | obex_hs_ep_in_desc.bEndpointAddress = |
359 | obex_fs_ep_in_desc.bEndpointAddress; |
360 | obex_hs_ep_out_desc.bEndpointAddress = |
361 | obex_fs_ep_out_desc.bEndpointAddress; |
362 | |
363 | status = usb_assign_descriptors(f, fs: fs_function, hs: hs_function, NULL, |
364 | NULL); |
365 | if (status) |
366 | goto fail; |
367 | |
368 | dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: IN/%s OUT/%s\n" , |
369 | obex->port_num, |
370 | obex->port.in->name, obex->port.out->name); |
371 | |
372 | return 0; |
373 | |
374 | fail: |
375 | ERROR(cdev, "%s/%p: can't bind, err %d\n" , f->name, f, status); |
376 | |
377 | return status; |
378 | } |
379 | |
380 | static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) |
381 | { |
382 | return container_of(to_config_group(item), struct f_serial_opts, |
383 | func_inst.group); |
384 | } |
385 | |
386 | static void obex_attr_release(struct config_item *item) |
387 | { |
388 | struct f_serial_opts *opts = to_f_serial_opts(item); |
389 | |
390 | usb_put_function_instance(fi: &opts->func_inst); |
391 | } |
392 | |
393 | static struct configfs_item_operations obex_item_ops = { |
394 | .release = obex_attr_release, |
395 | }; |
396 | |
397 | static ssize_t f_obex_port_num_show(struct config_item *item, char *page) |
398 | { |
399 | return sprintf(buf: page, fmt: "%u\n" , to_f_serial_opts(item)->port_num); |
400 | } |
401 | |
402 | CONFIGFS_ATTR_RO(f_obex_, port_num); |
403 | |
404 | static struct configfs_attribute *acm_attrs[] = { |
405 | &f_obex_attr_port_num, |
406 | NULL, |
407 | }; |
408 | |
409 | static const struct config_item_type obex_func_type = { |
410 | .ct_item_ops = &obex_item_ops, |
411 | .ct_attrs = acm_attrs, |
412 | .ct_owner = THIS_MODULE, |
413 | }; |
414 | |
415 | static void obex_free_inst(struct usb_function_instance *f) |
416 | { |
417 | struct f_serial_opts *opts; |
418 | |
419 | opts = container_of(f, struct f_serial_opts, func_inst); |
420 | gserial_free_line(port_line: opts->port_num); |
421 | kfree(objp: opts); |
422 | } |
423 | |
424 | static struct usb_function_instance *obex_alloc_inst(void) |
425 | { |
426 | struct f_serial_opts *opts; |
427 | int ret; |
428 | |
429 | opts = kzalloc(size: sizeof(*opts), GFP_KERNEL); |
430 | if (!opts) |
431 | return ERR_PTR(error: -ENOMEM); |
432 | |
433 | opts->func_inst.free_func_inst = obex_free_inst; |
434 | ret = gserial_alloc_line_no_console(port_line: &opts->port_num); |
435 | if (ret) { |
436 | kfree(objp: opts); |
437 | return ERR_PTR(error: ret); |
438 | } |
439 | config_group_init_type_name(group: &opts->func_inst.group, name: "" , |
440 | type: &obex_func_type); |
441 | |
442 | return &opts->func_inst; |
443 | } |
444 | |
445 | static void obex_free(struct usb_function *f) |
446 | { |
447 | struct f_obex *obex; |
448 | |
449 | obex = func_to_obex(f); |
450 | kfree(objp: obex); |
451 | } |
452 | |
453 | static void obex_unbind(struct usb_configuration *c, struct usb_function *f) |
454 | { |
455 | usb_free_all_descriptors(f); |
456 | } |
457 | |
458 | static struct usb_function *obex_alloc(struct usb_function_instance *fi) |
459 | { |
460 | struct f_obex *obex; |
461 | struct f_serial_opts *opts; |
462 | |
463 | /* allocate and initialize one new instance */ |
464 | obex = kzalloc(size: sizeof(*obex), GFP_KERNEL); |
465 | if (!obex) |
466 | return ERR_PTR(error: -ENOMEM); |
467 | |
468 | opts = container_of(fi, struct f_serial_opts, func_inst); |
469 | |
470 | obex->port_num = opts->port_num; |
471 | |
472 | obex->port.connect = obex_connect; |
473 | obex->port.disconnect = obex_disconnect; |
474 | |
475 | obex->port.func.name = "obex" ; |
476 | /* descriptors are per-instance copies */ |
477 | obex->port.func.bind = obex_bind; |
478 | obex->port.func.unbind = obex_unbind; |
479 | obex->port.func.set_alt = obex_set_alt; |
480 | obex->port.func.get_alt = obex_get_alt; |
481 | obex->port.func.disable = obex_disable; |
482 | obex->port.func.free_func = obex_free; |
483 | obex->port.func.bind_deactivated = true; |
484 | |
485 | return &obex->port.func; |
486 | } |
487 | |
488 | DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc); |
489 | MODULE_AUTHOR("Felipe Balbi" ); |
490 | MODULE_LICENSE("GPL" ); |
491 | |