/* A really crude USBIP client */ /* XXX - there is a concept in USBIP called "unlink" which, I suppose, * can be used to discard a ongoing transaction that has been sent. * This code makes no use of this concept, but probably could. */ /* XXX XXX XXX - PLEASE NOTE... exiting this client BEFORE closing any * programs that are using the USB device will likely cause a kernel panic * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define VHCI_NADDRS 16 /* The number of outstanding packets that were sent could be * reduced to VHCI_NADDRS if ISOCHRONOUS didn't perform a * record of the packet. There is little need for them to * do that.... */ #define NUM_OUTSTANDING 512 /* This is pretty big. It was noted that data packets of 65536 were * seen with some BULK transfers, but maybe not anything larger. * Attempts are made to do as few reads and writes to the VHCI fd * as possible, but this is really a large buffer and does not account * for the largest that is theorically possble. Maybe should do chunks... */ #define DATA_BUF_SIZE 1572864 uint16_t debug_level = 0; #define DEBUG_CONTROL 0x0100 #define DEBUG_INTERRUPT 0x0200 #define DEBUG_BULK 0x0400 #define DEBUG_ISOCHRONOUS 0x0800 #define DEBUG_NETWORK 0x1000 #define DEBUG_ANY 0xff00 bool is_debug(uint8_t level, uint16_t what) { bool r = false; uint8_t set_level = (uint8_t)debug_level & 0x00ff; uint8_t set_what = (uint8_t)(debug_level >> 8); if (set_level >= level && ((uint8_t)(what >> 8) & set_what)) { r = true; } return r; } /* This is lifted from sys/dev/usb/usbdivar.h, which isn't * copied to /usr/include */ typedef enum { /* keep in sync with usbd_error_strs */ USBD_NORMAL_COMPLETION = 0, /* must be 0 */ USBD_IN_PROGRESS, /* 1 */ /* errors */ USBD_PENDING_REQUESTS, /* 2 */ USBD_NOT_STARTED, /* 3 */ USBD_INVAL, /* 4 */ USBD_NOMEM, /* 5 */ USBD_CANCELLED, /* 6 */ USBD_BAD_ADDRESS, /* 7 */ USBD_IN_USE, /* 8 */ USBD_NO_ADDR, /* 9 */ USBD_SET_ADDR_FAILED, /* 10 */ USBD_NO_POWER, /* 11 */ USBD_TOO_DEEP, /* 12 */ USBD_IOERROR, /* 13 */ USBD_NOT_CONFIGURED, /* 14 */ USBD_TIMEOUT, /* 15 */ USBD_SHORT_XFER, /* 16 */ USBD_STALLED, /* 17 */ USBD_INTERRUPTED, /* 18 */ USBD_ERROR_MAX /* must be last */ } usbd_status; /* Keep track of the last action performed in the main poll loop */ typedef enum { LAST_ACTION_START, LAST_ACTION_NETWORK, LAST_ACTION_PACKET, LAST_ACTION_ISOCPACKET } last_actions; /* This is a packet record and has support for keeping track of the * literal last three packets. That feature is only used when this code * is trying to handle STALLS... really don't use that feature and just * let the kernel drivers do that. */ #define NUM_PACKETS_RECORD 3 int next_sequence = 1; struct psent { int ep; int seq; int dir; size_t reqsize; uint8_t *packets[NUM_PACKETS_RECORD]; int packet_sizes[NUM_PACKETS_RECORD]; }; struct psent packets_sent[NUM_OUTSTANDING]; /* Keep track of the end points that are currently busy. That is, * something was sent, but a reply has not occured yet. */ bool endpoints_busy[VHCI_NADDRS]; /* This is just so that SIGINFO can display an error when a record * of a packet didn't happen. */ int error_count = 0; /* Form up the USBIP direction and transfer_flags based upon what * we understand the direction to be. It is a little different for * CONTROL packets, which have that information encoded in the request * and the other packets which do not. */ static uint32_t form_direction_ctrl(int reqtype) { uint32_t r = USBIP_DIR_OUT; if (reqtype & UT_READ) { r = USBIP_DIR_IN; } return r; } static uint32_t form_transfer_flags_ctrl(int reqtype) { uint32_t r = 0; if (reqtype & UT_READ) { r = USBIP_TF_DIR_IN; } return r; } static uint32_t form_direction_req(uint16_t flags) { uint32_t r = USBIP_DIR_OUT; if (flags & VHCI_PACKET_REQ_UTOH) { r = USBIP_DIR_IN; } return r; } static uint32_t form_transfer_flags_req(uint16_t flags) { uint32_t r = 0; if (flags & VHCI_PACKET_REQ_UTOH) { r = USBIP_TF_DIR_IN; } return r; } /* Handle the packet record. */ static void clear_packet_record() { for(int i=0;i < NUM_OUTSTANDING;i++) { packets_sent[i].ep = -1; packets_sent[i].seq = -1; packets_sent[i].dir = -1; packets_sent[i].reqsize = -1; for(int ii=0; ii < NUM_PACKETS_RECORD;ii++) { packets_sent[i].packets[ii] = NULL; packets_sent[i].packet_sizes[ii] = -1; } } return; } static int record_packet(int ep, int seq, int dir, uint32_t rsize, bool increment) { int r=-1; for(int i=0; i < NUM_OUTSTANDING; i++) { if (packets_sent[i].seq == -1) { r = i; packets_sent[i].ep = ep; packets_sent[i].seq = seq; packets_sent[i].dir = dir; packets_sent[i].reqsize = rsize; if (increment) { next_sequence++; } break; } } if (r == -1) error_count++; return r; } /* This is only really useful when trying to handle a STALL. */ static int record_packet_with_data(int ep, int seq, int dir, uint32_t rsize, bool increment, uint8_t *data, int datasize) { #ifdef HANDLE_STALL int r=-1; for(int i=0; i < NUM_OUTSTANDING; i++) { if (packets_sent[i].seq == -1) { r = i; packets_sent[i].ep = ep; packets_sent[i].seq = seq; packets_sent[i].dir = dir; packets_sent[i].reqsize = rsize; if (data != NULL && datasize > 0) { int ii; for(ii=0; ii < NUM_PACKETS_RECORD; ii++) { if (packets_sent[i].packets[ii] == NULL) { break; } } if (packets_sent[i].packets[ii] == NULL) { packets_sent[i].packets[ii] = malloc(datasize); packets_sent[i].packet_sizes[ii] = datasize; memcpy(packets_sent[i].packets[ii],data,datasize); } else { printf("RAN OUT OF SPACE IN PACKET RECORD FOR DATA: %d %d %d %d\n",ep, seq, dir, rsize); } } if (increment) { next_sequence++; } break; } } return r; #else return record_packet(ep, seq, dir, rsize, increment); #endif } /* Same here... no need to keep the data, unless we are trying to replay it */ static int record_packet_data(int index, uint8_t *data, uint32_t datasize) { #ifdef HANDLE_STALL int r = 0; if (data != NULL && datasize > 0) { int ii; for(ii=0; ii < NUM_PACKETS_RECORD; ii++) { if (packets_sent[index].packets[ii] == NULL) { break; } } if (packets_sent[index].packets[ii] == NULL) { packets_sent[index].packets[ii] = malloc(datasize); packets_sent[index].packet_sizes[ii] = datasize; memcpy(packets_sent[index].packets[ii],data,datasize); r = 1; } else { printf("RAN OUT OF SPACE IN PACKET RECORD FOR DATA: %d\n",index); } } return r; #else return 1; #endif } static int unrecord_packet(int seq) { int r=-1; for(int i=0; i < NUM_OUTSTANDING; i++) { if (packets_sent[i].seq == seq) { r = i; packets_sent[i].ep = -1; packets_sent[i].seq = -1; packets_sent[i].dir = -1; packets_sent[i].reqsize = -1; for(int ii=0; ii < NUM_PACKETS_RECORD;ii++) { if (packets_sent[i].packets[ii] != NULL) { free(packets_sent[i].packets[ii]); packets_sent[i].packets[ii] = NULL; packets_sent[i].packet_sizes[ii] = -1; } } break; } } return r; } static int find_packet_by_ep(int ep) { int r=-1; for(int i=0; i < NUM_OUTSTANDING; i++) { if (packets_sent[i].ep == ep) { r = i; break; } } return r; } static int find_packet_by_seq(int seq) { int r=-1; for(int i=0; i < NUM_OUTSTANDING; i++) { if (packets_sent[i].seq == seq) { r = i; break; } } return r; } static int count_packets() { int r=0; for(int i=0; i < NUM_OUTSTANDING; i++) { if (packets_sent[i].seq != -1) { r++; } } return r; } /* Some of this was lifted a bit from the syzkaller fuzzer. Nothing is particular * to that, however and I have seen other implemtations very much like this */ static int vhci_open(void) { char path[1024]; snprintf(path, sizeof(path), "/dev/vhci0"); return open(path, O_RDWR); } static int vhci_setport(int fd, u_int port) { struct vhci_ioc_set_port args; args.port = port; return ioctl(fd, VHCI_IOC_SET_PORT, &args); } static int vhci_setaddr(int fd, u_int8_t addr) { struct vhci_ioc_set_addr args; args.addr = addr; return ioctl(fd, VHCI_IOC_SET_ADDR, &args); } static int vhci_set_result_errors(int fd, bool errors) { struct vhci_ioc_set_result_errors args; args.result_errors = errors; return ioctl(fd, VHCI_IOC_SET_RES_ERR, &args); } static int vhci_gethead(int fd, int what, struct vhci_ioc_get_head *hh) { hh->what = what; return ioctl(fd, VHCI_IOC_GET_HEAD, hh); } static int vhci_usb_attach(int fd) { return ioctl(fd, VHCI_IOC_USB_ATTACH, NULL); } static int read_exact(int fd, void* buf, size_t size, int *errp) { uint8_t* ptr = (uint8_t*)buf; while (1) { ssize_t done = read(fd, ptr, size); if (done < 0) { if (errp != NULL) *errp = errno; return -1; } if ((size_t)done == size) return 0; size -= done; ptr += done; } } static int write_exact(int fd, void* buf, size_t size, int *errp) { uint8_t* ptr = (uint8_t*)buf; while (1) { ssize_t done = write(fd, ptr, size); if (done <= 0) { if (errp != NULL) *errp = errno; return -1; } if ((size_t)done == size) return 0; size -= done; ptr += done; } } /* Decode and print a bunch of the structures. Mosty for debugging... */ void decode_import_req(struct usbip_op_req_import *import) { printf("version: %08x\n",import->version); printf("command: %08x\n",import->command); printf("status: %08x\n",import->status); printf("busid: %s\n",import->busid); } void decode_head(struct vhci_ioc_get_head *ahead) { printf("what: %02x\n",ahead->what); printf("len: %ld\n",ahead->len); printf("ptype: %02x\n",ahead->ptype); printf("npkts: %d\n",ahead->npkts); printf("flags: %04x\n",ahead->flags); } void decode_vhci_request(vhci_request_t *r,int type) { printf("type: %04x\n",(*r).type); switch(type) { case VHCI_REQ_CTRL: printf("bmRequestType: %02x\n",(*r).u.ctrl.bmRequestType); printf("bRequest: %02x\n",(*r).u.ctrl.bRequest); printf("wValue: %04x\n",UGETW((*r).u.ctrl.wValue)); printf("wIndex: %04x\n",UGETW((*r).u.ctrl.wIndex)); printf("wLength: %04x\n",UGETW((*r).u.ctrl.wLength)); break; case VHCI_REQ_INTERRUPT: case VHCI_REQ_BULK: printf("flags: %02x\n",(*r).u.req.flags); printf("length: %08x\n",(*r).u.req.length); printf("interval: %04x\n",(*r).u.req.interval); break; case VHCI_REQ_ISOCHRONOUS: printf("flags: %02x\n",(*r).u.reqisoc.flags); printf("nframes: %04x\n",(*r).u.reqisoc.nframes); printf("interval: %04x\n",(*r).u.reqisoc.interval); break; default: printf("Unknown packet type. Can not decode.\n"); } return; } void decode_submit_cmd(struct usbip_cmd_submit *r) { printf("HEADER COMMAND: %08x\n",ntohl((*r).command.command)); printf("HEADER SEQNUM: %08x\n",ntohl((*r).command.seqnum)); printf("HEADER DEVID: %08x\n",ntohl((*r).command.devid)); printf("HEADER DIRECTION: %08x\n",ntohl((*r).command.direction)); printf("HEADER EP: %08x\n",ntohl((*r).command.ep)); printf("TRANSFER_flags: %08x\n",ntohl((*r).transfer_flags)); printf("TRANSFER_BUFFER_LENGTH: %08x\n",ntohl((*r).transfer_buffer_length)); printf("START_FRAME: %08x\n",ntohl((*r).start_frame)); printf("NUMBER_OF_PACKETS: %08x\n",ntohl((*r).number_of_packets)); printf("INTERVAL: %08x\n",ntohl((*r).interval)); printf("SETUP: "); for(int ii=0;ii < USBIP_SETUP_SIZE;ii++) { printf("%02x ",(*r).setup[ii]); } printf("\n"); return; } void decode_submit_ret(struct usbip_ret_submit *r) { printf("HEADER COMMAND: %08x\n",ntohl((*r).command.command)); printf("HEADER SEQNUM: %08x\n",ntohl((*r).command.seqnum)); printf("HEADER DEVID: %08x\n",ntohl((*r).command.devid)); printf("HEADER DIRECTION: %08x\n",ntohl((*r).command.direction)); printf("HEADER EP: %08x\n",ntohl((*r).command.ep)); printf("\tSTATUS: %08x\n",ntohl((*r).status)); printf("\tACTUAL_LENGTH: %08x\n",ntohl((*r).actual_length)); printf("\tSTART_FRAME: %08x\n",ntohl((*r).start_frame)); printf("\tNUMBER_OF_PACKETS: %08x\n",ntohl((*r).number_of_packets)); printf("\tERROR_COUNT: %08x\n",ntohl((*r).error_count)); return; } /* So... there is a lot of undocumented behavior with USBIP. This is one of them... * For a CONTROL packet that is a GET DESCRIPTOR call, the first one (any maybe all * of them) must have a wLength of 64 bytes. Even setting this to the size of a * descriptor, which should be large enough, will not work. The remote end is * unresponsive. * * XXX - this could be cleaned up some... there has been only one fixup needed * so far... */ int fixup_control_request(usb_device_request_t *c) { int r = 0; if (is_debug(2,DEBUG_CONTROL)) printf("FIXUP: %02x %02x -- %02x %02x\n",(*c).bRequest,UGETW((*c).wValue) >> 8,UR_GET_DESCRIPTOR,UDESC_DEVICE); if ((*c).bRequest == UR_GET_DESCRIPTOR && UGETW((*c).wValue) >> 8 == UDESC_DEVICE) { uint32_t l = UGETW((*c).wLength); if (is_debug(2,DEBUG_CONTROL)) printf("SIZE L: %d\n",l); if (l != USB_DEVICE_DESCRIPTOR_SIZE) { USETW((*c).wLength,64); r = 1; } } return r; } /* More undocumented behavior.... * * In this case, we have to eat certain control requests. Currently only the SET * ADDRESS call is eaten. If you allow that one to do though, the remote end gets * very confused and will become unresponsive to any further calls. NetBSD does not * seem to mind that this was ignored, which is fortunate (at least with the devices * I have on hand). * * In the past, certain other calls were filtered, but that may not be needed at this * point. */ int skip_control_request(uint8_t reqtype, uint8_t req, void *setup) { int r = -1; uint16_t wFeatureSelector; uint16_t wEndpoint; usb_device_request_t *s; switch(reqtype) { case 0x00: /* control messasge */ switch(req) { case 0x05: /* UR_SET_ADDRESS */ r=req; default: break; } break; #ifdef __NOT case 0x02: /* CLEAR FEATURE */ switch(req) { case 0x01: /* CLEAR FEATURE */ s = (usb_device_request_t *)setup; wFeatureSelector = UGETW(s->wValue); wEndpoint = UGETW(s->wIndex); if (is_debug(2,DEBUG_CONTROL)) printf("skip_control_request CLEAR FEATURE: %02x %02x\n",wFeatureSelector,wEndpoint); if (wEndpoint < 128) { r=0x55; } default: break; } break; case 0x21: /* reset */ switch(req) { case 0xff: /* reset */ r=req; default: break; } break; #endif default: break; } return r; } /* ISOCHRONOUS requests make use of the heap to create a USBIP ISOCHRONOUS * request. This requires that the pointer lengths and the data be put together * in a specific way. * * Note the Linux error code... that is required. * * XXX - It is probably possible to avoid the memcpy in the loop by doing * pointer math aginst buf * * XXX - This is really only for outgoing requests.... probably... */ uint8_t *create_isoc_packet(uint16_t nframes,uint16_t *pointers,size_t datalen,uint8_t *data) { uint8_t *buf = NULL; struct usbip_iso_packet_descriptor iso_descriptor; uint32_t offset; uint32_t boffset; uint16_t a_length; buf = malloc((nframes * sizeof(struct usbip_iso_packet_descriptor)) + datalen); if (buf != NULL) { memcpy(buf,data,datalen); boffset = datalen; offset = 0; for(int i=0;i < nframes;i++) { iso_descriptor.offset = htonl(offset); a_length = pointers[i]; iso_descriptor.length = htonl((uint32_t)a_length); iso_descriptor.actual_length = 0; iso_descriptor.status = htonl(USBIP_ERROR_EXDEV); memcpy(buf + boffset,(uint8_t *)&iso_descriptor,sizeof(struct usbip_iso_packet_descriptor)); boffset += sizeof(struct usbip_iso_packet_descriptor); offset += a_length; } } return buf; } /* Handle a CONTROL request. May be incoming or outgoing. * Unlike most handlers, this one will return true or false, depending * on whether or not it skipped the request. */ bool handle_control_packet(int vfd, int endpoint, int network_socket, uint32_t devid, struct vhci_ioc_get_head *head, uint8_t *data) { int rr = true; int r; vhci_request_t req; struct usbip_cmd_submit packet_to_dev; if (is_debug(1,DEBUG_CONTROL)) { printf("--------------------CP----------------------------\n"); printf("CONTROL HEAD SIZE %d: %d %d\n",endpoint,head->len,sizeof(vhci_request_t)); } r = read_exact(vfd, &req, sizeof(vhci_request_t),NULL); if (r == 0) { int fdir; int reqsize; int did_fixup; if (is_debug(1,DEBUG_CONTROL)) decode_vhci_request(&req, head->ptype); did_fixup = fixup_control_request(&req.u.ctrl); if (is_debug(2,DEBUG_CONTROL)) { printf("After fixup:\n"); decode_vhci_request(&req, head->ptype); } packet_to_dev.command.command = htonl(USBIP_CMD_SUBMIT); packet_to_dev.command.seqnum = htonl(next_sequence); packet_to_dev.command.devid = htonl(devid); fdir = form_direction_ctrl(req.u.ctrl.bmRequestType); packet_to_dev.command.direction = htonl(fdir); packet_to_dev.command.ep = endpoint; packet_to_dev.transfer_flags = htonl(form_transfer_flags_ctrl(req.u.ctrl.bmRequestType)); /* XXX - this could be cleaned up... * assumes just one type of fixup... * * see note in fixup_control_request */ if (did_fixup == 1) { reqsize = 64; } else { reqsize = UGETW(req.u.ctrl.wLength); } packet_to_dev.transfer_buffer_length = htonl(reqsize); packet_to_dev.start_frame = 0; packet_to_dev.number_of_packets = 0; packet_to_dev.interval = 0; memcpy(packet_to_dev.setup,&req.u.ctrl,USBIP_SETUP_SIZE); if (is_debug(2,DEBUG_CONTROL)) { printf("\n"); for(int ii=0;ii < sizeof(usb_device_request_t);ii++) { printf("%02x ",packet_to_dev.setup[ii]); } printf("\n"); } if (skip_control_request(req.u.ctrl.bmRequestType,req.u.ctrl.bRequest,&req.u.ctrl) == -1) { r = record_packet(0, next_sequence, fdir, reqsize, true); if (r == -1) { printf("CONTROL WRITE COULD NOT RECORD PACKET: %d\n",next_sequence); } else { if (is_debug(1,DEBUG_CONTROL)) printf("CONTROL RECORDED: %d %d %d\n",r,packets_sent[r].ep,packets_sent[r].seq); } r = write(network_socket, &packet_to_dev, sizeof(struct usbip_cmd_submit)); if (r < 0) { printf("CONTROL WRITE R: %d: %d\n",endpoint,r); } else { if (is_debug(1,DEBUG_CONTROL)) printf("CONTROL WRITE WROTE %d\n",r); endpoints_busy[endpoint] = true; } if (fdir == USBIP_DIR_OUT && reqsize > 0) { memset(data,0,reqsize); r = read_exact(vfd, data, reqsize, NULL); if (r == 0) { r = write(network_socket, data, reqsize); if (r < 0) { printf("CONTROL WRITE DATA EROR R: %d",r); } else { if (is_debug(1,DEBUG_CONTROL)) printf("CONTROL WRITE DATA WROTE: R: %d\n",r); } } else { printf("CONTROL WRITE RECEIVE DATA FAILED: %d\n",r); } } } else { /* Just go ahead and call skipped requests GOOD */ if (is_debug(1,DEBUG_CONTROL)) printf("SKIPPING bmRequestType/bRequest %02x %02x\n",req.u.ctrl.bmRequestType,req.u.ctrl.bRequest); vhci_response_t response_size; response_size.error = USBD_NORMAL_COMPLETION; response_size.size = 0; r = write_exact(vfd, &response_size, sizeof(vhci_response_t),NULL); if (r != 0) { printf("SKIPPING COULD NOT SEND RESPONSE: %d\n",r); } rr = false; } } else { printf("CONTROL VHCI ERROR READ R %d: %d\n",endpoint,r); } return rr; } /* Handle an INTERRUPT request. May be incoming or outgoing */ void handle_interrupt_packet(int vfd, int endpoint, int network_socket, uint32_t devid, struct vhci_ioc_get_head *head, uint8_t *data) { int r; vhci_request_t req; struct usbip_cmd_submit packet_to_dev; if (is_debug(1,DEBUG_INTERRUPT)) { printf("--------------------IP----------------------------\n"); printf("INTERRUPT PACKET -- LENGTH: %d %d\n",head->len,sizeof(vhci_request_t)); } r = read_exact(vfd, &req, sizeof(vhci_request_t), NULL); if (r == 0) { int fdir; uint32_t length; if (is_debug(1,DEBUG_INTERRUPT)) decode_vhci_request(&req, head->ptype); length = req.u.req.length; packet_to_dev.command.command = htonl(USBIP_CMD_SUBMIT); packet_to_dev.command.seqnum = htonl(next_sequence); packet_to_dev.command.devid = htonl(devid); fdir = form_direction_req(req.u.req.flags); packet_to_dev.command.direction = htonl(fdir); packet_to_dev.command.ep = htonl(endpoint); packet_to_dev.transfer_flags = htonl(form_transfer_flags_req(req.u.req.flags)); packet_to_dev.transfer_buffer_length = htonl(length); packet_to_dev.start_frame = 0xffffffff; packet_to_dev.number_of_packets = 0; packet_to_dev.interval = htonl(req.u.req.interval); memset(packet_to_dev.setup,0,USBIP_SETUP_SIZE); r = record_packet(endpoint, next_sequence, fdir, length, true); if (r == -1) { printf("INTERRUPT WRITE COULD NOT RECORD PACKET: %d\n",next_sequence); } else { if (is_debug(1,DEBUG_INTERRUPT)) printf("INTERRUPT RECORDED: %d %d %d\n",r,packets_sent[r].ep,packets_sent[r].seq); } r = write(network_socket, &packet_to_dev, sizeof(struct usbip_cmd_submit)); if (r < 0) { printf("INTERRUPT WRITE R: %d: %d\n",endpoint,r); } else { if (is_debug(1,DEBUG_INTERRUPT)) printf("INTERRUPT WRITE WROTE %d\n",r); endpoints_busy[endpoint] = true; } } else { printf("INTERRUPT VHCI ERROR READ R: %d: %d\n",endpoint,r); } return; } /* Handle a BULK request. May be incoming or outgoing */ void handle_bulk_packet(int vfd, int endpoint, int network_socket, uint32_t devid, struct vhci_ioc_get_head *head, uint8_t *dataptr) { int r; vhci_request_t *reqptr; struct usbip_cmd_submit packet_to_dev; int ofd; char ofd_name[20]; uint8_t *data = dataptr; size_t total_len; int pindex; if (head->flags & VHCI_PACKET_IS_REQ) { bool utoh = (head->flags & VHCI_PACKET_REQ_UTOH); if (utoh) { /* DEV TO HOST */ if (head->flags & VHCI_PACKET_REQ_WITH_DATA) { total_len = sizeof(vhci_request_t) + head->len; } else { total_len = sizeof(vhci_request_t); } if (is_debug(1,DEBUG_BULK)) { printf("--------------------BP----------------------------\n"); printf("BULK PACKET UTOH -- LENGTH: %d ; TOTAL_LEN: %ld ; NPKTS: %d\n",head->len,total_len,head->npkts); } if (head->len > DATA_BUF_SIZE) { printf("BULK PACKET UTOH -- LENGTH BIGGER THAN ALLOWED TRANSPORT (%d): %ld\n",DATA_BUF_SIZE,head->len); } r = read_exact(vfd, dataptr, total_len, NULL); if (r == 0) { int fdir; uint32_t length; reqptr = (vhci_request_t *)dataptr; if (is_debug(1,DEBUG_BULK)) { printf("BULK PACKET UTOH FROM VHCI DEVICE:\n"); decode_vhci_request(reqptr, head->ptype); printf("--\n"); } length = (*reqptr).u.req.length; packet_to_dev.command.command = htonl(USBIP_CMD_SUBMIT); packet_to_dev.command.seqnum = htonl(next_sequence); packet_to_dev.command.devid = htonl(devid); fdir = form_direction_req((*reqptr).u.req.flags); packet_to_dev.command.direction = htonl(fdir); packet_to_dev.command.ep = htonl(endpoint); packet_to_dev.transfer_flags = htonl(form_transfer_flags_req((*reqptr).u.req.flags)); packet_to_dev.transfer_buffer_length = htonl(length); packet_to_dev.start_frame = 0xffffffff; packet_to_dev.number_of_packets = 0; packet_to_dev.interval = htonl((*reqptr).u.req.interval); memset(packet_to_dev.setup,0,USBIP_SETUP_SIZE); if (is_debug(3,DEBUG_BULK)) { printf("BULK PACKET UTOH TO NETWORK:\n"); decode_submit_cmd(&packet_to_dev); printf("--\n"); } r = record_packet_with_data(endpoint, next_sequence, fdir, length, true, (uint8_t *)&packet_to_dev, sizeof(struct usbip_cmd_submit)); if (r == -1) { printf("BULK PACKET UTOH WRITE COULD NOT RECORD PACKET: %d\n",next_sequence); } else { if (is_debug(1,DEBUG_BULK)) printf("BULK PACKET UTOH RECORDED: %d %d %d\n",r,packets_sent[r].ep,packets_sent[r].seq); } pindex = r; r = write(network_socket, &packet_to_dev, sizeof(struct usbip_cmd_submit)); if (r < 0) { printf("BULK PACKET UTOH WRITE R: %d: %d\n",endpoint,r); } else { if (is_debug(1,DEBUG_BULK)) printf("BULK PACKET UTOH WRITE WROTE %d\n",r); endpoints_busy[endpoint] = true; } if (head->flags & VHCI_PACKET_REQ_WITH_DATA) { uint32_t datalength = total_len - sizeof(vhci_request_t); data = dataptr + sizeof(vhci_request_t); if (is_debug(1,DEBUG_BULK)) printf("BULK UTOH DATA FRAGEMENT OF SIZE: %d\n",datalength); r = record_packet_data(pindex, data, datalength); if (r == 0) { printf("BULK PACKET UTOH WRITE COULD NOT RECORD LITERAL PACKET DATA\n"); } r = write(network_socket, data, datalength); if (r < 0) { printf("BULK PACKET UTOH WRITE DATA ERROR R: %d: %d\n",endpoint,r); } else { if (is_debug(1,DEBUG_BULK)) printf("BULK PACKET UTOH WRITE WROTE DATA %d\n",r); endpoints_busy[endpoint] = true; } } } else { printf("BULK PACKET UTOH VHCI ERROR READ R: %d: %d\n",endpoint,r); } } else { /* HOST TO DEV */ total_len = head->len; if (is_debug(1,DEBUG_BULK)) { printf("--------------------BP----------------------------\n"); printf("BULK PACKET HTOU -- LENGTH: %d ; TOTAL_LEN: %ld ; NPKTS: %d\n",head->len,total_len,head->npkts); } if (head->len > DATA_BUF_SIZE) { printf("BULK PACKET HTOU -- LENGTH BIGGER THAN ALLOWED TRANSPORT (%d): %ld\n",DATA_BUF_SIZE,head->len); } r = read_exact(vfd, dataptr, total_len, NULL); if (r == 0) { int fdir; uint32_t length; uint32_t datalength = total_len - sizeof(vhci_request_t); reqptr = (vhci_request_t *)dataptr; data = dataptr + sizeof(vhci_request_t); if (is_debug(1,DEBUG_BULK)) { printf("BULK PACKET HTOU FROM VHCI DEVICE:\n"); decode_vhci_request(reqptr, head->ptype); printf("--\n"); if (is_debug(3,DEBUG_BULK)) { for(int ii=0;ii < sizeof(vhci_request_t);ii++) { printf("%02x ",dataptr[ii]); } printf("\n----\n"); } } length = (*reqptr).u.req.length; packet_to_dev.command.command = htonl(USBIP_CMD_SUBMIT); packet_to_dev.command.seqnum = htonl(next_sequence); packet_to_dev.command.devid = htonl(devid); fdir = form_direction_req((*reqptr).u.req.flags); packet_to_dev.command.direction = htonl(fdir); packet_to_dev.command.ep = htonl(endpoint); packet_to_dev.transfer_flags = htonl(form_transfer_flags_req((*reqptr).u.req.flags)); packet_to_dev.transfer_buffer_length = htonl(length); packet_to_dev.start_frame = 0xffffffff; packet_to_dev.number_of_packets = 0; packet_to_dev.interval = htonl((*reqptr).u.req.interval); memset(packet_to_dev.setup,0,USBIP_SETUP_SIZE); if (is_debug(3,DEBUG_BULK)) { printf("BULK PACKET HTOU TO NETWORK:\n"); decode_submit_cmd(&packet_to_dev); printf("--\n"); } r = record_packet_with_data(endpoint, next_sequence, fdir, length, true, (uint8_t *)&packet_to_dev, sizeof(struct usbip_cmd_submit)); if (r == -1) { printf("BULK PACKET HTOU WRITE COULD NOT RECORD PACKET: %d\n",next_sequence); } else { if (is_debug(1,DEBUG_BULK)) printf("BULK PACKET HTOU RECORDED: %d %d %d\n",r,packets_sent[r].ep,packets_sent[r].seq); } pindex = r; r = write(network_socket, &packet_to_dev, sizeof(struct usbip_cmd_submit)); if (r < 0) { printf("BULK PACKET HTOU WRITE R: %d: %d\n",endpoint,r); endpoints_busy[endpoint] = true; } else { if (is_debug(1,DEBUG_BULK)) printf("BULK PACKET HTOU WRITE WROTE %d\n",r); } if (datalength > 0) { if (is_debug(1,DEBUG_BULK)) printf("BULK PACKET HTOU DATA FRAGMENT: DATALENGTH: %d\n",datalength); r = record_packet_data(pindex, data, datalength); if (r == 0) { printf("BULK PACKET HTOU WRITE COULD NOT RECORD LITERAL PACKET DATA\n"); } r = write(network_socket, data, datalength); if (r < 0) { printf("BULK PACKET HTOU WRITE DATA ERROR R: %d: %d\n",endpoint,r); } else { if (is_debug(1,DEBUG_BULK)) printf("BULK PACKET HTOU WRITE WROTE DATA %d\n",r); endpoints_busy[endpoint] = true; } } if (head->flags & VHCI_PACKET_REQ_WITH_DATA) { if (is_debug(1,DEBUG_BULK)) printf("BULK HTOU SECOND DATA PACKET OF SIZE: %d\n",length); r = read_exact(vfd, dataptr, length, NULL); if (r == 0) { r = record_packet_data(pindex, dataptr, length); if (r == 0) { printf("BULK PACKET UTOH SECOND PACKET WRITE COULD NOT RECORD LITERAL PACKET DATA\n"); } r = write(network_socket, dataptr, length); if (r < 0) { printf("BULK PACKET UTOH SECOND PACKET WRITE DATA ERROR R: %d: %d\n",endpoint,r); } else { if (is_debug(1,DEBUG_BULK)) printf("BULK PACKET UTOH SECOND PACKET WRITE WROTE DATA %d\n",r); endpoints_busy[endpoint] = true; } } else { printf("BULK PACKET UTOH SECOND PACKET VHCI ERROR READ R: %d: %d\n",endpoint,r); } } } else { printf("BULK PACKET HTOU VHCI ERROR READ R: %d: %d\n",endpoint,r); } } } else { printf("UNEXPECTED. BULK PACKET THIS SHOULD HAVE BEEN A REQUEST PACKET: %02d\n",head->flags); } return; } /* Handle an ISOCHRONOUS. As a practical matter, only outgoing requests have * any hope of working. Note that this sends a GOOD completion response and * does not wait on the remote end. * * What was observed on the wire with a Linux client was two ISOCHRONOUS * requests where sent and then responses. The requests were also much smaller, * 5 or 6 subpackets at most. This code makes no attempt to do that, but the * Wireshark capture of the behavior here did not indicate any errors. * * XXX - this probably does not deal correctly with incoming ISOCHRONOUS requests. */ void handle_isochronous_packet(int vfd, int endpoint, int network_socket, uint32_t devid, struct vhci_ioc_get_head *head, uint8_t *data) { int r; vhci_request_t req; struct usbip_cmd_submit packet_to_dev; uint8_t *isoc_pointers = NULL; uint8_t *isoc_data = NULL; uint8_t *isoc_packet = NULL; if (is_debug(1,DEBUG_ISOCHRONOUS)) { printf("--------------------ISOCP----------------------------\n"); printf("ISOCHRONOUS PACKET -- LENGTH: %d %d\n",head->len,sizeof(vhci_request_t)); } r = read_exact(vfd, &req, sizeof(vhci_request_t),NULL); if (r == 0) { struct vhci_ioc_get_head isoc_head_pointers; struct vhci_ioc_get_head isoc_head_data; if (is_debug(1,DEBUG_ISOCHRONOUS)) decode_vhci_request(&req, head->ptype); if (vhci_gethead(vfd,VHCI_HEAD_HTOU,&isoc_head_pointers) == 0) { if (is_debug(2,DEBUG_ISOCHRONOUS)) printf("ISOC DATA HEAD1 POINTERS: %d %d %d\n",isoc_head_pointers.what,isoc_head_pointers.len,isoc_head_pointers.ptype); } isoc_pointers = malloc(isoc_head_pointers.len); if (isoc_pointers != NULL) { r = read_exact(vfd,isoc_pointers,isoc_head_pointers.len,NULL); if (r == 0) { #ifdef __NOT sprintf(ofd_name,"ISOC.POINTERS.%d",next_sequence); ofd=open(ofd_name,O_RDWR | O_CREAT,0777); write(ofd,data,plength); close(ofd); #endif if (vhci_gethead(vfd,VHCI_HEAD_HTOU,&isoc_head_data) == 0) { if (is_debug(2,DEBUG_ISOCHRONOUS)) printf("ISOC DATA HEAD2 DATA: %d %d %d\n",isoc_head_data.what,isoc_head_data.len,isoc_head_data.ptype); isoc_data = malloc(isoc_head_data.len); if (isoc_data != NULL) { r = read_exact(vfd,data,isoc_head_data.len,NULL); #ifdef __NOT if (r == 0) { ofd=open(ofd_name,O_RDWR | O_CREAT,0777); write(ofd,data,hh.len); close(ofd); } else { printf("ISOC COULD NOT RECEIVE DATA: %d\n",r); } #endif if (r == 0) { int fdir; #ifdef __NOT sprintf(ofd_name,"ISOC.DATA.%d",next_sequence); ofd=open(ofd_name,O_RDWR | O_CREAT,0777); write(ofd,data,isoc_head_data.len); close(ofd); #endif packet_to_dev.command.command = htonl(USBIP_CMD_SUBMIT); packet_to_dev.command.seqnum = htonl(next_sequence); packet_to_dev.command.devid = htonl(devid); fdir = form_direction_req(req.u.reqisoc.flags); packet_to_dev.command.direction = htonl(fdir); packet_to_dev.command.ep = htonl(endpoint); packet_to_dev.transfer_flags = htonl(form_transfer_flags_req(req.u.reqisoc.flags)); packet_to_dev.transfer_buffer_length = htonl(isoc_head_data.len); packet_to_dev.start_frame = 0; packet_to_dev.number_of_packets = htonl(req.u.reqisoc.nframes); packet_to_dev.interval = htonl(req.u.reqisoc.interval); memset(packet_to_dev.setup,0,USBIP_SETUP_SIZE); isoc_packet = create_isoc_packet(req.u.reqisoc.nframes,(uint16_t *)isoc_pointers,isoc_head_data.len,isoc_data); if (isoc_packet != NULL) { r = record_packet(endpoint, next_sequence, fdir, isoc_head_data.len, true); if (r == -1) { printf("ISOC WRITE COULD NOT RECORD PACKET: %d\n",next_sequence); } else { if (is_debug(1,DEBUG_ISOCHRONOUS)) printf("ISOC RECORDED: %d %d %d\n",r,packets_sent[r].ep,packets_sent[r].seq); } r = write(network_socket, &packet_to_dev, sizeof(struct usbip_cmd_submit)); if (r > 0) { if (is_debug(1,DEBUG_ISOCHRONOUS)) printf("ISOC WROTE PACKET HEADER: %d\n",r); r = write(network_socket, isoc_packet, (req.u.reqisoc.nframes * sizeof(struct usbip_iso_packet_descriptor)) + isoc_head_data.len); #ifdef __NOT sprintf(ofd_name,"ISOC.PACKET.%d",next_sequence); ofd=open(ofd_name,O_RDWR | O_CREAT,0777); write(ofd,isoc_packet,(req.u.reqisoc.nframes * sizeof(uint16_t)) + isoc_head_data.len); close(ofd); #endif if (r < 0) { printf("ISOC WRITE ERROR: %d: %d\n",endpoint,r); } else { if (is_debug(1,DEBUG_ISOCHRONOUS)) printf("ISOC WRITE WROTE: %d\n",r); } } else { printf("ISOC COULD NOT WRITE REQUEST PACKET HEADER: %d\n",r); } } else { printf("COULD NOT CREATE ISOC PACKET\n"); } } else { printf("ISOC COULD NOT RECEIVE DATA %d\n",r); } } else { printf("COULD NOT MALLOC SPACE FOR ISOC DATA\n"); isoc_data = NULL; } } next_sequence++; } else { printf("ISOC COULD NOT RECEIVE POINTER LENGTHS: %d\n",r); } } else { printf("COULD NOT MALLOC SPACE FOR ISOC POINTERS\n"); isoc_pointers = NULL; } if (isoc_pointers != NULL) { free(isoc_pointers); isoc_pointers = NULL; } if (isoc_data != NULL) { free(isoc_data); isoc_data = NULL; } if (isoc_packet != NULL) { free(isoc_packet); isoc_packet = NULL; } /* Complete everything and call it GOOD */ vhci_response_t response_size; response_size.error = USBD_NORMAL_COMPLETION; response_size.size = 0; r = write_exact(vfd, &response_size, sizeof(vhci_response_t),NULL); if (r != 0) { if (is_debug(1,DEBUG_ISOCHRONOUS)) printf("NULL OUT ISOC PACKET: %d\n",r); } } else { printf("ISOCHRONOUS VHCI ERROR READ R: %d: %d\n",endpoint,r); } } /* In theory this should never be called at this point */ void handle_unknown_packet(int vfd, int endpoint, int network_socket, uint32_t devid, struct vhci_ioc_get_head *head, uint8_t *data, int *unknown_packet) { int r; vhci_request_t req; struct usbip_cmd_submit packet_to_dev; int ofd; char ofd_name[20]; sprintf(ofd_name,"UNKNOWN.%d.%d",unknown_packet,endpoint); r = read_exact(vfd, &req, sizeof(vhci_request_t),NULL); printf("--------------------UNKNP----------------------------\n"); printf("GOT UNKNON OUTGOING PACKET ON EP: %d ; LEN: %d %d; UNKNOWN_PACKET: %d\n",endpoint,head->len, sizeof(vhci_request_t), unknown_packet); ofd=open(ofd_name,O_RDWR | O_CREAT, 0777); write(ofd,&req,sizeof(vhci_request_t)); close(ofd); *unknown_packet++; if (head->len == sizeof(vhci_request_t)) { decode_vhci_request(&req, head->ptype); } else { for(int ii=0; ii < sizeof(vhci_request_t);ii++) { printf("%02x ",data[ii]); } printf("\n"); } return; } /* Don't use this.. it was an attempt at handling stalls here as opposed to the kernel. * The kernel does a better job at it.... */ #ifdef HANDLE_STALL int handle_stall(int network_socket, int packet_record_index, uint32_t devid) { int rr = USBD_STALLED; int r; struct usbip_cmd_submit packet_to_dev; usb_device_request_t sreq; struct pollfd dev_to_packet_poll[1]; struct usbip_ret_submit dev_to_packet; int endpoint; if (packet_record_index == -1) { printf("HANDLE STALL. INVALID PACKET_RECORD_INDEX\n"); return rr; } endpoint = packets_sent[packet_record_index].ep; printf("HANDLING STALL.. SOCKET: %d FOR PACKET_RECORD_INDEX: %d ; ENDPOINT: %d\n",network_socket, packet_record_index, endpoint); sreq.bmRequestType = UT_ENDPOINT; sreq.bRequest = UR_CLEAR_FEATURE; USETW(sreq.wValue,UF_ENDPOINT_HALT); USETW(sreq.wIndex,endpoint | UT_READ); USETW(sreq.wLength,0); packet_to_dev.command.command = htonl(USBIP_CMD_SUBMIT); packet_to_dev.command.seqnum = htonl(next_sequence); packet_to_dev.command.devid = htonl(devid); packet_to_dev.command.direction = htonl(USBIP_DIR_OUT); packet_to_dev.command.ep = 0; packet_to_dev.transfer_flags = 0; packet_to_dev.transfer_buffer_length = 0; packet_to_dev.start_frame = 0xffffffff; packet_to_dev.number_of_packets = 0; packet_to_dev.interval = 0; memcpy(packet_to_dev.setup,&sreq,USBIP_SETUP_SIZE); r = write(network_socket, &packet_to_dev, sizeof(struct usbip_cmd_submit)); if (r < 0) { printf("HANDLING STALL. COULD NOT SEND CLEAR PACKET: %d\n",r); } else { next_sequence++; printf("HANDING A STALL. NEW SEQUENCE: %d\n",next_sequence); dev_to_packet_poll[0].fd = network_socket; dev_to_packet_poll[0].events = POLLIN; dev_to_packet_poll[0].revents = 0; r = poll(dev_to_packet_poll,1,dev_to_packet_to); if (r == 1) { r = read_exact(network_socket,&dev_to_packet,sizeof(struct usbip_ret_submit),NULL); if (r == 0) { if (dev_to_packet.status == 0) { struct usbip_cmd_submit *sub; if (packets_sent[packet_record_index].packets[0] != NULL) { bool had_error = false; sub = (struct usbip_cmd_submit *)packets_sent[packet_record_index].packets[0]; (*sub).command.seqnum = htonl(next_sequence); packets_sent[packet_record_index].seq = next_sequence; printf("HANDLING STALL. NEW PACKET:\n"); decode_submit_cmd(sub); next_sequence++; for(int ii=0;ii < NUM_PACKETS_RECORD;ii++) { if (packets_sent[packet_record_index].packets[ii] != NULL) { r = write(network_socket, packets_sent[packet_record_index].packets[ii], packets_sent[packet_record_index].packet_sizes[ii]); if (r > 0) { printf("STALL RESENT PACKET: %d %d %d\n",packet_record_index, packets_sent[packet_record_index].ep, r); } else { printf("STALL RESENT ERROR SENDING PACKET: %d %d %d\n",packet_record_index, packets_sent[packet_record_index].ep, r); had_error = true; break; } } } if (! had_error) { rr = USBD_NORMAL_COMPLETION; } else { printf("HANDLING STALL. HAD AN OVERALL ERROR RESENDING PACKETS\n"); } } else { printf("HANDLING STALL. PACKET RECORD DID NOT RECORD ANY PACKETS TO RESEND AT INDEX 0\n"); } } else { printf("HANDLING STALL. CLEAR RETURNED ERROR STATUS: %d\n",dev_to_packet.status); } } else { printf("HANDLING STALL. READ BACK OF CLEAR RETURNED ERROR: %d\n",r); } } else { printf("HANDLING STALL. POLL RETURNED ERROR OR ZERO: %d\n",r); } } return rr; } #endif /* Handle network traffic back from the device. * * The USBIP protocol has fields in the USBIP response for a lot of things * that would be useful, but does not fill them it. In particular, the end * point... this means that one has to track outgoing sequence numbers to * end points, which is something that would have been trival to not have * to do if the USBIP response contained the end point in it. * * XXX - This does not deal with ISOCHRONOUS packets completely. Mostly it just * detects that the response was for one of those and eats it. */ void handle_network_traffic(int vfd, int network_socket, uint32_t devid, struct vhci_ioc_get_head *head, uint8_t *data) { int r; struct usbip_ret_submit dev_to_packet; if (is_debug(1,DEBUG_NETWORK)) printf("--------------------N----------------------------\n"); r = read_exact(network_socket,&dev_to_packet,sizeof(struct usbip_ret_submit),NULL); if (r == 0) { if (is_debug(1,DEBUG_NETWORK)) printf("READ NETWORK\n"); if (is_debug(2,DEBUG_NETWORK)) decode_submit_ret(&dev_to_packet); uint32_t r_seqnum = ntohl(dev_to_packet.command.seqnum); int epi = find_packet_by_seq(r_seqnum); int ep; if (epi == -1) { printf("NETWORK COULD NOT FIND EP FOR R_SEQNUM. %d\n",r_seqnum); unrecord_packet(r_seqnum); return; } else { ep = packets_sent[epi].ep; } endpoints_busy[ep] = false; int32_t status = ntohl(dev_to_packet.status); if (status == 0) { if (r_seqnum > 0) { int dir; uint32_t r_actual_length = ntohl(dev_to_packet.actual_length); uint32_t r_nframes = ntohl(dev_to_packet.number_of_packets); dir = packets_sent[epi].dir; if (is_debug(1,DEBUG_NETWORK)) { printf("NETWORK DIRECTION: %d - EP: %d - SEQ: %d\n",dir,ep,r_seqnum); printf("NETWORK ACTUAL_LENGTH: %d\n",r_actual_length); } if (r_actual_length > 0 && dir == USBIP_DIR_IN) { r = read_exact(network_socket,data,r_actual_length,NULL); if (is_debug(2,DEBUG_NETWORK)) { for(int jj = 0; jj < r_actual_length;jj++) { printf("%02x ",data[jj]); } printf("\n"); } } else { r=0; } bool is_isoc = false; /* Ya, more undocumented USBIP... * * A response may indicate that this isn't a ISOCHRONOUS response by * setting the nframe to 0 or -1 (effectively). The online docs only * mention -1, but packet dumps also indicate that 0 is possible. */ if (r_nframes > 0 && r_nframes != 0xffffffff) { uint8_t *r_isoc_descriptors; uint32_t desc_len; desc_len = r_nframes * sizeof(struct usbip_iso_packet_descriptor); r_isoc_descriptors = malloc(desc_len); r = read_exact(network_socket,r_isoc_descriptors,desc_len,NULL); if (r_isoc_descriptors != NULL) { free(r_isoc_descriptors); } is_isoc = true; unrecord_packet(r_seqnum); r = -1; } if (r == 0) { if (is_debug(1,DEBUG_NETWORK)) printf("NETWORK EP FOR R_SEQNUM: %d %d\n",ep,r_seqnum); r = vhci_setaddr(vfd,ep); if (is_debug(2,DEBUG_NETWORK)) printf("NETWORK VHCI SET ADDR: R: %d - VFD: %d - EP: %d\n",r,vfd,ep); if (ep > -1) { vhci_response_t response_size; int err=0; response_size.error = USBD_NORMAL_COMPLETION; response_size.size = r_actual_length; r = write_exact(vfd, &response_size, sizeof(vhci_response_t),&err); if (r == 0) { if (r_actual_length > 0 && dir == USBIP_DIR_IN) { r = write_exact(vfd, data, r_actual_length, NULL); r = unrecord_packet(r_seqnum); if (is_debug(2,DEBUG_NETWORK)) printf("READ NETWORK DATA UNRECORD PACKET: SEQ: %d %d\n",r_seqnum,r); } else { r = unrecord_packet(r_seqnum); if (is_debug(2,DEBUG_NETWORK)) printf("READ NETWORK UNRECORD PACKET: SEQ: %d %d\n",r_seqnum,r); } } else { /* This probably means that VHCI got something it didn't expect... * and something is likely out of sync.... * * Maybe... should try sending a error response with a zero length... or * ask the queue if there is a response expected... */ printf("READ NETWORK COULD NOT SEND SIZE: VFD: %d - R: %d - ACTUAL_SIZE: %d - ERR: %d\n",vfd,r,r_actual_length,err); r = unrecord_packet(r_seqnum); /* XXX - this probably should not be a message.... */ printf("READ NETWORK COULD NOT SEND SIZE UNRECORD PACKET: SEQ: %d %d\n",r_seqnum,r); } } else { /* This probably means that the packet could not be recorded due to * the packet record being too small. */ printf("READ NETWORK COULD NOT FIND EP RECORDED: SEQ: %d\n",r_seqnum); } } else { /* XXX - this might just mean that the device was done.. that is * the end using code closed, but there was still some traffic to * handle. This situation isn't dealt with completely. */ if (! is_isoc) printf("READ NETWORK DATA ERROR: %d\n",r); } } else { printf("NETWORK SEQNUM ODD: %d\n",r_seqnum); } } else { int pi, dir; if (status != USBIP_ERROR_EPIPE || is_debug(1,DEBUG_NETWORK)) printf("READ NETWORK USBIP ERROR: %d %d\n",r_seqnum,status); vhci_response_t rs; if (r_seqnum > 0) { pi = find_packet_by_seq(r_seqnum); if (pi != -1) { uint32_t length = packets_sent[pi].reqsize; dir = packets_sent[pi].dir; int ep = packets_sent[pi].ep; if (status != USBIP_ERROR_EPIPE || is_debug(2,DEBUG_NETWORK)) printf("READ NETWORK USBIP ERROR SENDING 0 LENGTH RESPONSE: %d %d %d -- STATUS: %d\n",ep,length,dir,status); rs.size = length; /* Note that these error status is really a Linux errno. * * It is likely that only a small number of non-GOOD responses * have been observed. The one that is currently known to be * non-fatal is EPIPE which indicates a remote USB STALL. This is * a normal thing and is handled well by the kernel code. Just translate * that to USBD_STALLED. Mostly just make everything else an I/O error. */ switch(status) { case USBIP_ERROR_EPIPE: #ifdef HANDLE_STALL rs.error = handle_stall(network_socket, pi, devid); #else rs.error = USBD_STALLED; #endif break; case USBIP_ERROR_ETIME: default: rs.error = USBD_IOERROR; } if (status != USBIP_ERROR_EPIPE || is_debug(1,DEBUG_NETWORK)) printf("READ NETWORK USBIP ERROR SENDING %d %d\n",rs.size,rs.error); #ifdef HANDLE_STALL if (rs.error != USBD_NORMAL_COMPLETION) { #endif vhci_setaddr(vfd,ep); r = write_exact(vfd, &rs, sizeof(vhci_response_t),NULL); if (r == 0) { if (length > 0 && dir == USBIP_DIR_IN) { memset(data,0,length); r = write_exact(vfd, data, length, NULL); } } else { printf("READ NETWORK USBIP ERROR SENDING 0 COULD NOT SEND SIZE: %d %d\n",r,r_seqnum); } #ifdef HANDLE_STALL } #endif } else { printf("READ NETWORK USBIP ERROR COULD NOT FIND: %d\n",r_seqnum); } } #ifdef HANDLE_STALL if (rs.error != USBD_NORMAL_COMPLETION) { #endif r = unrecord_packet(r_seqnum); if (status != USBIP_ERROR_EPIPE || is_debug(2,DEBUG_NETWORK)) printf("READ NETWORK USBIP ERROR UNRECORD PACKET: SEQ: %d %d\n",r_seqnum,r); #ifdef HANDLE_STALL } #endif } } else { /* XXX - this probably means that the remote USBIP server closed the connection. * Also note this isn't handled.... */ if (r < 0) { printf("READ NETWORK ERROR OR ZERO: %d\n",r); } } } /* Print some interesting stuff when CTRL-T is hit */ static void handle_siginfo() { printf("______________COUNT_PACKETS: %d\n",count_packets()); printf("______________ENDPOINTS BUSY: "); for(int i=0; i < VHCI_NADDRS;i++) printf("%d ",endpoints_busy[i]); printf("\n"); printf("______________ERROR_COUNT: %d\n",error_count); } /* Alter the poll mode... */ #define PLM_IGNORE_EP_BUSY 0x01 #define PLM_POLL_FOR_NET_ONLY 0x02 #define PLM_STRICT_SEQUENCE 0x04 int main(int argc, char *argv[]) { char *host; int vport; char port[5] = "3240"; struct addrinfo *res, *res0; int save_errno, error; int s; int slow_poll_timeout; int fast_poll_timeout; uint8_t poll_loop_mode; clear_packet_record(); for(int i=0;i < VHCI_NADDRS;i++) endpoints_busy[i] = false; signal(SIGINFO,handle_siginfo); if (argc == 8) { int error; poll_loop_mode = (uint8_t)strtoi(argv[7], NULL, 0, 0, 255, &error); if (error) { warnc(error, "Conversion of `%s' to a poll_loop_mode " "failed, using %04x", argv[7], poll_loop_mode); } debug_level = (uint16_t)strtoi(argv[4], NULL, 0, 0, 0xffff, &error); if (error) { warnc(error, "Conversion of `%s' to a debug_level " "failed, using %04x", argv[4], debug_level); } slow_poll_timeout = (int)strtoi(argv[5], NULL, 0, -1, 10000, &error); if (error) { warnc(error, "Conversion of `%s' to a slow_poll_timeout " "failed, using %04x", argv[4], slow_poll_timeout); } fast_poll_timeout = (int)strtoi(argv[5], NULL, 0, -1, 10000, &error); if (error) { warnc(error, "Conversion of `%s' to a fast_poll_timeout " "failed, using %04x", argv[6], fast_poll_timeout); } vport = atoi(argv[3]); int vfd; vfd = vhci_open(); if (vfd > 0) { if (is_debug(1,DEBUG_ANY)) printf("VHCI FD: %d\n",vfd); int r = 0; r = r + vhci_set_result_errors(vfd,true); r = r + vhci_setport(vfd,vport); r = r + vhci_setaddr(vfd,0); if (r > 0) { printf("Error setting up vfd: R: %d\n",r); exit(2); } } else { printf("Could not open vfd\n"); exit(1); } if (is_debug(3,DEBUG_ANY)) printf("OPENED VHCI DEVICE AND DID SETUP\n"); host = argv[1]; if ((error = getaddrinfo(host, port, NULL, &res0))) errx(1, "getaddrinfo: %s", gai_strerror(error)); for (res = res0; res; res = res->ai_next) { if (s = socket(PF_INET, SOCK_STREAM, 0) < 0) continue; if (connect(s, res->ai_addr, res->ai_addrlen) == 0) break; save_errno = errno; close(s); errno = save_errno; s = -1; } if (s > -1) { if (is_debug(1,DEBUG_ANY)) printf("SOCKET FD: %d\n",s); char *busid; busid = argv[2]; int r; struct pollfd import_poll[1]; struct usbip_op_req_import import_req; struct usbip_op_rep_import import_rep; import_req.version = htons(USBIP_VERSION); import_req.command = htons(USBIP_OP_REQ_IMPORT); import_req.status = 0; memset(import_req.busid,0,USBIP_SIZE_BUSID); strncpy(import_req.busid,busid,USBIP_SIZE_BUSID); if (is_debug(3,DEBUG_ANY)) { printf("WRITING IMPORT REQUEST\n"); decode_import_req(&import_req); } r = write(s,&import_req,sizeof(struct usbip_op_req_import)); import_poll[0].fd = s; import_poll[0].events = POLLIN; import_poll[0].revents = 0; if (is_debug(3,DEBUG_ANY)) printf("WAITING FOR IMPORT REPLY\n"); r = poll(import_poll,1,-1); if (r <= 0) { printf("IMPORT REQ POLL ERROR: %d\n",r); } r = read_exact(s,&import_rep,sizeof(struct usbip_op_rep_import),NULL); if (r == 0) { uint32_t import_rep_status; import_rep_status = ntohl(import_rep.status); if (is_debug(1,DEBUG_ANY)) { printf("VERSION: %04x\n",ntohs(import_rep.version)); printf("REPLYCODE: %04x\n",ntohs(import_rep.replycode)); printf("STATUS: %08x\n",import_rep_status); } if (import_rep_status == 0) { struct vhci_ioc_get_head h; uint8_t data[DATA_BUF_SIZE]; uint32_t devid = (ntohl(import_rep.device.busnum) << 16) | ntohl(import_rep.device.devnum); int unknown_packet = 1; if (is_debug(1,DEBUG_ANY)) { printf("\tPATH: %s\n",import_rep.device.path); printf("\tBUSID: %s\n",import_rep.device.busid); printf("\tBUSNUM: %08x\n",ntohl(import_rep.device.busnum)); printf("\tDEVNUM: %08x\n",ntohl(import_rep.device.devnum)); printf("\tSPEED: %08x\n",ntohl(import_rep.device.speed)); printf("\tIDVENDOR: %04x\n",ntohs(import_rep.device.idVendor)); printf("\tPRODUCT: %04x\n",ntohs(import_rep.device.idProduct)); printf("\tBCDDEVICE: %04x\n",ntohs(import_rep.device.bcdDevice)); printf("\tBDEVICECLASS: %02x\n",import_rep.device.bDeviceClass); printf("\tBDEVICESUBCLASS: %02x\n",import_rep.device.bDeviceSubClass); printf("\tBDEVICEPROTOCOL: %02x\n",import_rep.device.bDeviceProtocol); printf("\tBCONFIGURATIONVALUE: %02x\n",import_rep.device.bConfigurationValue); printf("\tBNUMCONFIGURATIONS: %02x\n",import_rep.device.bNumConfigurations); printf("\tBNUMINTERFACES: %02x\n\n",import_rep.device.bNumInterfaces); printf("\nDEVID: %08x\n",devid); } vhci_usb_attach(vfd); struct pollfd loop_poll[2]; int poll_num; int poll_to; last_actions last_action; last_action = LAST_ACTION_START; int poll_slot = 0; int poll_endpoint = 0; int poll_count = 1; bool last_poll_endpoint_state; while(1) { if (is_debug(2,DEBUG_ANY)) printf("--------------------POLL--------------------------\n"); switch(last_action) { case LAST_ACTION_START: loop_poll[0].fd = vfd; loop_poll[0].events = POLLIN; loop_poll[0].revents = 0; loop_poll[1].fd = -1; loop_poll[1].events = -1; loop_poll[1].revents = 0; poll_to = slow_poll_timeout; poll_num = 1; break; case LAST_ACTION_NETWORK: loop_poll[0].fd = s; loop_poll[0].events = POLLIN; loop_poll[0].revents = 0; loop_poll[1].fd = vfd; loop_poll[1].events = POLLIN; loop_poll[1].revents = 0; poll_to = slow_poll_timeout; poll_num = 2; /* loop_poll[0].fd = vfd; loop_poll[0].events = POLLIN; loop_poll[0].revents = 0; loop_poll[1].fd = -1; loop_poll[1].events = -1; loop_poll[1].revents = 0; poll_to = slow_poll_timeout; poll_num = 1; */ break; case LAST_ACTION_PACKET: loop_poll[0].fd = s; loop_poll[0].events = POLLIN; loop_poll[0].revents = 0; /* If the past action was to send a USBIP packet, it is possible * to alter the poll loop so that we strictly listen only for * network respones. This should really not be needed */ if (poll_loop_mode & PLM_POLL_FOR_NET_ONLY) { loop_poll[1].fd = -1; loop_poll[1].events = -1; loop_poll[1].revents = 0; poll_to = slow_poll_timeout; poll_num = 1; } else { loop_poll[1].fd = vfd; loop_poll[1].events = POLLIN; loop_poll[1].revents = 0; poll_to = slow_poll_timeout; poll_num = 2; } break; /* note that this polls very fast, and once it is stuck here it * won't come out until another request type occures */ case LAST_ACTION_ISOCPACKET: loop_poll[0].fd = s; loop_poll[0].events = POLLIN; loop_poll[0].revents = 0; loop_poll[1].fd = vfd; loop_poll[1].events = POLLIN; loop_poll[1].revents = 0; poll_to = fast_poll_timeout; poll_num = 2; break; } if (is_debug(3,DEBUG_ANY)) { printf("last_action: %d\n",last_action); printf("loop_poll[0].fd: %d\n",loop_poll[0].fd); printf("loop_poll[0].events: %d\n",loop_poll[0].events); printf("loop_poll[0].revents: %d\n",loop_poll[0].revents); printf("loop_poll[1].fd: %d\n",loop_poll[1].fd); printf("loop_poll[1].events: %d\n",loop_poll[1].events); printf("loop_poll[1].revents: %d\n",loop_poll[1].revents); printf("poll_to: %d\n",poll_to); printf("poll_num: %d\n",poll_num); } r = poll(loop_poll,poll_num,poll_to); if (loop_poll[0].revents & POLLHUP) { printf("LOOP_POLL[0] POLLHUP: FD: %d\n",loop_poll[0].fd); } if (loop_poll[1].revents & POLLHUP) { printf("LOOP_POLL[1] POLLHUP: FD: %d\n",loop_poll[1].fd); } if (is_debug(3,DEBUG_ANY)) { printf("POLL R: %d\n",r); printf("AFTER loop_poll[0].revents: %d\n",loop_poll[0].revents); printf("AFTER loop_poll[1].revents: %d\n",loop_poll[1].revents); } if (r > 0) { /* There may be two poll slots, one for the VHCI fd and one * for the network FD. */ for(int poll_slot = 0; poll_slot < 2; poll_slot++) { if (loop_poll[poll_slot].fd == -1) break; /* Prefer to look at the network first, as there may be a response * coming back and the kernel is waiting. */ if ((loop_poll[poll_slot].fd == s) && (loop_poll[poll_slot].revents & POLLIN)) { handle_network_traffic(vfd,s,devid,&h,data); if (last_action != LAST_ACTION_ISOCPACKET) { last_action = LAST_ACTION_NETWORK; break; } } if ((loop_poll[poll_slot].fd == vfd) && (loop_poll[poll_slot].revents & POLLIN)) { poll_count=1; /* Look for a max of the number of possible end points. * However, it is possible to exit early. The code will * then pick up where it left off in the next loop. * Usually it is ok to look at all of the endpoints in a * single poll cycle. */ while (poll_count <= VHCI_NADDRS) { if (is_debug(3,DEBUG_ANY)) printf("CHECKING ENDPOINT COUNT 000000000000: %d -- ENDPOINT: %d -- ENDPOINT_BUSY: %d\n",poll_count, poll_endpoint,endpoints_busy[poll_endpoint]); last_poll_endpoint_state = endpoints_busy[poll_endpoint]; /* It is possible to ignore the busy indicator for an end point. That is, * usually one should not send a request to an end point when a response * is still pending. */ if ((! endpoints_busy[poll_endpoint]) || (poll_loop_mode & PLM_IGNORE_EP_BUSY)) { if (is_debug(3,DEBUG_ANY)) printf("CHECKING ENDPOINT COUNT: %d -- ENDPOINT: %d\n",poll_count, poll_endpoint); vhci_setaddr(vfd,poll_endpoint); if (vhci_gethead(vfd,VHCI_HEAD_HTOU,&h) == 0) { if (h.len > 0 && (h.flags & VHCI_PACKET_IS_REQ)) { if (is_debug(3,DEBUG_ANY)) decode_head(&h); if (h.flags & VHCI_PACKET_IS_REQ) { bool rr; int ofd; char ofd_name[20]; switch (h.ptype) { case VHCI_REQ_CTRL: /* CONTROL requests really should only be on * end point 0. Mention something if that * is not true. */ if (poll_endpoint != 0) { printf("UNEXPECTED.... CONTROL PACKET FOR NON-CONTROL ENDPOINT: %d",poll_endpoint); } rr = handle_control_packet(vfd,poll_endpoint,s,devid,&h,data); /* It is possible for a CONTROL request to be * skipped... if so, a PACKET wasn't sent and * just move along. */ if (rr) last_action = LAST_ACTION_PACKET; break; case VHCI_REQ_INTERRUPT: handle_interrupt_packet(vfd,poll_endpoint,s,devid,&h,data); last_action = LAST_ACTION_PACKET; break; case VHCI_REQ_BULK: handle_bulk_packet(vfd,poll_endpoint,s,devid,&h,data); last_action = LAST_ACTION_PACKET; break; case VHCI_REQ_ISOCHRONOUS: handle_isochronous_packet(vfd,poll_endpoint,s,devid,&h,data); last_action = LAST_ACTION_ISOCPACKET; break; default: handle_unknown_packet(vfd,poll_endpoint,s,devid,&h,data,&unknown_packet); last_action = LAST_ACTION_PACKET; break; } } else { /* We better be handling requests, otherwise something * has gotten out of sync. */ printf("UNEXPECTED. PACKET WAS NOT A REQUEST. FLAGS: %04x",h.flags); } } } } else { if (is_debug(3,DEBUG_ANY)) printf("ENDPOINT IS MARKED AS BUSY: %d\n",poll_endpoint); } /* There are a couple of things going on here... * 1) Update the poll_count and poll_endpoint, wrapping the end point * if we hit the max. * * 2) If it has been indicated that strict sequencing is desired, exit here * if any packets are handled. The loop will then give a chance for a response * from the network. This should not be needed. */ int previous_poll_endpoint = poll_endpoint; poll_count++; poll_endpoint++; if (poll_endpoint >= VHCI_NADDRS) poll_endpoint = 0; if (poll_loop_mode & PLM_STRICT_SEQUENCE) { if (last_poll_endpoint_state != endpoints_busy[previous_poll_endpoint]) { if (is_debug(3,DEBUG_ANY)) printf("LAST_POLL_ENDPOINT_STATE: %d ; ENDPOINTS_BUSY: %d ; PREVIOUS_POLL_ENDPOINT: %d ; POLL_ENDPOINT: %d\n",last_poll_endpoint_state,endpoints_busy[previous_poll_endpoint],previous_poll_endpoint,poll_endpoint); break; } } } if (poll_count > VHCI_NADDRS) poll_count = 1; } } } } } else { printf("BAD IMPORT STATUS: %d\n",import_rep_status); exit(1); } } else { printf("BAD IMPORT POLL STATUS: %d %d\n",r,errno); exit(1); } } else { errx(1, "getaddrinfo / socket: %d", errno); } } else { printf("t_usbip9 IP BUSID PORT DEBUG_LEVEL SLOW_POLL FAST_POLL POLL_MODE\n"); printf("DEBUG_LEVEL - hex, usually...\n"); printf("\t0x01nn - Control packet handling\n"); printf("\t0x02nn - Interrupt packet handling\n"); printf("\t0x04nn - Bulk packet handling\n"); printf("\t0x08nn - Isochronous packet handling\n"); printf("\t0x10nn - Network packet handling\n"); printf("\tnn - debug level\n"); printf("SLOW_POLL - -1 is forever\n"); printf("FAST_POLL - usually 0\n"); printf("POLL_MODE - bit map.. default of 0 would be to use the busy flag, poll network and packet, and check all endpoints\n"); printf("\t0x01 - Ignore busy endpoint flag\n"); printf("\t0x02 - after doing a packet poll only for the network and not network and packet\n"); printf("\t0x04 - strict sequence.. if a packet is single endpoint is handled, handle the network next\n"); exit(1); } }