proxy.c

February 23, 2024 ยท View on GitHub

#define _POSIX_C_SOURCE 200809L #include <assert.h> #include <errno.h> #include <signal.h> #include <stdarg.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <pthread.h> #include <sys/ioctl.h> #include <linux/usb/raw_gadget.h> #include <linux/usb/ch9.h> #include <libusb.h> #include "intpack.h"

struct config { struct libusb_config_descriptor *desc; struct interface *iface; size_t numiface; int index; };

struct interface { const struct libusb_interface *desc; const struct libusb_interface_descriptor *alt; struct endpoint *ep; size_t numep; };

struct endpoint { const struct libusb_endpoint_descriptor *desc; int addr; int handle; int running; pthread_t thread; };

static int rawfd; static libusb_device_handle *dev; static struct config config;

static void fatal(const char *msg, ...) { va_list ap;

va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
if (!msg)
	fprintf(stderr, "%s\n", strerror(errno));
else if (*msg && msg[strlen(msg) - 1] == ':')
	fprintf(stderr, " %s\n", strerror(errno));
else
	fputc('\n', stderr);
exit(1);

}

static void dump(const char *name, const unsigned char *buf, size_t len) { size_t i;

assert(len <= 1024);
if (name)
	fputs(name, stdout);
if (len > 0) {
	if (name)
		putchar('\t');
	printf("%.2x", buf[0]);
}
for (i = 1; i < len; ++i)
	printf(" %.2x", buf[i]);
putchar('\n');

}

static void rawconnect(void) { }

static void controldone(struct libusb_transfer *transfer) { *(int *)transfer->user_data = 1; }

static void * bulkreader(void *arg) { struct endpoint *ep; struct usb_raw_ep_io *io; int ret, err, len;

ep = arg;
fprintf(stderr, "bulkreader %#.2x\n", ep->desc->bEndpointAddress);
io = malloc(sizeof *io + 1024);
if (!io)
	fatal(NULL);
io->ep = ep->handle;
io->flags = 0;
for (;;) {
	err = libusb_bulk_transfer(dev, ep->addr, io->data, 1024, &len, 0);
	if (err)
		fatal("libusb_bulk_transfer: %s", libusb_strerror(err));
	io->length = len;
	//dump("bulk <-", io->data, len);
	ret = ioctl(rawfd, USB_RAW_IOCTL_EP_WRITE, io);
	if (ret < 0)
		fatal("usb_raw_ep_read %#.2x:", ep->desc->bEndpointAddress);
}
free(io);
return NULL;

}

static void * bulkwriter(void *arg) { struct endpoint *ep; struct usb_raw_ep_io *io; int ret, len, err; unsigned char *buf;

ep = arg;
fprintf(stderr, "bulkwriter %#.2x\n", ep->desc->bEndpointAddress);
io = malloc(sizeof *io + 1024);
if (!io)
	fatal(NULL);
io->ep = ep->handle;
io->flags = 0;
io->length = 1024;
for (;;) {
	ret = ioctl(rawfd, USB_RAW_IOCTL_EP_READ, io);
	if (ret < 0)
		fatal("usb_raw_ep_read %#.2x:", ep->desc->bEndpointAddress);
	//dump("bulk ->", io->data, ret);
	buf = io->data;
	while (ret > 0) {
		err = libusb_bulk_transfer(dev, ep->addr, buf, ret, &len, 0);
		if (err)
			fatal("libusb_bulk_transfer: %s", libusb_strerror(err));
		ret -= len;
		buf += len;
	}
}
free(io);
return NULL;

}

