예제 #1
0
/**
 * \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);
}
예제 #2
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);
}