qemu-usb: add isochronous packet support to XHCI

fixes #2910
This commit is contained in:
Sebastian Sumpf 2018-07-06 15:30:09 +02:00 committed by Christian Helmuth
parent 0ed2ef230b
commit 82075a340d
5 changed files with 438 additions and 122 deletions

View File

@ -1 +1 @@
a7b787edadc09700d4619f783404b70ef63681d2 2ff18fbfd11a955148cfbdcaa81d4a1162750fcd

View File

@ -16,24 +16,83 @@
#include <qemu_emul.h> #include <qemu_emul.h>
#include <hw/usb.h> #include <hw/usb.h>
#include <extern_c_end.h> #include <extern_c_end.h>
#include <base/debug.h>
using namespace Genode; using namespace Genode;
static bool const verbose_devices = false; static bool const verbose_devices = false;
static bool const verbose_host = false; static bool const verbose_host = false;
static bool const verbose_warnings = false;
Lock _lock; Lock _lock;
static void update_ep(USBDevice *); static void update_ep(USBDevice *);
static bool claim_interfaces(USBDevice *dev); static bool claim_interfaces(USBDevice *dev);
using Packet_alloc_failed = Usb::Session::Tx::Source::Packet_alloc_failed;
using Packet_type = Usb::Packet_descriptor::Type;
using Packet_error = Usb::Packet_descriptor::Error;
static unsigned endpoint_number(USBEndpoint const *usb_ep)
{
bool in = usb_ep->pid == USB_TOKEN_IN;
return usb_ep->nr | (in ? USB_DIR_IN : 0);
}
class Isoc_packet : Fifo<Isoc_packet>::Element
{
friend class Fifo<Isoc_packet>;
friend class Usb_host_device;
private:
Usb::Packet_descriptor _packet;
int _offset { 0 };
char *_content;
int _size;
public:
Isoc_packet(Usb::Packet_descriptor packet, char *content)
: _packet(packet), _content(content),
_size (_packet.read_transfer() ? _packet.transfer.actual_size : _packet.size())
{ }
bool copy(USBPacket *usb_packet)
{
if (!valid()) return false;
int remaining = _size - _offset;
int copy_size = min(usb_packet->iov.size, remaining);
usb_packet_copy(usb_packet, _content + _offset, copy_size);
_offset += copy_size;
if (!_packet.read_transfer()) {
_packet.transfer.packet_size[_packet.transfer.number_of_packets] = copy_size;
_packet.transfer.number_of_packets++;
}
return remaining <= usb_packet->iov.size;
}
bool valid() const { return _content != nullptr; }
unsigned packet_count() const { return _packet.transfer.number_of_packets; }
Usb::Packet_descriptor& packet() { return _packet; }
};
struct Completion : Usb::Completion struct Completion : Usb::Completion
{ {
USBPacket *p = nullptr; USBPacket *p = nullptr;
USBDevice *dev = nullptr; USBDevice *dev = nullptr;
uint8_t *data = nullptr; uint8_t *data = nullptr;
unsigned endpoint = 0;
enum State { VALID, FREE, CANCELED }; enum State { VALID, FREE, CANCELED };
State state = FREE; State state = FREE;
@ -41,17 +100,18 @@ struct Completion : Usb::Completion
void complete(Usb::Packet_descriptor &packet, char *content) void complete(Usb::Packet_descriptor &packet, char *content)
{ {
if (state != VALID) if (state != VALID) {
return; return;
}
int actual_size = 0; int actual_size = 0;
switch (packet.type) { switch (packet.type) {
case Usb::Packet_descriptor::CTRL: case Packet_type::CTRL:
actual_size = packet.control.actual_size; actual_size = packet.control.actual_size;
break; break;
case Usb::Packet_descriptor::BULK: case Packet_type::BULK:
case Usb::Packet_descriptor::IRQ: case Packet_type::IRQ:
actual_size = packet.transfer.actual_size; actual_size = packet.transfer.actual_size;
default: default:
break; break;
@ -60,7 +120,7 @@ struct Completion : Usb::Completion
if (actual_size < 0) actual_size = 0; if (actual_size < 0) actual_size = 0;
if (verbose_host) if (verbose_host)
log(__func__, ": packet.type: ", (int)packet.type, " " log(__func__, ": packet: ", p, " packet.type: ", (int)packet.type, " "
"actual_size: ", Hex(actual_size)); "actual_size: ", Hex(actual_size));
p->actual_length = 0; p->actual_length = 0;
@ -75,29 +135,34 @@ struct Completion : Usb::Completion
if (packet.succeded) if (packet.succeded)
p->status = USB_RET_SUCCESS; p->status = USB_RET_SUCCESS;
else { else {
if (packet.error == Usb::Packet_descriptor::STALL_ERROR) if (packet.error == Packet_error::STALL_ERROR)
p->status = USB_RET_STALL; p->status = USB_RET_STALL;
else else
p->status = USB_RET_IOERROR; p->status = USB_RET_IOERROR;
} }
switch (packet.type) { switch (packet.type) {
case Usb::Packet_descriptor::CONFIG: case Packet_type::CONFIG:
if (!claim_interfaces(dev)) if (!claim_interfaces(dev))
p->status = USB_RET_IOERROR; p->status = USB_RET_IOERROR;
case Usb::Packet_descriptor::ALT_SETTING: case Packet_type::ALT_SETTING:
update_ep(dev); update_ep(dev);
case Usb::Packet_descriptor::CTRL: case Packet_type::CTRL:
usb_generic_async_ctrl_complete(dev, p); usb_generic_async_ctrl_complete(dev, p);
break; break;
case Usb::Packet_descriptor::BULK: case Packet_type::BULK:
case Usb::Packet_descriptor::IRQ: case Packet_type::IRQ:
usb_packet_complete(dev, p); usb_packet_complete(dev, p);
break; break;
default: default:
break; break;
} }
} }
bool valid() const { return state == VALID; }
void cancel() { state = CANCELED; }
void free() { state = FREE; }
unsigned ep() const { return p ? endpoint_number(p->ep) : endpoint; }
}; };
@ -114,7 +179,7 @@ struct Dev_info
vendor(vendor), product(product), bus(bus), dev(dev) vendor(vendor), product(product), bus(bus), dev(dev)
{ } { }
void print(Genode::Output &out) const void print(Output &out) const
{ {
Genode::print(out, Hex(bus, Hex::OMIT_PREFIX, Hex::PAD), ":", Genode::print(out, Hex(bus, Hex::OMIT_PREFIX, Hex::PAD), ":",
Hex(dev, Hex::OMIT_PREFIX, Hex::PAD), " (", Hex(dev, Hex::OMIT_PREFIX, Hex::PAD), " (",
@ -137,21 +202,27 @@ struct Dev_info
struct Usb_host_device : List<Usb_host_device>::Element struct Usb_host_device : List<Usb_host_device>::Element
{ {
struct Could_not_create_device : Genode::Exception { }; struct Could_not_create_device : Exception { };
bool deleted = false; bool deleted = false;
char const *label = nullptr; char const *label = nullptr;
Dev_info const info; Dev_info const info;
USBHostDevice *qemu_dev; USBHostDevice *qemu_dev;
Completion completion[Usb::Session::TX_QUEUE_SIZE];
/* submit queue + ack queue + 1 -> max nr of packets in flight */
enum { NUM_COMPLETIONS = Usb::Session::TX_QUEUE_SIZE * 2 + 1 };
Completion completion[NUM_COMPLETIONS];
Fifo<Isoc_packet> isoc_read_queue;
Reconstructible<Isoc_packet> isoc_write_packet { Usb::Packet_descriptor(), nullptr };
Signal_receiver &sig_rec; Signal_receiver &sig_rec;
Signal_dispatcher<Usb_host_device> state_dispatcher { sig_rec, *this, &Usb_host_device::state_change }; Signal_dispatcher<Usb_host_device> state_dispatcher { sig_rec, *this, &Usb_host_device::state_change };
Allocator_avl _alloc; Allocator &_alloc;
Usb::Connection usb_raw; // { &alloc, label, 1024*1024, state_dispatcher }; Allocator_avl _usb_alloc { &_alloc };
Usb::Connection usb_raw; //{ &_usb_alloc, label, 1024*1024, state_dispatcher };
Signal_dispatcher<Usb_host_device> ack_avail_dispatcher { sig_rec, *this, &Usb_host_device::ack_avail }; Signal_dispatcher<Usb_host_device> ack_avail_dispatcher { sig_rec, *this, &Usb_host_device::ack_avail };
@ -160,10 +231,12 @@ struct Usb_host_device : List<Usb_host_device>::Element
Usb::Config_descriptor cdescr; Usb::Config_descriptor cdescr;
Usb::Device_descriptor ddescr; Usb::Device_descriptor ddescr;
usb_raw.config_descriptor(&ddescr, &cdescr); try { usb_raw.config_descriptor(&ddescr, &cdescr); }
catch (Usb::Session::Device_not_found) { return; }
for (unsigned i = 0; i < cdescr.num_interfaces; i++) { for (unsigned i = 0; i < cdescr.num_interfaces; i++) {
usb_raw.release_interface(i); try { usb_raw.release_interface(i); }
catch (Usb::Session::Device_not_found) { return; }
} }
} }
@ -172,7 +245,8 @@ struct Usb_host_device : List<Usb_host_device>::Element
Usb::Config_descriptor cdescr; Usb::Config_descriptor cdescr;
Usb::Device_descriptor ddescr; Usb::Device_descriptor ddescr;
usb_raw.config_descriptor(&ddescr, &cdescr); try { usb_raw.config_descriptor(&ddescr, &cdescr); }
catch (Usb::Session::Device_not_found) { return false; }
bool result = true; bool result = true;
for (unsigned i = 0; i < cdescr.num_interfaces; i++) { for (unsigned i = 0; i < cdescr.num_interfaces; i++) {
@ -189,11 +263,11 @@ struct Usb_host_device : List<Usb_host_device>::Element
} }
Usb_host_device(Signal_receiver &sig_rec, Allocator &alloc, Usb_host_device(Signal_receiver &sig_rec, Allocator &alloc,
Genode::Env &env, char const *label, Env &env, char const *label,
Dev_info info) Dev_info info)
: :
label(label), _alloc(&alloc), label(label), _alloc(alloc),
usb_raw(env, &_alloc, label, 6*1024*1024, state_dispatcher), usb_raw(env, &_usb_alloc, label, 6*1024*1024, state_dispatcher),
info(info), sig_rec(sig_rec) info(info), sig_rec(sig_rec)
{ {
usb_raw.tx_channel()->sigh_ack_avail(ack_avail_dispatcher); usb_raw.tx_channel()->sigh_ack_avail(ack_avail_dispatcher);
@ -207,6 +281,11 @@ struct Usb_host_device : List<Usb_host_device>::Element
throw Could_not_create_device(); throw Could_not_create_device();
} }
~Usb_host_device()
{
isoc_in_flush(0, true);
}
static int to_qemu_speed(unsigned speed) static int to_qemu_speed(unsigned speed)
{ {
switch (speed) { switch (speed) {
@ -227,12 +306,148 @@ struct Usb_host_device : List<Usb_host_device>::Element
while (usb_raw.source()->ack_avail()) { while (usb_raw.source()->ack_avail()) {
Usb::Packet_descriptor packet = usb_raw.source()->get_acked_packet(); Usb::Packet_descriptor packet = usb_raw.source()->get_acked_packet();
Completion *c = dynamic_cast<Completion *>(packet.completion);
char *packet_content = usb_raw.source()->packet_content(packet); if ((packet.type == Packet_type::ISOC && !packet.read_transfer()) ||
dynamic_cast<Completion *>(packet.completion)->complete(packet, packet_content); (c && c->state == Completion::CANCELED)) {
free_packet(packet); free_packet(packet);
continue;
}
char *content = usb_raw.source()->packet_content(packet);
if (packet.type != Packet_type::ISOC) {
c->complete(packet, content);
free_packet(packet);
} else {
/* isochronous in */
free_completion(packet);
_isoc_in_pending--;
isoc_read_queue.enqueue(new (_alloc) Isoc_packet(packet, content));
} }
} }
}
/**********************************
** Isochronous packet handling **
**********************************/
bool isoc_read(USBPacket *packet)
{
if (isoc_read_queue.empty()) {
return false;
}
if (isoc_read_queue.head()->copy(packet)) {
free_packet(isoc_read_queue.dequeue());
}
return true;
}
unsigned _isoc_in_pending = 0;
bool isoc_new_packet()
{
unsigned count = 0;
for (Isoc_packet *packet = isoc_read_queue.head(); packet;
packet = packet->next(), count++);
return (count + _isoc_in_pending) < 3 ? true : false;
}
void isoc_in_packet(USBPacket *usb_packet)
{
enum { NUMBER_OF_PACKETS = 2 };
isoc_read(usb_packet);
if (!isoc_new_packet())
return;
size_t size = usb_packet->ep->max_packet_size * NUMBER_OF_PACKETS;
try {
Usb::Packet_descriptor packet = alloc_packet(size);
packet.type = Packet_type::ISOC;
packet.transfer.ep = usb_packet->ep->nr | USB_DIR_IN;
packet.transfer.polling_interval = Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL;
packet.transfer.number_of_packets = NUMBER_OF_PACKETS;
for (unsigned i = 0; i < NUMBER_OF_PACKETS; i++) {
packet.transfer.packet_size[i] = usb_packet->ep->max_packet_size;
}
Completion *c = dynamic_cast<Completion *>(packet.completion);
c->p = nullptr;
c->dev = usb_packet->ep->dev;
c->data = nullptr;
c->endpoint = endpoint_number(usb_packet->ep);
_isoc_in_pending++;
submit(packet);
} catch (Packet_alloc_failed) {
if (verbose_warnings)
warning("xHCI: packet allocation failed (size ", Hex(size), "in ", __func__, ")");
}
}
void isoc_in_flush(unsigned ep, bool all = false)
{
/* flush finished and stored data */
for (Isoc_packet *packet = isoc_read_queue.head(); packet; )
{
if (!all && (!packet->valid() || packet->packet().transfer.ep != ep)) {
packet = packet->next();
continue;
}
Isoc_packet *next = packet->next();
isoc_read_queue.remove(packet);
free_packet(packet);
packet = next;
}
/* flush in flight packets */
flush_completion(ep);
}
void isoc_out_packet(USBPacket *usb_packet)
{
enum { NUMBER_OF_PACKETS = 32 };
bool valid = isoc_write_packet->valid();
if (valid) {
isoc_write_packet->copy(usb_packet);
if (isoc_write_packet->packet_count() < NUMBER_OF_PACKETS)
return;
submit(isoc_write_packet->packet());
}
size_t size = usb_packet->ep->max_packet_size * NUMBER_OF_PACKETS;
try {
Usb::Packet_descriptor packet = alloc_packet(size, false);
packet.type = Packet_type::ISOC;
packet.transfer.ep = usb_packet->ep->nr;
packet.transfer.polling_interval = Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL;
packet.transfer.number_of_packets = 0;
isoc_write_packet.construct(packet, usb_raw.source()->packet_content(packet));
if (!valid) isoc_write_packet->copy(usb_packet);
} catch (Packet_alloc_failed) {
if (verbose_warnings)
warning("xHCI: packet allocation failed (size ", Hex(size), "in ", __func__, ")");
isoc_write_packet.construct(Usb::Packet_descriptor(), nullptr);
return;
}
}
/***********************
** Device disconnect **
***********************/
void _destroy() void _destroy()
{ {
@ -262,45 +477,89 @@ struct Usb_host_device : List<Usb_host_device>::Element
_destroy(); _destroy();
} }
Usb::Packet_descriptor alloc_packet(int length)
{
if (!usb_raw.source()->ready_to_submit()) /*******************
throw -1; ** Packet stream **
*******************/
Usb::Packet_descriptor alloc_packet(int length, bool completion = true)
{
if (!usb_raw.source()->ready_to_submit()) {
throw Packet_alloc_failed();
}
Usb::Packet_descriptor packet = usb_raw.source()->alloc_packet(length); Usb::Packet_descriptor packet = usb_raw.source()->alloc_packet(length);
if (!completion) {
packet.completion = nullptr;
return packet;
}
packet.completion = alloc_completion(); packet.completion = alloc_completion();
if (!packet.completion) {
usb_raw.source()->release_packet(packet);
throw Packet_alloc_failed();
}
return packet; return packet;
} }
void free_packet(Usb::Packet_descriptor &packet) void free_packet(Usb::Packet_descriptor &packet)
{ {
dynamic_cast<Completion *>(packet.completion)->state = Completion::FREE; free_completion(packet);
usb_raw.source()->release_packet(packet); usb_raw.source()->release_packet(packet);
} }
void free_packet(Isoc_packet *packet)
{
free_packet(packet->packet());
Genode::destroy(_alloc, packet);
}
Completion *alloc_completion() Completion *alloc_completion()
{ {
for (unsigned i = 0; i < Usb::Session::TX_QUEUE_SIZE; i++) for (unsigned i = 0; i < NUM_COMPLETIONS; i++)
if (completion[i].state == Completion::FREE) { if (completion[i].state == Completion::FREE) {
completion[i]. state = Completion::VALID; completion[i].state = Completion::VALID;
return &completion[i]; return &completion[i];
} }
return nullptr; return nullptr;
} }
Completion *find_completion(USBPacket *p) void free_completion(Usb::Packet_descriptor &packet)
{ {
for (unsigned i = 0; i < Usb::Session::TX_QUEUE_SIZE; i++) if (packet.completion) {
if (completion[i].p == p) dynamic_cast<Completion *>(packet.completion)->free();
}
}
Completion *find_valid_completion(USBPacket *p)
{
for (unsigned i = 0; i < NUM_COMPLETIONS; i++)
if (completion[i].p == p && completion[i].valid())
return &completion[i]; return &completion[i];
return nullptr; return nullptr;
} }
void submit(Usb::Packet_descriptor p) { void flush_completion(unsigned ep)
usb_raw.source()->submit_packet(p); } {
for (unsigned i = 0; i < NUM_COMPLETIONS; i++)
{
if (!completion[i].valid())
continue;
if (completion[i].ep() == ep) {
completion[i].cancel();
}
}
}
void submit(Usb::Packet_descriptor p)
{
usb_raw.source()->submit_packet(p);
}
bool claim_interfaces() { return _claim_interfaces(); } bool claim_interfaces() { return _claim_interfaces(); }
@ -308,6 +567,7 @@ struct Usb_host_device : List<Usb_host_device>::Element
{ {
_release_interfaces(); _release_interfaces();
try {
Usb::Packet_descriptor packet = alloc_packet(0); Usb::Packet_descriptor packet = alloc_packet(0);
packet.type = Usb::Packet_descriptor::CONFIG; packet.type = Usb::Packet_descriptor::CONFIG;
packet.number = value; packet.number = value;
@ -317,10 +577,17 @@ struct Usb_host_device : List<Usb_host_device>::Element
c->dev = cast_USBDevice(qemu_dev); c->dev = cast_USBDevice(qemu_dev);
submit(packet); submit(packet);
p->status = USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} catch (...) {
if (verbose_warnings)
warning(__func__, " packet allocation failed");
p->status = USB_RET_NAK;
}
} }
void set_interface(int index, uint8_t value, USBPacket *p) void set_interface(int index, uint8_t value, USBPacket *p)
{ {
try {
Usb::Packet_descriptor packet = alloc_packet(0); Usb::Packet_descriptor packet = alloc_packet(0);
packet.type = Usb::Packet_descriptor::ALT_SETTING; packet.type = Usb::Packet_descriptor::ALT_SETTING;
packet.interface.number = index; packet.interface.number = index;
@ -331,6 +598,11 @@ struct Usb_host_device : List<Usb_host_device>::Element
c->dev = cast_USBDevice(qemu_dev); c->dev = cast_USBDevice(qemu_dev);
submit(packet); submit(packet);
p->status = USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} catch (...) {
if (verbose_warnings)
warning(__func__, " packet allocation failed");
p->status = USB_RET_NAK;
}
} }
void update_ep(USBDevice *udev) void update_ep(USBDevice *udev)
@ -341,7 +613,8 @@ struct Usb_host_device : List<Usb_host_device>::Element
Usb::Config_descriptor cdescr; Usb::Config_descriptor cdescr;
Usb::Device_descriptor ddescr; Usb::Device_descriptor ddescr;
usb_raw.config_descriptor(&ddescr, &cdescr); try { usb_raw.config_descriptor(&ddescr, &cdescr); }
catch (Usb::Session::Device_not_found) { return; }
for (unsigned i = 0; i < cdescr.num_interfaces; i++) { for (unsigned i = 0; i < cdescr.num_interfaces; i++) {
udev->altsetting[i] = usb_raw.alt_settings(i); udev->altsetting[i] = usb_raw.alt_settings(i);
@ -407,7 +680,8 @@ static void usb_host_realize(USBDevice *udev, Error **errp)
Usb::Config_descriptor cdescr; Usb::Config_descriptor cdescr;
Usb::Device_descriptor ddescr; Usb::Device_descriptor ddescr;
dev->usb_raw.config_descriptor(&ddescr, &cdescr); try { dev->usb_raw.config_descriptor(&ddescr, &cdescr); }
catch (Usb::Session::Device_not_found) { return; }
if (verbose_host) if (verbose_host)
log("set udev->speed to %d", Usb_host_device::to_qemu_speed(ddescr.speed)); log("set udev->speed to %d", Usb_host_device::to_qemu_speed(ddescr.speed));
@ -425,8 +699,10 @@ static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p)
{ {
USBHostDevice *d = USB_HOST_DEVICE(udev); USBHostDevice *d = USB_HOST_DEVICE(udev);
Usb_host_device *dev = (Usb_host_device *)d->data; Usb_host_device *dev = (Usb_host_device *)d->data;
Completion *c = dev->find_completion(p); Completion *c = dev->find_valid_completion(p);
c->state = Completion::CANCELED;
if (c)
c->cancel();
} }
@ -434,26 +710,32 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
{ {
USBHostDevice *d = USB_HOST_DEVICE(udev); USBHostDevice *d = USB_HOST_DEVICE(udev);
Usb_host_device *dev = (Usb_host_device *)d->data; Usb_host_device *dev = (Usb_host_device *)d->data;
Genode::size_t size = 0; Genode::size_t size = 0;
Usb::Packet_descriptor::Type type = Usb::Packet_descriptor::BULK; Usb::Packet_descriptor::Type type = Usb::Packet_descriptor::BULK;
bool const in = p->pid == USB_TOKEN_IN;
switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) { switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) {
case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_BULK:
type = Usb::Packet_descriptor::BULK; type = Usb::Packet_descriptor::BULK;
size = usb_packet_size(p); size = usb_packet_size(p);
p->status = USB_RET_ASYNC;
break; break;
case USB_ENDPOINT_XFER_INT: case USB_ENDPOINT_XFER_INT:
type = Usb::Packet_descriptor::IRQ; type = Usb::Packet_descriptor::IRQ;
size = p->iov.size; size = p->iov.size;
p->status = USB_RET_ASYNC;
break; break;
case USB_ENDPOINT_XFER_ISOC:
if (in)
dev->isoc_in_packet(p);
else
dev->isoc_out_packet(p);
return;
default: default:
error("not supported data request"); error("not supported data request");
break; break;
} }
bool const in = p->pid == USB_TOKEN_IN;
try { try {
Usb::Packet_descriptor packet = dev->alloc_packet(size); Usb::Packet_descriptor packet = dev->alloc_packet(size);
packet.type = type; packet.type = type;
@ -471,10 +753,9 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
c->data = nullptr; c->data = nullptr;
dev->submit(packet); dev->submit(packet);
p->status = USB_RET_ASYNC; } catch (Packet_alloc_failed) {
} catch (...) { if (verbose_warnings)
/* submit queue full or packet larger than packet stream */ warning("xHCI: packet allocation failed (size ", Hex(size), "in ", __func__, ")");
Genode::warning("xHCI: packet allocation failed (size ", Genode::Hex(size), ")");
p->status = USB_RET_NAK; p->status = USB_RET_NAK;
} }
} }
@ -512,7 +793,11 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
Usb::Packet_descriptor packet; Usb::Packet_descriptor packet;
try { try {
packet = dev->alloc_packet(length); packet = dev->alloc_packet(length);
} catch (...) { error("Packet allocation failed"); return; } } catch (...) {
if (verbose_warnings)
warning("Packet allocation failed");
return;
}
packet.type = Usb::Packet_descriptor::CTRL; packet.type = Usb::Packet_descriptor::CTRL;
packet.control.request_type = request >> 8; packet.control.request_type = request >> 8;
@ -535,6 +820,24 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
} }
static void usb_host_ep_stopped(USBDevice *udev, USBEndpoint *usb_ep)
{
USBHostDevice *d = USB_HOST_DEVICE(udev);
Usb_host_device *dev = (Usb_host_device *)d->data;
bool in = usb_ep->pid == USB_TOKEN_IN;
unsigned ep = endpoint_number(usb_ep);
switch (usb_ep->type) {
case USB_ENDPOINT_XFER_ISOC:
if (in)
dev->isoc_in_flush(ep);
default:
return;
}
}
static Property usb_host_dev_properties[] = { static Property usb_host_dev_properties[] = {
DEFINE_PROP_END_OF_LIST(), DEFINE_PROP_END_OF_LIST(),
}; };
@ -550,6 +853,7 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data)
uc->cancel_packet = usb_host_cancel_packet; uc->cancel_packet = usb_host_cancel_packet;
uc->handle_data = usb_host_handle_data; uc->handle_data = usb_host_handle_data;
uc->handle_control = usb_host_handle_control; uc->handle_control = usb_host_handle_control;
uc->ep_stopped = usb_host_ep_stopped;
dc->props = usb_host_dev_properties; dc->props = usb_host_dev_properties;
} }
@ -624,10 +928,10 @@ struct Usb_devices : List<Usb_host_device>
Dev_info const dev_info(bus, dev, vendor, product); Dev_info const dev_info(bus, dev, vendor, product);
Genode::String<128> label; String<128> label;
try { try {
node.attribute("label").value(&label); node.attribute("label").value(&label);
} catch (Genode::Xml_attribute::Nonexistent_attribute) { } catch (Xml_attribute::Nonexistent_attribute) {
error("no label found for device ", dev_info); error("no label found for device ", dev_info);
return; return;
} }
@ -653,7 +957,6 @@ struct Usb_devices : List<Usb_host_device>
insert(new_device); insert(new_device);
log("Attach USB device ", dev_info); log("Attach USB device ", dev_info);
} catch (...) { } catch (...) {
error("could not attach USB device ", dev_info); error("could not attach USB device ", dev_info);
} }
@ -669,7 +972,7 @@ struct Usb_devices : List<Usb_host_device>
_garbage_collect(); _garbage_collect();
} }
Usb_devices(Signal_receiver *sig_rec, Allocator &alloc, Genode::Env &env) Usb_devices(Signal_receiver *sig_rec, Allocator &alloc, Env &env)
: _sig_rec(*sig_rec), _env(env), _alloc(alloc) : _sig_rec(*sig_rec), _env(env), _alloc(alloc)
{ {
_devices_rom.sigh(_device_dispatcher); _devices_rom.sigh(_device_dispatcher);
@ -707,9 +1010,9 @@ extern "C" void usb_host_update_devices()
/* /*
* Do not use type_init macro because of name mangling * Do not use type_init macro because of name mangling
*/ */
extern "C" void _type_init_usb_host_register_types(Genode::Signal_receiver *sig_rec, extern "C" void _type_init_usb_host_register_types(Signal_receiver *sig_rec,
Genode::Allocator *alloc, Allocator *alloc,
Genode::Env *env) Env *env)
{ {
usb_host_register_types(); usb_host_register_types();

View File

@ -56,10 +56,8 @@ enum {
EINVAL = 22, EINVAL = 22,
}; };
void *malloc(size_t size);
void *memset(void *s, int c, size_t n); void *memset(void *s, int c, size_t n);
void *memcpy(void *dest, const void *src, size_t n); void *memcpy(void *dest, const void *src, size_t n);
void free(void *p);
void abort(); void abort();
@ -247,17 +245,17 @@ const char *object_get_typename(Object *obj);
** glib emulation ** ** glib emulation **
********************/ ********************/
void *g_malloc(size_t size);
void g_free(void *ptr);
#define g_new0(type, count)({ \ #define g_new0(type, count)({ \
typeof(type) *t = (typeof(type)*)malloc(sizeof(type) * count); \ typeof(type) *t = (typeof(type)*)g_malloc(sizeof(type) * count); \
memset(t, 0, sizeof(type) * count); \ memset(t, 0, sizeof(type) * count); \
t; \ t; \
}) })
#define g_free(p) free(p)
#define g_malloc malloc
#define g_malloc0(size) ({ \ #define g_malloc0(size) ({ \
void *t = malloc((size)); \ void *t = g_malloc((size)); \
memset(t, 0, (size)); \ memset(t, 0, (size)); \
t; \ t; \
}) })

