brintos

brintos / linux-shallow public Read only

0
0
Text · 9.5 KiB · 96616eb Raw
387 lines · c
1/*2 * This is free and unencumbered software released into the public domain.3 *4 * Anyone is free to copy, modify, publish, use, compile, sell, or5 * distribute this software, either in source code form or as a compiled6 * binary, for any purpose, commercial or non-commercial, and by any7 * means.8 *9 * In jurisdictions that recognize copyright laws, the author or authors10 * of this software dedicate any and all copyright interest in the11 * software to the public domain. We make this dedication for the benefit12 * of the public at large and to the detriment of our heirs and13 * successors. We intend this dedication to be an overt act of14 * relinquishment in perpetuity of all present and future rights to this15 * software under copyright law.16 *17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR23 * OTHER DEALINGS IN THE SOFTWARE.24 *25 * For more information, please refer to <http://unlicense.org/>26 */27 28/* $(CROSS_COMPILE)cc -g -o aio_simple aio_simple.c -laio */29 30#define _DEFAULT_SOURCE /* for endian.h */31 32#include <endian.h>33#include <errno.h>34#include <fcntl.h>35#include <stdarg.h>36#include <stdio.h>37#include <stdlib.h>38#include <string.h>39#include <sys/ioctl.h>40#include <sys/stat.h>41#include <sys/types.h>42#include <sys/poll.h>43#include <unistd.h>44#include <stdbool.h>45#include <sys/eventfd.h>46 47#include "libaio.h"48#define IOCB_FLAG_RESFD         (1 << 0)49 50#include <linux/usb/functionfs.h>51 52#define BUF_LEN		819253 54/*55 * cpu_to_le16/32 are used when initializing structures, a context where a56 * function call is not allowed. To solve this, we code cpu_to_le16/32 in a way57 * that allows them to be used when initializing structures.58 */59 60#if BYTE_ORDER == __LITTLE_ENDIAN61#define cpu_to_le16(x)  (x)62#define cpu_to_le32(x)  (x)63#else64#define cpu_to_le16(x)  ((((x) >> 8) & 0xffu) | (((x) & 0xffu) << 8))65#define cpu_to_le32(x)  \66	((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >>  8) | \67	(((x) & 0x0000ff00u) <<  8) | (((x) & 0x000000ffu) << 24))68#endif69 70/******************** Descriptors and Strings *******************************/71 72static const struct {73	struct usb_functionfs_descs_head_v2 header;74	__le32 fs_count;75	__le32 hs_count;76	struct {77		struct usb_interface_descriptor intf;78		struct usb_endpoint_descriptor_no_audio bulk_sink;79		struct usb_endpoint_descriptor_no_audio bulk_source;80	} __attribute__ ((__packed__)) fs_descs, hs_descs;81} __attribute__ ((__packed__)) descriptors = {82	.header = {83		.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),84		.flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |85				     FUNCTIONFS_HAS_HS_DESC),86		.length = cpu_to_le32(sizeof(descriptors)),87	},88	.fs_count = cpu_to_le32(3),89	.fs_descs = {90		.intf = {91			.bLength = sizeof(descriptors.fs_descs.intf),92			.bDescriptorType = USB_DT_INTERFACE,93			.bNumEndpoints = 2,94			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,95			.iInterface = 1,96		},97		.bulk_sink = {98			.bLength = sizeof(descriptors.fs_descs.bulk_sink),99			.bDescriptorType = USB_DT_ENDPOINT,100			.bEndpointAddress = 1 | USB_DIR_IN,101			.bmAttributes = USB_ENDPOINT_XFER_BULK,102		},103		.bulk_source = {104			.bLength = sizeof(descriptors.fs_descs.bulk_source),105			.bDescriptorType = USB_DT_ENDPOINT,106			.bEndpointAddress = 2 | USB_DIR_OUT,107			.bmAttributes = USB_ENDPOINT_XFER_BULK,108		},109	},110	.hs_count = cpu_to_le32(3),111	.hs_descs = {112		.intf = {113			.bLength = sizeof(descriptors.hs_descs.intf),114			.bDescriptorType = USB_DT_INTERFACE,115			.bNumEndpoints = 2,116			.bInterfaceClass = USB_CLASS_VENDOR_SPEC,117			.iInterface = 1,118		},119		.bulk_sink = {120			.bLength = sizeof(descriptors.hs_descs.bulk_sink),121			.bDescriptorType = USB_DT_ENDPOINT,122			.bEndpointAddress = 1 | USB_DIR_IN,123			.bmAttributes = USB_ENDPOINT_XFER_BULK,124			.wMaxPacketSize = cpu_to_le16(512),125		},126		.bulk_source = {127			.bLength = sizeof(descriptors.hs_descs.bulk_source),128			.bDescriptorType = USB_DT_ENDPOINT,129			.bEndpointAddress = 2 | USB_DIR_OUT,130			.bmAttributes = USB_ENDPOINT_XFER_BULK,131			.wMaxPacketSize = cpu_to_le16(512),132		},133	},134};135 136#define STR_INTERFACE "AIO Test"137 138static const struct {139	struct usb_functionfs_strings_head header;140	struct {141		__le16 code;142		const char str1[sizeof(STR_INTERFACE)];143	} __attribute__ ((__packed__)) lang0;144} __attribute__ ((__packed__)) strings = {145	.header = {146		.magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),147		.length = cpu_to_le32(sizeof(strings)),148		.str_count = cpu_to_le32(1),149		.lang_count = cpu_to_le32(1),150	},151	.lang0 = {152		cpu_to_le16(0x0409), /* en-us */153		STR_INTERFACE,154	},155};156 157/******************** Endpoints handling *******************************/158 159static void display_event(struct usb_functionfs_event *event)160{161	static const char *const names[] = {162		[FUNCTIONFS_BIND] = "BIND",163		[FUNCTIONFS_UNBIND] = "UNBIND",164		[FUNCTIONFS_ENABLE] = "ENABLE",165		[FUNCTIONFS_DISABLE] = "DISABLE",166		[FUNCTIONFS_SETUP] = "SETUP",167		[FUNCTIONFS_SUSPEND] = "SUSPEND",168		[FUNCTIONFS_RESUME] = "RESUME",169	};170	switch (event->type) {171	case FUNCTIONFS_BIND:172	case FUNCTIONFS_UNBIND:173	case FUNCTIONFS_ENABLE:174	case FUNCTIONFS_DISABLE:175	case FUNCTIONFS_SETUP:176	case FUNCTIONFS_SUSPEND:177	case FUNCTIONFS_RESUME:178		printf("Event %s\n", names[event->type]);179	}180}181 182static void handle_ep0(int ep0, bool *ready)183{184	struct usb_functionfs_event event;185	int ret;186 187	struct pollfd pfds[1];188	pfds[0].fd = ep0;189	pfds[0].events = POLLIN;190 191	ret = poll(pfds, 1, 0);192 193	if (ret && (pfds[0].revents & POLLIN)) {194		ret = read(ep0, &event, sizeof(event));195		if (!ret) {196			perror("unable to read event from ep0");197			return;198		}199		display_event(&event);200		switch (event.type) {201		case FUNCTIONFS_SETUP:202			if (event.u.setup.bRequestType & USB_DIR_IN)203				write(ep0, NULL, 0);204			else205				read(ep0, NULL, 0);206			break;207 208		case FUNCTIONFS_ENABLE:209			*ready = true;210			break;211 212		case FUNCTIONFS_DISABLE:213			*ready = false;214			break;215 216		default:217			break;218		}219	}220}221 222int main(int argc, char *argv[])223{224	int i, ret;225	char *ep_path;226 227	int ep0;228	int ep[2];229 230	io_context_t ctx;231 232	int evfd;233	fd_set rfds;234 235	char *buf_in, *buf_out;236	struct iocb *iocb_in, *iocb_out;237	int req_in = 0, req_out = 0;238	bool ready;239 240	if (argc != 2) {241		printf("ffs directory not specified!\n");242		return 1;243	}244 245	ep_path = malloc(strlen(argv[1]) + 4 /* "/ep#" */ + 1 /* '\0' */);246	if (!ep_path) {247		perror("malloc");248		return 1;249	}250 251	/* open endpoint files */252	sprintf(ep_path, "%s/ep0", argv[1]);253	ep0 = open(ep_path, O_RDWR);254	if (ep0 < 0) {255		perror("unable to open ep0");256		return 1;257	}258	if (write(ep0, &descriptors, sizeof(descriptors)) < 0) {259		perror("unable do write descriptors");260		return 1;261	}262	if (write(ep0, &strings, sizeof(strings)) < 0) {263		perror("unable to write strings");264		return 1;265	}266	for (i = 0; i < 2; ++i) {267		sprintf(ep_path, "%s/ep%d", argv[1], i+1);268		ep[i] = open(ep_path, O_RDWR);269		if (ep[i] < 0) {270			printf("unable to open ep%d: %s\n", i+1,271			       strerror(errno));272			return 1;273		}274	}275 276	free(ep_path);277 278	memset(&ctx, 0, sizeof(ctx));279	/* setup aio context to handle up to 2 requests */280	if (io_setup(2, &ctx) < 0) {281		perror("unable to setup aio");282		return 1;283	}284 285	evfd = eventfd(0, 0);286	if (evfd < 0) {287		perror("unable to open eventfd");288		return 1;289	}290 291	/* alloc buffers and requests */292	buf_in = malloc(BUF_LEN);293	buf_out = malloc(BUF_LEN);294	iocb_in = malloc(sizeof(*iocb_in));295	iocb_out = malloc(sizeof(*iocb_out));296 297	while (1) {298		FD_ZERO(&rfds);299		FD_SET(ep0, &rfds);300		FD_SET(evfd, &rfds);301 302		ret = select(((ep0 > evfd) ? ep0 : evfd)+1,303			     &rfds, NULL, NULL, NULL);304		if (ret < 0) {305			if (errno == EINTR)306				continue;307			perror("select");308			break;309		}310 311		if (FD_ISSET(ep0, &rfds))312			handle_ep0(ep0, &ready);313 314		/* we are waiting for function ENABLE */315		if (!ready)316			continue;317 318		/* if something was submitted we wait for event */319		if (FD_ISSET(evfd, &rfds)) {320			uint64_t ev_cnt;321			ret = read(evfd, &ev_cnt, sizeof(ev_cnt));322			if (ret < 0) {323				perror("unable to read eventfd");324				break;325			}326 327			struct io_event e[2];328			/* we wait for one event */329			ret = io_getevents(ctx, 1, 2, e, NULL);330			/* if we got event */331			for (i = 0; i < ret; ++i) {332				if (e[i].obj->aio_fildes == ep[0]) {333					printf("ev=in; ret=%lu\n", e[i].res);334					req_in = 0;335				} else if (e[i].obj->aio_fildes == ep[1]) {336					printf("ev=out; ret=%lu\n", e[i].res);337					req_out = 0;338				}339			}340		}341 342		if (!req_in) { /* if IN transfer not requested*/343			/* prepare write request */344			io_prep_pwrite(iocb_in, ep[0], buf_in, BUF_LEN, 0);345			/* enable eventfd notification */346			iocb_in->u.c.flags |= IOCB_FLAG_RESFD;347			iocb_in->u.c.resfd = evfd;348			/* submit table of requests */349			ret = io_submit(ctx, 1, &iocb_in);350			if (ret >= 0) { /* if ret > 0 request is queued */351				req_in = 1;352				printf("submit: in\n");353			} else354				perror("unable to submit request");355		}356		if (!req_out) { /* if OUT transfer not requested */357			/* prepare read request */358			io_prep_pread(iocb_out, ep[1], buf_out, BUF_LEN, 0);359			/* enable eventfs notification */360			iocb_out->u.c.flags |= IOCB_FLAG_RESFD;361			iocb_out->u.c.resfd = evfd;362			/* submit table of requests */363			ret = io_submit(ctx, 1, &iocb_out);364			if (ret >= 0) { /* if ret > 0 request is queued */365				req_out = 1;366				printf("submit: out\n");367			} else368				perror("unable to submit request");369		}370	}371 372	/* free resources */373 374	io_destroy(ctx);375 376	free(buf_in);377	free(buf_out);378	free(iocb_in);379	free(iocb_out);380 381	for (i = 0; i < 2; ++i)382		close(ep[i]);383	close(ep0);384 385	return 0;386}387