static void setinterface(int idx, int alt) { int i, err; struct interface *iface; struct endpoint *ep; void *(*func)(void *); struct usb_endpoint_descriptor epd;

fprintf(stderr, "SET_INTERFACE %d %d\n", idx, alt);
for (i = 0; i < config.numiface; ++i) {
	iface = &config.iface[i];
	if (iface->desc->altsetting[0].bInterfaceNumber == idx)
		break;
}
if (i == config.numiface)
	fatal("invalid interface %d", idx);
for (i = 0; i < iface->numep; ++i) {
	ep = &iface->ep[i];
	if (ep->running) {
		err = pthread_kill(ep->thread, SIGUSR1);
		if (err)
			fatal("pthread_kill: %s", strerror(err));
		err = pthread_join(ep->thread, NULL);
		if (err)
			fatal("pthread_join: %s", strerror(err));
	}
	if (ioctl(rawfd, USB_RAW_IOCTL_EP_DISABLE, ep->handle) != 0)
		fatal("usb_raw_ep_disable %d:", ep->handle);
}
for (i = 0; i < iface->desc->num_altsetting; ++i) {
	iface->alt = &iface->desc->altsetting[i];
	if (iface->alt->bAlternateSetting == alt)
		break;
}
if (i == iface->desc->num_altsetting)
	fatal("invalid interface %d alt. setting %d", idx, alt);
iface->numep = iface->alt->bNumEndpoints;
iface->ep = realloc(iface->ep, iface->numep * sizeof *iface->ep);
if (!iface->ep && iface->numep > 0)
	fatal(NULL);
for (i = 0; i < iface->numep; ++i) {
	ep = &iface->ep[i];
	ep->desc = &iface->alt->endpoint[i];
	ep->addr = ep->desc->bEndpointAddress;
	fprintf(stderr, "ep enable %#.2x\n", ep->addr);
	epd.bLength = ep->desc->bLength;
	epd.bDescriptorType = ep->desc->bDescriptorType;
	epd.bEndpointAddress = ep->desc->bEndpointAddress;
	epd.bmAttributes = ep->desc->bmAttributes;
	epd.wMaxPacketSize = ep->desc->wMaxPacketSize;
	epd.bInterval = ep->desc->bInterval;
	ep->handle = ioctl(rawfd, USB_RAW_IOCTL_EP_ENABLE, &epd);
	if (ep->handle < 0)
		fatal("usb_raw_ep_enable %#.2x\n", epd.bEndpointAddress);
	func = NULL;
	switch (usb_endpoint_type((const struct usb_endpoint_descriptor *)ep->desc)) {
	case USB_ENDPOINT_XFER_BULK:
		func = usb_endpoint_dir_in((const struct usb_endpoint_descriptor *)ep->desc) ? bulkreader : bulkwriter;
		break;
	}
	if (func) {
		ep->running = true;
		err = pthread_create(&ep->thread, NULL, func, ep);
	} else {
		ep->running = false;
	}
}

}

static void controlin(const unsigned char *data) { int err, index, alt, i;

switch (data[1]) {
case USB_REQ_SET_CONFIGURATION:
	if (ioctl(rawfd, USB_RAW_IOCTL_CONFIGURE) != 0)
		fatal("usb_raw_configure:");
	index = getle16(data + 4);
	fprintf(stderr, "SET_CONFIGURATION %d\n", index);
	if (config.index != -1) {
		libusb_free_config_descriptor(config.desc);
	}
	config.index = index;
	err = libusb_get_config_descriptor(libusb_get_device(dev), index, &config.desc);
	if (err != 0)
		fatal("libusb_get_config_descriptor %d: %s", index, libusb_strerror(err));
	config.numiface = config.desc->bNumInterfaces;
	config.iface = realloc(config.iface, config.numiface * sizeof *config.iface);
	if (!config.iface)
		fatal(NULL);
	for (i = 0; i < config.numiface; ++i) {
		config.iface[i].desc = &config.desc->interface[i];
		config.iface[i].numep = 0;
		libusb_claim_interface(dev, config.desc->interface[i].altsetting[0].bInterfaceNumber);
		setinterface(config.desc->interface[i].altsetting[0].bInterfaceNumber, 0);
	}
	break;
case USB_REQ_SET_INTERFACE:
	index = getle16(data + 4);
	alt = getle16(data + 2);
	setinterface(index, alt);
	break;
default:
	fprintf(stderr, "control in %d\n", data[1]);
}

}