View File

@ -1,5 +1,5 @@
diff --git a/src/lib/qemu/hw/usb/hcd-xhci.c b/src/lib/qemu/hw/usb/hcd-xhci.c diff --git a/src/lib/qemu/hw/usb/hcd-xhci.c b/src/lib/qemu/hw/usb/hcd-xhci.c
index c673bed..b2a8939 100644 index c673bed..cd930da 100644
--- a/src/lib/qemu/hw/usb/hcd-xhci.c --- a/src/lib/qemu/hw/usb/hcd-xhci.c
+++ b/src/lib/qemu/hw/usb/hcd-xhci.c +++ b/src/lib/qemu/hw/usb/hcd-xhci.c
@@ -486,6 +486,8 @@ struct XHCIState { @@ -486,6 +486,8 @@ struct XHCIState {
@ -11,7 +11,22 @@ index c673bed..b2a8939 100644
#define XHCI(obj) \ #define XHCI(obj) \
OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI) OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI)
@@ -2361,6 +2363,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, @@ -1349,6 +1351,14 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
static void xhci_ep_kick_timer(void *opaque)
{
XHCIEPContext *epctx = opaque;
+
+ /*
+ * We might have blocked in the entry lock, while the endpoint was stopped
+ * by another thread
+ */
+ if (epctx->state == CC_STOPPED)
+ return;
+
xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid, 0);
}
@@ -2361,6 +2371,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
if (bsr) { if (bsr) {
slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT; slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
@ -19,7 +34,7 @@ index c673bed..b2a8939 100644
} else { } else {
USBPacket p; USBPacket p;
uint8_t buf[1]; uint8_t buf[1];
@@ -3914,3 +3917,5 @@ static void xhci_register_types(void) @@ -3914,3 +3925,5 @@ static void xhci_register_types(void)
} }
type_init(xhci_register_types) type_init(xhci_register_types)

