HelenOS sources
This source file includes following definitions.
- clear_self_endpoint_halt
- transfer_common
- setup_dma_buffer
- transfer_wrap_dma
- prepare_control
- usb_pipe_control_read
- usb_pipe_control_write
- usb_pipe_alloc_buffer
- usb_pipe_free_buffer
- usb_pipe_read
- usb_pipe_write
- usb_pipe_read_dma
- usb_pipe_write_dma
- usb_pipe_initialize
- usb_pipe_initialize_default_control
- usb_pipe_register
- usb_pipe_unregister
#include <usb/dev/pipes.h>
#include <usb/dev/request.h>
#include <usb/usb.h>
#include <usb/dma_buffer.h>
#include <assert.h>
#include <bitops.h>
#include <async.h>
#include <as.h>
#include <errno.h>
#include <mem.h>
static void clear_self_endpoint_halt(usb_pipe_t *pipe)
{
assert(pipe != NULL);
if (!pipe->auto_reset_halt || (pipe->desc.endpoint_no != 0)) {
return;
}
pipe->auto_reset_halt = false;
usb_pipe_clear_halt(pipe, pipe);
pipe->auto_reset_halt = true;
}
typedef struct {
usb_pipe_t *pipe;
usb_direction_t dir;
bool is_control;
usbhc_iface_transfer_request_t req;
size_t transferred_size;
} transfer_t;
static errno_t transfer_common(transfer_t *t)
{
if (!t->pipe)
return EBADMEM;
if ((t->dir != USB_DIRECTION_OUT || !t->is_control) && t->req.size == 0)
return EINVAL;
if (!dma_buffer_is_set(&t->req.buffer) && t->req.size != 0)
return EINVAL;
if (t->pipe->desc.direction != USB_DIRECTION_BOTH &&
t->pipe->desc.direction != t->dir)
return EBADF;
if ((t->pipe->desc.transfer_type == USB_TRANSFER_CONTROL) != t->is_control)
return EBADF;
async_exch_t *exch = async_exchange_begin(t->pipe->bus_session);
if (!exch)
return ENOMEM;
t->req.dir = t->dir;
t->req.endpoint = t->pipe->desc.endpoint_no;
const errno_t rc = usbhc_transfer(exch, &t->req, &t->transferred_size);
async_exchange_end(exch);
if (rc == ESTALL)
clear_self_endpoint_halt(t->pipe);
return rc;
}
static void setup_dma_buffer(transfer_t *t, void *base, void *ptr, size_t size)
{
t->req.buffer.virt = base;
t->req.buffer.policy = t->pipe->desc.transfer_buffer_policy;
t->req.offset = ptr - base;
t->req.size = size;
}
static errno_t transfer_wrap_dma(transfer_t *t, void *buf, size_t size)
{
if (size == 0) {
setup_dma_buffer(t, NULL, NULL, 0);
return transfer_common(t);
}
void *dma_buf = usb_pipe_alloc_buffer(t->pipe, size);
setup_dma_buffer(t, dma_buf, dma_buf, size);
if (t->dir == USB_DIRECTION_OUT)
memcpy(dma_buf, buf, size);
const errno_t err = transfer_common(t);
if (!err && t->dir == USB_DIRECTION_IN)
memcpy(buf, dma_buf, t->transferred_size);
usb_pipe_free_buffer(t->pipe, dma_buf);
return err;
}
static errno_t prepare_control(transfer_t *t, const void *setup, size_t setup_size)
{
if ((setup == NULL) || (setup_size != 8))
return EINVAL;
memcpy(&t->req.setup, setup, 8);
return EOK;
}
errno_t usb_pipe_control_read(usb_pipe_t *pipe,
const void *setup_buffer, size_t setup_buffer_size,
void *buffer, size_t buffer_size, size_t *transferred_size)
{
errno_t err;
transfer_t transfer = {
.pipe = pipe,
.dir = USB_DIRECTION_IN,
.is_control = true,
};
if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
return err;
if ((err = transfer_wrap_dma(&transfer, buffer, buffer_size)))
return err;
if (transferred_size)
*transferred_size = transfer.transferred_size;
return EOK;
}
errno_t usb_pipe_control_write(usb_pipe_t *pipe,
const void *setup_buffer, size_t setup_buffer_size,
const void *buffer, size_t buffer_size)
{
assert(pipe);
errno_t err;
transfer_t transfer = {
.pipe = pipe,
.dir = USB_DIRECTION_OUT,
.is_control = true,
};
if ((err = prepare_control(&transfer, setup_buffer, setup_buffer_size)))
return err;
return transfer_wrap_dma(&transfer, (void *) buffer, buffer_size);
}
void *usb_pipe_alloc_buffer(usb_pipe_t *pipe, size_t size)
{
dma_buffer_t buf;
if (dma_buffer_alloc_policy(&buf, size, pipe->desc.transfer_buffer_policy))
return NULL;
return buf.virt;
}
void usb_pipe_free_buffer(usb_pipe_t *pipe, void *buffer)
{
dma_buffer_t buf;
buf.virt = buffer;
dma_buffer_free(&buf);
}
errno_t usb_pipe_read(usb_pipe_t *pipe,
void *buffer, size_t size, size_t *size_transferred)
{
assert(pipe);
errno_t err;
transfer_t transfer = {
.pipe = pipe,
.dir = USB_DIRECTION_IN,
};
if ((err = transfer_wrap_dma(&transfer, buffer, size)))
return err;
if (size_transferred)
*size_transferred = transfer.transferred_size;
return EOK;
}
errno_t usb_pipe_write(usb_pipe_t *pipe, const void *buffer, size_t size)
{
assert(pipe);
transfer_t transfer = {
.pipe = pipe,
.dir = USB_DIRECTION_OUT,
};
return transfer_wrap_dma(&transfer, (void *) buffer, size);
}
errno_t usb_pipe_read_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size,
size_t *size_transferred)
{
assert(pipe);
errno_t err;
transfer_t transfer = {
.pipe = pipe,
.dir = USB_DIRECTION_IN,
};
setup_dma_buffer(&transfer, base, ptr, size);
if ((err = transfer_common(&transfer)))
return err;
if (size_transferred)
*size_transferred = transfer.transferred_size;
return EOK;
}
errno_t usb_pipe_write_dma(usb_pipe_t *pipe, void *base, void *ptr, size_t size)
{
assert(pipe);
transfer_t transfer = {
.pipe = pipe,
.dir = USB_DIRECTION_OUT,
};
setup_dma_buffer(&transfer, base, ptr, size);
return transfer_common(&transfer);
}
errno_t usb_pipe_initialize(usb_pipe_t *pipe, usb_dev_session_t *bus_session)
{
assert(pipe);
pipe->auto_reset_halt = false;
pipe->bus_session = bus_session;
return EOK;
}
static const usb_pipe_desc_t default_control_pipe = {
.endpoint_no = 0,
.transfer_type = USB_TRANSFER_CONTROL,
.direction = USB_DIRECTION_BOTH,
.max_transfer_size = CTRL_PIPE_MIN_PACKET_SIZE,
.transfer_buffer_policy = DMA_POLICY_STRICT,
};
errno_t usb_pipe_initialize_default_control(usb_pipe_t *pipe,
usb_dev_session_t *bus_session)
{
const errno_t ret = usb_pipe_initialize(pipe, bus_session);
if (ret)
return ret;
pipe->desc = default_control_pipe;
pipe->auto_reset_halt = true;
return EOK;
}
errno_t usb_pipe_register(usb_pipe_t *pipe,
const usb_standard_endpoint_descriptor_t *ep_desc,
const usb_superspeed_endpoint_companion_descriptor_t *comp_desc)
{
assert(pipe);
assert(pipe->bus_session);
assert(ep_desc);
async_exch_t *exch = async_exchange_begin(pipe->bus_session);
if (!exch)
return ENOMEM;
usb_endpoint_descriptors_t descriptors = { 0 };
#define COPY(field) descriptors.endpoint.field = ep_desc->field
COPY(endpoint_address);
COPY(attributes);
COPY(max_packet_size);
COPY(poll_interval);
#undef COPY
#define COPY(field) descriptors.companion.field = comp_desc->field
if (comp_desc) {
COPY(max_burst);
COPY(attributes);
COPY(bytes_per_interval);
}
#undef COPY
const errno_t ret = usbhc_register_endpoint(exch,
&pipe->desc, &descriptors);
async_exchange_end(exch);
return ret;
}
errno_t usb_pipe_unregister(usb_pipe_t *pipe)
{
assert(pipe);
assert(pipe->bus_session);
async_exch_t *exch = async_exchange_begin(pipe->bus_session);
if (!exch)
return ENOMEM;
const errno_t ret = usbhc_unregister_endpoint(exch, &pipe->desc);
async_exchange_end(exch);
return ret;
}
HelenOS homepage, sources at GitHub