static void rawcontrol(const unsigned char *data, size_t len) { unsigned char *databuf; struct usb_raw_ep_io *io; struct libusb_transfer *transfer; int err, ret, done;

if (len != 8)
	fatal("control transfer has unexpected size");
transfer = libusb_alloc_transfer(0);
if (!transfer)
	fatal("libusb_alloc_transfer failed");
if (!(data[0] & USB_DIR_IN)) {
	len = getle16(data + 6);
	io = malloc(sizeof io + len);
	if (!io)
		fatal(NULL);
	io->ep = 0;
	io->flags = 0;
	io->length = len;
	ret = ioctl(rawfd, USB_RAW_IOCTL_EP0_READ, io);
	if (ret < 0)
		fatal("usb_raw_ep0_read:");
	dump("h<-", io->data, io->length);
	len = 8 + ret;
	databuf = malloc(len);
	memcpy(databuf, data, 8);
	memcpy(databuf + 8, io->data, ret);
	free(io);
	controlin(databuf);
}
done = 0;
libusb_fill_control_transfer(transfer, dev, (unsigned char *)data, controldone, &done, 0);
err = libusb_submit_transfer(transfer);
if (err != 0)
	fatal("libusb_submit_transfer: %s", libusb_strerror(err));
while (!done) {
	err = libusb_handle_events_completed(NULL, &done);
	if (err < 0)
		fatal("libusb_handle_events_completed: %s", libusb_strerror(err));
}
if (transfer->status != LIBUSB_TRANSFER_COMPLETED)
	fatal("control transfer unsuccessful: %d", transfer->status);
if (data[0] & USB_DIR_IN) {
	io = malloc(sizeof io + transfer->actual_length);
	if (!io)
		fatal(NULL);
	io->ep = 0;
	io->flags = 0;
	io->length = transfer->actual_length;
	dump("d<-", libusb_control_transfer_get_data(transfer), transfer->actual_length);
	memcpy(io->data, libusb_control_transfer_get_data(transfer), transfer->actual_length);
	ret = ioctl(rawfd, USB_RAW_IOCTL_EP0_WRITE, io);
	if (ret < 0)
		fatal("usb_raw_ep0_write:");
	free(io);
}
libusb_free_transfer(transfer);

}

static void sighandler(int sig) { }

int main(int argc, char *argv[]) { struct sigaction sa; struct usb_raw_init init; struct usb_raw_event *event; int err;

memset(&sa, 0, sizeof sa);
sa.sa_handler = sighandler;
if (sigaction(SIGUSR1, &sa, NULL) != 0)
	fatal("sigaction SIGUSR1");

err = libusb_init(NULL);
if (err)
	fatal("libusb_init: %s", libusb_strerror(err));
dev = libusb_open_device_with_vid_pid(NULL, 0x2a39, 0x3fd9);
if (!dev)
	fatal("libusb_open failed");
err = libusb_set_auto_detach_kernel_driver(dev, 1);
if (err)
	fatal("libusb_set_auto_detach_kernel_driver: %s", libusb_strerror(err));

rawfd = open("/dev/raw-gadget", O_RDWR);
if (rawfd < 0)
	fatal("open /dev/raw-gadget:");
strcpy((char *)init.driver_name, "fe980000.usb");
strcpy((char *)init.device_name, "fe980000.usb");
init.speed = USB_SPEED_HIGH;

if (ioctl(rawfd, USB_RAW_IOCTL_INIT, &init) != 0)
	fatal("usb_raw_init:");
if (ioctl(rawfd, USB_RAW_IOCTL_RUN, NULL) != 0)
	fatal("usb_raw_run:");

event = malloc(sizeof event + 1024);
for (;;) {
	event->length = 1024;
	if (ioctl(rawfd, USB_RAW_IOCTL_EVENT_FETCH, event) != 0)
		fatal("usb_raw_event_fetch:");
	switch (event->type) {
	case USB_RAW_EVENT_CONNECT:
		rawconnect();
		break;
	case USB_RAW_EVENT_CONTROL:
		rawcontrol(event->data, event->length);
		break;
	}
}

}