1 #define _BSD_SOURCE /* for endian.h */ 2 3 #include <endian.h> 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <stdarg.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <sys/ioctl.h> 11 #include <sys/stat.h> 12 #include <sys/types.h> 13 #include <sys/poll.h> 14 #include <unistd.h> 15 #include <stdbool.h> 16 #include <sys/eventfd.h> 17 18 #include "libaio.h" 19 #define IOCB_FLAG_RESFD (1 << 0) 20 21 #include <linux/usb/functionfs.h> 22 23 #define BUF_LEN 8192 24 25 /******************** Descriptors and Strings *******************************/ 26 27 static const struct { 28 struct usb_functionfs_descs_head header; 29 struct { 30 struct usb_interface_descriptor intf; 31 struct usb_endpoint_descriptor_no_audio bulk_sink; 32 struct usb_endpoint_descriptor_no_audio bulk_source; 33 } __attribute__ ((__packed__)) fs_descs, hs_descs; 34 } __attribute__ ((__packed__)) descriptors = { 35 .header = { 36 .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC), 37 .length = htole32(sizeof(descriptors)), 38 .fs_count = htole32(3), 39 .hs_count = htole32(3), 40 }, 41 .fs_descs = { 42 .intf = { 43 .bLength = sizeof(descriptors.fs_descs.intf), 44 .bDescriptorType = USB_DT_INTERFACE, 45 .bNumEndpoints = 2, 46 .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 47 .iInterface = 1, 48 }, 49 .bulk_sink = { 50 .bLength = sizeof(descriptors.fs_descs.bulk_sink), 51 .bDescriptorType = USB_DT_ENDPOINT, 52 .bEndpointAddress = 1 | USB_DIR_IN, 53 .bmAttributes = USB_ENDPOINT_XFER_BULK, 54 }, 55 .bulk_source = { 56 .bLength = sizeof(descriptors.fs_descs.bulk_source), 57 .bDescriptorType = USB_DT_ENDPOINT, 58 .bEndpointAddress = 2 | USB_DIR_OUT, 59 .bmAttributes = USB_ENDPOINT_XFER_BULK, 60 }, 61 }, 62 .hs_descs = { 63 .intf = { 64 .bLength = sizeof(descriptors.hs_descs.intf), 65 .bDescriptorType = USB_DT_INTERFACE, 66 .bNumEndpoints = 2, 67 .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 68 .iInterface = 1, 69 }, 70 .bulk_sink = { 71 .bLength = sizeof(descriptors.hs_descs.bulk_sink), 72 .bDescriptorType = USB_DT_ENDPOINT, 73 .bEndpointAddress = 1 | USB_DIR_IN, 74 .bmAttributes = USB_ENDPOINT_XFER_BULK, 75 }, 76 .bulk_source = { 77 .bLength = sizeof(descriptors.hs_descs.bulk_source), 78 .bDescriptorType = USB_DT_ENDPOINT, 79 .bEndpointAddress = 2 | USB_DIR_OUT, 80 .bmAttributes = USB_ENDPOINT_XFER_BULK, 81 }, 82 }, 83 }; 84 85 #define STR_INTERFACE "AIO Test" 86 87 static const struct { 88 struct usb_functionfs_strings_head header; 89 struct { 90 __le16 code; 91 const char str1[sizeof(STR_INTERFACE)]; 92 } __attribute__ ((__packed__)) lang0; 93 } __attribute__ ((__packed__)) strings = { 94 .header = { 95 .magic = htole32(FUNCTIONFS_STRINGS_MAGIC), 96 .length = htole32(sizeof(strings)), 97 .str_count = htole32(1), 98 .lang_count = htole32(1), 99 }, 100 .lang0 = { 101 htole16(0x0409), /* en-us */ 102 STR_INTERFACE, 103 }, 104 }; 105 106 /******************** Endpoints handling *******************************/ 107 108 static void display_event(struct usb_functionfs_event *event) 109 { 110 static const char *const names[] = { 111 [FUNCTIONFS_BIND] = "BIND", 112 [FUNCTIONFS_UNBIND] = "UNBIND", 113 [FUNCTIONFS_ENABLE] = "ENABLE", 114 [FUNCTIONFS_DISABLE] = "DISABLE", 115 [FUNCTIONFS_SETUP] = "SETUP", 116 [FUNCTIONFS_SUSPEND] = "SUSPEND", 117 [FUNCTIONFS_RESUME] = "RESUME", 118 }; 119 switch (event->type) { 120 case FUNCTIONFS_BIND: 121 case FUNCTIONFS_UNBIND: 122 case FUNCTIONFS_ENABLE: 123 case FUNCTIONFS_DISABLE: 124 case FUNCTIONFS_SETUP: 125 case FUNCTIONFS_SUSPEND: 126 case FUNCTIONFS_RESUME: 127 printf("Event %s\n", names[event->type]); 128 } 129 } 130 131 static void handle_ep0(int ep0, bool *ready) 132 { 133 struct usb_functionfs_event event; 134 int ret; 135 136 struct pollfd pfds[1]; 137 pfds[0].fd = ep0; 138 pfds[0].events = POLLIN; 139 140 ret = poll(pfds, 1, 0); 141 142 if (ret && (pfds[0].revents & POLLIN)) { 143 ret = read(ep0, &event, sizeof(event)); 144 if (!ret) { 145 perror("unable to read event from ep0"); 146 return; 147 } 148 display_event(&event); 149 switch (event.type) { 150 case FUNCTIONFS_SETUP: 151 if (event.u.setup.bRequestType & USB_DIR_IN) 152 write(ep0, NULL, 0); 153 else 154 read(ep0, NULL, 0); 155 break; 156 157 case FUNCTIONFS_ENABLE: 158 *ready = true; 159 break; 160 161 case FUNCTIONFS_DISABLE: 162 *ready = false; 163 break; 164 165 default: 166 break; 167 } 168 } 169 } 170 171 int main(int argc, char *argv[]) 172 { 173 int i, ret; 174 char *ep_path; 175 176 int ep0; 177 int ep[2]; 178 179 io_context_t ctx; 180 181 int evfd; 182 fd_set rfds; 183 184 char *buf_in, *buf_out; 185 struct iocb *iocb_in, *iocb_out; 186 int req_in = 0, req_out = 0; 187 bool ready; 188 189 if (argc != 2) { 190 printf("ffs directory not specified!\n"); 191 return 1; 192 } 193 194 ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */); 195 if (!ep_path) { 196 perror("malloc"); 197 return 1; 198 } 199 200 /* open endpoint files */ 201 sprintf(ep_path, "%s/ep0", argv[1]); 202 ep0 = open(ep_path, O_RDWR); 203 if (ep0 < 0) { 204 perror("unable to open ep0"); 205 return 1; 206 } 207 if (write(ep0, &descriptors, sizeof(descriptors)) < 0) { 208 perror("unable do write descriptors"); 209 return 1; 210 } 211 if (write(ep0, &strings, sizeof(strings)) < 0) { 212 perror("unable to write strings"); 213 return 1; 214 } 215 for (i = 0; i < 2; ++i) { 216 sprintf(ep_path, "%s/ep%d", argv[1], i+1); 217 ep[i] = open(ep_path, O_RDWR); 218 if (ep[i] < 0) { 219 printf("unable to open ep%d: %s\n", i+1, 220 strerror(errno)); 221 return 1; 222 } 223 } 224 225 free(ep_path); 226 227 memset(&ctx, 0, sizeof(ctx)); 228 /* setup aio context to handle up to 2 requests */ 229 if (io_setup(2, &ctx) < 0) { 230 perror("unable to setup aio"); 231 return 1; 232 } 233 234 evfd = eventfd(0, 0); 235 if (evfd < 0) { 236 perror("unable to open eventfd"); 237 return 1; 238 } 239 240 /* alloc buffers and requests */ 241 buf_in = malloc(BUF_LEN); 242 buf_out = malloc(BUF_LEN); 243 iocb_in = malloc(sizeof(*iocb_in)); 244 iocb_out = malloc(sizeof(*iocb_out)); 245 246 while (1) { 247 FD_ZERO(&rfds); 248 FD_SET(ep0, &rfds); 249 FD_SET(evfd, &rfds); 250 251 ret = select(((ep0 > evfd) ? ep0 : evfd)+1, 252 &rfds, NULL, NULL, NULL); 253 if (ret < 0) { 254 if (errno == EINTR) 255 continue; 256 perror("select"); 257 break; 258 } 259 260 if (FD_ISSET(ep0, &rfds)) 261 handle_ep0(ep0, &ready); 262 263 /* we are waiting for function ENABLE */ 264 if (!ready) 265 continue; 266 267 /* if something was submitted we wait for event */ 268 if (FD_ISSET(evfd, &rfds)) { 269 uint64_t ev_cnt; 270 ret = read(evfd, &ev_cnt, sizeof(ev_cnt)); 271 if (ret < 0) { 272 perror("unable to read eventfd"); 273 break; 274 } 275 276 struct io_event e[2]; 277 /* we wait for one event */ 278 ret = io_getevents(ctx, 1, 2, e, NULL); 279 /* if we got event */ 280 for (i = 0; i < ret; ++i) { 281 if (e[i].obj->aio_fildes == ep[0]) { 282 printf("ev=in; ret=%lu\n", e[i].res); 283 req_in = 0; 284 } else if (e[i].obj->aio_fildes == ep[1]) { 285 printf("ev=out; ret=%lu\n", e[i].res); 286 req_out = 0; 287 } 288 } 289 } 290 291 if (!req_in) { /* if IN transfer not requested*/ 292 /* prepare write request */ 293 io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0); 294 /* enable eventfd notification */ 295 iocb_in->u.c.flags |= IOCB_FLAG_RESFD; 296 iocb_in->u.c.resfd = evfd; 297 /* submit table of requests */ 298 ret = io_submit(ctx, 1, &iocb_in); 299 if (ret >= 0) { /* if ret > 0 request is queued */ 300 req_in = 1; 301 printf("submit: in\n"); 302 } else 303 perror("unable to submit request"); 304 } 305 if (!req_out) { /* if OUT transfer not requested */ 306 /* prepare read request */ 307 io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0); 308 /* enable eventfs notification */ 309 iocb_out->u.c.flags |= IOCB_FLAG_RESFD; 310 iocb_out->u.c.resfd = evfd; 311 /* submit table of requests */ 312 ret = io_submit(ctx, 1, &iocb_out); 313 if (ret >= 0) { /* if ret > 0 request is queued */ 314 req_out = 1; 315 printf("submit: out\n"); 316 } else 317 perror("unable to submit request"); 318 } 319 } 320 321 /* free resources */ 322 323 io_destroy(ctx); 324 325 free(buf_in); 326 free(buf_out); 327 free(iocb_in); 328 free(iocb_out); 329 330 for (i = 0; i < 2; ++i) 331 close(ep[i]); 332 close(ep0); 333 334 return 0; 335 } 336