/** * \brief handles the start of a control transfer */ static uint8_t usb_transfer_ctrl_start(struct usb_xfer *xfer) { USB_DEBUG_TR_ENTER; uint16_t length; /* check if the control transfer ended up in a stalled state while active */ if (xfer->flags.pipe_stalled && xfer->flags_internal.ctrl_active) { xfer->flags_internal.ctrl_stall = 1; xfer->flags_internal.ctrl_active = 0; } else { xfer->flags_internal.ctrl_stall = 1; } if (xfer->num_frames > 2) { USB_DEBUG("ERROR: too many frames %"PRIu32"", xfer->num_frames); USB_DEBUG_TR_RETURN; return (1); } /* if the ctrl transfer is already active the header was sent, so reset * the control header flag */ if (xfer->flags_internal.ctrl_active) { if (xfer->flags_internal.ctrl_header) { xfer->flags_internal.ctrl_header = 0; } length = xfer->sum_bytes; } else { if (xfer->frame_lengths[0] != sizeof(struct usb_device_request)) { /* * the first frame must be of size device request, otherwise * this is an error */ USB_DEBUG("ERROR: wrong frame length: %"PRIu32" / %zx", xfer->frame_lengths[0], sizeof(struct usb_device_request)); USB_DEBUG_TR_RETURN; return (1); } /* get the device request to have access to the length information */ struct usb_device_request req; usb_mem_copy_out(xfer->frame_buffers[0], 0, &req, sizeof(req)); xfer->flags_internal.remaining_bytes = req.wLength; xfer->flags_internal.ctrl_header = 1; length = xfer->sum_bytes - sizeof(struct usb_device_request); } if (length > xfer->flags_internal.remaining_bytes) { USB_DEBUG("ERROR: length (%u) remaining length is greater than " "remaining length (%u)", length, xfer->flags_internal.remaining_bytes); USB_DEBUG_TR_RETURN; return (1); } /* check for short transfers i.e. too less data than expected */ if (xfer->flags.short_xfer_forced) { xfer->flags_internal.remaining_bytes = 0; } else { if ((length != xfer->max_data_length) && (length != xfer->flags_internal.remaining_bytes) && (xfer->num_frames != 1)) { USB_DEBUG("ERROR: short xfer w/o force_short_xfer\n"); USB_DEBUG_TR_RETURN; return (1); } xfer->flags_internal.remaining_bytes -= length; } if ((xfer->flags_internal.remaining_bytes > 0) || xfer->flags.manual_status) { xfer->flags_internal.ctrl_active = 1; if ((!xfer->flags_internal.ctrl_header) && (xfer->num_frames == 1)) { USB_DEBUG("ERROR: invalid parameter combination!\n"); USB_DEBUG_TR_RETURN; return (1); } } else { xfer->flags_internal.ctrl_active = 0; } /* all fine */ return (0); }
/** * \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); }