From 0de116c9cc1c41917e8cc5ef48f23da214fd6243 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sun, 9 Jul 2017 10:19:14 +0100 Subject: [PATCH] USB handling in PXT app --- cmds/cmds.ts | 4 +-- editor/extension.ts | 9 +++--- editor/wrap.ts | 78 ++++++++++++++++++++++++++++++++++++++------- libs/core/linux.cpp | 47 ++++++++++++++++++++++++--- 4 files changed, 116 insertions(+), 22 deletions(-) diff --git a/cmds/cmds.ts b/cmds/cmds.ts index 66c86b30..bec3277e 100644 --- a/cmds/cmds.ts +++ b/cmds/cmds.ts @@ -4,9 +4,9 @@ require("./editor") declare namespace pxt.editor { - function deployCoreAsync(resp: pxtc.CompileResult, isCli?: boolean): Promise; + function deployCoreAsync(resp: pxtc.CompileResult, disconnect?: boolean): Promise; } export function deployCoreAsync(resp: pxtc.CompileResult) { - return pxt.editor.deployCoreAsync(resp, true) + return pxt.editor.deployCoreAsync(resp, process.env["PXT_SERIAL"] ? false : true) } diff --git a/editor/extension.ts b/editor/extension.ts index e2b8d865..dd65cf74 100644 --- a/editor/extension.ts +++ b/editor/extension.ts @@ -42,6 +42,8 @@ namespace pxt.editor { w = w_ if (w.isStreaming) U.userError("please stop the program first") + return w.stopAsync() + }).then(() => { return w.rmAsync(elfPath) }).then(() => { let f = U.stringToUint8Array(atob(resp.outfiles[pxt.outputName()])) @@ -60,6 +62,7 @@ namespace pxt.editor { return w.disconnectAsync() else return Promise.resolve() + //return Promise.delay(1000).then(() => w.dmesgAsync()) }) } @@ -72,11 +75,7 @@ namespace pxt.editor { /* .then(w => w.streamFileAsync("/tmp/serial.txt", buf => { let str = Util.fromUTF8(Util.uint8ArrayToString(buf)) - window.postMessage({ - type: 'serial', - id: 'n/a', // TODO - data: str - }, "*") + })) */ return Promise.resolve(res); diff --git a/editor/wrap.ts b/editor/wrap.ts index 32170e59..87c32bc5 100644 --- a/editor/wrap.ts +++ b/editor/wrap.ts @@ -13,26 +13,37 @@ namespace pxt.editor { } const runTemplate = "C00882010084XX0060640301606400" + const usbMagic = 0x3d3f export class Ev3Wrapper { msgs = new U.PromiseBuffer() private cmdSeq = U.randomUint32() & 0xffff; private lock = new U.PromiseQueue(); isStreaming = false; + dataDump = false; constructor(public io: pxt.HF2.PacketIO) { io.onData = buf => { buf = buf.slice(0, HF2.read16(buf, 0) + 2) - if (HF2.read16(buf, 4) == 0x3d3f) { + if (HF2.read16(buf, 4) == usbMagic) { let code = HF2.read16(buf, 6) let payload = buf.slice(8) - if (code == 1) - console.log("Serial: " + U.uint8ArrayToString(payload)) - else + if (code == 1) { + let str = U.uint8ArrayToString(payload) + if (Util.isNodeJS) + console.log("SERIAL: " + str.replace(/\n+$/, "")) + else + window.postMessage({ + type: 'serial', + id: 'n/a', // TODO? + data: str + }, "*") + } else console.log("Magic: " + code + ": " + U.toHex(payload)) return } - //log("DATA: " + U.toHex(buf)) + if (this.dataDump) + log("RECV: " + U.toHex(buf)) this.msgs.push(buf) } } @@ -52,6 +63,30 @@ namespace pxt.editor { return buf } + private allocCustom(code: number, addSize = 0) { + let buf = this.allocCore(1 + 2 + addSize, 0) + HF2.write16(buf, 4, usbMagic) + HF2.write16(buf, 6, code) + return buf + } + + stopAsync() { + return this.isVmAsync() + .then(vm => { + if (vm) return Promise.resolve(); + log(`stopping PXT app`) + let buf = this.allocCustom(2) + return this.justSendAsync(buf) + .then(() => Promise.delay(500)) + }) + } + + dmesgAsync() { + log(`asking for DMESG buffer over serial`) + let buf = this.allocCustom(3) + return this.justSendAsync(buf) + } + runAsync(path: string) { let codeHex = runTemplate.replace("XX", U.toHex(U.stringToUint8Array(path))) let code = U.fromHex(codeHex) @@ -59,21 +94,30 @@ namespace pxt.editor { HF2.write16(pkt, 5, 0x0800) U.memcpy(pkt, 7, code) log(`run ${path}`) - return this.talkAsync(pkt) - .then(buf => { - }) + return this.justSendAsync(pkt) + } + + justSendAsync(buf: Uint8Array) { + return this.lock.enqueue("talk", () => { + this.msgs.drain() + if (this.dataDump) + log("SEND: " + U.toHex(buf)) + return this.io.sendPacketAsync(buf) + }) } talkAsync(buf: Uint8Array, altResponse = 0) { return this.lock.enqueue("talk", () => { this.msgs.drain() + if (this.dataDump) + log("TALK: " + U.toHex(buf)) return this.io.sendPacketAsync(buf) .then(() => this.msgs.shiftAsync(1000)) .then(resp => { if (resp[2] != buf[2] || resp[3] != buf[3]) U.userError("msg count de-sync") if (buf[4] == 1) { - if (resp[5] != buf[5]) + if (altResponse != -1 && resp[5] != buf[5]) U.userError("cmd de-sync") if (altResponse != -1 && resp[6] != 0 && resp[6] != altResponse) U.userError("cmd error: " + resp[6]) @@ -84,7 +128,7 @@ namespace pxt.editor { } flashAsync(path: string, file: Uint8Array) { - log(`write ${file.length} to ${path}`) + log(`write ${file.length} bytes to ${path}`) let handle = -1 @@ -138,10 +182,22 @@ namespace pxt.editor { let rmReq = this.allocSystem(path.length + 1, 0x9c) U.memcpy(rmReq, 6, U.stringToUint8Array(path)) - return this.talkAsync(rmReq) + return this.talkAsync(rmReq, 5) .then(resp => { }) } + isVmAsync(): Promise { + let path = "/no/such/dir" + let mkdirReq = this.allocSystem(path.length + 1, 0x9b) + U.memcpy(mkdirReq, 6, U.stringToUint8Array(path)) + return this.talkAsync(mkdirReq, -1) + .then(resp => { + let isVM = resp[6] == 0x05 + log(`${isVM ? "PXT app" : "VM"} running`) + return isVM + }) + } + private streamFileOnceAsync(path: string, cb: (d: Uint8Array) => void) { let fileSize = 0 let filePtr = 0 diff --git a/libs/core/linux.cpp b/libs/core/linux.cpp index da02a67a..a7ee17e1 100644 --- a/libs/core/linux.cpp +++ b/libs/core/linux.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include void *operator new(size_t size) { @@ -71,19 +72,56 @@ Event *mkEvent(int source, int value) { #define USB_MAGIC 0x3d3f #define USB_SERIAL 1 +#define USB_RESTART 2 +#define USB_DMESG 3 struct UsbPacket { uint16_t size; uint16_t msgcount; uint16_t magic; uint16_t code; - char buf[1000]; + char buf[1024 - 8]; }; -void sendUsb(uint16_t code, const char *data, int len) { - if (usbFD == 0) - usbFD = open("/dev/lms_usbdev", O_RDWR, 0666); +void *usbThread(void *) { + UsbPacket pkt; + UsbPacket resp; + while (true) { + int len = read(usbFD, &pkt, sizeof(pkt)); + if (len <= 4) { + sleep_core_us(20000); + continue; + } + resp.msgcount = pkt.msgcount; + if (pkt.magic == USB_MAGIC) { + if (pkt.code == USB_RESTART) { + target_reset(); + } else if (pkt.code == USB_DMESG) { + dumpDmesg(); + } + /* + resp.magic = pkt.magic; + resp.code = pkt.code; + resp.size = 8; + write(usbFD, &resp, sizeof(resp)); + */ + } else { + resp.magic = 0xffff; + resp.size = 4; + write(usbFD, &resp, sizeof(resp)); + } + sleep_core_us(1000); + } +} +static void startUsb() { + usbFD = open("/dev/lms_usbdev", O_RDWR, 0666); + pthread_t pid; + pthread_create(&pid, NULL, usbThread, NULL); + pthread_detach(pid); +} + +void sendUsb(uint16_t code, const char *data, int len) { while (len > 0) { int sz = len; if (sz > 1000) @@ -398,6 +436,7 @@ void initRuntime() { startTime = currTime(); DMESG("runtime starting..."); stopLMS(); + startUsb(); pthread_t disp; pthread_create(&disp, NULL, evtDispatcher, NULL); pthread_detach(disp);