blob: a289865225cec2e8b1c2e0439ebe0dc57e33126e [file] [log] [blame]
Harald Welte63653742019-01-03 16:54:16 +01001
Harald Weltee73a1df2019-05-15 22:27:02 +02002#include <errno.h>
Harald Welte63653742019-01-03 16:54:16 +01003#include <stdint.h>
4#include <endian.h>
5#include <sys/types.h>
6#include <linux/usb/functionfs.h>
7
8#include "ccid_proto.h"
9
Harald Welte63653742019-01-03 16:54:16 +010010#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>
Harald Welte63976d42019-05-16 11:05:52 +0200130#include <string.h>
Harald Welte63653742019-01-03 16:54:16 +0100131#include <assert.h>
132#include <fcntl.h>
133#include <sys/stat.h>
134#include <osmocom/core/select.h>
135#include <osmocom/core/utils.h>
Harald Welted5d555c2019-05-15 19:53:24 +0200136#include <osmocom/core/msgb.h>
Harald Weltee73a1df2019-05-15 22:27:02 +0200137#include <osmocom/core/utils.h>
138#include <osmocom/core/application.h>
139#include <osmocom/core/logging.h>
Harald Welted5d555c2019-05-15 19:53:24 +0200140
141#include "ccid_device.h"
Harald Welte63653742019-01-03 16:54:16 +0100142
143#ifndef FUNCTIONFS_SUPPORTS_POLL
144#include <libaio.h>
145struct aio_help {
Harald Welted5d555c2019-05-15 19:53:24 +0200146 struct msgb *msg;
Harald Welte63653742019-01-03 16:54:16 +0100147 struct iocb *iocb;
148};
149#endif
150
151/* usb function handle */
152struct ufunc_handle {
153 struct osmo_fd ep0;
154 struct osmo_fd ep_in;
155 struct osmo_fd ep_out;
156 struct osmo_fd ep_int;
Harald Welte63976d42019-05-16 11:05:52 +0200157 struct llist_head ep_in_queue;
158 struct llist_head ep_int_queue;
Harald Welte63653742019-01-03 16:54:16 +0100159#ifndef FUNCTIONFS_SUPPORTS_POLL
160 struct osmo_fd aio_evfd;
161 io_context_t aio_ctx;
162 struct aio_help aio_in;
163 struct aio_help aio_out;
164 struct aio_help aio_int;
165#endif
Harald Welted5d555c2019-05-15 19:53:24 +0200166 struct ccid_instance *ccid_handle;
Harald Welte63653742019-01-03 16:54:16 +0100167};
168
169static int ep_int_cb(struct osmo_fd *ofd, unsigned int what)
170{
Harald Weltee73a1df2019-05-15 22:27:02 +0200171 LOGP(DUSB, LOGL_DEBUG, "%s\n", __func__);
Harald Welte63653742019-01-03 16:54:16 +0100172 return 0;
173}
174
175static int ep_out_cb(struct osmo_fd *ofd, unsigned int what)
176{
Harald Welted5d555c2019-05-15 19:53:24 +0200177 struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data;
178 struct msgb *msg = msgb_alloc(512, "OUT-Rx");
Harald Welte63653742019-01-03 16:54:16 +0100179 int rc;
180
Harald Weltee73a1df2019-05-15 22:27:02 +0200181 LOGP(DUSB, LOGL_DEBUG, "%s\n", __func__);
Harald Welte63653742019-01-03 16:54:16 +0100182 if (what & BSC_FD_READ) {
Harald Welted5d555c2019-05-15 19:53:24 +0200183 rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg));
184 if (rc <= 0) {
185 msgb_free(msg);
186 return rc;
187 }
188 msgb_put(msg, rc);
189 ccid_handle_out(uh->ccid_handle, msg);
Harald Welte63653742019-01-03 16:54:16 +0100190 }
191 return 0;
192}
193
194static int ep_in_cb(struct osmo_fd *ofd, unsigned int what)
195{
Harald Weltee73a1df2019-05-15 22:27:02 +0200196 LOGP(DUSB, LOGL_DEBUG, "%s\n", __func__);
Harald Welte63653742019-01-03 16:54:16 +0100197 if (what & BSC_FD_WRITE) {
198 /* write what we have to write */
199 }
200 return 0;
201}
202
203const struct value_string ffs_evt_type_names[] = {
204 { FUNCTIONFS_BIND, "BIND" },
205 { FUNCTIONFS_UNBIND, "UNBIND" },
206 { FUNCTIONFS_ENABLE, "ENABLE" },
207 { FUNCTIONFS_DISABLE, "DISABLE" },
208 { FUNCTIONFS_SETUP, "SETUP" },
209 { FUNCTIONFS_SUSPEND, "SUSPEND" },
210 { FUNCTIONFS_RESUME, "RESUME" },
211 { 0, NULL }
212};
213
214static void handle_setup(const struct usb_ctrlrequest *setup)
215{
Harald Weltee73a1df2019-05-15 22:27:02 +0200216 LOGP(DUSB, LOGL_NOTICE, "EP0 SETUP bRequestType=0x%02x, bRequest=0x%02x wValue=0x%04x, "
217 "wIndex=0x%04x, wLength=%u\n", setup->bRequestType, setup->bRequest,
218 le16_to_cpu(setup->wValue), le16_to_cpu(setup->wIndex), le16_to_cpu(setup->wLength));
219 /* FIXME: Handle control transfer */
Harald Welte63653742019-01-03 16:54:16 +0100220}
221
222static void aio_refill_out(struct ufunc_handle *uh);
223
224static int ep_0_cb(struct osmo_fd *ofd, unsigned int what)
225{
226 struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data;
227 int rc;
228
Harald Welte63653742019-01-03 16:54:16 +0100229 if (what & BSC_FD_READ) {
230 struct usb_functionfs_event evt;
231 rc = read(ofd->fd, (uint8_t *)&evt, sizeof(evt));
232 if (rc < sizeof(evt))
233 return -23;
Harald Weltee73a1df2019-05-15 22:27:02 +0200234 LOGP(DUSB, LOGL_NOTICE, "EP0 %s\n", get_value_string(ffs_evt_type_names, evt.type));
Harald Welte63653742019-01-03 16:54:16 +0100235 switch (evt.type) {
236 case FUNCTIONFS_ENABLE:
237 aio_refill_out(uh);
238 break;
239 case FUNCTIONFS_SETUP:
240 handle_setup(&evt.u.setup);
241 break;
242 }
243
244 }
245 return 0;
246}
247
248#ifndef FUNCTIONFS_SUPPORTS_POLL
249
Harald Welte63976d42019-05-16 11:05:52 +0200250/* an AIO read (OUT) has just completed, let's refill the transfer */
Harald Welte63653742019-01-03 16:54:16 +0100251static void aio_refill_out(struct ufunc_handle *uh)
252{
253 int rc;
254 struct aio_help *ah = &uh->aio_out;
Harald Weltee73a1df2019-05-15 22:27:02 +0200255
256 LOGP(DUSB, LOGL_DEBUG, "%s\n", __func__);
Harald Welte63976d42019-05-16 11:05:52 +0200257 OSMO_ASSERT(!ah->msg);
258 ah->msg = msgb_alloc(512, "OUT-Rx-AIO");
259 OSMO_ASSERT(ah->msg);
Harald Welted5d555c2019-05-15 19:53:24 +0200260 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 +0100261 io_set_eventfd(ah->iocb, uh->aio_evfd.fd);
262 rc = io_submit(uh->aio_ctx, 1, &ah->iocb);
263 OSMO_ASSERT(rc >= 0);
264}
265
Harald Welte63976d42019-05-16 11:05:52 +0200266/* dequeue the next msgb from ep_in_queue and set up AIO for it */
267static void dequeue_aio_write_in(struct ufunc_handle *uh)
268{
269 struct aio_help *ah = &uh->aio_in;
270 struct msgb *d;
271 int rc;
272
273 if (ah->msg)
274 return;
275
276 d = msgb_dequeue(&uh->ep_in_queue);
277 if (!d)
278 return;
279
280 OSMO_ASSERT(ah->iocb);
281 ah->msg = d;
282 io_prep_pwrite(ah->iocb, uh->ep_in.fd, msgb_data(d), msgb_length(d), 0);
283 io_set_eventfd(ah->iocb, uh->aio_evfd.fd);
284 rc = io_submit(uh->aio_ctx, 1, &ah->iocb);
285 OSMO_ASSERT(rc >= 0);
286
287}
288
289/* dequeue the next msgb from ep_int_queue and set up AIO for it */
290static void dequeue_aio_write_int(struct ufunc_handle *uh)
291{
292 struct aio_help *ah = &uh->aio_int;
293 struct msgb *d;
294 int rc;
295
296 if (ah->msg)
297 return;
298
299 d = msgb_dequeue(&uh->ep_int_queue);
300 if (!d)
301 return;
302
303 OSMO_ASSERT(ah->iocb);
304 ah->msg = d;
305 io_prep_pwrite(ah->iocb, uh->ep_int.fd, msgb_data(d), msgb_length(d), 0);
306 io_set_eventfd(ah->iocb, uh->aio_evfd.fd);
307 rc = io_submit(uh->aio_ctx, 1, &ah->iocb);
308 OSMO_ASSERT(rc >= 0);
309}
310
Harald Welte63653742019-01-03 16:54:16 +0100311static int evfd_cb(struct osmo_fd *ofd, unsigned int what)
312{
313 struct ufunc_handle *uh = (struct ufunc_handle *) ofd->data;
314 struct io_event evt[3];
Harald Welte63976d42019-05-16 11:05:52 +0200315 struct msgb *msg;
Harald Welte63653742019-01-03 16:54:16 +0100316 uint64_t ev_cnt;
317 int i, rc;
318
319 rc = read(ofd->fd, &ev_cnt, sizeof(ev_cnt));
320 assert(rc == sizeof(ev_cnt));
321
322 rc = io_getevents(uh->aio_ctx, 1, 3, evt, NULL);
Harald Weltee73a1df2019-05-15 22:27:02 +0200323 if (rc <= 0) {
324 LOGP(DUSB, LOGL_ERROR, "error in io_getevents(): %d\n", rc);
Harald Welte63653742019-01-03 16:54:16 +0100325 return rc;
Harald Weltee73a1df2019-05-15 22:27:02 +0200326 }
Harald Welte63653742019-01-03 16:54:16 +0100327
328 for (i = 0; i < rc; i++) {
329 int fd = evt[i].obj->aio_fildes;
330 if (fd == uh->ep_int.fd) {
331 /* interrupt endpoint AIO has completed. This means the IRQ transfer
332 * which we generated has reached the host */
Harald Weltee73a1df2019-05-15 22:27:02 +0200333 LOGP(DUSB, LOGL_DEBUG, "IRQ AIO completed, free()ing msgb\n");
334 msgb_free(uh->aio_in.msg);
335 uh->aio_in.msg = NULL;
Harald Welte63976d42019-05-16 11:05:52 +0200336 dequeue_aio_write_int(uh);
Harald Welte63653742019-01-03 16:54:16 +0100337 } else if (fd == uh->ep_in.fd) {
338 /* IN endpoint AIO has completed. This means the IN transfer which
339 * we sent to the host has completed */
Harald Weltee73a1df2019-05-15 22:27:02 +0200340 LOGP(DUSB, LOGL_DEBUG, "IN AIO completed, free()ing msgb\n");
Harald Weltebcbc1972019-05-15 21:57:32 +0200341 msgb_free(uh->aio_in.msg);
342 uh->aio_in.msg = NULL;
Harald Welte63976d42019-05-16 11:05:52 +0200343 dequeue_aio_write_in(uh);
Harald Welte63653742019-01-03 16:54:16 +0100344 } else if (fd == uh->ep_out.fd) {
Harald Weltee73a1df2019-05-15 22:27:02 +0200345 /* OUT endpoint AIO has completed. This means the host has sent us
Harald Welte63653742019-01-03 16:54:16 +0100346 * some OUT data */
Harald Weltee73a1df2019-05-15 22:27:02 +0200347 LOGP(DUSB, LOGL_DEBUG, "OUT AIO completed, dispatching received msg\n");
Harald Welted5d555c2019-05-15 19:53:24 +0200348 msgb_put(uh->aio_out.msg, evt[i].res);
Harald Weltee73a1df2019-05-15 22:27:02 +0200349 //printf("\t%s\n", msgb_hexdump(uh->aio_out.msg));
Harald Welte63976d42019-05-16 11:05:52 +0200350 msg = uh->aio_out.msg;
351 uh->aio_out.msg = NULL;
352 /* CCID handler takes ownership of msgb */
353 ccid_handle_out(uh->ccid_handle, msg);
Harald Welte63653742019-01-03 16:54:16 +0100354 aio_refill_out(uh);
355 }
356 }
Harald Weltebcbc1972019-05-15 21:57:32 +0200357 return 0;
Harald Welte63653742019-01-03 16:54:16 +0100358}
359#endif
360
361
362static int ep0_init(struct ufunc_handle *uh)
363{
364 int rc;
365
366 /* open control endpoint and write descriptors to it */
367 rc = open("ep0", O_RDWR);
368 assert(rc >= 0);
369 osmo_fd_setup(&uh->ep0, rc, BSC_FD_READ, &ep_0_cb, uh, 0);
370 osmo_fd_register(&uh->ep0);
371 rc = write(uh->ep0.fd, &descriptors, sizeof(descriptors));
Harald Weltee73a1df2019-05-15 22:27:02 +0200372 if (rc != sizeof(descriptors)) {
373 LOGP(DUSB, LOGL_ERROR, "Cannot write descriptors: %s\n", strerror(errno));
Harald Welte63653742019-01-03 16:54:16 +0100374 return -1;
Harald Weltee73a1df2019-05-15 22:27:02 +0200375 }
Harald Welte63653742019-01-03 16:54:16 +0100376 rc = write(uh->ep0.fd, &strings, sizeof(strings));
Harald Weltee73a1df2019-05-15 22:27:02 +0200377 if (rc != sizeof(strings)) {
378 LOGP(DUSB, LOGL_ERROR, "Cannot write strings: %s\n", strerror(errno));
Harald Welte63653742019-01-03 16:54:16 +0100379 return -1;
Harald Weltee73a1df2019-05-15 22:27:02 +0200380 }
Harald Welte63653742019-01-03 16:54:16 +0100381
382 /* open other endpoint file descriptors */
Harald Welte63976d42019-05-16 11:05:52 +0200383 INIT_LLIST_HEAD(&uh->ep_int_queue);
Harald Welte63653742019-01-03 16:54:16 +0100384 rc = open("ep1", O_RDWR);
385 assert(rc >= 0);
386 osmo_fd_setup(&uh->ep_int, rc, 0, &ep_int_cb, uh, 1);
387#ifdef FUNCTIONFS_SUPPORTS_POLL
388 osmo_fd_register(&uh->ep_int);
389#endif
390
391 rc = open("ep2", O_RDWR);
392 assert(rc >= 0);
393 osmo_fd_setup(&uh->ep_out, rc, BSC_FD_READ, &ep_out_cb, uh, 2);
394#ifdef FUNCTIONFS_SUPPORTS_POLL
395 osmo_fd_register(&uh->ep_out);
396#endif
397
Harald Welte63976d42019-05-16 11:05:52 +0200398 INIT_LLIST_HEAD(&uh->ep_in_queue);
Harald Welte63653742019-01-03 16:54:16 +0100399 rc = open("ep3", O_RDWR);
400 assert(rc >= 0);
401 osmo_fd_setup(&uh->ep_in, rc, 0, &ep_in_cb, uh, 3);
402#ifdef FUNCTIONFS_SUPPORTS_POLL
403 osmo_fd_register(&uh->ep_in);
404#endif
405
406#ifndef FUNCTIONFS_SUPPORTS_POLL
407#include <sys/eventfd.h>
408 /* for some absolutely weird reason, gadgetfs+functionfs don't support
409 * the standard methods of non-blocking I/o (select/poll). We need to
410 * work around using Linux AIO, which is not to be confused with POSIX AIO! */
411
412 memset(&uh->aio_ctx, 0, sizeof(uh->aio_ctx));
413 rc = io_setup(3, &uh->aio_ctx);
414 OSMO_ASSERT(rc >= 0);
415
416 /* create an eventfd, which will be marked readable once some AIO completes */
417 rc = eventfd(0, 0);
418 OSMO_ASSERT(rc >= 0);
419 osmo_fd_setup(&uh->aio_evfd, rc, BSC_FD_READ, &evfd_cb, uh, 0);
420 osmo_fd_register(&uh->aio_evfd);
421
Harald Welte63653742019-01-03 16:54:16 +0100422 uh->aio_out.iocb = malloc(sizeof(struct iocb));
Harald Welte63976d42019-05-16 11:05:52 +0200423 uh->aio_in.iocb = malloc(sizeof(struct iocb));
424 uh->aio_int.iocb = malloc(sizeof(struct iocb));
Harald Welte63653742019-01-03 16:54:16 +0100425#endif
426
427 return 0;
428}
429
Harald Weltebcbc1972019-05-15 21:57:32 +0200430static int ccid_ops_send_in(struct ccid_instance *ci, struct msgb *msg)
431{
432 struct ufunc_handle *uh = ci->priv;
Harald Weltebcbc1972019-05-15 21:57:32 +0200433
Harald Welte63976d42019-05-16 11:05:52 +0200434 /* append to the queue */
435 msgb_enqueue(&uh->ep_in_queue, msg);
Harald Weltebcbc1972019-05-15 21:57:32 +0200436
Harald Welte63976d42019-05-16 11:05:52 +0200437 /* trigger, if needed */
438#ifndef FUNCTIONFS_SUPPORTS_POLL
439 dequeue_aio_write_in(uh);
440#else
441 uh->ep_in.when |= BSC_FD_WRITE;
442#endif
443 return 0;
444}
445
446static int ccid_ops_send_int(struct ccid_instance *ci, struct msgb *msg)
447{
448 struct ufunc_handle *uh = ci->priv;
449
450 /* append to the queue */
451 msgb_enqueue(&uh->ep_int_queue, msg);
452
453 /* trigger, if needed */
454#ifndef FUNCTIONFS_SUPPORTS_POLL
455 dequeue_aio_write_int(uh);
456#else
457 uh->ep_int.when |= BSC_FD_WRITE;
458#endif
Harald Weltebcbc1972019-05-15 21:57:32 +0200459 return 0;
460}
461
462static const struct ccid_ops c_ops = {
463 .send_in = ccid_ops_send_in,
464};
Harald Welte63653742019-01-03 16:54:16 +0100465
Harald Weltee73a1df2019-05-15 22:27:02 +0200466static const struct log_info_cat log_info_cat[] = {
467 [DUSB] = {
468 .name = "USB",
469 .description = "USB Transport",
470 .enabled = 1,
471 .loglevel = LOGL_NOTICE,
472 },
473 [DCCID] = {
474 .name = "CCID",
475 .description = "CCID Core",
476 .color = "\033[1;35m",
477 .enabled = 1,
478 .loglevel = LOGL_DEBUG,
479 },
480};
481
482static const struct log_info log_info = {
483 .cat = log_info_cat,
484 .num_cat = ARRAY_SIZE(log_info_cat),
485};
486
487static void *tall_main_ctx;
488
Harald Welte63653742019-01-03 16:54:16 +0100489int main(int argc, char **argv)
490{
491 struct ufunc_handle ufh = (struct ufunc_handle) { 0, };
Harald Weltebcbc1972019-05-15 21:57:32 +0200492 struct ccid_instance ci = (struct ccid_instance) { 0, };
Harald Welte63653742019-01-03 16:54:16 +0100493 int rc;
494
Harald Weltee73a1df2019-05-15 22:27:02 +0200495 tall_main_ctx = talloc_named_const(NULL, 0, "ccid_main_functionfs");
496 msgb_talloc_ctx_init(tall_main_ctx, 0);
497 osmo_init_logging2(tall_main_ctx, &log_info);
498
Harald Weltebcbc1972019-05-15 21:57:32 +0200499 ccid_instance_init(&ci, &c_ops, "", &ufh);
500 ufh.ccid_handle = &ci;
501
Harald Weltee73a1df2019-05-15 22:27:02 +0200502 if (argc < 2) {
503 fprintf(stderr, "You have to specify the mount-path of the functionfs\n");
504 exit(2);
505 }
506
Harald Welte63653742019-01-03 16:54:16 +0100507 chdir(argv[1]);
508 rc = ep0_init(&ufh);
509 if (rc < 0) {
510 fprintf(stderr, "Error %d\n", rc);
Harald Weltee73a1df2019-05-15 22:27:02 +0200511 exit(1);
Harald Welte63653742019-01-03 16:54:16 +0100512 }
513
514 while (1) {
515 osmo_select_main(0);
516 }
517}