usb_host_drv: improve error handling

Fixes 
This commit is contained in:
Christian Prochaska 2021-02-01 11:18:00 +01:00 committed by Norman Feske
parent 91a7fb1da7
commit 2670ae399b
5 changed files with 282 additions and 53 deletions
repos
dde_linux/src
drivers
usb/raw
usb_host
include
lx_emul/impl
lx_kit
os/include/usb_session

@ -28,6 +28,9 @@
#include <lx_kit/malloc.h>
#include <lx_kit/scheduler.h>
/* definition from scsi.h conflicts with USB session timeout error */
#undef TIMEOUT_ERROR
using namespace Genode;
extern "C" int usb_set_configuration(struct usb_device *dev, int configuration);
@ -175,9 +178,27 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
kfree(buf);
p.control.actual_size = err;
if (err >= 0) {
p.succeded = true;
p.control.actual_size = err;
} else {
p.control.actual_size = 0;
p.succeded = (err < 0 && err != -EPIPE) ? false : true;
if (err == -ENOENT)
p.error = Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
else if ((err == -ENODEV) || (err == -ESHUTDOWN))
p.error = Packet_descriptor::NO_DEVICE_ERROR;
else if ((err == -EPROTO) || (err == -EILSEQ))
p.error = Packet_descriptor::PROTOCOL_ERROR;
else if (err == -EPIPE)
p.error = Packet_descriptor::STALL_ERROR;
else if (err == -ETIMEDOUT)
p.error = Packet_descriptor::TIMEOUT_ERROR;
else {
Genode::error(__func__, ": unhandled error: ", err);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
}
}
/**
@ -194,16 +215,34 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
p.control.request, p.control.request_type,
p.control.value, p.control.index, buf, p.size(),
p.control.timeout);
if (err >= 0 || err== -EPIPE) {
p.control.actual_size = err;
if (err >= 0) {
p.succeded = true;
p.control.actual_size = err;
if (p.control.request == USB_REQ_CLEAR_FEATURE &&
p.control.value == USB_ENDPOINT_HALT) {
usb_reset_endpoint(_device->udev, p.control.index);
}
} else {
p.control.actual_size = 0;
if (err == -ENOENT)
p.error = Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
else if ((err == -ENODEV) || (err == -ESHUTDOWN))
p.error = Packet_descriptor::NO_DEVICE_ERROR;
else if ((err == -EPROTO) || (err == -EILSEQ))
p.error = Packet_descriptor::PROTOCOL_ERROR;
else if (err == -EPIPE)
p.error = Packet_descriptor::STALL_ERROR;
else if (err == -ETIMEDOUT)
p.error = Packet_descriptor::TIMEOUT_ERROR;
else {
Genode::error(__func__, ": unhandled error: ", err);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
}
if (err >= 0
&& p.control.request == USB_REQ_CLEAR_FEATURE
&& p.control.value == USB_ENDPOINT_HALT) {
usb_reset_endpoint(_device->udev, p.control.index);
}
kfree(buf);
}
@ -242,10 +281,15 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (read)
Genode::memcpy(_sink->packet_content(p), urb->transfer_buffer,
urb->actual_length);
}
if (urb->status == -EPIPE) {
} else if (urb->status == -ESHUTDOWN) {
p.error = Packet_descriptor::NO_DEVICE_ERROR;
} else if ((urb->status == -EPROTO) || (urb->status == -EILSEQ)) {
p.error = Packet_descriptor::PROTOCOL_ERROR;
} else if (urb->status == -EPIPE) {
p.error = Packet_descriptor::STALL_ERROR;
} else {
Genode::error(__func__, ": unhandled error: ", urb->status);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
_ack_packet(p);
@ -287,7 +331,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!bulk_urb) {
error("Failed to allocate bulk URB");
dma_free(buf);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
p.error = Usb::Packet_descriptor::MEMORY_ERROR;
return false;
}
@ -298,8 +342,14 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
int ret = usb_submit_urb(bulk_urb, GFP_KERNEL);
if (ret != 0) {
error("Failed to submit URB, error: ", ret);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
if (ret == -ENOENT)
p.error = Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
else if ((ret == -ENODEV) || (ret == -ESHUTDOWN))
p.error = Packet_descriptor::NO_DEVICE_ERROR;
else {
Genode::error(__func__, ": unhandled error: ", ret);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
free_complete_data(data);
usb_free_urb(bulk_urb);
@ -329,7 +379,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!irq_urb) {
error("Failed to allocate interrupt URB");
dma_free(buf);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
p.error = Usb::Packet_descriptor::MEMORY_ERROR;
return false;
}
@ -345,7 +395,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!ep) {
error("could not get ep: ", p.transfer.ep);
dma_free(buf);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
p.error = Usb::Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
return false;
}
@ -359,8 +409,14 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
int ret = usb_submit_urb(irq_urb, GFP_KERNEL);
if (ret != 0) {
error("Failed to submit URB, error: ", ret);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
if (ret == -ENOENT)
p.error = Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
else if ((ret == -ENODEV) || (ret == -ESHUTDOWN))
p.error = Packet_descriptor::NO_DEVICE_ERROR;
else {
Genode::error(__func__, ": unhandled error: ", ret);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
free_complete_data(data);
usb_free_urb(irq_urb);
@ -393,7 +449,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!ep) {
error("could not get ep: ", p.transfer.ep);
dma_free(buf);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
p.error = Usb::Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
return false;
}
@ -401,7 +457,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!urb) {
error("Failed to allocate isochronous URB");
dma_free(buf);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
p.error = Usb::Packet_descriptor::MEMORY_ERROR;
return false;
}
@ -429,8 +485,14 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (ret == 0)
return true;
error("Failed to submit URB, error: ", ret);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
if (ret == -ENOENT)
p.error = Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
else if ((ret == -ENODEV) || (ret == -ESHUTDOWN))
p.error = Packet_descriptor::NO_DEVICE_ERROR;
else {
Genode::error(__func__, ": unhandled error: ", ret);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
free_complete_data(data);
usb_free_urb(urb);
@ -447,6 +509,8 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
p.interface.alt_setting);
if (!err)
p.succeded = true;
else
Genode::error(__func__, ": unhandled error: ", err);
}
/**
@ -469,6 +533,8 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!err)
p.succeded = true;
else
Genode::error(__func__, ": unhandled error: ", err);
}
/**
@ -503,7 +569,15 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
_p_in_flight++;
if (!_device || !_device->udev || !_sink->packet_valid(p)) {
if (!_device || !_device->udev ||
_device->udev->state == USB_STATE_NOTATTACHED) {
p.error = Packet_descriptor::NO_DEVICE_ERROR;
_ack_packet(p);
continue;
}
if (!_sink->packet_valid(p)) {
p.error = Packet_descriptor::PACKET_INVALID_ERROR;
_ack_packet(p);
continue;
}

@ -186,9 +186,27 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
kfree(buf);
p.control.actual_size = err;
if (err >= 0) {
p.succeded = true;
p.control.actual_size = err;
} else {
p.control.actual_size = 0;
p.succeded = (err < 0 && err != -EPIPE) ? false : true;
if (err == -ENOENT)
p.error = Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
else if ((err == -ENODEV) || (err == -ESHUTDOWN))
p.error = Packet_descriptor::NO_DEVICE_ERROR;
else if ((err == -EPROTO) || (err == -EILSEQ))
p.error = Packet_descriptor::PROTOCOL_ERROR;
else if (err == -EPIPE)
p.error = Packet_descriptor::STALL_ERROR;
else if (err == -ETIMEDOUT)
p.error = Packet_descriptor::TIMEOUT_ERROR;
else {
Genode::error(__func__, ": unhandled error: ", err);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
}
}
/**
@ -205,16 +223,34 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
p.control.request, p.control.request_type,
p.control.value, p.control.index, buf, p.size(),
p.control.timeout);
if (err >= 0 || err== -EPIPE) {
p.control.actual_size = err;
if (err >= 0) {
p.succeded = true;
p.control.actual_size = err;
if (p.control.request == USB_REQ_CLEAR_FEATURE &&
p.control.value == USB_ENDPOINT_HALT) {
usb_reset_endpoint(_device->udev, p.control.index);
}
} else {
p.control.actual_size = 0;
if (err == -ENOENT)
p.error = Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
else if ((err == -ENODEV) || (err == -ESHUTDOWN))
p.error = Packet_descriptor::NO_DEVICE_ERROR;
else if ((err == -EPROTO) || (err == -EILSEQ))
p.error = Packet_descriptor::PROTOCOL_ERROR;
else if (err == -EPIPE)
p.error = Packet_descriptor::STALL_ERROR;
else if (err == -ETIMEDOUT)
p.error = Packet_descriptor::TIMEOUT_ERROR;
else {
Genode::error(__func__, ": unhandled error: ", err);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
}
if (err >= 0
&& p.control.request == USB_REQ_CLEAR_FEATURE
&& p.control.value == USB_ENDPOINT_HALT) {
usb_reset_endpoint(_device->udev, p.control.index);
}
kfree(buf);
}
@ -251,12 +287,17 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
p.succeded = true;
if (read)
Genode::memcpy(_sink->packet_content(p), urb->transfer_buffer,
Genode::memcpy(_sink->packet_content(p), urb->transfer_buffer,
urb->actual_length);
}
if (urb->status == -EPIPE) {
} else if (urb->status == -ESHUTDOWN) {
p.error = Packet_descriptor::NO_DEVICE_ERROR;
} else if ((urb->status == -EPROTO) || (urb->status == -EILSEQ)) {
p.error = Packet_descriptor::PROTOCOL_ERROR;
} else if (urb->status == -EPIPE) {
p.error = Packet_descriptor::STALL_ERROR;
} else {
Genode::error(__func__, ": unhandled error: ", urb->status);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
_ack_packet(p);
@ -298,7 +339,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!bulk_urb) {
error("Failed to allocate bulk URB");
dma_free(buf);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
p.error = Usb::Packet_descriptor::MEMORY_ERROR;
return false;
}
@ -309,8 +350,14 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
int ret = usb_submit_urb(bulk_urb, GFP_KERNEL);
if (ret != 0) {
error("Failed to submit URB, error: ", ret);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
if (ret == -ENOENT)
p.error = Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
else if ((ret == -ENODEV) || (ret == -ESHUTDOWN))
p.error = Packet_descriptor::NO_DEVICE_ERROR;
else {
Genode::error(__func__, ": unhandled error: ", ret);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
free_complete_data(data);
usb_free_urb(bulk_urb);
@ -340,7 +387,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!irq_urb) {
error("Failed to allocate interrupt URB");
dma_free(buf);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
p.error = Usb::Packet_descriptor::MEMORY_ERROR;
return false;
}
@ -356,7 +403,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!ep) {
error("could not get ep: ", p.transfer.ep);
dma_free(buf);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
p.error = Usb::Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
return false;
}
@ -370,8 +417,14 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
int ret = usb_submit_urb(irq_urb, GFP_KERNEL);
if (ret != 0) {
error("Failed to submit URB, error: ", ret);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
if (ret == -ENOENT)
p.error = Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
else if ((ret == -ENODEV) || (ret == -ESHUTDOWN))
p.error = Packet_descriptor::NO_DEVICE_ERROR;
else {
Genode::error(__func__, ": unhandled error: ", ret);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
free_complete_data(data);
usb_free_urb(irq_urb);
@ -404,7 +457,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!ep) {
error("could not get ep: ", p.transfer.ep);
dma_free(buf);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
p.error = Usb::Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
return false;
}
@ -412,7 +465,7 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!urb) {
error("Failed to allocate isochronous URB");
dma_free(buf);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
p.error = Usb::Packet_descriptor::MEMORY_ERROR;
return false;
}
@ -440,8 +493,14 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (ret == 0)
return true;
error("Failed to submit URB, error: ", ret);
p.error = Usb::Packet_descriptor::SUBMIT_ERROR;
if (ret == -ENOENT)
p.error = Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR;
else if ((ret == -ENODEV) || (ret == -ESHUTDOWN))
p.error = Packet_descriptor::NO_DEVICE_ERROR;
else {
Genode::error(__func__, ": unhandled error: ", ret);
p.error = Packet_descriptor::UNKNOWN_ERROR;
}
free_complete_data(data);
usb_free_urb(urb);
@ -458,6 +517,8 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
p.interface.alt_setting);
if (!err)
p.succeded = true;
else
Genode::error(__func__, ": unhandled error: ", err);
}
/**
@ -480,6 +541,8 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
if (!err)
p.succeded = true;
else
Genode::error(__func__, ": unhandled error: ", err);
}
/**
@ -515,8 +578,14 @@ class Usb::Worker : public Genode::Weak_object<Usb::Worker>
_p_in_flight++;
if (!_device || !_device->udev ||
_device->udev->state == USB_STATE_NOTATTACHED ||
!_sink->packet_valid(p)) {
_device->udev->state == USB_STATE_NOTATTACHED) {
p.error = Packet_descriptor::NO_DEVICE_ERROR;
_ack_packet(p);
continue;
}
if (!_sink->packet_valid(p)) {
p.error = Packet_descriptor::PACKET_INVALID_ERROR;
_ack_packet(p);
continue;
}

@ -47,8 +47,16 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe,
Genode::construct_at<Sync_ctrl_urb>(scu, *(Usb::Connection*)(dev->bus->controller), *u);
scu->send(timeout);
int ret = u->actual_length;
kfree(scu);
int ret;
if (u->status >= 0)
ret = u->actual_length;
else
ret = u->status;
usb_free_urb(u);
kfree(dr);
return ret;
}
@ -74,6 +82,22 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
Genode::construct_at<Urb>(u, *(Usb::Connection*)(urb->dev->bus->controller), *urb);
/*
* Self-destruction of the 'Urb' object in its completion function
* would not work if the 'Usb' session gets closed before the
* completion function was called. So we store the pointer in the
* otherwise unused 'hcpriv' member and free it in a following
* 'usb_submit_urb()' or 'usb_free_urb()' call.
*/
if (urb->hcpriv) {
Urb *prev_urb = (Urb*)urb->hcpriv;
prev_urb->~Urb();
kfree(urb->hcpriv);
}
urb->hcpriv = u;
u->send();
return 0;
}
@ -81,5 +105,15 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
void usb_free_urb(struct urb *urb)
{
if (!urb)
return;
/* free 'Urb' object */
if (urb->hcpriv) {
Urb *u = (Urb*)urb->hcpriv;
u->~Urb();
kfree(urb->hcpriv);
}
kfree(urb);
}

