usb_host: handle control URBs asynchronously

In the Genode C API and the DDE Linux USB host driver, turn control
URBs into asynchronously handled ones.

Fix genodelabs/genode#4535
This commit is contained in:
Stefan Kalkowski 2022-06-24 14:35:53 +02:00 committed by Christian Helmuth
parent 1ca2265fd4
commit 0b5ad90bde
3 changed files with 221 additions and 155 deletions

View File

@ -48,15 +48,7 @@ static int usb_drv_probe(struct usb_interface *interface,
return -ENODEV; } return -ENODEV; }
static void usb_drv_disconnect(struct usb_interface *iface) static void usb_drv_disconnect(struct usb_interface *iface) { }
{
struct usb_iface_urbs * urbs = usb_get_intfdata(iface);
if (urbs) {
urbs->in_delete = 1;
usb_kill_anchored_urbs(&urbs->submitted);
kfree(urbs);
}
}
static struct usb_driver usb_drv = { static struct usb_driver usb_drv = {
@ -213,12 +205,7 @@ static struct task_struct * usb_rpc_task;
static int claim_iface(struct usb_interface * iface) static int claim_iface(struct usb_interface * iface)
{ {
struct usb_iface_urbs *urbs = (struct usb_iface_urbs*) return usb_driver_claim_interface(&usb_drv, iface, NULL);
kmalloc(sizeof(struct usb_iface_urbs), GFP_KERNEL);
init_usb_anchor(&urbs->submitted);
urbs->in_delete = 0;
return usb_driver_claim_interface(&usb_drv, iface, urbs);
} }
@ -266,8 +253,13 @@ static int usb_rpc_call(void * data)
release_iface(iface); release_iface(iface);
} }
if (usb_rpc_args.call == RELEASE_ALL) if (usb_rpc_args.call == RELEASE_ALL) {
struct usb_iface_urbs * urbs = dev_get_drvdata(&udev->dev);
urbs->in_delete = 1;
usb_kill_anchored_urbs(&urbs->submitted);
urbs->in_delete = 0;
usb_reset_device(udev); usb_reset_device(udev);
}
usb_rpc_args.ret = ret; usb_rpc_args.ret = ret;
} }
@ -337,39 +329,6 @@ struct genode_usb_rpc_callbacks lx_emul_usb_rpc_callbacks = {
}; };
static genode_usb_request_ret_t
handle_ctrl_request(struct genode_usb_request_control * req,
void * buf, unsigned long size, void * data)
{
struct usb_device * udev = (struct usb_device *) data;
int pipe = (req->request_type & 0x80)
? usb_rcvctrlpipe(udev, 0) : usb_sndctrlpipe(udev, 0);
int err = usb_control_msg(udev, pipe, req->request, req->request_type,
req->value, req->index, buf, size, req->timeout);
if (err >= 0) {
req->actual_size = err;
return NO_ERROR;
}
req->actual_size = 0;
switch (err) {
case -ENOENT: return INTERFACE_OR_ENDPOINT_ERROR;
case -ENODEV: return NO_DEVICE_ERROR;
case -ESHUTDOWN: return NO_DEVICE_ERROR;
case -EPROTO: return PROTOCOL_ERROR;
case -EILSEQ: return PROTOCOL_ERROR;
case -EPIPE: return STALL_ERROR;
case -ETIMEDOUT: return TIMEOUT_ERROR;
}
return UNKNOWN_ERROR;
}
static genode_usb_request_ret_t static genode_usb_request_ret_t
handle_string_request(struct genode_usb_request_string * req, handle_string_request(struct genode_usb_request_string * req,
void * buf, unsigned long size, void * data) void * buf, unsigned long size, void * data)
@ -426,26 +385,53 @@ handle_flush_request(unsigned char ep, void * data)
return NO_ERROR; return NO_ERROR;
} }
enum Timer_state { TIMER_OFF, TIMER_ACTIVE, TIMER_TRIGGERED };
struct usb_urb_context
{
genode_usb_session_handle_t session;
genode_usb_request_handle_t request;
struct urb * urb;
struct timer_list timeo;
enum Timer_state timer_state;
};
static genode_usb_request_ret_t static genode_usb_request_ret_t
handle_transfer_response(struct genode_usb_request_transfer * req, handle_transfer_response(struct genode_usb_request_urb req,
void * data) void * data)
{ {
struct urb * urb = (struct urb *) data; struct usb_urb_context * context = (struct usb_urb_context *) data;
struct urb * urb = context->urb;
struct genode_usb_request_control * ctrl =
genode_usb_get_request_control(&req);
struct genode_usb_request_transfer * transfer =
genode_usb_get_request_transfer(&req);
int i; int i;
if (urb->status == 0) { if (urb->status == 0) {
req->actual_size = urb->actual_length; if (ctrl)
ctrl->actual_size = urb->actual_length;
if (transfer) {
transfer->actual_size = urb->actual_length;
if (usb_pipein(urb->pipe)) if (usb_pipein(urb->pipe))
for (i = 0; i < urb->number_of_packets; i++) for (i = 0; i < urb->number_of_packets; i++)
req->actual_packet_size[i] = urb->iso_frame_desc[i].actual_length; transfer->actual_packet_size[i] =
urb->iso_frame_desc[i].actual_length;
}
return NO_ERROR; return NO_ERROR;
} }
if (ctrl)
ctrl->actual_size = 0;
if (context->timer_state == TIMER_TRIGGERED)
return TIMEOUT_ERROR;
switch (urb->status) { switch (urb->status) {
case -ENOENT: return INTERFACE_OR_ENDPOINT_ERROR;
case -ENODEV: return NO_DEVICE_ERROR;
case -ESHUTDOWN: return NO_DEVICE_ERROR; case -ESHUTDOWN: return NO_DEVICE_ERROR;
case -EPROTO: return PROTOCOL_ERROR; case -EPROTO: return PROTOCOL_ERROR;
case -EILSEQ: return PROTOCOL_ERROR; case -EILSEQ: return PROTOCOL_ERROR;
@ -455,55 +441,83 @@ handle_transfer_response(struct genode_usb_request_transfer * req,
} }
static struct usb_interface * usb_get_iface_from_urb(struct urb * urb) static void usb_free_complete_urb(struct urb * urb)
{ {
unsigned i, j; if (!urb)
struct usb_host_endpoint * ep = usb_pipe_endpoint(urb->dev, urb->pipe); return;
if (urb->setup_packet)
if (!ep || !urb->dev || !urb->dev->actconfig) kfree(urb->setup_packet);
return NULL; if (urb->context) {
struct usb_urb_context * context =
for (i = 0; i < urb->dev->actconfig->desc.bNumInterfaces; i++) { (struct usb_urb_context *) urb->context;
struct usb_interface * iface = urb->dev->actconfig->interface[i]; if (context->timer_state != TIMER_OFF)
if (!iface || !iface->cur_altsetting) del_timer_sync(&context->timeo);
continue; kfree(context);
for (j = 0; j < iface->cur_altsetting->desc.bNumEndpoints; j++) {
if (&iface->cur_altsetting->endpoint[j] != ep)
continue;
return iface;
} }
} usb_free_urb(urb);
return NULL;
} }
static void async_complete(struct urb *urb) static void async_complete(struct urb *urb)
{ {
struct usb_interface * iface; struct usb_iface_urbs * urbs = dev_get_drvdata(&urb->dev->dev);
struct usb_iface_urbs * urbs = NULL;
unsigned long handle = (unsigned long)urb->context; struct usb_urb_context * context =
genode_usb_session_handle_t session = (struct usb_urb_context*) urb->context;
(genode_usb_session_handle_t) (handle >> 16);
genode_usb_request_handle_t request =
(genode_usb_request_handle_t) (handle & 0xffff);
genode_usb_ack_request(session, request, genode_usb_ack_request(context->session, context->request,
handle_transfer_response, (void*)urb); handle_transfer_response, (void*)context);
iface = usb_get_iface_from_urb(urb);
if (iface)
urbs = usb_get_intfdata(iface);
if (!urbs || !urbs->in_delete) { if (!urbs || !urbs->in_delete) {
usb_free_urb(urb); usb_free_complete_urb(urb);
lx_user_handle_io(); lx_user_handle_io();
} }
} }
static void urb_timeout(struct timer_list *t)
{
struct usb_urb_context * context = from_timer(context, t, timeo);
context->timer_state = TIMER_TRIGGERED;
usb_kill_urb(context->urb);
}
static int fill_ctrl_urb(struct usb_device * udev,
struct genode_usb_request_control * req,
void * handle,
void * buf,
unsigned long size,
int read,
struct urb ** urb)
{
int pipe = read ? usb_rcvctrlpipe(udev, 0) : usb_sndctrlpipe(udev, 0);
struct usb_ctrlrequest * ucr =
kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
*urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ucr || !*urb) {
if (ucr) kfree(ucr);
if (*urb) usb_free_urb(*urb);
return -ENOMEM;
}
ucr->bRequestType = req->request_type;
ucr->bRequest = req->request;
ucr->wValue = cpu_to_le16(req->value);
ucr->wIndex = cpu_to_le16(req->index);
ucr->wLength = cpu_to_le16(size);
usb_fill_control_urb(*urb, udev, pipe, (unsigned char*)ucr, buf, size,
async_complete, handle);
return 0;
}
static int fill_bulk_urb(struct usb_device * udev, static int fill_bulk_urb(struct usb_device * udev,
struct genode_usb_request_transfer * req, struct genode_usb_request_transfer * req,
unsigned long handle, void * handle,
void * buf, void * buf,
unsigned long size, unsigned long size,
int read, int read,
@ -516,15 +530,14 @@ static int fill_bulk_urb(struct usb_device * udev,
if (!*urb) if (!*urb)
return -ENOMEM; return -ENOMEM;
usb_fill_bulk_urb(*urb, udev, pipe, buf, size, usb_fill_bulk_urb(*urb, udev, pipe, buf, size, async_complete, handle);
async_complete, (void*)handle);
return 0; return 0;
} }
static int fill_irq_urb(struct usb_device * udev, static int fill_irq_urb(struct usb_device * udev,
struct genode_usb_request_transfer * req, struct genode_usb_request_transfer * req,
unsigned long handle, void * handle,
void * buf, void * buf,
unsigned long size, unsigned long size,
int read, int read,
@ -551,14 +564,14 @@ static int fill_irq_urb(struct usb_device * udev,
polling_interval = req->polling_interval; polling_interval = req->polling_interval;
usb_fill_int_urb(*urb, udev, pipe, buf, size, usb_fill_int_urb(*urb, udev, pipe, buf, size,
async_complete, (void*)handle, polling_interval); async_complete, handle, polling_interval);
return 0; return 0;
} }
static int fill_isoc_urb(struct usb_device * udev, static int fill_isoc_urb(struct usb_device * udev,
struct genode_usb_request_transfer * req, struct genode_usb_request_transfer * req,
unsigned long handle, void * handle,
void * buf, void * buf,
unsigned long size, unsigned long size,
int read, int read,
@ -584,7 +597,7 @@ static int fill_isoc_urb(struct usb_device * udev,
(*urb)->transfer_buffer_length = size; (*urb)->transfer_buffer_length = size;
(*urb)->number_of_packets = req->number_of_packets; (*urb)->number_of_packets = req->number_of_packets;
(*urb)->interval = 1 << min(15, ep->desc.bInterval - 1); (*urb)->interval = 1 << min(15, ep->desc.bInterval - 1);
(*urb)->context = (void *)handle; (*urb)->context = handle;
(*urb)->transfer_flags = URB_ISO_ASAP | (read ? URB_DIR_IN : URB_DIR_OUT); (*urb)->transfer_flags = URB_ISO_ASAP | (read ? URB_DIR_IN : URB_DIR_OUT);
(*urb)->complete = async_complete; (*urb)->complete = async_complete;
@ -599,54 +612,71 @@ static int fill_isoc_urb(struct usb_device * udev,
static genode_usb_request_ret_t static genode_usb_request_ret_t
handle_transfer_request(struct genode_usb_request_transfer * req, handle_urb_request(struct genode_usb_request_urb req,
genode_usb_transfer_type_t type,
genode_usb_session_handle_t session_handle, genode_usb_session_handle_t session_handle,
genode_usb_request_handle_t request_handle, genode_usb_request_handle_t request_handle,
void * buf, unsigned long size, void * data) void * buf, unsigned long size, void * data)
{ {
struct usb_device * udev = (struct usb_device *) data; struct usb_device * udev = (struct usb_device *) data;
struct usb_iface_urbs * urbs = dev_get_drvdata(&udev->dev);
struct genode_usb_request_control * ctrl =
genode_usb_get_request_control(&req);
struct genode_usb_request_transfer * transfer =
genode_usb_get_request_transfer(&req);
int err = 0; int err = 0;
int read = (req->ep & 0x80); int read = transfer ? (transfer->ep & 0x80) : 0;
unsigned long handle = session_handle << 16 | request_handle;
struct urb * urb; struct urb * urb;
switch (type) { struct usb_urb_context * context =
kmalloc(sizeof(struct usb_urb_context), GFP_NOIO);
if (!context)
return MEMORY_ERROR;
context->session = session_handle;
context->request = request_handle;
switch (req.type) {
case CTRL:
err = fill_ctrl_urb(udev, ctrl, context, buf, size,
(ctrl->request_type & 0x80), &urb);
break;
case BULK: case BULK:
err = fill_bulk_urb(udev, req, handle, buf, size, read, &urb); err = fill_bulk_urb(udev, transfer, context, buf, size, read, &urb);
break; break;
case IRQ: case IRQ:
err = fill_irq_urb(udev, req, handle, buf, size, read, &urb); err = fill_irq_urb(udev, transfer, context, buf, size, read, &urb);
break; break;
case ISOC: case ISOC:
err = fill_isoc_urb(udev, req, handle, buf, size, read, &urb); err = fill_isoc_urb(udev, transfer, context, buf, size, read, &urb);
break; break;
default: default:
printk("Unknown USB transfer request!\n"); printk("Unknown USB transfer request!\n");
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
}; };
if (!err) { if (err)
struct usb_iface_urbs * urbs; goto free_context;
struct usb_interface * iface = usb_get_iface_from_urb(urb);
if (!iface) {
usb_free_urb(urb);
return NO_DEVICE_ERROR;
}
if (!usb_interface_claimed(iface)) context->urb = urb;
claim_iface(iface);
urbs = usb_get_intfdata(iface);
usb_anchor_urb(urb, &urbs->submitted); usb_anchor_urb(urb, &urbs->submitted);
err = usb_submit_urb(urb, GFP_KERNEL); err = usb_submit_urb(urb, GFP_KERNEL);
if (err)
goto free_urb;
if (!err) if (ctrl && ctrl->timeout) {
return NO_ERROR; context->timer_state = TIMER_ACTIVE;
timer_setup(&context->timeo, urb_timeout, 0);
usb_free_urb(urb); mod_timer(&context->timeo, jiffies + msecs_to_jiffies(ctrl->timeout));
} }
return 0;
free_urb:
usb_unanchor_urb(urb);
usb_free_complete_urb(urb);
free_context:
kfree(context);
switch (err) { switch (err) {
case -ENOENT: return INTERFACE_OR_ENDPOINT_ERROR; case -ENOENT: return INTERFACE_OR_ENDPOINT_ERROR;
case -ENODEV: return NO_DEVICE_ERROR; case -ENODEV: return NO_DEVICE_ERROR;
@ -659,8 +689,7 @@ handle_transfer_request(struct genode_usb_request_transfer * req,
static struct genode_usb_request_callbacks request_callbacks = { static struct genode_usb_request_callbacks request_callbacks = {
.control_fn = handle_ctrl_request, .urb_fn = handle_urb_request,
.transfer_fn = handle_transfer_request,
.string_fn = handle_string_request, .string_fn = handle_string_request,
.altsetting_fn = handle_altsetting_request, .altsetting_fn = handle_altsetting_request,
.config_fn = handle_config_request, .config_fn = handle_config_request,
@ -744,6 +773,12 @@ static int raw_notify(struct notifier_block *nb, unsigned long action, void *dat
*/ */
unsigned long class; unsigned long class;
unsigned i; unsigned i;
struct usb_iface_urbs *urbs = (struct usb_iface_urbs*)
kmalloc(sizeof(struct usb_iface_urbs), GFP_KERNEL);
init_usb_anchor(&urbs->submitted);
urbs->in_delete = 0;
dev_set_drvdata(&udev->dev, urbs);
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface * iface = udev->actconfig->interface[i]; struct usb_interface * iface = udev->actconfig->interface[i];
@ -764,7 +799,14 @@ static int raw_notify(struct notifier_block *nb, unsigned long action, void *dat
case USB_DEVICE_REMOVE: case USB_DEVICE_REMOVE:
{ {
struct usb_iface_urbs * urbs = dev_get_drvdata(&udev->dev);
urbs->in_delete = 1;
usb_kill_anchored_urbs(&urbs->submitted);
kfree(urbs);
genode_usb_discontinue_device(udev->bus->busnum, udev->devnum); genode_usb_discontinue_device(udev->bus->busnum, udev->devnum);
break; break;
} }

View File

@ -148,8 +148,6 @@ struct genode_usb_request_control
}; };
enum Iso { MAX_PACKETS = 32 }; enum Iso { MAX_PACKETS = 32 };
enum Transfer { BULK, IRQ, ISOC };
typedef enum Transfer genode_usb_transfer_type_t;
struct genode_usb_request_transfer struct genode_usb_request_transfer
{ {
@ -161,6 +159,27 @@ struct genode_usb_request_transfer
unsigned long actual_packet_size[MAX_PACKETS]; unsigned long actual_packet_size[MAX_PACKETS];
}; };
enum Urb_type { CTRL, BULK, IRQ, ISOC };
typedef enum Urb_type genode_usb_urb_t;
struct genode_usb_request_urb
{
genode_usb_urb_t type;
void * req;
};
static inline struct genode_usb_request_control *
genode_usb_get_request_control(struct genode_usb_request_urb * urb)
{
return (urb->type == CTRL) ? (struct genode_usb_request_control*)urb->req : 0;
}
static inline struct genode_usb_request_transfer *
genode_usb_get_request_transfer(struct genode_usb_request_urb * urb)
{
return (urb->type != CTRL) ? (struct genode_usb_request_transfer*)urb->req : 0;
}
enum Request_return_error { enum Request_return_error {
NO_ERROR, NO_ERROR,
INTERFACE_OR_ENDPOINT_ERROR, INTERFACE_OR_ENDPOINT_ERROR,
@ -174,15 +193,8 @@ enum Request_return_error {
}; };
typedef enum Request_return_error genode_usb_request_ret_t; typedef enum Request_return_error genode_usb_request_ret_t;
typedef genode_usb_request_ret_t (*genode_usb_req_ctrl_t) typedef genode_usb_request_ret_t (*genode_usb_req_urb_t)
(struct genode_usb_request_control * req, (struct genode_usb_request_urb req,
void * payload,
unsigned long payload_size,
void * opaque_data);
typedef genode_usb_request_ret_t (*genode_usb_req_transfer_t)
(struct genode_usb_request_transfer * req,
genode_usb_transfer_type_t type,
genode_usb_session_handle_t session_handle, genode_usb_session_handle_t session_handle,
genode_usb_request_handle_t request_handle, genode_usb_request_handle_t request_handle,
void * payload, void * payload,
@ -205,12 +217,11 @@ typedef genode_usb_request_ret_t (*genode_usb_req_flush_t)
(unsigned char ep, void * opaque_data); (unsigned char ep, void * opaque_data);
typedef genode_usb_request_ret_t (*genode_usb_response_t) typedef genode_usb_request_ret_t (*genode_usb_response_t)
(struct genode_usb_request_transfer * req, (struct genode_usb_request_urb req,
void * opaque_data); void * opaque_data);
struct genode_usb_request_callbacks { struct genode_usb_request_callbacks {
genode_usb_req_ctrl_t control_fn; genode_usb_req_urb_t urb_fn;
genode_usb_req_transfer_t transfer_fn;
genode_usb_req_string_t string_fn; genode_usb_req_string_t string_fn;
genode_usb_req_altsetting_t altsetting_fn; genode_usb_req_altsetting_t altsetting_fn;
genode_usb_req_config_t config_fn; genode_usb_req_config_t config_fn;

View File

@ -450,23 +450,20 @@ bool genode_usb_session::request(genode_usb_request_callbacks & req, void * data
addr, p.size(), data), p); addr, p.size(), data), p);
break; break;
case Packet_descriptor::CTRL: case Packet_descriptor::CTRL:
_ack(req.control_fn((genode_usb_request_control*)&p.control, packets[idx].construct(p);
addr, p.size(), data), p); req.urb_fn({ CTRL, &p.control }, _id, idx, addr, p.size(), data);
break; break;
case Packet_descriptor::BULK: case Packet_descriptor::BULK:
packets[idx].construct(p); packets[idx].construct(p);
req.transfer_fn((genode_usb_request_transfer*)&p.transfer, BULK, req.urb_fn({ BULK, &p.transfer }, _id, idx, addr, p.size(), data);
_id, idx, addr, p.size(), data);
break; break;
case Packet_descriptor::IRQ: case Packet_descriptor::IRQ:
packets[idx].construct(p); packets[idx].construct(p);
req.transfer_fn((genode_usb_request_transfer*)&p.transfer, IRQ, req.urb_fn({ IRQ, &p.transfer }, _id, idx, addr, p.size(), data);
_id, idx, addr, p.size(), data);
break; break;
case Packet_descriptor::ISOC: case Packet_descriptor::ISOC:
packets[idx].construct(p); packets[idx].construct(p);
req.transfer_fn((genode_usb_request_transfer*)&p.transfer, ISOC, req.urb_fn({ ISOC, &p.transfer }, _id, idx, addr, p.size(), data);
_id, idx, addr, p.size(), data);
break; break;
case Packet_descriptor::ALT_SETTING: case Packet_descriptor::ALT_SETTING:
_ack(req.altsetting_fn(p.interface.number, _ack(req.altsetting_fn(p.interface.number,
@ -491,9 +488,25 @@ void genode_usb_session::handle_response(genode_usb_request_handle_t id,
genode_usb_response_t callback, genode_usb_response_t callback,
void * callback_data) void * callback_data)
{ {
Usb::Packet_descriptor p = *packets[id]; using Packet_descriptor = Usb::Packet_descriptor;
_ack(callback((genode_usb_request_transfer*)&p.transfer,
callback_data), p); Packet_descriptor p = *packets[id];
switch (p.type) {
case Packet_descriptor::CTRL:
_ack(callback({ CTRL, &p.control }, callback_data), p);
break;
case Packet_descriptor::BULK:
_ack(callback({ BULK, &p.transfer }, callback_data), p);
break;
case Packet_descriptor::IRQ:
_ack(callback({ IRQ, &p.transfer }, callback_data), p);
break;
case Packet_descriptor::ISOC:
_ack(callback({ ISOC, &p.transfer }, callback_data), p);
break;
default:
error("Invalid packet descriptor for asynchronous response");
};
packets[id].destruct(); packets[id].destruct();
} }