blob: c019cde9961c59d9edb6a200fc93f8acde11605a [file] [log] [blame]
Harald Weltea7c1eb02019-09-12 15:16:43 +02001
2#include <errno.h>
3#include <stdint.h>
4#include <endian.h>
5#include <signal.h>
6#include <sys/types.h>
7#include <linux/usb/functionfs.h>
8#include <linux/usb/ch11.h>
9
10#if __BYTE_ORDER == __LITTLE_ENDIAN
11#define cpu_to_le16(x) (x)
12#define cpu_to_le32(x) (x)
13#else
14#define cpu_to_le16(x) ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))
15#define cpu_to_le32(x) \
16 ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) | \
17 (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24))
18#endif
19
20#define le32_to_cpu(x) le32toh(x)
21#define le16_to_cpu(x) le16toh(x)
22
23enum {
24 DUSB,
25};
26
27/***********************************************************************
28 * Actual USB Descriptors
29 ***********************************************************************/
30
31static const struct {
32 struct usb_functionfs_descs_head_v2 header;
33 __le32 fs_count;
34 struct {
35 struct usb_interface_descriptor intf;
36 struct usb_endpoint_descriptor_no_audio ep_int;
37 } __attribute__ ((packed)) fs_descs;
38} __attribute__ ((packed)) descriptors = {
39 .header = {
40 .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
41 .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_ALL_CTRL_RECIP),
42 .length = cpu_to_le32(sizeof(descriptors)),
43 },
44 .fs_count = cpu_to_le32(2),
45 .fs_descs = {
46 .intf = {
47 .bLength = sizeof(descriptors.fs_descs.intf),
48 .bDescriptorType = USB_DT_INTERFACE,
49 .bNumEndpoints = 1,
50 .bInterfaceClass = USB_CLASS_HUB,
51 .iInterface = 1,
52 },
53 .ep_int = {
54 .bLength = sizeof(descriptors.fs_descs.ep_int),
55 .bDescriptorType = USB_DT_ENDPOINT,
56 .bEndpointAddress = 1 | USB_DIR_IN,
57 .bmAttributes = USB_ENDPOINT_XFER_INT,
58 .wMaxPacketSize = 64,
59 .bInterval = 12,
60 },
61 },
62};
63
64#define STR_INTERFACE_ "Osmocom USB Hub"
65
66static const struct {
67 struct usb_functionfs_strings_head header;
68 struct {
69 __le16 code;
70 const char str1[sizeof(STR_INTERFACE_)];
71 } __attribute__((packed)) lang0;
72} __attribute__((packed)) strings = {
73 .header = {
74 .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
75 .length = cpu_to_le32(sizeof(strings)),
76 .str_count = cpu_to_le32(1),
77 .lang_count = cpu_to_le32(1),
78 },
79 .lang0 = {
80 cpu_to_le16(0x0409), /* en-us */
81 STR_INTERFACE_,
82 },
83};
84
85
86struct usb2_hub_desc_header {
87 __u8 bDescLength;
88 __u8 bDescriptorType;
89 __u8 bNbrPorts;
90 __le16 wHubCharacteristics;
91 __u8 bPwrOn2PwrGood;
92 __u8 bHubContrCurrent;
93 __u8 data[0];
94};
95
96#define NUM_PORTS 31
97#define HDESC_ARR_BYTES ((NUM_PORTS + 1 + 7) / 8)
98static const struct {
99 struct usb2_hub_desc_header hdr;
100 uint8_t DeviceRemovable[HDESC_ARR_BYTES];
101 uint8_t PortPwrCtrlMask[HDESC_ARR_BYTES];
102} __attribute__ ((packed)) hub_desc = {
103 .hdr = {
104 .bDescLength = sizeof(hub_desc),
105 .bDescriptorType = USB_DT_HUB,
106 .bNbrPorts = NUM_PORTS,
107 .wHubCharacteristics = cpu_to_le16(0x0001),
108 .bPwrOn2PwrGood = 100,
109 .bHubContrCurrent = 0,
110 },
111};
112
113
114/***********************************************************************
115 * USB FunctionFS interface
116 ***********************************************************************/
117
118#include <stdlib.h>
119#include <stdio.h>
120#include <unistd.h>
121#include <string.h>
122#include <assert.h>
123#include <fcntl.h>
124#include <sys/stat.h>
125#include <osmocom/core/select.h>
126#include <osmocom/core/utils.h>
127#include <osmocom/core/msgb.h>
128#include <osmocom/core/utils.h>
129#include <osmocom/core/application.h>
130#include <osmocom/core/logging.h>
131
132#ifndef FUNCTIONFS_SUPPORTS_POLL
133#include <libaio.h>
134struct aio_help {
135 struct msgb *msg;
136 struct iocb *iocb;
137};
138#endif
139
140/* usb function handle */
141struct ufunc_handle {
142 struct osmo_fd ep0;
143 struct osmo_fd ep_int;
144 struct llist_head ep_int_queue;
145#ifndef FUNCTIONFS_SUPPORTS_POLL
146 struct osmo_fd aio_evfd;
147 io_context_t aio_ctx;
148 struct aio_help aio_int;
149#endif
150};
151
152static int ep_int_cb(struct osmo_fd *ofd, unsigned int what)
153{
154 LOGP(DUSB, LOGL_DEBUG, "%s\n", __func__);
155 return 0;
156}
157
158const struct value_string ffs_evt_type_names[] = {
159 { FUNCTIONFS_BIND, "BIND" },
160 { FUNCTIONFS_UNBIND, "UNBIND" },
161 { FUNCTIONFS_ENABLE, "ENABLE" },
162 { FUNCTIONFS_DISABLE, "DISABLE" },
163 { FUNCTIONFS_SETUP, "SETUP" },
164 { FUNCTIONFS_SUSPEND, "SUSPEND" },
165 { FUNCTIONFS_RESUME, "RESUME" },
166 { 0, NULL }
167};
168
169/* local, stand-alone definition of a USB control request */
170struct _usb_ctrl_req {
171 uint8_t bRequestType;
172 uint8_t bRequest;
173 uint16_t wValue;
174 uint16_t wIndex;
175 uint16_t wLength;
176} __attribute__ ((packed));;
177
178/* class requests from the USB 2.0 hub spec, table 11-15 */
179#define HUB_CLASS_REQ(dir, type, request) ((((dir) | (type)) << 8) | (request))
180/* GetBusState and SetHubDescriptor are optional, omitted */
181#define ClearHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, USB_REQ_CLEAR_FEATURE)
182#define ClearPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, USB_REQ_CLEAR_FEATURE)
183#define GetHubDescriptor HUB_CLASS_REQ(USB_DIR_IN, USB_RT_HUB, USB_REQ_GET_DESCRIPTOR)
184#define GetHubStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_HUB, USB_REQ_GET_STATUS)
185#define GetPortStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, USB_REQ_GET_STATUS)
186#define SetHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, USB_REQ_SET_FEATURE)
187#define SetPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, USB_REQ_SET_FEATURE)
188
189static const struct value_string hub_class_spec_req_vals[] = {
190 OSMO_VALUE_STRING(USB_REQ_CLEAR_FEATURE),
191 OSMO_VALUE_STRING(USB_REQ_GET_DESCRIPTOR),
192 OSMO_VALUE_STRING(USB_REQ_GET_STATUS),
193 OSMO_VALUE_STRING(USB_REQ_SET_FEATURE),
194 { 0, NULL }
195};
196
197#define CCID_CTRL_RET_INVALID -1
198#define CCID_CTRL_RET_UNKNOWN -2
199#define CCID_CTRL_RET_OK 0
200
201/*! Handle [class specific] CTRL request. We assume the caller has already verified that the
202 * request was made to the correct interface as well as it is a class-specific request.
203 * \param[in] ci CCID Instance for which CTRL request was received
204 * \param[in] ctrl_req buffer holding the 8 bytes CTRL transfer header
205 * \param[out] data_in data to be returned to the host in the IN transaction (if any)
206 * \returns CCID_CTRL_RET_OK, CCID_CTRL_RET_INVALID or CCID_CTRL_RET_UNKNOWN
207 */
208int hub_handle_ctrl(void *ci, const uint8_t *ctrl_req, const uint8_t **data_in)
209{
210 const struct _usb_ctrl_req *req = (const struct _usb_ctrl_req *) ctrl_req;
211 static uint16_t status[2];
212 int rc;
213
214 LOGP(DUSB, LOGL_NOTICE, "CTRL bmReqT=0x%02X bRequest=%s, wValue=0x%04X, wIndex=0x%04X, wLength=%d\n",
215 req->bRequestType, get_value_string(hub_class_spec_req_vals, req->bRequest),
216 req->wValue, req->wIndex, req->wLength);
217
218 switch (req->bRequestType & 0x7f) {
219 case USB_RT_HUB:
220 switch (req->bRequest) {
221 case USB_REQ_GET_DESCRIPTOR:
222 if (req->wIndex != 0) {
223 LOGP(DUSB, LOGL_ERROR, "GET_DESC wIndex invalid\n");
224 return CCID_CTRL_RET_INVALID;
225 }
226 if (0) // ctrl_req->wValue != FIXME
227 return CCID_CTRL_RET_INVALID;
228 *data_in = (const uint8_t *) &hub_desc;
229 return sizeof(hub_desc);
230 case USB_REQ_CLEAR_FEATURE:
231 switch (req->wValue) {
232 case C_HUB_LOCAL_POWER:
233 case C_HUB_OVER_CURRENT:
234 return CCID_CTRL_RET_OK;
235 }
236 break;
237 case USB_REQ_GET_STATUS:
238 status[0] = cpu_to_le16(HUB_STATUS_LOCAL_POWER);
239 status[1] = cpu_to_le16(0);
240 *data_in = (const uint8_t *) status;
241 return sizeof(status);
242 case USB_REQ_SET_FEATURE:
243 if (req->wValue > 1)
244 return CCID_CTRL_RET_INVALID;
245 return CCID_CTRL_RET_OK;
246 }
247 break;
248 case USB_RT_PORT:
249 switch (req->bRequest) {
250 case USB_REQ_CLEAR_FEATURE:
251 switch (req->wValue) {
252 case USB_PORT_FEAT_CONNECTION:
253 case USB_PORT_FEAT_ENABLE:
254 case USB_PORT_FEAT_SUSPEND:
255 case USB_PORT_FEAT_OVER_CURRENT:
256 case USB_PORT_FEAT_RESET:
257 case USB_PORT_FEAT_L1:
258 case USB_PORT_FEAT_POWER:
259 case USB_PORT_FEAT_LOWSPEED:
260 case USB_PORT_FEAT_C_CONNECTION:
261 case USB_PORT_FEAT_C_ENABLE:
262 case USB_PORT_FEAT_C_SUSPEND:
263 case USB_PORT_FEAT_C_OVER_CURRENT:
264 case USB_PORT_FEAT_C_RESET:
265 case USB_PORT_FEAT_TEST:
266 case USB_PORT_FEAT_C_PORT_L1:
267 return CCID_CTRL_RET_OK;
268 }
269 break;
270 case USB_REQ_GET_STATUS:
271 status[0] = cpu_to_le16(0);
272 status[1] = cpu_to_le16(0);
273 *data_in = (const uint8_t *) status;
274 return sizeof(status);
275 case USB_REQ_SET_FEATURE:
276 //selector = wIndex >> 8
277 //port = wIndex & 0xff
278 return CCID_CTRL_RET_OK;
279 }
280 }
281 return CCID_CTRL_RET_UNKNOWN;
282}
283
284static void handle_setup(int fd, const struct usb_ctrlrequest *setup)
285{
286 const uint8_t *data_in = NULL;
287 int rc;
288
289 LOGP(DUSB, LOGL_NOTICE, "EP0 SETUP bRequestType=0x%02x, bRequest=0x%02x wValue=0x%04x, "
290 "wIndex=0x%04x, wLength=%u\n", setup->bRequestType, setup->bRequest,
291 le16_to_cpu(setup->wValue), le16_to_cpu(setup->wIndex), le16_to_cpu(setup->wLength));
292
293 rc = hub_handle_ctrl(NULL, (const uint8_t *) setup, &data_in);
294 switch (rc) {
295 case CCID_CTRL_RET_INVALID:
296 if (setup->bRequestType & USB_DIR_IN)
297 read(fd, NULL, 0); /* cause stall */
298 else
299 write(fd, NULL, 0); /* cause stall */
300 break;
301 case CCID_CTRL_RET_UNKNOWN:
302 /* FIXME: is this correct behavior? */
303 if (setup->bRequestType & USB_DIR_IN)
304 write(fd, NULL, 0); /* send ZLP */
305 else
306 read(fd, NULL, 0);
307 break;
308 default:
309 if (setup->bRequestType & USB_DIR_IN) {
310 uint16_t len = rc;
311 if (setup->wLength < len)
312 len = setup->wLength;
313 LOGP(DUSB, LOGL_NOTICE, "Writing %u: %s\n", len, osmo_hexdump_nospc(data_in, len));
314 write(fd, data_in, le16_to_cpu(len));
315 } else
316 read(fd, NULL, 0); /* FIXME: control OUT? */
317 break;
318 }
319}
320
321static int ep_0_cb(struct osmo_fd *ofd, unsigned int what)
322{
323 struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data;
324 int rc;
325
326 if (what & BSC_FD_READ) {
327 struct usb_functionfs_event evt;
328 rc = read(ofd->fd, (uint8_t *)&evt, sizeof(evt));
329 if (rc < sizeof(evt))
330 return -23;
331 LOGP(DUSB, LOGL_NOTICE, "EP0 %s\n", get_value_string(ffs_evt_type_names, evt.type));
332 switch (evt.type) {
333 case FUNCTIONFS_ENABLE:
334 //aio_refill_out(uh);
335 break;
336 case FUNCTIONFS_SETUP:
337 handle_setup(ofd->fd, &evt.u.setup);
338 break;
339 }
340
341 }
342 return 0;
343}
344
345#ifndef FUNCTIONFS_SUPPORTS_POLL
346
347/* dequeue the next msgb from ep_int_queue and set up AIO for it */
348static void dequeue_aio_write_int(struct ufunc_handle *uh)
349{
350 struct aio_help *ah = &uh->aio_int;
351 struct msgb *d;
352 int rc;
353
354 if (ah->msg)
355 return;
356
357 d = msgb_dequeue(&uh->ep_int_queue);
358 if (!d)
359 return;
360
361 OSMO_ASSERT(ah->iocb);
362 ah->msg = d;
363 io_prep_pwrite(ah->iocb, uh->ep_int.fd, msgb_data(d), msgb_length(d), 0);
364 io_set_eventfd(ah->iocb, uh->aio_evfd.fd);
365 rc = io_submit(uh->aio_ctx, 1, &ah->iocb);
366 OSMO_ASSERT(rc >= 0);
367}
368
369static int evfd_cb(struct osmo_fd *ofd, unsigned int what)
370{
371 struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data;
372 struct io_event evt[1];
373 struct msgb *msg;
374 uint64_t ev_cnt;
375 int i, rc;
376
377 rc = read(ofd->fd, &ev_cnt, sizeof(ev_cnt));
378 assert(rc == sizeof(ev_cnt));
379
380 rc = io_getevents(uh->aio_ctx, 1, 1, evt, NULL);
381 if (rc <= 0) {
382 LOGP(DUSB, LOGL_ERROR, "error in io_getevents(): %d\n", rc);
383 return rc;
384 }
385
386 for (i = 0; i < rc; i++) {
387 int fd = evt[i].obj->aio_fildes;
388 if (fd == uh->ep_int.fd) {
389 /* interrupt endpoint AIO has completed. This means the IRQ transfer
390 * which we generated has reached the host */
391 LOGP(DUSB, LOGL_DEBUG, "IRQ AIO completed, free()ing msgb\n");
392 msgb_free(uh->aio_int.msg);
393 uh->aio_int.msg = NULL;
394 dequeue_aio_write_int(uh);
395 }
396 }
397 return 0;
398}
399#endif
400
401
402static int ep0_init(struct ufunc_handle *uh)
403{
404 int rc;
405
406 /* open control endpoint and write descriptors to it */
407 rc = open("ep0", O_RDWR);
408 assert(rc >= 0);
409 osmo_fd_setup(&uh->ep0, rc, BSC_FD_READ, &ep_0_cb, uh, 0);
410 osmo_fd_register(&uh->ep0);
411 rc = write(uh->ep0.fd, &descriptors, sizeof(descriptors));
412 if (rc != sizeof(descriptors)) {
413 LOGP(DUSB, LOGL_ERROR, "Cannot write descriptors: %s\n", strerror(errno));
414 return -1;
415 }
416 rc = write(uh->ep0.fd, &strings, sizeof(strings));
417 if (rc != sizeof(strings)) {
418 LOGP(DUSB, LOGL_ERROR, "Cannot write strings: %s\n", strerror(errno));
419 return -1;
420 }
421
422 /* open other endpoint file descriptors */
423 INIT_LLIST_HEAD(&uh->ep_int_queue);
424 rc = open("ep1", O_RDWR);
425 assert(rc >= 0);
426 osmo_fd_setup(&uh->ep_int, rc, 0, &ep_int_cb, uh, 1);
427#ifdef FUNCTIONFS_SUPPORTS_POLL
428 osmo_fd_register(&uh->ep_int);
429#endif
430
431#ifndef FUNCTIONFS_SUPPORTS_POLL
432#include <sys/eventfd.h>
433 /* for some absolutely weird reason, gadgetfs+functionfs don't support
434 * the standard methods of non-blocking I/o (select/poll). We need to
435 * work around using Linux AIO, which is not to be confused with POSIX AIO! */
436
437 memset(&uh->aio_ctx, 0, sizeof(uh->aio_ctx));
438 rc = io_setup(1, &uh->aio_ctx);
439 OSMO_ASSERT(rc >= 0);
440
441 /* create an eventfd, which will be marked readable once some AIO completes */
442 rc = eventfd(0, 0);
443 OSMO_ASSERT(rc >= 0);
444 osmo_fd_setup(&uh->aio_evfd, rc, BSC_FD_READ, &evfd_cb, uh, 0);
445 osmo_fd_register(&uh->aio_evfd);
446
447 uh->aio_int.iocb = malloc(sizeof(struct iocb));
448#endif
449
450 return 0;
451}
452
453static const struct log_info_cat log_info_cat[] = {
454 [DUSB] = {
455 .name = "USB",
456 .description = "USB Transport",
457 .enabled = 1,
458 .loglevel = LOGL_NOTICE,
459 },
460};
461
462static const struct log_info log_info = {
463 .cat = log_info_cat,
464 .num_cat = ARRAY_SIZE(log_info_cat),
465};
466
467static void *tall_main_ctx;
468
469static void signal_handler(int signal)
470{
471 switch (signal) {
472 case SIGUSR1:
473 talloc_report_full(tall_main_ctx, stderr);
474 break;
475 }
476}
477
478
479int main(int argc, char **argv)
480{
481 struct ufunc_handle ufh = (struct ufunc_handle) { 0, };
482 int rc;
483
484 tall_main_ctx = talloc_named_const(NULL, 0, "hub_main_functionfs");
485 msgb_talloc_ctx_init(tall_main_ctx, 0);
486 osmo_init_logging2(tall_main_ctx, &log_info);
487
488 signal(SIGUSR1, &signal_handler);
489
490 if (argc < 2) {
491 fprintf(stderr, "You have to specify the mount-path of the functionfs\n");
492 exit(2);
493 }
494
495 chdir(argv[1]);
496 rc = ep0_init(&ufh);
497 if (rc < 0) {
498 fprintf(stderr, "Error %d\n", rc);
499 exit(1);
500 }
501
502 while (1) {
503 osmo_select_main(0);
504 }
505}