From 3a86555a7211f15fa1cb55de3cc898a761a188fd Mon Sep 17 00:00:00 2001
From: Alex Evans <715855+mmalex@users.noreply.github.com>
Date: Thu, 7 Nov 2024 15:54:15 +0000
Subject: first baby steps at extending the webusb protocol to longer messages,
processed faster, and add messages to read spi and wavetable ram
---
sample_editor.html | 192 ++++++++++++++++++++
sw/Core/Src/gfx.h | 4 +-
sw/Core/Src/plinky.c | 1 +
sw/Core/Src/spi.h | 3 +-
sw/Core/Src/tinyusb/src/usbmidi.c | 4 +
sw/Core/Src/webusb.h | 364 ++++++++++++++++++++++++--------------
6 files changed, 432 insertions(+), 136 deletions(-)
create mode 100644 sample_editor.html
diff --git a/sample_editor.html b/sample_editor.html
new file mode 100644
index 0000000..7322305
--- /dev/null
+++ b/sample_editor.html
@@ -0,0 +1,192 @@
+
+
+
+
+
+WebUSB App
+
+
+
+
+
+
+
+
+
diff --git a/sw/Core/Src/gfx.h b/sw/Core/Src/gfx.h
index d5a33ff..28feed5 100755
--- a/sw/Core/Src/gfx.h
+++ b/sw/Core/Src/gfx.h
@@ -71,7 +71,7 @@ void vline(int x1, int y1, int y2, int c) {
} else {
*dst |= b1 & mask;
dst += W;
- for (; --n; dst += W)
+ if (--n) for (; --n; dst += W)
*dst |= mask;
*dst |= b2 & mask;
}
@@ -81,7 +81,7 @@ void vline(int x1, int y1, int y2, int c) {
} else {
*dst &= ~b1;
dst += W;
- for (; --n; dst += W)
+ if (--n) for (; --n; dst += W)
*dst = 0;
*dst &= ~b2;
}
diff --git a/sw/Core/Src/plinky.c b/sw/Core/Src/plinky.c
index 14ffafe..5c45fc5 100755
--- a/sw/Core/Src/plinky.c
+++ b/sw/Core/Src/plinky.c
@@ -681,6 +681,7 @@ int arpdebugi;
#endif
+extern const short wavetable[17][1031];
//s16 wavetable[WAVETABLE_SIZE*WT_LAST];
#ifndef EMU
diff --git a/sw/Core/Src/spi.h b/sw/Core/Src/spi.h
index 9affd1f..b69e7e8 100755
--- a/sw/Core/Src/spi.h
+++ b/sw/Core/Src/spi.h
@@ -46,7 +46,8 @@ void spi_read_done(void) {
resetspistate();
} else {
int dacchan = spistate-MAX_SPI_STATE;
- spi_update_dac(dacchan);
+ if (dacchan<4 && dacchan>=0)
+ spi_update_dac(dacchan);
}
}
else {
diff --git a/sw/Core/Src/tinyusb/src/usbmidi.c b/sw/Core/Src/tinyusb/src/usbmidi.c
index 39bf514..70b153d 100755
--- a/sw/Core/Src/tinyusb/src/usbmidi.c
+++ b/sw/Core/Src/tinyusb/src/usbmidi.c
@@ -180,12 +180,14 @@ int miditest(void) {
void tud_mount_cb(void)
{
blink_interval_ms = BLINK_MOUNTED;
+ web_serial_connected = true;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
blink_interval_ms = BLINK_NOT_MOUNTED;
+ web_serial_connected = false;
}
// Invoked when usb bus is suspended
@@ -195,12 +197,14 @@ void tud_suspend_cb(bool remote_wakeup_en)
{
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
+ web_serial_connected = false;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
blink_interval_ms = BLINK_MOUNTED;
+ web_serial_connected = true;
}
//--------------------------------------------------------------------+
diff --git a/sw/Core/Src/webusb.h b/sw/Core/Src/webusb.h
index 495acd7..76da03e 100755
--- a/sw/Core/Src/webusb.h
+++ b/sw/Core/Src/webusb.h
@@ -1,6 +1,6 @@
#pragma once
#ifdef DEBUG
-#define DEBUG_WU
+//#define DEBUG_WU
#endif
#ifdef DEBUG_WU
#define WUDebugLog DebugLog
@@ -11,153 +11,251 @@
#ifdef EMU
static inline uint32_t tud_vendor_write_available(void) { return 0; }
static inline uint32_t tud_vendor_available(void) { return 0; }
-static inline uint32_t tud_vendor_read(void* buffer, uint32_t bufsize) { return 0; }
-static inline uint32_t tud_vendor_write(const void* buffer, uint32_t bufsize) { return 0; }
+static inline uint32_t tud_vendor_read(void *buffer, uint32_t bufsize) { return 0; }
+static inline uint32_t tud_vendor_write(const void *buffer, uint32_t bufsize) { return 0; }
#else
-uint32_t tud_vendor_n_write_available (uint8_t itf);
-uint32_t tud_vendor_n_available (uint8_t itf);
-uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
-uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
-static inline uint32_t tud_vendor_write_available (void)
-{
- return tud_vendor_n_write_available(0);
-}
-static inline uint32_t tud_vendor_available (void)
-{
- return tud_vendor_n_available(0);
-}
-static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize)
-{
- return tud_vendor_n_read(0, buffer, bufsize);
-}
-static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize)
-{
- return tud_vendor_n_write(0, buffer, bufsize);
-}
+uint32_t tud_vendor_n_write_available(uint8_t itf);
+uint32_t tud_vendor_n_available(uint8_t itf);
+uint32_t tud_vendor_n_read(uint8_t itf, void *buffer, uint32_t bufsize);
+uint32_t tud_vendor_n_write(uint8_t itf, void const *buffer, uint32_t bufsize);
+static inline uint32_t tud_vendor_write_available(void) { return tud_vendor_n_write_available(0); }
+static inline uint32_t tud_vendor_available(void) { return tud_vendor_n_available(0); }
+static inline uint32_t tud_vendor_read(void *buffer, uint32_t bufsize) { return tud_vendor_n_read(0, buffer, bufsize); }
+static inline uint32_t tud_vendor_write(void const *buffer, uint32_t bufsize) { return tud_vendor_n_write(0, buffer, bufsize); }
#endif
-/* webusb wire format. 10 byte header, then data.
+
+extern const short wavetable[17][1031];
+
+/* webusb wire format. 10 byte header, then data.
u32 magic = 0xf30fabca
u8 cmd // 0 = get, 1=set
u8 idx // 0
-u8 idx2 // 0
+u8 idx2 // 0
u8 idx3 // 0
u16 datalen // in bytes, not including this header, 0 for get, 1552 for set
*/
typedef struct WebUSBHeader { // 10 byte header
- u8 magic[4];
- u8 cmd,idx;
- u16 offset;
- u16 len;
-} WebUSBHeader;
-const static u8 wu_magic[4] = { 0xf3,0x0f,0xab,0xca }; // we expect magic to be these
-const static u8 wu_magic_ext[4] = { 0xf4,0x1f,0xcb,0xdb }; // we expect magic to be these
+ u8 magic[4];
+ u8 cmd, idx;
+ union {
+ struct {
+ u16 offset_16;
+ u16 len_16;
+ };
+ struct { // these are valid if magic3 is 0xcc
+ u32 offset_32;
+ u32 len_32;
+ };
+ };
+} __attribute__((packed)) WebUSBHeader;
+const static u8 wu_magic[4] = {0xf3, 0x0f, 0xab, 0xca};
+const static u8 wu_magic_ext[4] = {0xf3, 0x0f, 0xab, 0xcb}; // 32 bit version
#define WEB_USB_TIMEOUT 500
enum { // state machine ticks thru these in order, more or less
- WU_MAGIC0,
- WU_MAGIC1,
- WU_MAGIC2,
- WU_MAGIC3,
- WU_RECVHDR,
- WU_RECVDATA,
- WU_SENDHDR,
- WU_SENDDATA,
+ WU_MAGIC0,
+ WU_MAGIC1,
+ WU_MAGIC2,
+ WU_MAGIC3,
+ WU_RECVHDR,
+ WU_RECVDATA,
+ WU_SENDHDR,
+ WU_SENDDATA,
};
u8 wu_state = WU_MAGIC0; // current state
-WebUSBHeader wu_hdr; // header of current command
-u8* wu_data=(u8*)&wu_hdr; // buffer where we are reading/writing atm
-int wu_len=1; // how much left to read/write before state transition
-int wu_lasteventtime; // for timeout detection
-void SetWUState(u8 state, u8* data, int len) { wu_state = state; wu_data = data; wu_len = len; }
+WebUSBHeader wu_hdr; // header of current command
+int is_wu_hdr_32bit(void) { return wu_hdr.magic[3] == wu_magic_ext[3]; }
+int wu_hdr_len(void) { return is_wu_hdr_32bit() ? wu_hdr.len_32 : wu_hdr.len_16; }
+int wu_hdr_offset(void) { return is_wu_hdr_32bit() ? wu_hdr.offset_32 : wu_hdr.offset_16; }
+u8 *wu_data = (u8 *)&wu_hdr; // buffer where we are reading/writing atm
+int wu_len = 1; // how much left to read/write before state transition
+int wu_lasteventtime; // for timeout detection
+void SetWUState(u8 state, u8 *data, int len) {
+ wu_state = state;
+ wu_data = data;
+ wu_len = len;
+}
void PumpWebUSB() {
- while (1) {
- if (millis() > wu_lasteventtime + WEB_USB_TIMEOUT) { // timeout!
- reset:
- SetWUState(WU_MAGIC0, wu_hdr.magic, 1);
- }
- int n;
- if (wu_state < WU_SENDHDR) { // receive
- //n = tud_vendor_available();
- //if (n<6) break;
- n = tud_vendor_read(wu_data, wu_len);
- }
- else { // send
- if (wu_len <= 0) {
- // odd corner case. the state doesnt want to send anything. just pretend the state is done
- goto statedone;
- }
- n = tud_vendor_write_available();
- if (n > wu_len) n = wu_len;
- if (n <= 0) break;
- n = tud_vendor_write(wu_data, n);
- }
- if (n <= 0) break; // nothing to do
+ bool need_tud_task = false;
+ while (1) {
+ need_tud_task = true;
+ if (millis() > wu_lasteventtime + WEB_USB_TIMEOUT) { // timeout!
+ reset:
+ SetWUState(WU_MAGIC0, wu_hdr.magic, 1);
+ }
+ int n;
+ if (wu_state < WU_SENDHDR) { // receive
+ // n = tud_vendor_available();
+ // if (n<6) break;
+ if (need_tud_task)
+ tud_task();
+
+ n = tud_vendor_read(wu_data, wu_len);
+ } else { // send
+ if (wu_len <= 0) {
+ // odd corner case. the state doesnt want to send anything. just pretend the state is done
+ goto statedone;
+ }
+ n = tud_vendor_write_available();
+ if (n > wu_len)
+ n = wu_len;
+ if (n <= 0)
+ break;
+ n = tud_vendor_write(wu_data, n);
+ }
+ if (n <= 0)
+ break; // nothing to do
#ifdef DEBUG_WU
- WUDebugLog("%d (%d): ", wu_state,wu_len);
- for (int i=0;i 0) break; // not done with this state yet. next time!
- // ok! end of a state. whats next
-statedone:
- switch (wu_state) {
- case WU_MAGIC0: case WU_MAGIC1: case WU_MAGIC2: case WU_MAGIC3: {
- u8 m = wu_hdr.magic[wu_state];
- if (m != wu_magic[wu_state]) {
- WUDebugLog("magic fail %02x %02x\r\n",m, wu_magic[wu_state]);
- wu_hdr.magic[0] = m;
- wu_len = 1;
- wu_state = (m == wu_magic[0]) ? WU_MAGIC1 : WU_MAGIC0; // if we got the first byte, we can tick into next state!
- wu_data = wu_hdr.magic + wu_state;
- continue;
- }
- wu_state++;
- wu_len = 1;
- if (wu_state == WU_RECVHDR) { // time to get rest of header
- WUDebugLog("get header!\r\n");
- wu_len = 10-4;
- }
- continue; }
- case WU_RECVHDR: // ok we got the header. what do they want:
- WUDebugLog("got header %d %d!\r\n", wu_hdr.cmd,wu_hdr.len);
- if (wu_hdr.cmd > 1) goto reset; // invalid cmd?
- if (wu_hdr.cmd == 0) {
- // they asked to get a preset!
- if (wu_hdr.len + wu_hdr.offset > sizeof(Preset) ) goto reset;
- if (wu_hdr.offset >= sizeof(Preset)) goto reset;
- if (wu_hdr.idx >= 32) wu_hdr.idx = sysparams.curpreset;
- wu_hdr.cmd = 1;
- if (wu_hdr.len == 0)
- wu_hdr.len = sizeof(Preset) - wu_hdr.offset;
- SetWUState(WU_SENDHDR, (u8*)&wu_hdr, 10);
- }
- else {
- // they asked to set!
- if (wu_hdr.len + wu_hdr.offset > sizeof(Preset) || wu_hdr.len<=0) goto reset;
- if (wu_hdr.offset >= sizeof(Preset)) goto reset;
- if (wu_hdr.idx >= 32) wu_hdr.idx = sysparams.curpreset; // idx>32, just use current
- // switch to the specified preset ready to receive.
- SetPreset(wu_hdr.idx, false);
- SetWUState(WU_RECVDATA, ((u8*)&rampreset) + wu_hdr.offset, wu_hdr.len);
- }
- continue;
- case WU_RECVDATA:
- if (wu_hdr.cmd == 1) {
- // finished receiving preset. mark it dirty
- ramtime[GEN_PRESET] = millis();
- }
- goto reset;
- case WU_SENDHDR:// ok we sent them the header; now send them the data
- {
- u8* data = (wu_hdr.idx == sysparams.curpreset) ? (u8 * )&rampreset : (u8*)GetSavedPreset(wu_hdr.idx);
- SetWUState(WU_SENDDATA, data + wu_hdr.offset, wu_hdr.len);
- continue;
- }
- case WU_SENDDATA: // ok we finished sending our data. nothing to do except get ready for more.
- goto reset;
- } // state
- } // while loop
+ wu_lasteventtime = millis();
+ wu_len -= n;
+ wu_data += n;
+ if (wu_len > 0) {
+ continue; // used to be break!
+ }
+ // ok! end of a state. whats next
+ statedone:
+ switch (wu_state) {
+ case WU_MAGIC0:
+ case WU_MAGIC1:
+ case WU_MAGIC2:
+ case WU_MAGIC3: {
+ u8 m = wu_hdr.magic[wu_state];
+ if (m != wu_magic[wu_state] && m != wu_magic_ext[wu_state]) {
+ WUDebugLog("magic fail %02x %02x\r\n", m, wu_magic[wu_state]);
+ // this resyncs to the incoming message!
+ wu_hdr.magic[0] = m;
+ wu_len = 1;
+ wu_state = (m == wu_magic[0]) ? WU_MAGIC1 : WU_MAGIC0; // if we got the first byte, we can tick into next state!
+ wu_data = wu_hdr.magic + wu_state;
+ continue;
+ }
+ wu_state++;
+ wu_len = 1;
+ if (wu_state == WU_RECVHDR) { // time to get rest of header
+ WUDebugLog("get header!\r\n");
+ wu_len = 10 - 4;
+ if (is_wu_hdr_32bit())
+ wu_len = 14 - 4;
+ }
+ continue;
+ }
+ case WU_RECVHDR: { // ok we got the header. what do they want:
+ WUDebugLog("got header %d %d!\r\n", wu_hdr.cmd, wu_hdr_len());
+ if (wu_hdr.cmd >= 6)
+ goto reset; // invalid cmd?
+ int maxsize = (wu_hdr.idx >= 64) ? sizeof(SampleInfo) : sizeof(Preset);
+ if (wu_hdr.cmd<2) {
+ if (wu_hdr.idx >= 64 + 8)
+ wu_hdr.idx = ((ramsample1_idx - 1) & 7) + 64;
+ else if (wu_hdr.idx >= 32)
+ wu_hdr.idx = sysparams.curpreset;
+ } else if (wu_hdr.cmd ==2 || wu_hdr.cmd == 3) {
+ maxsize= 1024*1024*32;
+ } else if (wu_hdr.cmd ==4 || wu_hdr.cmd == 5) {
+ maxsize=sizeof(wavetable);
+ }
+ if (wu_hdr_len() + wu_hdr_offset() > maxsize || wu_hdr_len() < 0 || wu_hdr_offset() < 0)
+ goto reset;
+ if (wu_hdr.cmd == 4) {
+ // read wavetable ram
+ wu_hdr.cmd = 5;
+ int ofs = wu_hdr_offset();
+ int len = wu_hdr_len();
+ wu_hdr.len_32 = len;
+ wu_hdr.offset_32 = ofs;
+ wu_hdr.magic[3] = wu_magic_ext[3]; // 32 bit mode
+ SetWUState(WU_SENDHDR, (u8 *)&wu_hdr, is_wu_hdr_32bit() ? 14 : 10);
+ } else if (wu_hdr.cmd == 2) {
+ // read sample ram
+ wu_hdr.cmd = 3;
+ wu_hdr.idx &= 7;
+ int ofs = wu_hdr_offset();
+ int len = wu_hdr_len();
+ wu_hdr.len_32 = len;
+ wu_hdr.offset_32 = ofs;
+ wu_hdr.magic[3] = wu_magic_ext[3]; // 32 bit mode
+ SetWUState(WU_SENDHDR, (u8 *)&wu_hdr, is_wu_hdr_32bit() ? 14 : 10);
+ } else if (wu_hdr.cmd == 0) {
+ wu_hdr.cmd = 1;
+ if (wu_hdr_len() == 0) {
+ int ofs = wu_hdr_offset();
+ wu_hdr.len_16 = maxsize - ofs;
+ wu_hdr.offset_16 = ofs;
+ wu_hdr.magic[3] = wu_magic[3]; // 16 bit mode
+ }
+ SetWUState(WU_SENDHDR, (u8 *)&wu_hdr, is_wu_hdr_32bit() ? 14 : 10);
+ } else if (wu_hdr.cmd == 1) {
+ // they asked to set!
+ if (wu_hdr.idx >= 64 && wu_hdr.idx < 64 + 8) {
+ cur_sample1 = wu_hdr.idx - 64 + 1;
+ CopySampleToRam(false);
+ ramsample1_idx = cur_sample1;
+ SetWUState(WU_RECVDATA, ((u8 *)&ramsample) + wu_hdr_offset(), wu_hdr_len());
+ } else {
+ SetPreset(wu_hdr.idx, false);
+ SetWUState(WU_RECVDATA, ((u8 *)&rampreset) + wu_hdr_offset(), wu_hdr_len());
+ }
+ } else
+ goto reset;
+ continue;
+ }
+ case WU_RECVDATA:
+ if (wu_hdr.cmd == 1) {
+ // finished receiving preset. mark it dirty
+ if (wu_hdr.idx >= 64) {
+ ramtime[GEN_SAMPLE] = millis();
+ } else
+ ramtime[GEN_PRESET] = millis();
+ } else if (wu_hdr.cmd == 3) {
+ // TODO: write to spi ram
+ } else if (wu_hdr.cmd == 5) {
+ // TODO: write to wavetable ram
+ }
+ goto reset;
+ case WU_SENDHDR: // ok we sent them the header; now send them the data
+ send_more_data: {
+ int len = wu_hdr_len();
+ int ofs = wu_hdr_offset();
+
+ u8 *data;
+ if (wu_hdr.cmd == 5) { // send wavetable data
+ SetWUState(WU_SENDDATA, ((u8 *)&wavetable) + ofs, len);
+ } else if (wu_hdr.cmd == 3) { // send spi data
+ if (len > 256)
+ len = 256;
+ wu_hdr.len_32 -= len;
+ wu_hdr.offset_32 += len;
+ while (spistate)
+ ;
+ spistate = 255;
+ memset(spibigrx, -2, sizeof(spibigrx));
+ spi_read256(ofs);
+ SetWUState(WU_SENDDATA, spibigrx + 4, len);
+ } else if (wu_hdr.cmd == 1) {
+ if (wu_hdr.idx == ((ramsample1_idx - 1) & 7) + 64)
+ data = (u8 *)&ramsample;
+ else if (wu_hdr.idx >= 64 && wu_hdr.idx < 64 + 8)
+ data = (u8 *)GetSavedSampleInfo(wu_hdr.idx - 64);
+ else
+ data = (wu_hdr.idx == sysparams.curpreset) ? (u8 *)&rampreset : (u8 *)GetSavedPreset(wu_hdr.idx);
+ SetWUState(WU_SENDDATA, data + wu_hdr_offset(), wu_hdr_len());
+ }
+ continue;
+ }
+ case WU_SENDDATA: // ok we finished sending our data. nothing to do except get ready for more.
+ if (wu_hdr.cmd == 3) {
+ if (wu_hdr.len_32 <= 0) {
+ spistate = 0;
+ } else {
+ goto send_more_data;
+ }
+ }
+ goto reset;
+ } // state
+ } // while loop
}
--
cgit v1.2.3