mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 06:33:31 +00:00
lx_emul: cleanup URBs in USB driver in release
* Claim and release USB interfaces * Collect URBs in flight in anchor list * When releasing interfaces, wait until all anchored URBs finally got killed successfully Fix genodelabs/genode#4483
This commit is contained in:
parent
4b983f92c5
commit
af0531398a
@ -11,9 +11,15 @@
|
|||||||
* version 2.
|
* version 2.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define KBUILD_MODNAME "genode_usb_driver"
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/usb/hcd.h>
|
#include <linux/usb/hcd.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
|
||||||
#include <lx_emul/shared_dma_buffer.h>
|
#include <lx_emul/shared_dma_buffer.h>
|
||||||
#include <lx_emul/task.h>
|
#include <lx_emul/task.h>
|
||||||
@ -30,6 +36,37 @@ struct usb_find_request {
|
|||||||
struct usb_device * ret;
|
struct usb_device * ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct usb_iface_urbs {
|
||||||
|
struct usb_anchor submitted;
|
||||||
|
int in_delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int usb_drv_probe(struct usb_interface *interface,
|
||||||
|
const struct usb_device_id *id) {
|
||||||
|
return -ENODEV; }
|
||||||
|
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
.name = "genode",
|
||||||
|
.probe = usb_drv_probe,
|
||||||
|
.disconnect = usb_drv_disconnect,
|
||||||
|
.supports_autosuspend = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static int check_usb_device(struct usb_device *usb_dev, void * data)
|
static int check_usb_device(struct usb_device *usb_dev, void * data)
|
||||||
{
|
{
|
||||||
struct usb_find_request * req = (struct usb_find_request *) data;
|
struct usb_find_request * req = (struct usb_find_request *) data;
|
||||||
@ -155,6 +192,133 @@ static int endpoint_descriptor(genode_usb_bus_num_t bus,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum usb_rpc_call_type {
|
||||||
|
CLAIM,
|
||||||
|
RELEASE_IF,
|
||||||
|
RELEASE_ALL
|
||||||
|
} usb_rpc_call_type_t;
|
||||||
|
|
||||||
|
struct usb_rpc_call_args {
|
||||||
|
genode_usb_bus_num_t bus;
|
||||||
|
genode_usb_dev_num_t dev;
|
||||||
|
unsigned iface_num;
|
||||||
|
usb_rpc_call_type_t call;
|
||||||
|
int ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static struct usb_rpc_call_args usb_rpc_args;
|
||||||
|
static struct task_struct * usb_rpc_task;
|
||||||
|
|
||||||
|
|
||||||
|
static int claim_iface(struct usb_interface * iface)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
return usb_driver_claim_interface(&usb_drv, iface, urbs);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void release_iface(struct usb_interface * iface)
|
||||||
|
{
|
||||||
|
usb_driver_release_interface(&usb_drv, iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int usb_rpc_call(void * data)
|
||||||
|
{
|
||||||
|
struct usb_device * udev;
|
||||||
|
struct usb_interface * iface;
|
||||||
|
unsigned i, num;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
lx_emul_task_schedule(true);
|
||||||
|
|
||||||
|
udev = find_usb_device(usb_rpc_args.bus, usb_rpc_args.dev);
|
||||||
|
if (!udev) {
|
||||||
|
usb_rpc_args.ret = -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usb_rpc_args.call == RELEASE_ALL) {
|
||||||
|
i = 0;
|
||||||
|
num = udev->actconfig->desc.bNumInterfaces;
|
||||||
|
} else {
|
||||||
|
i = usb_rpc_args.iface_num;
|
||||||
|
num = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
for (; i < num; i++) {
|
||||||
|
iface = usb_ifnum_to_if(udev, i);
|
||||||
|
if (!iface) {
|
||||||
|
ret = -2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usb_rpc_args.call == CLAIM)
|
||||||
|
ret = claim_iface(iface);
|
||||||
|
else
|
||||||
|
release_iface(iface);
|
||||||
|
}
|
||||||
|
usb_rpc_args.ret = ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int usb_rpc_finished(void)
|
||||||
|
{
|
||||||
|
return (usb_rpc_args.ret <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int claim(genode_usb_bus_num_t bus,
|
||||||
|
genode_usb_dev_num_t dev,
|
||||||
|
unsigned iface_num)
|
||||||
|
{
|
||||||
|
usb_rpc_args.ret = 1;
|
||||||
|
usb_rpc_args.call = CLAIM;
|
||||||
|
usb_rpc_args.bus = bus;
|
||||||
|
usb_rpc_args.dev = dev;
|
||||||
|
usb_rpc_args.iface_num = iface_num;
|
||||||
|
lx_emul_task_unblock(usb_rpc_task);
|
||||||
|
lx_emul_execute_kernel_until(&usb_rpc_finished);
|
||||||
|
return usb_rpc_args.ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int release(genode_usb_bus_num_t bus,
|
||||||
|
genode_usb_dev_num_t dev,
|
||||||
|
unsigned iface_num)
|
||||||
|
{
|
||||||
|
usb_rpc_args.ret = 1;
|
||||||
|
usb_rpc_args.call = RELEASE_IF;
|
||||||
|
usb_rpc_args.bus = bus;
|
||||||
|
usb_rpc_args.dev = dev;
|
||||||
|
usb_rpc_args.iface_num = iface_num;
|
||||||
|
lx_emul_task_unblock(usb_rpc_task);
|
||||||
|
lx_emul_execute_kernel_until(&usb_rpc_finished);
|
||||||
|
return usb_rpc_args.ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void release_all(genode_usb_bus_num_t bus,
|
||||||
|
genode_usb_dev_num_t dev)
|
||||||
|
{
|
||||||
|
usb_rpc_args.ret = 1;
|
||||||
|
usb_rpc_args.call = RELEASE_ALL;
|
||||||
|
usb_rpc_args.bus = bus;
|
||||||
|
usb_rpc_args.dev = dev;
|
||||||
|
lx_emul_task_unblock(usb_rpc_task);
|
||||||
|
lx_emul_execute_kernel_until(&usb_rpc_finished);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct genode_usb_rpc_callbacks lx_emul_usb_rpc_callbacks = {
|
struct genode_usb_rpc_callbacks lx_emul_usb_rpc_callbacks = {
|
||||||
.alloc_fn = lx_emul_shared_dma_buffer_allocate,
|
.alloc_fn = lx_emul_shared_dma_buffer_allocate,
|
||||||
.free_fn = lx_emul_shared_dma_buffer_free,
|
.free_fn = lx_emul_shared_dma_buffer_free,
|
||||||
@ -163,6 +327,9 @@ struct genode_usb_rpc_callbacks lx_emul_usb_rpc_callbacks = {
|
|||||||
.iface_desc_fn = interface_descriptor,
|
.iface_desc_fn = interface_descriptor,
|
||||||
.iface_extra_fn = interface_extra,
|
.iface_extra_fn = interface_extra,
|
||||||
.endp_desc_fn = endpoint_descriptor,
|
.endp_desc_fn = endpoint_descriptor,
|
||||||
|
.claim_fn = claim,
|
||||||
|
.release_fn = release,
|
||||||
|
.release_all_fn = release_all,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -284,8 +451,30 @@ handle_transfer_response(struct genode_usb_request_transfer * req,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct usb_interface * usb_get_iface_from_urb(struct urb * urb)
|
||||||
|
{
|
||||||
|
unsigned i, j;
|
||||||
|
struct usb_host_endpoint * ep = usb_pipe_endpoint(urb->dev, urb->pipe);
|
||||||
|
|
||||||
|
for (i = 0; i < urb->dev->actconfig->desc.bNumInterfaces; i++) {
|
||||||
|
struct usb_interface * iface = urb->dev->actconfig->interface[i];
|
||||||
|
if (!iface || !iface->cur_altsetting)
|
||||||
|
continue;
|
||||||
|
for (j = 0; j < iface->cur_altsetting->desc.bNumEndpoints; j++) {
|
||||||
|
if (&iface->cur_altsetting->endpoint[j] != ep)
|
||||||
|
continue;
|
||||||
|
return iface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
unsigned long handle = (unsigned long)urb->context;
|
unsigned long handle = (unsigned long)urb->context;
|
||||||
genode_usb_session_handle_t session =
|
genode_usb_session_handle_t session =
|
||||||
(genode_usb_session_handle_t) (handle >> 16);
|
(genode_usb_session_handle_t) (handle >> 16);
|
||||||
@ -294,8 +483,13 @@ static void async_complete(struct urb *urb)
|
|||||||
|
|
||||||
genode_usb_ack_request(session, request,
|
genode_usb_ack_request(session, request,
|
||||||
handle_transfer_response, (void*)urb);
|
handle_transfer_response, (void*)urb);
|
||||||
|
|
||||||
|
iface = usb_get_iface_from_urb(urb);
|
||||||
|
urbs = usb_get_intfdata(iface);
|
||||||
|
if (!urbs->in_delete) {
|
||||||
usb_free_urb(urb);
|
usb_free_urb(urb);
|
||||||
lx_user_handle_io();
|
lx_user_handle_io();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -425,6 +619,13 @@ handle_transfer_request(struct genode_usb_request_transfer * req,
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!err) {
|
if (!err) {
|
||||||
|
struct usb_iface_urbs * urbs;
|
||||||
|
struct usb_interface * iface = usb_get_iface_from_urb(urb);
|
||||||
|
if (!usb_interface_claimed(iface))
|
||||||
|
claim_iface(iface);
|
||||||
|
urbs = usb_get_intfdata(iface);
|
||||||
|
usb_anchor_urb(urb, &urbs->submitted);
|
||||||
|
|
||||||
err = usb_submit_urb(urb, GFP_KERNEL);
|
err = usb_submit_urb(urb, GFP_KERNEL);
|
||||||
|
|
||||||
if (!err)
|
if (!err)
|
||||||
@ -503,6 +704,8 @@ void lx_user_init(void)
|
|||||||
{
|
{
|
||||||
int pid = kernel_thread(usb_poll_sessions, NULL, CLONE_FS | CLONE_FILES);
|
int pid = kernel_thread(usb_poll_sessions, NULL, CLONE_FS | CLONE_FILES);
|
||||||
lx_user_task = find_task_by_pid_ns(pid, NULL);;
|
lx_user_task = find_task_by_pid_ns(pid, NULL);;
|
||||||
|
pid = kernel_thread(usb_rpc_call, NULL, CLONE_FS | CLONE_FILES);
|
||||||
|
usb_rpc_task = find_task_by_pid_ns(pid, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -571,6 +774,11 @@ struct notifier_block usb_nb =
|
|||||||
|
|
||||||
static int usbnet_init(void)
|
static int usbnet_init(void)
|
||||||
{
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if ((err = usb_register(&usb_drv)))
|
||||||
|
return err;
|
||||||
|
|
||||||
usb_register_notify(&usb_nb);
|
usb_register_notify(&usb_nb);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user