blob: 7117fbb3087486b07544e3260ead6401d4ed45ca [file] [log] [blame]
Harald Welte63653742019-01-03 16:54:16 +01001
2#include <stdint.h>
3#include <endian.h>
4#include <sys/types.h>
5#include <linux/usb/functionfs.h>
6
7#include "ccid_proto.h"
8
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
23/***********************************************************************
24 * Actual USB CCID Descriptors
25 ***********************************************************************/
26
27static const struct {
28 struct usb_functionfs_descs_head_v2 header;
29 __le32 fs_count;
30 struct {
31 struct usb_interface_descriptor intf;
32 struct usb_ccid_class_descriptor ccid;
33 struct usb_endpoint_descriptor_no_audio ep_irq;
34 struct usb_endpoint_descriptor_no_audio ep_out;
35 struct usb_endpoint_descriptor_no_audio ep_in;
36 } __attribute__ ((packed)) fs_descs;
37} __attribute__ ((packed)) descriptors = {
38 .header = {
39 .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
40 .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC),
41 .length = cpu_to_le32(sizeof(descriptors)),
42 },
43 .fs_count = cpu_to_le32(5),
44 .fs_descs = {
45 .intf = {
46 .bLength = sizeof(descriptors.fs_descs.intf),
47 .bDescriptorType = USB_DT_INTERFACE,
48 .bNumEndpoints = 3,
49 .bInterfaceClass = 11,
50 .iInterface = 1,
51 },
52 .ccid = {
53 .bLength = sizeof(descriptors.fs_descs.ccid),
54 .bDescriptorType = 33,
55 .bcdCCID = cpu_to_le16(0x0110),
56 .bMaxSlotIndex = 7,
57 .bVoltageSupport = 0x07, /* 5/3/1.8V */
58 .dwProtocols = cpu_to_le32(1), /* T=0 only */
59 .dwDefaultClock = cpu_to_le32(5000),
60 .dwMaximumClock = cpu_to_le32(20000),
61 .bNumClockSupported = 0,
62 .dwDataRate = cpu_to_le32(9600),
63 .dwMaxDataRate = cpu_to_le32(921600),
64 .bNumDataRatesSupported = 0,
65 .dwMaxIFSD = cpu_to_le32(0),
66 .dwSynchProtocols = cpu_to_le32(0),
67 .dwMechanical = cpu_to_le32(0),
68 .dwFeatures = cpu_to_le32(0x10),
69 .dwMaxCCIDMessageLength = 272,
70 .bClassGetResponse = 0xff,
71 .bClassEnvelope = 0xff,
72 .wLcdLayout = cpu_to_le16(0),
73 .bPINSupport = 0,
74 .bMaxCCIDBusySlots = 8,
75 },
76 .ep_irq = {
77 .bLength = sizeof(descriptors.fs_descs.ep_irq),
78 .bDescriptorType = USB_DT_ENDPOINT,
79 .bEndpointAddress = 1 | USB_DIR_IN,
80 .bmAttributes = USB_ENDPOINT_XFER_INT,
81 .wMaxPacketSize = 64,
82 },
83 .ep_out = {
84 .bLength = sizeof(descriptors.fs_descs.ep_out),
85 .bDescriptorType = USB_DT_ENDPOINT,
86 .bEndpointAddress = 2 | USB_DIR_OUT,
87 .bmAttributes = USB_ENDPOINT_XFER_BULK,
88 /* .wMaxPacketSize = autoconfiguration (kernel) */
89 },
90 .ep_in = {
91 .bLength = sizeof(descriptors.fs_descs.ep_in),
92 .bDescriptorType = USB_DT_ENDPOINT,
93 .bEndpointAddress = 3 | USB_DIR_IN,
94 .bmAttributes = USB_ENDPOINT_XFER_BULK,
95 /* .wMaxPacketSize = autoconfiguration (kernel) */
96 },
97 },
98};
99
100#define STR_INTERFACE_ "Osmocom CCID Interface"
101
102static const struct {
103 struct usb_functionfs_strings_head header;
104 struct {
105 __le16 code;
106 const char str1[sizeof(STR_INTERFACE_)];
107 } __attribute__((packed)) lang0;
108} __attribute__((packed)) strings = {
109 .header = {
110 .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
111 .length = cpu_to_le32(sizeof(strings)),
112 .str_count = cpu_to_le32(1),
113 .lang_count = cpu_to_le32(1),
114 },
115 .lang0 = {
116 cpu_to_le16(0x0409), /* en-us */
117 STR_INTERFACE_,
118 },
119};
120
121
122
123/***********************************************************************
124 * USB FunctionFS interface
125 ***********************************************************************/
126
127#include <stdlib.h>
128#include <stdio.h>
129#include <unistd.h>
130#include <assert.h>
131#include <fcntl.h>
132#include <sys/stat.h>
133#include <osmocom/core/select.h>
134#include <osmocom/core/utils.h>
135
136#ifndef FUNCTIONFS_SUPPORTS_POLL
137#include <libaio.h>
138struct aio_help {
139 uint8_t buf[64];
140 struct iocb *iocb;
141};
142#endif
143
144/* usb function handle */
145struct ufunc_handle {
146 struct osmo_fd ep0;
147 struct osmo_fd ep_in;
148 struct osmo_fd ep_out;
149 struct osmo_fd ep_int;
150#ifndef FUNCTIONFS_SUPPORTS_POLL
151 struct osmo_fd aio_evfd;
152 io_context_t aio_ctx;
153 struct aio_help aio_in;
154 struct aio_help aio_out;
155 struct aio_help aio_int;
156#endif
157};
158
159static int ep_int_cb(struct osmo_fd *ofd, unsigned int what)
160{
161 printf("INT\n");
162 return 0;
163}
164
165static int ep_out_cb(struct osmo_fd *ofd, unsigned int what)
166{
167 uint8_t buf[64];
168 int rc;
169
170 printf("OUT\n");
171 if (what & BSC_FD_READ) {
172 rc = read(ofd->fd, buf, sizeof(buf));
173 ccid_handle_out(uh->ccid_handle, buf, rc);
174 }
175 return 0;
176}
177
178static int ep_in_cb(struct osmo_fd *ofd, unsigned int what)
179{
180 printf("IN\n");
181 if (what & BSC_FD_WRITE) {
182 /* write what we have to write */
183 }
184 return 0;
185}
186
187const struct value_string ffs_evt_type_names[] = {
188 { FUNCTIONFS_BIND, "BIND" },
189 { FUNCTIONFS_UNBIND, "UNBIND" },
190 { FUNCTIONFS_ENABLE, "ENABLE" },
191 { FUNCTIONFS_DISABLE, "DISABLE" },
192 { FUNCTIONFS_SETUP, "SETUP" },
193 { FUNCTIONFS_SUSPEND, "SUSPEND" },
194 { FUNCTIONFS_RESUME, "RESUME" },
195 { 0, NULL }
196};
197
198static void handle_setup(const struct usb_ctrlrequest *setup)
199{
200 printf("bRequestType = %d\n", setup->bRequestType);
201 printf("bRequest = %d\n", setup->bRequest);
202 printf("wValue = %d\n", le16_to_cpu(setup->wValue));
203 printf("wIndex = %d\n", le16_to_cpu(setup->wIndex));
204 printf("wLength = %d\n", le16_to_cpu(setup->wLength));
205}
206
207static void aio_refill_out(struct ufunc_handle *uh);
208
209static int ep_0_cb(struct osmo_fd *ofd, unsigned int what)
210{
211 struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data;
212 int rc;
213
214 printf("EP0\n");
215
216 if (what & BSC_FD_READ) {
217 struct usb_functionfs_event evt;
218 rc = read(ofd->fd, (uint8_t *)&evt, sizeof(evt));
219 if (rc < sizeof(evt))
220 return -23;
221 printf("\t%s\n", get_value_string(ffs_evt_type_names, evt.type));
222 switch (evt.type) {
223 case FUNCTIONFS_ENABLE:
224 aio_refill_out(uh);
225 break;
226 case FUNCTIONFS_SETUP:
227 handle_setup(&evt.u.setup);
228 break;
229 }
230
231 }
232 return 0;
233}
234
235#ifndef FUNCTIONFS_SUPPORTS_POLL
236
237static void aio_refill_out(struct ufunc_handle *uh)
238{
239 int rc;
240 struct aio_help *ah = &uh->aio_out;
241 io_prep_pread(ah->iocb, uh->ep_out.fd, ah->buf, sizeof(ah->buf), 0);
242 io_set_eventfd(ah->iocb, uh->aio_evfd.fd);
243 rc = io_submit(uh->aio_ctx, 1, &ah->iocb);
244 OSMO_ASSERT(rc >= 0);
245}
246
247static int evfd_cb(struct osmo_fd *ofd, unsigned int what)
248{
249 struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data;
250 struct io_event evt[3];
251 uint64_t ev_cnt;
252 int i, rc;
253
254 rc = read(ofd->fd, &ev_cnt, sizeof(ev_cnt));
255 assert(rc == sizeof(ev_cnt));
256
257 rc = io_getevents(uh->aio_ctx, 1, 3, evt, NULL);
258 if (rc <= 0)
259 return rc;
260
261 for (i = 0; i < rc; i++) {
262 int fd = evt[i].obj->aio_fildes;
263 if (fd == uh->ep_int.fd) {
264 /* interrupt endpoint AIO has completed. This means the IRQ transfer
265 * which we generated has reached the host */
266 } else if (fd == uh->ep_in.fd) {
267 /* IN endpoint AIO has completed. This means the IN transfer which
268 * we sent to the host has completed */
269 } else if (fd == uh->ep_out.fd) {
270 /* IN endpoint AIO has completed. This means the host has sent us
271 * some OUT data */
272 //printf("\t%s\n", osmo_hexdump(uh->aio_out.buf, evt[i].res));
273 ccid_handle_out(uh->ccid_handle, uh->aio_out.buf, evt[i].res);
274 aio_refill_out(uh);
275 }
276 }
277}
278#endif
279
280
281static int ep0_init(struct ufunc_handle *uh)
282{
283 int rc;
284
285 /* open control endpoint and write descriptors to it */
286 rc = open("ep0", O_RDWR);
287 assert(rc >= 0);
288 osmo_fd_setup(&uh->ep0, rc, BSC_FD_READ, &ep_0_cb, uh, 0);
289 osmo_fd_register(&uh->ep0);
290 rc = write(uh->ep0.fd, &descriptors, sizeof(descriptors));
291 if (rc != sizeof(descriptors))
292 return -1;
293 rc = write(uh->ep0.fd, &strings, sizeof(strings));
294 if (rc != sizeof(strings))
295 return -1;
296
297 /* open other endpoint file descriptors */
298 rc = open("ep1", O_RDWR);
299 assert(rc >= 0);
300 osmo_fd_setup(&uh->ep_int, rc, 0, &ep_int_cb, uh, 1);
301#ifdef FUNCTIONFS_SUPPORTS_POLL
302 osmo_fd_register(&uh->ep_int);
303#endif
304
305 rc = open("ep2", O_RDWR);
306 assert(rc >= 0);
307 osmo_fd_setup(&uh->ep_out, rc, BSC_FD_READ, &ep_out_cb, uh, 2);
308#ifdef FUNCTIONFS_SUPPORTS_POLL
309 osmo_fd_register(&uh->ep_out);
310#endif
311
312 rc = open("ep3", O_RDWR);
313 assert(rc >= 0);
314 osmo_fd_setup(&uh->ep_in, rc, 0, &ep_in_cb, uh, 3);
315#ifdef FUNCTIONFS_SUPPORTS_POLL
316 osmo_fd_register(&uh->ep_in);
317#endif
318
319#ifndef FUNCTIONFS_SUPPORTS_POLL
320#include <sys/eventfd.h>
321 /* for some absolutely weird reason, gadgetfs+functionfs don't support
322 * the standard methods of non-blocking I/o (select/poll). We need to
323 * work around using Linux AIO, which is not to be confused with POSIX AIO! */
324
325 memset(&uh->aio_ctx, 0, sizeof(uh->aio_ctx));
326 rc = io_setup(3, &uh->aio_ctx);
327 OSMO_ASSERT(rc >= 0);
328
329 /* create an eventfd, which will be marked readable once some AIO completes */
330 rc = eventfd(0, 0);
331 OSMO_ASSERT(rc >= 0);
332 osmo_fd_setup(&uh->aio_evfd, rc, BSC_FD_READ, &evfd_cb, uh, 0);
333 osmo_fd_register(&uh->aio_evfd);
334
335 uh->aio_out.iocb = malloc(sizeof(struct iocb));
336
337#endif
338
339 return 0;
340}
341
342
343int main(int argc, char **argv)
344{
345 struct ufunc_handle ufh = (struct ufunc_handle) { 0, };
346 int rc;
347
348 chdir(argv[1]);
349 rc = ep0_init(&ufh);
350 if (rc < 0) {
351 fprintf(stderr, "Error %d\n", rc);
352 exit(2);
353 }
354
355 while (1) {
356 osmo_select_main(0);
357 }
358}