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