@ -27,6 +27,7 @@ class Urb : public Usb::Completion
urb & _urb;
Usb::Packet_descriptor _packet {
_usb.source()->alloc_packet(_urb.transfer_buffer_length) };
bool _completed { false };
public:
@ -79,6 +80,12 @@ class Urb : public Usb::Completion
};
}
~Urb()
{
if (!_completed)
_usb.source()->release_packet(_packet);
}
void send()
{
_usb.source()->submit_packet(_packet);
@ -88,6 +95,7 @@ class Urb : public Usb::Completion
{
if (packet.succeded) {
bool ctrl = (usb_pipetype(_urb.pipe) == PIPE_CONTROL);
_urb.status = 0;
_urb.actual_length = ctrl ? packet.control.actual_size
: packet.transfer.actual_size;
@ -96,10 +104,44 @@ class Urb : public Usb::Completion
Genode::memcpy(_urb.transfer_buffer,
_usb.source()->packet_content(packet),
_urb.actual_length);
} else {
_urb.actual_length = 0;
switch (packet.error) {
case Usb::Packet_descriptor::NO_ERROR:
Genode::error(__func__, ": got NO_ERROR code in error path");
_urb.status = -EIO;
break;
case Usb::Packet_descriptor::INTERFACE_OR_ENDPOINT_ERROR:
_urb.status = -ENOENT;
break;
case Usb::Packet_descriptor::MEMORY_ERROR:
_urb.status = -ENOMEM;
break;
case Usb::Packet_descriptor::NO_DEVICE_ERROR:
_urb.status = -ESHUTDOWN;
break;
case Usb::Packet_descriptor::PACKET_INVALID_ERROR:
_urb.status = -EINVAL;
break;
case Usb::Packet_descriptor::PROTOCOL_ERROR:
_urb.status = -EPROTO;
break;
case Usb::Packet_descriptor::STALL_ERROR:
_urb.status = -EPIPE;
break;
case Usb::Packet_descriptor::TIMEOUT_ERROR:
_urb.status = -ETIMEDOUT;
break;
case Usb::Packet_descriptor::UNKNOWN_ERROR:
Genode::error(__func__, ": got UNKNOWN_ERROR code");
_urb.status = -EIO;
break;
}
}
if (_urb.complete) _urb.complete(&_urb);
kfree(_packet.completion);
_completed = true;
}
};

@ -82,7 +82,17 @@ struct Usb::Packet_descriptor : Genode::Packet_descriptor
};
};
enum Error { NO_ERROR, STALL_ERROR, SUBMIT_ERROR };
enum Error {
NO_ERROR,
INTERFACE_OR_ENDPOINT_ERROR,
MEMORY_ERROR,
NO_DEVICE_ERROR,
PACKET_INVALID_ERROR,
PROTOCOL_ERROR,
STALL_ERROR,
TIMEOUT_ERROR,
UNKNOWN_ERROR
};
Error error = NO_ERROR;