/* ctrl complete function for devices which use usb_generic_handle_packet and may return USB_RET_ASYNC from their handle_control callback. Device code which does this *must* call this function instead of the normal usb_packet_complete to complete their async control packets. */ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) { if (p->result < 0) { s->setup_state = SETUP_STATE_IDLE; } switch (s->setup_state) { case SETUP_STATE_SETUP: if (p->result < s->setup_len) { s->setup_len = p->result; } s->setup_state = SETUP_STATE_DATA; p->result = 8; break; case SETUP_STATE_ACK: s->setup_state = SETUP_STATE_IDLE; p->result = 0; break; case SETUP_STATE_PARAM: if (p->result < s->setup_len) { s->setup_len = p->result; } if (p->pid == USB_TOKEN_IN) { p->result = 0; usb_packet_copy(p, s->data_buf, s->setup_len); } break; default: break; } usb_packet_complete(s, p); }
static int do_token_out(USBDevice *s, USBPacket *p) { assert(p->ep->nr == 0); switch(s->setup_state) { case SETUP_STATE_ACK: if (s->setup_buf[0] & USB_DIR_IN) { s->setup_state = SETUP_STATE_IDLE; /* transfer OK */ } else { /* ignore additional output */ } return 0; case SETUP_STATE_DATA: if (!(s->setup_buf[0] & USB_DIR_IN)) { int len = s->setup_len - s->setup_index; if (len > p->iov.size) { len = p->iov.size; } usb_packet_copy(p, s->data_buf + s->setup_index, len); s->setup_index += len; if (s->setup_index >= s->setup_len) s->setup_state = SETUP_STATE_ACK; return len; } s->setup_state = SETUP_STATE_IDLE; return USB_RET_STALL; default: return USB_RET_STALL; } }
static int do_parameter(USBDevice *s, USBPacket *p) { int request, value, index; int i, ret = 0; for (i = 0; i < 8; i++) { s->setup_buf[i] = p->parameter >> (i*8); } s->setup_state = SETUP_STATE_PARAM; s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; s->setup_index = 0; request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; index = (s->setup_buf[5] << 8) | s->setup_buf[4]; if (s->setup_len > sizeof(s->data_buf)) { fprintf(stderr, "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", s->setup_len, sizeof(s->data_buf)); return USB_RET_STALL; } if (p->pid == USB_TOKEN_OUT) { usb_packet_copy(p, s->data_buf, s->setup_len); } ret = usb_device_handle_control(s, p, request, value, index, s->setup_len, s->data_buf); if (ret < 0) { return ret; } if (ret < s->setup_len) { s->setup_len = ret; } if (p->pid == USB_TOKEN_IN) { usb_packet_copy(p, s->data_buf, s->setup_len); } return ret; }
static int streambuf_put(struct streambuf *buf, USBPacket *p) { uint32_t free = buf->size - (buf->prod - buf->cons); if (!free) { return 0; } assert(free >= USBAUDIO_PACKET_SIZE); usb_packet_copy(p, buf->data + (buf->prod % buf->size), USBAUDIO_PACKET_SIZE); buf->prod += USBAUDIO_PACKET_SIZE; return USBAUDIO_PACKET_SIZE; }
static int do_token_in(USBDevice *s, USBPacket *p) { int request, value, index; int ret = 0; if (p->devep != 0) return s->info->handle_data(s, p); request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; index = (s->setup_buf[5] << 8) | s->setup_buf[4]; switch(s->setup_state) { case SETUP_STATE_ACK: if (!(s->setup_buf[0] & USB_DIR_IN)) { ret = s->info->handle_control(s, p, request, value, index, s->setup_len, s->data_buf); if (ret == USB_RET_ASYNC) { return USB_RET_ASYNC; } s->setup_state = SETUP_STATE_IDLE; if (ret > 0) return 0; return ret; } /* return 0 byte */ return 0; case SETUP_STATE_DATA: if (s->setup_buf[0] & USB_DIR_IN) { int len = s->setup_len - s->setup_index; if (len > p->iov.size) { len = p->iov.size; } usb_packet_copy(p, s->data_buf + s->setup_index, len); s->setup_index += len; if (s->setup_index >= s->setup_len) s->setup_state = SETUP_STATE_ACK; return len; } s->setup_state = SETUP_STATE_IDLE; return USB_RET_STALL; default: return USB_RET_STALL; } }
static int do_token_setup(USBDevice *s, USBPacket *p) { int request, value, index; int ret = 0; if (p->iov.size != 8) { return USB_RET_STALL; } usb_packet_copy(p, s->setup_buf, p->iov.size); p->result = 0; s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; s->setup_index = 0; request = (s->setup_buf[0] << 8) | s->setup_buf[1]; value = (s->setup_buf[3] << 8) | s->setup_buf[2]; index = (s->setup_buf[5] << 8) | s->setup_buf[4]; if (s->setup_buf[0] & USB_DIR_IN) { ret = usb_device_handle_control(s, p, request, value, index, s->setup_len, s->data_buf); if (ret == USB_RET_ASYNC) { s->setup_state = SETUP_STATE_SETUP; return USB_RET_ASYNC; } if (ret < 0) return ret; if (ret < s->setup_len) s->setup_len = ret; s->setup_state = SETUP_STATE_DATA; } else { if (s->setup_len > sizeof(s->data_buf)) { fprintf(stderr, "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", s->setup_len, sizeof(s->data_buf)); return USB_RET_STALL; } if (s->setup_len == 0) s->setup_state = SETUP_STATE_ACK; else s->setup_state = SETUP_STATE_DATA; } return ret; }
static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) { USBSerialState *s = (USBSerialState *)dev; uint8_t devep = p->ep->nr; struct iovec *iov; uint8_t header[2]; int i, first_len, len; switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) goto fail; for (i = 0; i < p->iov.niov; i++) { iov = p->iov.iov + i; qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len); } p->actual_length = p->iov.size; break; case USB_TOKEN_IN: if (devep != 1) goto fail; first_len = RECV_BUF - s->recv_ptr; len = p->iov.size; if (len <= 2) { p->status = USB_RET_NAK; break; } header[0] = usb_get_modem_lines(s) | 1; /* We do not have the uart details */ /* handle serial break */ if (s->event_trigger && s->event_trigger & FTDI_BI) { s->event_trigger &= ~FTDI_BI; header[1] = FTDI_BI; usb_packet_copy(p, header, 2); break; } else { header[1] = 0; } len -= 2; if (len > s->recv_used) len = s->recv_used; if (!len) { p->status = USB_RET_NAK; break; } if (first_len > len) first_len = len; usb_packet_copy(p, header, 2); usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); if (len > first_len) usb_packet_copy(p, s->recv_buf, len - first_len); s->recv_used -= len; s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; break; default: DPRINTF("Bad token\n"); fail: p->status = USB_RET_STALL; break; } }