Skip to content

Commit

Permalink
Adding first working version
Browse files Browse the repository at this point in the history
  • Loading branch information
janweinstock committed Apr 3, 2024
1 parent 4088d8f commit 0142a8d
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 33 deletions.
2 changes: 1 addition & 1 deletion include/vcml/models/usb/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class device : public module, public usb_dev_if
usb_result res;
control_state state;
u8* ptr;
u8 buf[1024];
u8 buf[1u << 16];
} m_ep0;

bool cmd_usb_attach(const vector<string>& args, ostream& os);
Expand Down
11 changes: 10 additions & 1 deletion include/vcml/models/usb/hostdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,22 @@ class hostdev : public device
struct libusb_device* m_device;
struct libusb_device_handle* m_handle;

struct hostdev_interface {
bool detached;
bool claimed;
} m_ifs[16];

void init_device();
usb_result set_config(int i);

public:
property<u32> hostbus;
property<u32> hostaddr;

usb_target_socket usb_in;

hostdev(const sc_module_name& nm);
hostdev(const sc_module_name& nm): hostdev(nm, 0, 0) {}
hostdev(const sc_module_name& nm, u32 bus, u32 addr);
virtual ~hostdev();
VCML_KIND(usb::hostdev);

Expand Down
4 changes: 3 additions & 1 deletion src/vcml/models/usb/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,10 @@ usb_result device::handle_ep0(usb_packet& p) {
m_ep0.len = p.data[6] | (u16)p.data[7] << 8;
m_ep0.pos = 0;

if (m_ep0.len > sizeof(m_ep0.buf))
if ((size_t)m_ep0.len > sizeof(m_ep0.buf)) {
log_warn("control request too big: %hu bytes", m_ep0.len);
return USB_RESULT_BABBLE;
}

usb_result res = USB_RESULT_SUCCESS;
if (m_ep0.req & USB_REQ_IN) {
Expand Down
4 changes: 2 additions & 2 deletions src/vcml/models/usb/drive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ drive::~drive() {
}

usb_result drive::get_data(u32 ep, u8* data, size_t len) {
if (ep != 2) {
if (ep != 1) {
log_warn("invalid input endpoint: %u", ep);
return USB_RESULT_STALL;
}
Expand Down Expand Up @@ -348,7 +348,7 @@ usb_result drive::get_data(u32 ep, u8* data, size_t len) {
}

usb_result drive::set_data(u32 ep, const u8* data, size_t len) {
if (ep != 3) {
if (ep != 2) {
log_warn("invalid output endpoint: %u", ep);
return USB_RESULT_STALL;
}
Expand Down
151 changes: 139 additions & 12 deletions src/vcml/models/usb/hostdev_libusb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
******************************************************************************/

#include "vcml/models/usb/hostdev.h"
#include "vcml/core/version.h"

#include <libusb.h>

Expand Down Expand Up @@ -50,42 +49,170 @@ struct libusb {
}
};

hostdev::hostdev(const sc_module_name& nm):
constexpr usb_result usb_result_from_libusb(int r) {
switch (r) {
case LIBUSB_SUCCESS:
return USB_RESULT_SUCCESS;
case LIBUSB_ERROR_NO_DEVICE:
return USB_RESULT_NODEV;
case LIBUSB_ERROR_TIMEOUT:
return USB_RESULT_STALL;
case LIBUSB_ERROR_OVERFLOW:
return USB_RESULT_BABBLE;
case LIBUSB_ERROR_IO:
default:
return USB_RESULT_IOERROR;
}
}

static string libusb_str_desc(libusb_device_handle* h, u16 idx) {
unsigned char buffer[256]{};
libusb_get_string_descriptor_ascii(h, idx, buffer, sizeof(buffer));
return string((char*)buffer);
}

void hostdev::init_device() {
int r = libusb_open(m_device, &m_handle);
VCML_ERROR_ON(r, "Failed to open USB device: %s", libusb_strerror(r));

libusb_device_descriptor desc;
r = libusb_get_device_descriptor(m_device, &desc);
VCML_ERROR_ON(r, "Failed to fetch USB descriptor: %s", libusb_strerror(r));

m_desc.bcd_usb = desc.bcdUSB;
m_desc.device_class = desc.bDeviceClass;
m_desc.device_subclass = desc.bDeviceSubClass;
m_desc.device_protocol = desc.bDeviceProtocol;
m_desc.max_packet_size0 = desc.bMaxPacketSize0;
m_desc.vendor_id = desc.idVendor;
m_desc.product_id = desc.idProduct;
m_desc.bcd_device = desc.bcdDevice;
m_desc.manufacturer = libusb_str_desc(m_handle, desc.iManufacturer);
m_desc.product = libusb_str_desc(m_handle, desc.iProduct);
m_desc.serial_number = libusb_str_desc(m_handle, desc.iSerialNumber);

for (size_t i = 0; i < MWR_ARRAY_SIZE(m_ifs); i++) {
int r = libusb_kernel_driver_active(m_handle, i);
if (r > 0) {
log_debug("detaching kernel driver from interface %zu", i);
r = libusb_detach_kernel_driver(m_handle, i);
VCML_ERROR_ON(r < 0, "libusb_detach_kernel_driver: %s",
libusb_strerror(r));
m_ifs[i].detached = true;
}
}

log_debug("attached to device %04hx:%04hx (%s)", m_desc.vendor_id,
m_desc.product_id, m_desc.product.c_str());
}

usb_result hostdev::set_config(int config) {
if (!m_handle)
return USB_RESULT_NODEV;

for (size_t i = 0; i < MWR_ARRAY_SIZE(m_ifs); i++) {
if (m_ifs[i].claimed) {
libusb_release_interface(m_handle, i);
log_debug("released interface %zu", i);
}

m_ifs[i].claimed = false;
}

int r = libusb_set_configuration(m_handle, config);
VCML_ERROR_ON(r < 0, "libusb_set_config: %s", libusb_strerror(r));

for (size_t i = 0; i < MWR_ARRAY_SIZE(m_ifs); i++) {
if ((r = libusb_claim_interface(m_handle, i)) == 0) {
log_debug("claimed interface %zu", i);
m_ifs[i].claimed = true;
}
}

return USB_RESULT_SUCCESS;
}

hostdev::hostdev(const sc_module_name& nm, u32 bus, u32 addr):
device(nm, device_desc()),
m_device(),
m_handle(),
hostbus("hostbus", 0),
hostaddr("hostaddr", 0),
m_ifs(),
hostbus("hostbus", bus),
hostaddr("hostaddr", addr),
usb_in("usb_in") {
m_device = libusb::instance().find_device(hostbus, hostaddr);
VCML_ERROR_ON(!m_device, "no usb device on bus %u at address %u",
hostbus.get(), hostaddr.get());
int r = libusb_open(m_device, &m_handle);
VCML_ERROR_ON(r, "Failed to open USB device: %s", libusb_strerror(r));
memset(m_ifs, 0, sizeof(m_ifs));
if (hostbus > 0u && hostaddr > 0u) {
m_device = libusb::instance().find_device(hostbus, hostaddr);
if (!m_device) {
log_error("no USB device on bus %u at address %u", hostbus.get(),
hostaddr.get());
}
}

if (m_device)
init_device();
}

hostdev::~hostdev() {
for (size_t i = 0; i < MWR_ARRAY_SIZE(m_ifs); i++) {
if (m_ifs[i].claimed) {
log_debug("releasing interface %zu", i);
libusb_release_interface(m_handle, i);
}

if (m_ifs[i].detached) {
log_debug("re-attaching kernel driver to interface %zu", i);
libusb_attach_kernel_driver(m_handle, i);
}
}

if (m_handle)
libusb_close(m_handle);
if (m_device)
libusb_unref_device(m_device);
}

usb_result hostdev::get_data(u32 ep, u8* data, size_t len) {
return USB_RESULT_NACK;
if (!m_handle)
return USB_RESULT_NODEV;

int r = libusb_bulk_transfer(m_handle, usb_ep_in(ep), data, len, NULL, 0);
return usb_result_from_libusb(r);
}

usb_result hostdev::set_data(u32 ep, const u8* data, size_t len) {
return USB_RESULT_NACK;
if (!m_handle)
return USB_RESULT_NODEV;

u8* p = const_cast<u8*>(data);
int r = libusb_bulk_transfer(m_handle, usb_ep_out(ep), p, len, NULL, 0);
return usb_result_from_libusb(r);
}

usb_result hostdev::handle_control(u16 req, u16 val, u16 idx, u8* data,
size_t length) {
if (!m_handle)
return USB_RESULT_NODEV;

switch (req) {
default:
case USB_REQ_OUT | USB_REQ_DEVICE | USB_REQ_SET_ADDRESS:
return device::handle_control(req, val, idx, data, length);
case USB_REQ_OUT | USB_REQ_DEVICE | USB_REQ_SET_CONFIGURATION:
return set_config(val & 0xff);
}

u16 t = req >> 8;
u16 q = req & 0xff;
int r = libusb_control_transfer(m_handle, t, q, val, idx, data, length, 0);
if (r < 0)
return usb_result_from_libusb(r);
return USB_RESULT_SUCCESS;
}

void hostdev::usb_reset_device() {
if (m_handle)
libusb_reset_device(m_handle);

device::usb_reset_device();
}

Expand Down
30 changes: 17 additions & 13 deletions src/vcml/models/usb/hostdev_nolibusb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,45 @@
******************************************************************************/

#include "vcml/models/usb/hostdev.h"
#include "vcml/core/version.h"

namespace vcml {
namespace usb {

hostdev::hostdev(const sc_module_name& nm):
void hostdev::init_device() {
// nothing to do
}

hostdev::hostdev(const sc_module_name& nm, u32 bus, u32 addr):
device(nm, device_desc()),
m_device(),
m_handle(),
hostbus("hostbus", 0),
hostaddr("hostaddr", 0),
m_ifs(),
hostbus("hostbus", bus),
hostaddr("hostaddr", addr),
usb_in("usb_in") {
(void)m_device;
(void)m_handle;
if (hostbus > 0 || hostaddr > 0)
log_warn("USB host devices not supported (missing libusb)");
}

usb_result hostdev::set_config(int config) {
return USB_RESULT_NODEV;
}

hostdev::~hostdev() {
// nothing to do
}

usb_result hostdev::get_data(u32 ep, u8* data, size_t len) {
return USB_RESULT_NACK;
return USB_RESULT_NODEV;
}

usb_result hostdev::set_data(u32 ep, const u8* data, size_t len) {
return USB_RESULT_NACK;
return USB_RESULT_NODEV;
}

usb_result hostdev::handle_control(u16 req, u16 val, u16 idx, u8* data,
size_t length) {
switch (req) {
case USB_REQ_OUT | USB_REQ_DEVICE | USB_REQ_SET_ADDRESS:
default:
return device::handle_control(req, val, idx, data, length);
}
return USB_RESULT_NODEV;
}

void hostdev::usb_reset_device() {
Expand Down
2 changes: 1 addition & 1 deletion src/vcml/models/usb/keyboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ usb_result keyboard::set_report(u8* data, size_t length) {
}

usb_result keyboard::get_data(u32 ep, u8* data, size_t len) {
if (ep != 2) {
if (ep != 1) {
log_error("invalid endpoint contacted: %u", ep);
return USB_RESULT_NACK;
}
Expand Down
5 changes: 3 additions & 2 deletions src/vcml/models/usb/xhci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1015,11 +1015,12 @@ u32 xhci::handle_transmit(u32 slotid, u32 epid, trb& cmd) {

u64 addr = cmd.parameter;
u32 size = get_trb_data_length(cmd);
u32 epno = (epid + 1) / 2;
for (u32 off = 0; off < size; off += ep->max_psize) {
vector<u8> buf(min(size - off, ep->max_psize));

if (dirin) {
auto packet = usb_packet_in(slotid, epid, buf.data(), buf.size());
auto packet = usb_packet_in(slotid, epno, buf.data(), buf.size());
usb_out[slot->port].send(packet);
if (failed(packet))
return usb_packet_ccode(packet);
Expand All @@ -1035,7 +1036,7 @@ u32 xhci::handle_transmit(u32 slotid, u32 epid, trb& cmd) {
return TRB_CC_DATA_BUFFER_ERROR;
}

auto packet = usb_packet_out(slotid, epid, buf.data(), buf.size());
auto packet = usb_packet_out(slotid, epno, buf.data(), buf.size());
usb_out[slot->port].send(packet);
if (failed(packet))
return usb_packet_ccode(packet);
Expand Down
4 changes: 4 additions & 0 deletions test/models/xhci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class xhci_test : public test_base
usb::keyboard keyboard2;
usb::keyboard keyboard3;
usb::drive drive2;
usb::hostdev hostdev;

tlm_initiator_socket out;
gpio_target_socket irq;
Expand All @@ -32,11 +33,13 @@ class xhci_test : public test_base
keyboard2("keyboard2"),
keyboard3("keyboard3"),
drive2("drive2"),
hostdev("hostdev"),
out("out"),
irq("irq") {
xhci.usb_out[0].bind(keyboard2.usb_in);
xhci.usb_out[1].bind(keyboard3.usb_in);
xhci.usb_out[2].bind(drive2.usb_in);
xhci.usb_out[3].bind(hostdev.usb_in);

bus.bind(mem.in, 0, 0xfff);
bus.bind(xhci.in, 0x1000, 0x1fff);
Expand All @@ -58,6 +61,7 @@ class xhci_test : public test_base
EXPECT_STREQ(keyboard2.kind(), "vcml::usb::keyboard");
EXPECT_STREQ(keyboard3.kind(), "vcml::usb::keyboard");
EXPECT_STREQ(drive2.kind(), "vcml::usb::drive");
EXPECT_STREQ(hostdev.kind(), "vcml::usb::hostdev");
}

void test_capabilities() {
Expand Down

0 comments on commit 0142a8d

Please sign in to comment.