|
|
|
@ -16,24 +16,83 @@
|
|
|
|
|
#include <qemu_emul.h>
|
|
|
|
|
#include <hw/usb.h>
|
|
|
|
|
#include <extern_c_end.h>
|
|
|
|
|
#include <base/debug.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
|
|
static bool const verbose_devices = false;
|
|
|
|
|
static bool const verbose_host = false;
|
|
|
|
|
static bool const verbose_devices = false;
|
|
|
|
|
static bool const verbose_host = false;
|
|
|
|
|
static bool const verbose_warnings = false;
|
|
|
|
|
Lock _lock;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void update_ep(USBDevice *);
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
USBPacket *p = nullptr;
|
|
|
|
|
USBDevice *dev = nullptr;
|
|
|
|
|
uint8_t *data = nullptr;
|
|
|
|
|
USBPacket *p = nullptr;
|
|
|
|
|
USBDevice *dev = nullptr;
|
|
|
|
|
uint8_t *data = nullptr;
|
|
|
|
|
unsigned endpoint = 0;
|
|
|
|
|
|
|
|
|
|
enum State { VALID, FREE, CANCELED };
|
|
|
|
|
State state = FREE;
|
|
|
|
|
|
|
|
|
@ -41,17 +100,18 @@ struct Completion : Usb::Completion
|
|
|
|
|
|
|
|
|
|
void complete(Usb::Packet_descriptor &packet, char *content)
|
|
|
|
|
{
|
|
|
|
|
if (state != VALID)
|
|
|
|
|
if (state != VALID) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int actual_size = 0;
|
|
|
|
|
|
|
|
|
|
switch (packet.type) {
|
|
|
|
|
case Usb::Packet_descriptor::CTRL:
|
|
|
|
|
case Packet_type::CTRL:
|
|
|
|
|
actual_size = packet.control.actual_size;
|
|
|
|
|
break;
|
|
|
|
|
case Usb::Packet_descriptor::BULK:
|
|
|
|
|
case Usb::Packet_descriptor::IRQ:
|
|
|
|
|
case Packet_type::BULK:
|
|
|
|
|
case Packet_type::IRQ:
|
|
|
|
|
actual_size = packet.transfer.actual_size;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
@ -60,7 +120,7 @@ struct Completion : Usb::Completion
|
|
|
|
|
if (actual_size < 0) actual_size = 0;
|
|
|
|
|
|
|
|
|
|
if (verbose_host)
|
|
|
|
|
log(__func__, ": packet.type: ", (int)packet.type, " "
|
|
|
|
|
log(__func__, ": packet: ", p, " packet.type: ", (int)packet.type, " "
|
|
|
|
|
"actual_size: ", Hex(actual_size));
|
|
|
|
|
|
|
|
|
|
p->actual_length = 0;
|
|
|
|
@ -75,29 +135,34 @@ struct Completion : Usb::Completion
|
|
|
|
|
if (packet.succeded)
|
|
|
|
|
p->status = USB_RET_SUCCESS;
|
|
|
|
|
else {
|
|
|
|
|
if (packet.error == Usb::Packet_descriptor::STALL_ERROR)
|
|
|
|
|
if (packet.error == Packet_error::STALL_ERROR)
|
|
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
|
else
|
|
|
|
|
p->status = USB_RET_IOERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (packet.type) {
|
|
|
|
|
case Usb::Packet_descriptor::CONFIG:
|
|
|
|
|
case Packet_type::CONFIG:
|
|
|
|
|
if (!claim_interfaces(dev))
|
|
|
|
|
p->status = USB_RET_IOERROR;
|
|
|
|
|
case Usb::Packet_descriptor::ALT_SETTING:
|
|
|
|
|
case Packet_type::ALT_SETTING:
|
|
|
|
|
update_ep(dev);
|
|
|
|
|
case Usb::Packet_descriptor::CTRL:
|
|
|
|
|
case Packet_type::CTRL:
|
|
|
|
|
usb_generic_async_ctrl_complete(dev, p);
|
|
|
|
|
break;
|
|
|
|
|
case Usb::Packet_descriptor::BULK:
|
|
|
|
|
case Usb::Packet_descriptor::IRQ:
|
|
|
|
|
case Packet_type::BULK:
|
|
|
|
|
case Packet_type::IRQ:
|
|
|
|
|
usb_packet_complete(dev, p);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
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,12 +179,12 @@ struct Dev_info
|
|
|
|
|
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), ":",
|
|
|
|
|
Hex(dev, Hex::OMIT_PREFIX, Hex::PAD), " (",
|
|
|
|
|
"vendor=", Hex(vendor, Hex::OMIT_PREFIX), ", ",
|
|
|
|
|
"product=", Hex(product, Hex::OMIT_PREFIX), ")");
|
|
|
|
|
Hex(dev, Hex::OMIT_PREFIX, Hex::PAD), " (",
|
|
|
|
|
"vendor=", Hex(vendor, Hex::OMIT_PREFIX), ", ",
|
|
|
|
|
"product=", Hex(product, Hex::OMIT_PREFIX), ")");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operator != (Dev_info const &other) const
|
|
|
|
@ -137,21 +202,27 @@ struct Dev_info
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
char const *label = nullptr;
|
|
|
|
|
Dev_info const info;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_dispatcher<Usb_host_device> state_dispatcher { sig_rec, *this, &Usb_host_device::state_change };
|
|
|
|
|
|
|
|
|
|
Allocator_avl _alloc;
|
|
|
|
|
Usb::Connection usb_raw; // { &alloc, label, 1024*1024, state_dispatcher };
|
|
|
|
|
Allocator &_alloc;
|
|
|
|
|
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 };
|
|
|
|
|
|
|
|
|
@ -160,10 +231,12 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
|
|
|
|
Usb::Config_descriptor cdescr;
|
|
|
|
|
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++) {
|
|
|
|
|
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::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;
|
|
|
|
|
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,
|
|
|
|
|
Genode::Env &env, char const *label,
|
|
|
|
|
Env &env, char const *label,
|
|
|
|
|
Dev_info info)
|
|
|
|
|
:
|
|
|
|
|
label(label), _alloc(&alloc),
|
|
|
|
|
usb_raw(env, &_alloc, label, 6*1024*1024, state_dispatcher),
|
|
|
|
|
label(label), _alloc(alloc),
|
|
|
|
|
usb_raw(env, &_usb_alloc, label, 6*1024*1024, state_dispatcher),
|
|
|
|
|
info(info), sig_rec(sig_rec)
|
|
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~Usb_host_device()
|
|
|
|
|
{
|
|
|
|
|
isoc_in_flush(0, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int to_qemu_speed(unsigned speed)
|
|
|
|
|
{
|
|
|
|
|
switch (speed) {
|
|
|
|
@ -227,13 +306,149 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
|
|
|
|
|
|
|
|
|
while (usb_raw.source()->ack_avail()) {
|
|
|
|
|
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);
|
|
|
|
|
dynamic_cast<Completion *>(packet.completion)->complete(packet, packet_content);
|
|
|
|
|
free_packet(packet);
|
|
|
|
|
if ((packet.type == Packet_type::ISOC && !packet.read_transfer()) ||
|
|
|
|
|
(c && c->state == Completion::CANCELED)) {
|
|
|
|
|
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()
|
|
|
|
|
{
|
|
|
|
|
/* mark delete before removing */
|
|
|
|
@ -262,45 +477,89 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
|
|
|
|
_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);
|
|
|
|
|
|
|
|
|
|
if (!completion) {
|
|
|
|
|
packet.completion = nullptr;
|
|
|
|
|
return packet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
packet.completion = alloc_completion();
|
|
|
|
|
if (!packet.completion) {
|
|
|
|
|
usb_raw.source()->release_packet(packet);
|
|
|
|
|
throw Packet_alloc_failed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void free_packet(Isoc_packet *packet)
|
|
|
|
|
{
|
|
|
|
|
free_packet(packet->packet());
|
|
|
|
|
Genode::destroy(_alloc, packet);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
completion[i]. state = Completion::VALID;
|
|
|
|
|
completion[i].state = Completion::VALID;
|
|
|
|
|
return &completion[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 (completion[i].p == p)
|
|
|
|
|
if (packet.completion) {
|
|
|
|
|
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 nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void submit(Usb::Packet_descriptor p) {
|
|
|
|
|
usb_raw.source()->submit_packet(p); }
|
|
|
|
|
void flush_completion(unsigned ep)
|
|
|
|
|
{
|
|
|
|
|
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(); }
|
|
|
|
|
|
|
|
|
@ -308,29 +567,42 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
|
|
|
|
{
|
|
|
|
|
_release_interfaces();
|
|
|
|
|
|
|
|
|
|
Usb::Packet_descriptor packet = alloc_packet(0);
|
|
|
|
|
packet.type = Usb::Packet_descriptor::CONFIG;
|
|
|
|
|
packet.number = value;
|
|
|
|
|
try {
|
|
|
|
|
Usb::Packet_descriptor packet = alloc_packet(0);
|
|
|
|
|
packet.type = Usb::Packet_descriptor::CONFIG;
|
|
|
|
|
packet.number = value;
|
|
|
|
|
|
|
|
|
|
Completion *c = dynamic_cast<Completion *>(packet.completion);
|
|
|
|
|
c->p = p;
|
|
|
|
|
c->dev = cast_USBDevice(qemu_dev);
|
|
|
|
|
submit(packet);
|
|
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
|
} catch (...) {
|
|
|
|
|
if (verbose_warnings)
|
|
|
|
|
warning(__func__, " packet allocation failed");
|
|
|
|
|
p->status = USB_RET_NAK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Completion *c = dynamic_cast<Completion *>(packet.completion);
|
|
|
|
|
c->p = p;
|
|
|
|
|
c->dev = cast_USBDevice(qemu_dev);
|
|
|
|
|
submit(packet);
|
|
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void set_interface(int index, uint8_t value, USBPacket *p)
|
|
|
|
|
{
|
|
|
|
|
Usb::Packet_descriptor packet = alloc_packet(0);
|
|
|
|
|
packet.type = Usb::Packet_descriptor::ALT_SETTING;
|
|
|
|
|
packet.interface.number = index;
|
|
|
|
|
packet.interface.alt_setting = value;
|
|
|
|
|
try {
|
|
|
|
|
Usb::Packet_descriptor packet = alloc_packet(0);
|
|
|
|
|
packet.type = Usb::Packet_descriptor::ALT_SETTING;
|
|
|
|
|
packet.interface.number = index;
|
|
|
|
|
packet.interface.alt_setting = value;
|
|
|
|
|
|
|
|
|
|
Completion *c = dynamic_cast<Completion *>(packet.completion);
|
|
|
|
|
c->p = p;
|
|
|
|
|
c->dev = cast_USBDevice(qemu_dev);
|
|
|
|
|
submit(packet);
|
|
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
|
Completion *c = dynamic_cast<Completion *>(packet.completion);
|
|
|
|
|
c->p = p;
|
|
|
|
|
c->dev = cast_USBDevice(qemu_dev);
|
|
|
|
|
submit(packet);
|
|
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
|
} catch (...) {
|
|
|
|
|
if (verbose_warnings)
|
|
|
|
|
warning(__func__, " packet allocation failed");
|
|
|
|
|
p->status = USB_RET_NAK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void update_ep(USBDevice *udev)
|
|
|
|
@ -341,7 +613,8 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
|
|
|
|
Usb::Config_descriptor cdescr;
|
|
|
|
|
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++) {
|
|
|
|
|
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::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)
|
|
|
|
|
log("set udev->speed to %d", Usb_host_device::to_qemu_speed(ddescr.speed));
|
|
|
|
@ -425,40 +699,48 @@ static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p)
|
|
|
|
|
{
|
|
|
|
|
USBHostDevice *d = USB_HOST_DEVICE(udev);
|
|
|
|
|
Usb_host_device *dev = (Usb_host_device *)d->data;
|
|
|
|
|
Completion *c = dev->find_completion(p);
|
|
|
|
|
c->state = Completion::CANCELED;
|
|
|
|
|
Completion *c = dev->find_valid_completion(p);
|
|
|
|
|
|
|
|
|
|
if (c)
|
|
|
|
|
c->cancel();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
|
|
|
|
|
{
|
|
|
|
|
USBHostDevice *d = USB_HOST_DEVICE(udev);
|
|
|
|
|
Usb_host_device *dev = (Usb_host_device *)d->data;
|
|
|
|
|
|
|
|
|
|
Genode::size_t size = 0;
|
|
|
|
|
USBHostDevice *d = USB_HOST_DEVICE(udev);
|
|
|
|
|
Usb_host_device *dev = (Usb_host_device *)d->data;
|
|
|
|
|
Genode::size_t size = 0;
|
|
|
|
|
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)) {
|
|
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
|
|
|
type = Usb::Packet_descriptor::BULK;
|
|
|
|
|
size = usb_packet_size(p);
|
|
|
|
|
type = Usb::Packet_descriptor::BULK;
|
|
|
|
|
size = usb_packet_size(p);
|
|
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
|
break;
|
|
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
|
type = Usb::Packet_descriptor::IRQ;
|
|
|
|
|
size = p->iov.size;
|
|
|
|
|
type = Usb::Packet_descriptor::IRQ;
|
|
|
|
|
size = p->iov.size;
|
|
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
|
break;
|
|
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
|
|
|
if (in)
|
|
|
|
|
dev->isoc_in_packet(p);
|
|
|
|
|
else
|
|
|
|
|
dev->isoc_out_packet(p);
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
error("not supported data request");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool const in = p->pid == USB_TOKEN_IN;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
Usb::Packet_descriptor packet = dev->alloc_packet(size);
|
|
|
|
|
packet.type = type;
|
|
|
|
|
packet.transfer.ep = p->ep->nr | (in ? USB_DIR_IN : 0);
|
|
|
|
|
packet.transfer.polling_interval = Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL;
|
|
|
|
|
Usb::Packet_descriptor packet = dev->alloc_packet(size);
|
|
|
|
|
packet.type = type;
|
|
|
|
|
packet.transfer.ep = p->ep->nr | (in ? USB_DIR_IN : 0);
|
|
|
|
|
packet.transfer.polling_interval = Usb::Packet_descriptor::DEFAULT_POLLING_INTERVAL;
|
|
|
|
|
|
|
|
|
|
if (!in) {
|
|
|
|
|
char * const content = dev->usb_raw.source()->packet_content(packet);
|
|
|
|
@ -471,10 +753,9 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
|
|
|
|
|
c->data = nullptr;
|
|
|
|
|
|
|
|
|
|
dev->submit(packet);
|
|
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
|
} catch (...) {
|
|
|
|
|
/* submit queue full or packet larger than packet stream */
|
|
|
|
|
Genode::warning("xHCI: packet allocation failed (size ", Genode::Hex(size), ")");
|
|
|
|
|
} catch (Packet_alloc_failed) {
|
|
|
|
|
if (verbose_warnings)
|
|
|
|
|
warning("xHCI: packet allocation failed (size ", Hex(size), "in ", __func__, ")");
|
|
|
|
|
p->status = USB_RET_NAK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -512,7 +793,11 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
|
|
|
|
|
Usb::Packet_descriptor packet;
|
|
|
|
|
try {
|
|
|
|
|
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.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[] = {
|
|
|
|
|
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->handle_data = usb_host_handle_data;
|
|
|
|
|
uc->handle_control = usb_host_handle_control;
|
|
|
|
|
uc->ep_stopped = usb_host_ep_stopped;
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
Genode::String<128> label;
|
|
|
|
|
String<128> label;
|
|
|
|
|
try {
|
|
|
|
|
node.attribute("label").value(&label);
|
|
|
|
|
} catch (Genode::Xml_attribute::Nonexistent_attribute) {
|
|
|
|
|
} catch (Xml_attribute::Nonexistent_attribute) {
|
|
|
|
|
error("no label found for device ", dev_info);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -653,7 +957,6 @@ struct Usb_devices : List<Usb_host_device>
|
|
|
|
|
insert(new_device);
|
|
|
|
|
|
|
|
|
|
log("Attach USB device ", dev_info);
|
|
|
|
|
|
|
|
|
|
} catch (...) {
|
|
|
|
|
error("could not attach USB device ", dev_info);
|
|
|
|
|
}
|
|
|
|
@ -669,7 +972,7 @@ struct Usb_devices : List<Usb_host_device>
|
|
|
|
|
_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)
|
|
|
|
|
{
|
|
|
|
|
_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
|
|
|
|
|
*/
|
|
|
|
|
extern "C" void _type_init_usb_host_register_types(Genode::Signal_receiver *sig_rec,
|
|
|
|
|
Genode::Allocator *alloc,
|
|
|
|
|
Genode::Env *env)
|
|
|
|
|
extern "C" void _type_init_usb_host_register_types(Signal_receiver *sig_rec,
|
|
|
|
|
Allocator *alloc,
|
|
|
|
|
Env *env)
|
|
|
|
|
{
|
|
|
|
|
usb_host_register_types();
|
|
|
|
|
|
|
|
|
|