/**
 * \brief callback function that initiates the sending the transfer done msg
 *
 * \param xfer the transfer that was done
 * \param err the outcome of the transfer
 *
 * NOTE: this function sends xfer done notification to the device driver
 *       interrupt transfers may automatically be restarted
 */
static void usb_transfer_complete_notify(struct usb_xfer *xfer, usb_error_t err)
{
    uint32_t data_length = xfer->actual_bytes;

    xfer->error = err;

    switch (xfer->type) {
    case USB_TYPE_INTR:
        xfer->frame_lengths[0] = xfer->max_data_length;
        if (data_length > 0) {
            struct usb_tdone_state *st = malloc(
                                             sizeof(struct usb_tdone_state));
            st->xfer = xfer;
            st->buf = xfer->frame_buffers[0]->buffer;
            st->bind = xfer->usb_driver_binding;
            usb_transfer_complete_tx(st);

        }
        /* autorestart if the transfer completed successfully */
        if (xfer->flags.auto_restart && (err == USB_ERR_OK)) {
            usb_transfer_start(xfer);
        }
        break;
    default:
        /* noop */
        break;
    }
}
void usb_rx_transfer_start_call(struct usb_manager_binding *bind, uint32_t tid)
{
    USB_DEBUG_IDC("usb_rx_transfer_start_call()\n");

    struct usb_tstart_state *st = malloc(sizeof(struct usb_tstart_state));

    if (st == NULL) {
        debug_printf("WARNING: Cannot reply, out of memory!\n");
    }
    st->bind = bind;

    struct usb_device *dev = (struct usb_device *) (bind->st);

    assert(dev != NULL);

    struct usb_xfer *xfer = dev->xfers;

    while (xfer) {
        if (xfer->xfer_id == tid) {
            break;
        }
        xfer = xfer->device_xfers_next;
    }

    if (xfer == NULL) {
        USB_DEBUG("no xfer!\n");
        st->error = USB_ERR_BAD_CONTEXT;
        usb_tx_transfer_start_response(st);
    }

    usb_transfer_start(xfer);

    st->error = xfer->error;

    usb_tx_transfer_start_response(st);
}
/**
 * \brief   this function is used to allocate the structure and resources
 *          needed for the default USB control endpoint transfer
 *
 * \param   device  the usb device we want to setup a usb transfer
 */
void usb_transfer_setup_ctrl_default(struct usb_device *device,
                                     struct usb_request_state *st)
{
    USB_DEBUG_TR_ENTER;
    /* setting up transfers for the USB root hub is not allowed */
    if (device->parent_hub == NULL) {
        USB_DEBUG("ERROR: setting up transfers for root hub not allowed\n");
        return;
    }

    /*
     * since the control transfers are always on the special control
     * ep, we can cache them and reuse later
     */
    struct usb_xfer *xfer = device->ctrl_xfer[0];

    uint8_t xfer_reuse = 0;

    if (xfer) {
        xfer_reuse = ((xfer->device_address == device->device_address)
                      && (device->ctrl_ep_desc.wMaxPacketSize
                          == device->device_desc.bMaxPacketSize0));

        if ((device->flags.usb_mode == USB_MODE_DEVICE) && xfer_reuse) {
            assert(!"NYI: device mode\n");
            usb_transfer_start(xfer);
            return;
        }
    }

    if (xfer_reuse) {
        USB_DEBUG_XFER("reusing the xfer... return.\n");
        return;
    }

    /*
     * we cannot reuse the USB transfer so we have to update the fields
     */
    device->ctrl_ep_desc.wMaxPacketSize = device->device_desc.bMaxPacketSize0;
    device->ctrl_ep.descriptor = &device->ctrl_ep_desc;

    usb_transfer_unsetup(device->ctrl_xfer, USB_DEVICE_CTRL_XFER_MAX);

    USB_DEBUG_XFER("setting up device ctrl xfer[0]\n");

    if (usb_transfer_setup(device, 0, &(device->ctrl_xfer[0]),
                           usb_control_ep_cfg)) {
        USB_DEBUG("usb_transfer_setup_ctrl_default(): "
                  "ERROR: could not allocate default control transfer\n");
        return;
    }

    USB_DEBUG_XFER("setting up device ctrl xfer[1]\n");

    if (usb_transfer_setup(device, 0, &(device->ctrl_xfer[1]),
                           usb_control_ep_cfg)) {
        debug_printf("usb_transfer_setup_ctrl_default(): "
                     "ERROR: could not allocate default control transfer\n");
        return;
    }

    USB_DEBUG_TR_RETURN;
}
Exemple #4
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);
}