View File

@ -92,20 +92,20 @@ void Qemu::usb_timer_callback(void (*cb)(void*), void *data)
** libc ** ** libc **
**********/ **********/
void *malloc(size_t size) { void *g_malloc(size_t size) {
return _heap->alloc(size); } return _heap->alloc(size); }
void g_free(void *p) {
if (!p) return;
_heap->free(p, 0);
}
void *memset(void *s, int c, size_t n) { void *memset(void *s, int c, size_t n) {
return Genode::memset(s, c, n); } return Genode::memset(s, c, n); }
void free(void *p) {
if (!p) return;
_heap->free(p, 0);
}
void q_printf(char const *fmt, ...) void q_printf(char const *fmt, ...)
{ {
va_list va; va_list va;
@ -400,7 +400,7 @@ Type type_register_static(TypeInfo const *t)
{ {
if (!Genode::strcmp(t->name, TYPE_XHCI)) { if (!Genode::strcmp(t->name, TYPE_XHCI)) {
Wrapper *w = &Object_pool::p()->obj[Object_pool::XHCI]; Wrapper *w = &Object_pool::p()->obj[Object_pool::XHCI];
w->_xhci_state = (XHCIState*) malloc(sizeof(XHCIState)); w->_xhci_state = (XHCIState*) g_malloc(sizeof(XHCIState));
Genode::memset(w->_xhci_state, 0, sizeof(XHCIState)); Genode::memset(w->_xhci_state, 0, sizeof(XHCIState));
t->class_init(&w->_object_class, 0); t->class_init(&w->_object_class, 0);
@ -439,7 +439,7 @@ void qbus_create_inplace(void* bus, size_t size , const char* type,
Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_BUS]; Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_BUS];
BusState *b = &w->_bus_state; BusState *b = &w->_bus_state;
char const *n = "xhci.0"; char const *n = "xhci.0";
b->name = (char *)malloc(Genode::strlen(n) + 1); b->name = (char *)g_malloc(Genode::strlen(n) + 1);
Genode::strncpy(b->name, n, Genode::strlen(n) + 1); Genode::strncpy(b->name, n, Genode::strlen(n) + 1);
} }
@ -453,7 +453,7 @@ void timer_del(QEMUTimer *t)
void timer_free(QEMUTimer *t) void timer_free(QEMUTimer *t)
{ {
_timer_queue->delete_timer(t); _timer_queue->delete_timer(t);
free(t); g_free(t);
} }
@ -465,7 +465,7 @@ void timer_mod(QEMUTimer *t, int64_t expire)
QEMUTimer* timer_new_ns(QEMUClockType, void (*cb)(void*), void *opaque) QEMUTimer* timer_new_ns(QEMUClockType, void (*cb)(void*), void *opaque)
{ {
QEMUTimer *t = (QEMUTimer*)malloc(sizeof(QEMUTimer)); QEMUTimer *t = (QEMUTimer*)g_malloc(sizeof(QEMUTimer));
if (t == nullptr) { if (t == nullptr) {
Genode::error("could not create QEMUTimer"); Genode::error("could not create QEMUTimer");
return nullptr; return nullptr;
@ -710,7 +710,7 @@ void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len)
" <= niov: ", niov); " <= niov: ", niov);
qiov->alloc_hint += 64; qiov->alloc_hint += 64;
iovec *new_iov = (iovec*) malloc(sizeof(iovec) * qiov->alloc_hint); iovec *new_iov = (iovec*) g_malloc(sizeof(iovec) * qiov->alloc_hint);
if (new_iov == nullptr) { if (new_iov == nullptr) {
Genode::error("could not reallocate iov"); Genode::error("could not reallocate iov");
throw -1; throw -1;
@ -721,7 +721,7 @@ void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len)
new_iov[i].iov_len = qiov->iov[i].iov_len; new_iov[i].iov_len = qiov->iov[i].iov_len;
} }
free(qiov->iov); g_free(qiov->iov);
qiov->iov = new_iov; qiov->iov = new_iov;
} }
@ -740,7 +740,7 @@ void qemu_iovec_destroy(QEMUIOVector *qiov)
{ {
qemu_iovec_reset(qiov); qemu_iovec_reset(qiov);
free(qiov->iov); g_free(qiov->iov);
qiov->iov = nullptr; qiov->iov = nullptr;
} }
@ -770,7 +770,7 @@ void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint)
qiov->alloc_hint = alloc_hint; qiov->alloc_hint = alloc_hint;
qiov->iov = (iovec*) malloc(sizeof(iovec) * alloc_hint); qiov->iov = (iovec*) g_malloc(sizeof(iovec) * alloc_hint);
if (qiov->iov == nullptr) { if (qiov->iov == nullptr) {
Genode::error("could not allocate iov"); Genode::error("could not allocate iov");
throw -1; throw -1;
@ -890,7 +890,7 @@ void error_setg(Error **errp, const char *fmt, ...)
{ {
assert(*errp == nullptr); assert(*errp == nullptr);
*errp = (Error*) malloc(sizeof(Error)); *errp = (Error*) g_malloc(sizeof(Error));
if (*errp == nullptr) { if (*errp == nullptr) {
Genode::error("could not allocate Error"); Genode::error("could not allocate Error");
return; return;
@ -911,4 +911,4 @@ void error_propagate(Error **dst_errp, Error *local_err) {
*dst_errp = local_err; } *dst_errp = local_err; }
void error_free(Error *err) { free(err); } void error_free(Error *err) { g_free(err); }