Skip to content

Commit

Permalink
iona_usb: WebUSB based transport for iona_stub
Browse files Browse the repository at this point in the history
  • Loading branch information
toyoshim committed Nov 13, 2023
1 parent 204e09b commit c3255f8
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 9 deletions.
5 changes: 3 additions & 2 deletions us/iona_hid.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// in the LICENSE file.

export class HID {
type = 'hid';
iona = null;
device = null;
report = null;
Expand All @@ -16,7 +17,7 @@ export class HID {
}

getDeviceDescriptor() {
return this.iona.createPseudoConfigurationDescriptor(
return this.iona.createPseudoDeviceDescriptor(
this.device.productId, this.device.vendorId);
}

Expand All @@ -29,7 +30,7 @@ export class HID {
return (new Uint8Array(this.report)).buffer;
}

async listen(callback) {
listen(callback) {
this.device.addEventListener('inputreport', e => {
callback(e.data.buffer);
});
Expand Down
56 changes: 54 additions & 2 deletions us/iona_stub.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export class IONA {
env: {
emscripten_memcpy_js: () => console.log('emscripten_memcpy_js'),
timer3_tick_raw: null,
iona_usb_out: null,
iona_usb_in: null,
},
wasi_snapshot_preview1: {
fd_write: null,
Expand All @@ -18,9 +20,12 @@ export class IONA {
communication_buffer_address = 0;
wasm = null;
exports = null;
intf = {};

constructor() {
this.imports.env.timer3_tick_raw = this.timer3_tick_raw.bind(this);
this.imports.env.iona_usb_out = this.usb_out.bind(this);
this.imports.env.iona_usb_in = this.usb_in.bind(this);
this.imports.wasi_snapshot_preview1.fd_write = this.fd_write.bind(this);
}

Expand All @@ -37,21 +42,36 @@ export class IONA {
}).bind(this));
}

setInterface(intf) {
this.intf = intf;
}

storeData(buffer) {
const u8 = new Uint8Array(buffer);
for (let i = 0; i < u8.byteLength; ++i) {
this.u8[this.communication_buffer_address + i] = u8[i];
}
}

loadData(address, size) {
const u8 = new Uint8Array(size);
for (let i = 0; i < size; ++i) {
u8[i] = this.u8[address + i];
}
return u8.buffer;
}

checkDeviceDescriptor(buffer) {
this.storeData(buffer);
this.exports.iona_usb_host_check_device_desc(this.communication_buffer_address);
}

checkConfigurationDescriptor(buffer) {
async checkConfigurationDescriptor(buffer) {
this.storeData(buffer);
this.exports.iona_usb_host_check_configuration_desc(this.communication_buffer_address);
const intf = this.exports.iona_usb_host_check_configuration_desc(this.communication_buffer_address);
if (this.intf.select) {
await this.intf.select(intf);
}
}

checkHidReportDescriptor(buffer) {
Expand All @@ -64,6 +84,17 @@ export class IONA {
this.exports.iona_usb_host_check_hid_report(this.communication_buffer_address, buffer.byteLength);
}

checkStatus() {
return {
ready: this.exports.iona_is_device_ready(),
type: this.exports.iona_get_device_type(),
};
}

poll() {
this.exports.iona_poll();
}

createPseudoDeviceDescriptor(pid, vid) {
const desc = new Uint8Array(18);
desc[0] = 0x12; // bLength
Expand Down Expand Up @@ -145,4 +176,25 @@ export class IONA {
console.log(chars.join(''));
return 0;
}

usb_out(ep, data, size) {
if (!this.intf.out) {
return false;
}
this.intf.out(ep, this.loadData(data, size)).then(() => {
this.exports.iona_transaction_complete(0);
});
return true;
}

usb_in(ep, size) {
if (!this.intf.in) {
return false;
}
this.intf.in(1, size).then(buffer => {
this.storeData(buffer);
this.exports.iona_transaction_complete(buffer.byteLength);
});
return true;
}
}
75 changes: 75 additions & 0 deletions us/iona_usb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2023 Takashi Toyoshima <toyoshim@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.

export class USB {
type = 'usb';
iona = null;
device = null;

constructor(iona) {
this.iona = iona;
this.iona.setInterface({
select: this.select.bind(this),
out: this.out.bind(this),
in: this.in.bind(this)
});
}

async initialize() {
this.device = (await navigator.usb.requestDevice({ filters: [] }));
}

async getDeviceDescriptor() {
if (!this.device.opened) {
await this.device.open();
}
const desc = await this.device.controlTransferIn({
requestType: 'standard',
recipient: 'device',
request: 6, // GET_DESCRIPTOR
value: 0x100, // Device descriptor
index: 0,
}, 18);
return desc.data.buffer;
}

async getConfigurationDescriptor() {
const setup = {
requestType: 'standard',
recipient: 'device',
request: 6, // GET_DESCRIPTOR
value: 0x200, // Configuration descriptor
index: 0,
};
const desc_header = await this.device.controlTransferIn(setup, 9);
const u8 = new Uint8Array(desc_header.data.buffer);
const desc_size = u8[2] | (u8[3] << 8);
const desc = await this.device.controlTransferIn(setup, desc_size);
return desc.data.buffer;
}

listen(callback) {
this.requestAnimationFrame();
}

async select(intf) {
await this.device.selectConfiguration(this.device.configurations[0].configurationValue);
await this.device.claimInterface(this.device.configurations[0].interfaces[intf].interfaceNumber);
}

async out(ep, data) {
this.device.transferOut(ep, data);
}

async in(ep, length) {
const result = await this.device.transferIn(ep, length);
return result.data.buffer;
}

requestAnimationFrame() {
this.iona.poll();
requestAnimationFrame(this.requestAnimationFrame.bind(this));
}

}
101 changes: 96 additions & 5 deletions us/wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,86 @@
#include "controller.h"
#include "settings.h"

bool iona_usb_out(uint8_t ep, uint8_t* data, uint8_t size);
bool iona_usb_in(uint8_t ep, uint8_t size);

enum {
TRANSACTION_STATE_IDLE,
TRANSACTION_STATE_SETUP,
TRANSACTION_STATE_IN,
TRANSACTION_STATE_OUT,
};

static struct hid hid;
static struct usb_host* usb_host = 0;
static struct settings settings;
static uint8_t communication_buffer[1024];
static uint8_t transaction_state = TRANSACTION_STATE_IDLE;

void detected(void) {
// printf("detected as type %d\n", hid_get_info(0)->type);
}

bool timer3_tick_raw_between(uint16_t begin, uint16_t end) {
uint16_t now = timer3_tick_raw();
if (begin < end) {
return begin < now && now < end;
}
return end < now || now < begin;
}

void usb_host_init(struct usb_host* host) {
usb_host = host;
}

void usb_host_poll(void) {}

bool usb_host_ready(uint8_t hub) {
return transaction_state == TRANSACTION_STATE_IDLE;
}

bool usb_host_idle(void) {
return transaction_state == TRANSACTION_STATE_IDLE;
}

bool usb_host_setup(uint8_t hub,
const struct usb_setup_req* req,
const uint8_t* data) {
// TODO
puts("usb_host_setup");
return true;
}

bool usb_host_in(uint8_t hub, uint8_t ep, uint8_t size) {
transaction_state = TRANSACTION_STATE_IN;
return iona_usb_in(ep, size);
}

bool usb_host_in_data0(uint8_t hub, uint8_t ep, uint8_t size) {
// TODO
puts("usb_host_in_data0");
return true;
}

bool usb_host_out(uint8_t hub, uint8_t ep, uint8_t* data, uint8_t size) {
transaction_state = TRANSACTION_STATE_OUT;
return iona_usb_out(ep, data, size);
}

bool usb_host_hid_get_report(uint8_t hub,
uint8_t type,
uint8_t id,
uint8_t size) {
// TODO
puts("usb_host_hid_get_report");
return true;
}

void usb_host_hub_switch(uint8_t hub, uint8_t address) {
// TODO
puts("usb_host_hub_switch");
}

struct settings* settings_get(void) {
return &settings;
}
Expand All @@ -37,12 +108,13 @@ EMSCRIPTEN_KEEPALIVE void iona_usb_host_check_device_desc(const uint8_t* desc) {
usb_host->check_device_desc(0, desc);
}

EMSCRIPTEN_KEEPALIVE void iona_usb_host_check_configuration_desc(
const uint8_t* desc) {
EMSCRIPTEN_KEEPALIVE uint8_t
iona_usb_host_check_configuration_desc(const uint8_t* desc) {
if (!usb_host) {
return error();
error();
return 0;
}
usb_host->check_configuration_desc(0, desc);
return usb_host->check_configuration_desc(0, desc);
}

EMSCRIPTEN_KEEPALIVE void iona_usb_host_check_hid_report_desc(
Expand All @@ -61,10 +133,29 @@ EMSCRIPTEN_KEEPALIVE void iona_usb_host_check_hid_report(uint8_t* data,
usb_host->hid_report(0, data, size);
}

EMSCRIPTEN_KEEPALIVE void iona_poll(void) {
hid_poll();
}

EMSCRIPTEN_KEEPALIVE void iona_transaction_complete(uint8_t size) {
if (transaction_state == TRANSACTION_STATE_IN) {
usb_host->in(0, communication_buffer, size);
}
transaction_state = TRANSACTION_STATE_IDLE;
}

EMSCRIPTEN_KEEPALIVE void* iona_get_communication_buffer(void) {
return communication_buffer;
}

EMSCRIPTEN_KEEPALIVE bool iona_is_device_ready(void) {
return hid_get_info(0)->state == HID_STATE_READY;
}

EMSCRIPTEN_KEEPALIVE uint8_t iona_get_device_type(void) {
return hid_get_info(0)->type;
}

EMSCRIPTEN_KEEPALIVE void iona_init(void) {
serial_init();

Expand All @@ -81,7 +172,7 @@ EMSCRIPTEN_KEEPALIVE void iona_init(void) {
}

hid.report = controller_update;
hid.detected = 0;
hid.detected = detected;
hid.get_flags = 0;
hid_init(&hid);
}

0 comments on commit c3255f8

Please sign in to comment.