/** * \brief this function allocates the resources for a number of usb transfers * * \param device the device we want to allocate the transfers * \param ifaces array of interfaces * \param usb_xfers pointer to an array of usb_xfer * \param setups setup parameter array * \para setup_count the number of setups we have to do * \ */ usb_error_t usb_transfer_setup(struct usb_device *device, const uint8_t iface, struct usb_xfer **ret_xfer, const struct usb_xfer_config *setup) { USB_DEBUG_TR_ENTER; struct usb_xfer_setup_params params; memset(¶ms, 0, sizeof(params)); params.device = device; params.speed = device->speed; params.hc_max_packet_count = 1; params.err = USB_ERR_OK; params.type = setup->usb_type; params.size[0] = 0; params.buf = NULL; params.xfer_setup = setup; struct usb_endpoint *ep = usb_endpoint_lookup(device, iface, setup); if ((ep == NULL) || (ep->pipe_fn == NULL)) { USB_DEBUG_XFER("WARNING: No associated pipe!\n"); USB_DEBUG_TR_RETURN; return (USB_ERR_NO_PIPE); } struct usb_xfer *xfer = malloc(sizeof(struct usb_xfer)); memset(xfer, 0, sizeof(*xfer)); xfer->xfer_id = device->xfer_id++; xfer->device_xfers_next = device->xfers; device->xfers = xfer; xfer->xfer_done_cb = setup->xfer_done_cb; xfer->type = setup->usb_type; xfer->device_address = device->device_address; xfer->host_controller = device->controller; xfer->device = device; xfer->endpoint = ep; params.curr_xfer = xfer; params.pipe_fn = xfer->endpoint->pipe_fn; (device->controller->hcdi_bus_fn->xfer_setup)(¶ms); if (params.err != USB_ERR_OK) { USB_DEBUG( "ERROR: hcdi_xfer_setup failed: %s\n", usb_get_error_string(params.err)); return (params.err); } xfer->endpoint->ref_allocation++; assert(xfer->endpoint->ref_allocation); *ret_xfer = xfer; USB_DEBUG_TR_RETURN; return (USB_ERR_OK); }
/** * \brief this function handles the USB requests and executes them either * on the device or calls the root hub emulation function */ usb_error_t usb_handle_request(struct usb_device *device, uint16_t flags, struct usb_device_request *req, struct usb_request_state *req_state, void *data, uint16_t *ret_length) { USB_DEBUG_TR_ENTER; struct usb_xfer *xfer; usb_error_t err = USB_ERR_OK; uint16_t length = req->wLength; uint16_t actual_length = 0; USB_DEBUG_REQ("bmRequestType = %x\n", *((uint8_t *)(&req->bType)));USB_DEBUG_REQ("bRequest = %x\n", *((uint8_t *)(&req->bRequest)));USB_DEBUG_REQ("wValue = %x\n", *((uint16_t *)(&req->wValue)));USB_DEBUG_REQ("wIndex = %x\n", *((uint16_t *)(&req->wIndex)));USB_DEBUG_REQ("wLength= %x\n", *((uint16_t *)(&req->wLength))); /* * check if the device is in the correct state to handle requests * the device must be at lease in the powered state */ if (device->state < USB_DEVICE_STATE_POWERED) { USB_DEBUG("Error: USB Device has not been configured\n"); if (req_state) { req_state->error = USB_ERR_NOT_CONFIGURED; req_state->callback(req_state->bind); } USB_DEBUG_TR_RETURN; return (USB_ERR_NOT_CONFIGURED); } /* * reset the length value */ if (ret_length) { *ret_length = 0; } /* * the device may be the root hub, so we need to take the root hub * execution function for this, the root hub is the device which * does not have a parent hub associated */ if (device->parent_hub == NULL) { /* * cannot write data to the root hub */ if ((req->bType.direction != USB_REQUEST_READ) && (length != 0)) { USB_DEBUG("Error: root hub does not support writing of data\n"); if (req_state) { req_state->error = USB_ERR_INVAL; req_state->callback(req_state->bind); } USB_DEBUG_TR_RETURN; return (USB_ERR_INVAL); } const void *ret_desc; uint16_t ret_size; err = USB_ERR_NO_ROOT_HUB; if (device->controller->hcdi_bus_fn->roothub_exec != NULL) { err = device->controller->hcdi_bus_fn->roothub_exec(device, req, &ret_desc, &ret_size); } if (err != USB_ERR_OK) { USB_DEBUG( "ERROR: root_hub_exec failed(): %s\n", usb_get_error_string(err)); if (req_state) { req_state->error = err; req_state->callback(req_state->bind); }USB_DEBUG_TR_RETURN; return (err); } /* * we have encountered a short transfer, this may be ok when the flag * is set */ if (length > ret_size) { if (!(flags & USB_REQUEST_FLAG_IGNORE_SHORT_XFER)) { if (req_state) { req_state->error = USB_ERR_SHORT_XFER; req_state->callback(req_state->bind); }USB_DEBUG_TR_RETURN; return (USB_ERR_SHORT_XFER); } // short xfers are ok so update the length length = ret_size; } if (ret_length) { *ret_length = length; } /* * we have some data that we have to return */ if (length > 0 && data != NULL) { memcpy(data, ret_desc, length); } if (req_state) { req_state->error = USB_ERR_OK; } return (USB_ERR_OK); } /* * we are executing the request on a real device so we have to setup * a new USB control transfer on this device */ usb_transfer_setup_ctrl_default(device, req_state); xfer = device->ctrl_xfer[0]; xfer->ed_direction = (req->bType.direction == USB_REQUEST_READ) ? USB_ENDPOINT_DIRECTION_IN : USB_ENDPOINT_DIRECTION_OUT; if (xfer == NULL) { USB_DEBUG("ERROR: No memory for setting up transfers\n"); return (USB_ERR_NOMEM); } if (req_state) { req_state->xfer = xfer; } /* * we have a xfer so set it up according to the setup * and the given flags */ if (flags & USB_REQUEST_FLAG_DELAY_STATUS) { xfer->flags.manual_status = 1; } else { xfer->flags.manual_status = 0; } if (flags & USB_REQUEST_FLAG_IGNORE_SHORT_XFER) { xfer->flags.short_xfer_ok = 1; } else { xfer->flags.short_xfer_ok = 1; } xfer->timeout = 1000; // TODO: TIMEOUT /* * copy the request into DMA memory */ usb_mem_copy_in(xfer->frame_buffers[0], 0, req, sizeof(struct usb_device_request)); xfer->frame_lengths[0] = sizeof(struct usb_device_request); /* * loop till we got all requested data */ uint16_t current_data_length; while (1) { current_data_length = length; if (current_data_length > xfer->max_data_length) { USB_DEBUG( "NOTICE: current_data_length (%u)> xfer->max_data_length (%u)\n", current_data_length, xfer->max_data_length); current_data_length = xfer->max_data_length; } // set the frame length of the data stage xfer->frame_lengths[1] = current_data_length; /* * we have a data stage, so we have to handle the data read or write * in the case of data write, we have to copy the data into the * second frame buffer and indicate that we have two frames */ if (current_data_length > 0) { if ((req->bType.direction == USB_REQUEST_WRITE)) { usb_mem_copy_in(xfer->frame_buffers[1], 0, data, current_data_length); } xfer->num_frames = 2; } else { if (xfer->frame_lengths[0] == 0) { if (xfer->flags.manual_status) { xfer->flags.manual_status = 0; } else { break; } } xfer->num_frames = 1; } USB_DEBUG_REQ("-------------------- starting transfer\n"); usb_transfer_start(xfer); /* wait till completed... */ while (!usb_transfer_completed(xfer)) { USB_WAIT(10); //thread_yield(); } /* * transfer is complete, check for error condition */ err = xfer->error; if (err != USB_ERR_OK) { break; } /* * get the actual number of frames */ if (xfer->actual_frames < 2) { actual_length = 0; // no data stage } else { actual_length = xfer->frame_lengths[1]; } /* * updating variables to catch short packets */ if (current_data_length > actual_length) { current_data_length = actual_length; length = current_data_length; } /* * copy the data out to buffer if it is a read request * and we have some bytes to read */ if ((current_data_length > 0) && (req->bType.direction == USB_REQUEST_READ)) { usb_mem_copy_out(xfer->frame_buffers[1], 0, data, current_data_length); } /* * update the frame length accordingly */ xfer->frame_lengths[0] = 0; length -= current_data_length; /* * advance buffer pointer */ data += current_data_length; if (ret_length) { (*ret_length) += current_data_length; } /* * TODO: Timeout */ } if (err != USB_ERR_OK) { usb_transfer_stop(xfer); } if (req_state) { req_state->error = (usb_error_t) err; req_state->callback(req_state); } USB_DEBUG_TR_RETURN; return ((usb_error_t) err); }