summaryrefslogtreecommitdiff
path: root/appl/lib/usb
diff options
context:
space:
mode:
Diffstat (limited to 'appl/lib/usb')
-rw-r--r--appl/lib/usb/mkfile16
-rw-r--r--appl/lib/usb/usb.b453
-rw-r--r--appl/lib/usb/usbmass.b465
-rw-r--r--appl/lib/usb/usbmct.b204
-rw-r--r--appl/lib/usb/usbmouse.b60
5 files changed, 1198 insertions, 0 deletions
diff --git a/appl/lib/usb/mkfile b/appl/lib/usb/mkfile
new file mode 100644
index 00000000..3fe78cca
--- /dev/null
+++ b/appl/lib/usb/mkfile
@@ -0,0 +1,16 @@
+<../../../mkconfig
+
+TARG=\
+ usb.dis\
+ usbmouse.dis\
+ usbmct.dis\
+ usbmass.dis
+
+MODULES=
+
+SYSMODULES= \
+ usb.m\
+
+DISBIN=$ROOT/dis/lib/usb
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/lib/usb/usb.b b/appl/lib/usb/usb.b
new file mode 100644
index 00000000..8c8fa139
--- /dev/null
+++ b/appl/lib/usb/usb.b
@@ -0,0 +1,453 @@
+#
+# Copyright © 2002 Vita Nuova Holdings Limited
+#
+implement Usb;
+
+include "sys.m";
+ sys: Sys;
+
+include "usb.m";
+
+include "string.m";
+ str: String;
+
+Proto: adt {
+ proto: int;
+ name: string;
+};
+
+SubClass: adt {
+ subclass: int;
+ name: string;
+ proto: array of Proto;
+};
+
+Class: adt {
+ class: int;
+ name: string;
+ subclass: array of SubClass;
+};
+
+classes := array [] of {
+ Class(Usb->CL_AUDIO, "audio",
+ array [] of {
+ SubClass(1, "control", nil),
+ SubClass(2, "stream", nil),
+ SubClass(3, "midi", nil),
+ }
+ ),
+ Class(Usb->CL_COMMS, "comms",
+ array [] of {
+ SubClass(1, "abstract",
+ array [] of {
+ Proto(1, "AT"),
+ }
+ )
+ }
+ ),
+ Class(Usb->CL_HID, "hid",
+ array [] of {
+ SubClass(1, "boot",
+ array [] of {
+ Proto(1, "kbd"),
+ Proto(2, "mouse"),
+ }
+ )
+ }
+ ),
+ Class(Usb->CL_PRINTER, "printer",
+ array [] of {
+ SubClass(1, "printer",
+ array [] of {
+ Proto(1, "uni"),
+ Proto(2, "bi"),
+ }
+ )
+ }
+ ),
+ Class(Usb->CL_HUB, "hub",
+ array [] of {
+ SubClass(1, "hub", nil),
+ }
+ ),
+ Class(Usb->CL_DATA, "data", nil),
+ Class(Usb->CL_MASS, "mass",
+ array [] of {
+ SubClass(1, "rbc",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ SubClass(2, "sff-8020i/mmc-2",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ SubClass(3, "qic-157",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ SubClass(4, "ufi",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ SubClass(5, "sff-8070i",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ SubClass(6, "scsi",
+ array [] of {
+ Proto(0, "cbi-cc"),
+ Proto(1, "cbi-nocc"),
+ Proto(16r50, "bulkonly"),
+ }
+ ),
+ }
+ ),
+};
+
+get2(b: array of byte): int
+{
+ return int b[0] | (int b[1] << 8);
+}
+
+put2(buf: array of byte, v: int)
+{
+ buf[0] = byte v;
+ buf[1] = byte (v >> 8);
+}
+
+get4(b: array of byte): int
+{
+ return int b[0] | (int b[1] << 8) | (int b[2] << 16) | (int b[3] << 24);
+}
+
+put4(buf: array of byte, v: int)
+{
+ buf[0] = byte v;
+ buf[1] = byte (v >> 8);
+ buf[2] = byte (v >> 16);
+ buf[3] = byte (v >> 24);
+}
+
+bigget2(b: array of byte): int
+{
+ return int b[1] | (int b[0] << 8);
+}
+
+bigput2(buf: array of byte, v: int)
+{
+ buf[1] = byte v;
+ buf[0] = byte (v >> 8);
+}
+
+bigget4(b: array of byte): int
+{
+ return int b[3] | (int b[2] << 8) | (int b[1] << 16) | (int b[0] << 24);
+}
+
+bigput4(buf: array of byte, v: int)
+{
+ buf[3] = byte v;
+ buf[2] = byte (v >> 8);
+ buf[1] = byte (v >> 16);
+ buf[0] = byte (v >> 24);
+}
+
+strtol(s: string, base: int): (int, string)
+{
+ if (str == nil)
+ str = load String String->PATH;
+ if (base != 0)
+ return str->toint(s, base);
+ if (len s >= 2 && (s[0:2] == "0X" || s[0:2] == "0x"))
+ return str->toint(s[2:], 16);
+ if (len s > 0 && s[0:1] == "0")
+ return str->toint(s[1:], 8);
+ return str->toint(s, 10);
+}
+
+memset(buf: array of byte, v: int)
+{
+ for (x := 0; x < len buf; x++)
+ buf[x] = byte v;
+}
+
+setupreq(setupfd: ref Sys->FD, typ, req, value, index: int, outbuf: array of byte, count: int): int
+{
+ additional: int;
+ if (outbuf != nil) {
+ additional = len outbuf;
+ # if there is an outbuf, then the count sent must be length of the payload
+ # this assumes that RH2D is set
+ count = additional;
+ }
+ else
+ additional = 0;
+ buf := array[8 + additional] of byte;
+ buf[0] = byte typ;
+ buf[1] = byte req;
+ put2(buf[2:], value);
+ put2(buf[4:], index);
+ put2(buf[6:], count);
+ if (additional)
+ buf[8:] = outbuf;
+ rv := sys->write(setupfd, buf, len buf);
+ if (rv < 0)
+ return -1;
+ if (rv != len buf)
+ return -1;
+ return rv;
+}
+
+setupreply(setupfd: ref Sys->FD, buf: array of byte): int
+{
+ nb := sys->read(setupfd, buf, len buf);
+ return nb;
+}
+
+setup(setupfd: ref Sys->FD, typ, req, value, index: int, outbuf: array of byte, inbuf: array of byte): int
+{
+ count: int;
+ if (inbuf != nil)
+ count = len inbuf;
+ else
+ count = 0;
+ if (setupreq(setupfd, typ, req, value, index, outbuf, count) < 0)
+ return -1;
+ if (count == 0)
+ return 0;
+ return setupreply(setupfd, inbuf);
+}
+
+get_descriptor(fd: ref Sys->FD, rtyp: int, dtyp: int, dindex: int, langid: int, buf: array of byte): int
+{
+ nr := -1;
+ if (setupreq(fd, RD2H | rtyp | Rdevice, GET_DESCRIPTOR, (dtyp << 8) | dindex, langid, nil, len buf) < 0
+ || (nr = setupreply(fd, buf)) < 1)
+ return -1;
+ return nr;
+}
+
+get_standard_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int
+{
+ return get_descriptor(fd, Rstandard, dtyp, index, 0, buf);
+}
+
+get_class_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int
+{
+ return get_descriptor(fd, Rclass, dtyp, index, 0, buf);
+}
+
+get_vendor_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int
+{
+ return get_descriptor(fd, Rvendor, dtyp, index, 0, buf);
+}
+
+get_status(fd: ref Sys->FD, port: int): int
+{
+ buf := array [4] of byte;
+ if (setupreq(fd, RD2H | Rclass | Rother, GET_STATUS, 0, port, nil, len buf) < 0
+ || setupreply(fd, buf) < len buf)
+ return -1;
+ return get2(buf);
+}
+
+set_address(fd: ref Sys->FD, address: int): int
+{
+ return setupreq(fd, RH2D | Rstandard | Rdevice, SET_ADDRESS, address, 0, nil, 0);
+}
+
+set_configuration(fd: ref Sys->FD, n: int): int
+{
+ return setupreq(fd, RH2D | Rstandard | Rdevice, SET_CONFIGURATION, n, 0, nil, 0);
+}
+
+setclear_feature(fd: ref Sys->FD, rtyp: int, value: int, index: int, on: int): int
+{
+ req: int;
+ if (on)
+ req = SET_FEATURE;
+ else
+ req = CLEAR_FEATURE;
+ return setupreq(fd, RH2D | rtyp, req, value, index, nil, 0);
+}
+
+parse_conf(b: array of byte): ref Configuration
+{
+ if (len b < DCONFLEN)
+ return nil;
+ conf := ref Configuration;
+ conf.id = int b[5];
+ conf.iface = array[int b[4]] of Interface;
+ conf.attr = int b[7];
+ conf.powerma = int b[8] * 2;
+ return conf;
+}
+
+parse_iface(conf: ref Configuration, b: array of byte): ref AltInterface
+{
+ if (len b < DINTERLEN || conf == nil)
+ return nil;
+ id := int b[2];
+ if (id >= len conf.iface)
+ return nil;
+ ai := ref AltInterface;
+ ai.id = int b[3];
+ if (int b[4] != 0)
+ ai.ep = array [int b[4]] of ref Endpt;
+ ai.class = int b[5];
+ ai.subclass = int b[6];
+ ai.proto = int b[7];
+ conf.iface[id].altiface = ai :: conf.iface[id].altiface;
+ return ai;
+}
+
+parse_endpt(conf: ref Configuration, ai: ref AltInterface, b: array of byte): ref Endpt
+{
+ if (len b < DENDPLEN || conf == nil || ai == nil || ai.ep == nil)
+ return nil;
+ for (i := 0; i < len ai.ep; i++)
+ if (ai.ep[i] == nil)
+ break;
+ if (i >= len ai.ep)
+ return nil;
+ ep := ref Endpt;
+ ai.ep[i] = ep;
+ ep.addr = int b[2];
+ ep.attr = int b[3];
+ ep.d2h = ep.addr & 16r80;
+ ep.etype = int b[3] & 3;
+ ep.isotype = (int b[3] >> 2) & 3;
+ ep.maxpkt = get2(b[4:]);
+ ep.interval = int b[6];
+ return ep;
+}
+
+get_parsed_configuration_descriptor(fd: ref Sys->FD, n: int): ref Configuration
+{
+ conf: ref Configuration;
+ altiface: ref AltInterface;
+
+ b := array [256] of byte;
+ nr := get_standard_descriptor(fd, CONFIGURATION, n, b);
+ if (nr < 0)
+ return nil;
+ conf = nil;
+ altiface = nil;
+ for (i := 0; nr - i > 2 && b[i] > byte 0 && int b[i] <= nr - i; i += int b[i]) {
+ ni := i + int b[i];
+ case int b[i + 1] {
+ Usb->CONFIGURATION =>
+ conf = parse_conf(b[i: ni]);
+ if (conf == nil)
+ return nil;
+ Usb->INTERFACE =>
+ altiface = parse_iface(conf, b[i: ni]);
+ if (altiface == nil)
+ return nil;
+ Usb->ENDPOINT =>
+ if (parse_endpt(conf, altiface, b[i: ni]) == nil)
+ return nil;
+ }
+ }
+ if (i < nr)
+ sys->print("usb: residue at end of descriptors\n");
+ return conf;
+}
+
+get_parsed_device_descriptor(fd: ref Sys->FD): ref Device
+{
+ b := array [256] of byte;
+ nr := get_standard_descriptor(fd, DEVICE, 0, b);
+ if (nr < DDEVLEN) {
+ if (nr == 8 || nr == 16) {
+ memset(b[nr: DDEVLEN - 1], 0);
+ b[DDEVLEN - 1] = byte 1;
+ nr = DDEVLEN;
+ }
+ else
+ return nil;
+ }
+ dev := ref Device;
+ dev.usbmajor = int b[3];
+ dev.usbminor = int b[2];
+ dev.class = int b[4];
+ dev.subclass = int b[5];
+ dev.proto = int b[6];
+ dev.maxpkt0 = int b[7];
+ dev.vid = get2(b[8:]);
+ dev.did = get2(b[10:]);
+ dev.relmajor = int b[13];
+ dev.relminor = int b[12];
+ dev.nconf = int b[17];
+ return dev;
+}
+
+dump_configuration(fd: ref Sys->FD, conf: ref Configuration)
+{
+ sys->fprint(fd, "configuration %d attr 0x%.x powerma %d\n", conf.id, conf.attr, conf.powerma);
+ for (i := 0; i < len conf.iface; i++) {
+ sys->fprint(fd, "\tinterface %d\n", i);
+ ail := conf.iface[i].altiface;
+ while (ail != nil) {
+ ai := hd ail;
+ sys->fprint(fd, "\t\t%d class %d subclass %d proto %d [%s]\n",
+ ai.id, ai.class, ai.subclass, ai.proto,
+ sclass(ai.class, ai.subclass, ai.proto));
+ for (e := 0; e < len ai.ep; e++) {
+ if (ai.ep[e] == nil) {
+ sys->fprint(fd, "\t\t\t missing descriptor\n");
+ continue;
+ }
+ sys->fprint(fd, "\t\t\t0x%.2ux attr 0x%.x maxpkt %d interval %d\n",
+ ai.ep[e].addr, ai.ep[e].attr, ai.ep[e].maxpkt, ai.ep[e].interval);
+ }
+ ail = tl ail;
+ }
+ }
+sys->fprint(fd, "done dumping\n");
+}
+
+sclass(class, subclass, proto: int): string
+{
+ for (c := 0; c < len classes; c++)
+ if (classes[c].class == class)
+ break;
+ if (c >= len classes)
+ return sys->sprint("%d.%d.%d", class, subclass, proto);
+ if (classes[c].subclass == nil)
+ return sys->sprint("%s.%d.%d", classes[c].name, subclass, proto);
+ for (sc := 0; sc < len classes[c].subclass; sc++)
+ if (classes[c].subclass[sc].subclass == subclass)
+ break;
+ if (sc >= len classes[c].subclass)
+ return sys->sprint("%s.%d.%d", classes[c].name, subclass, proto);
+ if (classes[c].subclass[sc].proto == nil)
+ return sys->sprint("%s.%s.%d", classes[c].name, classes[c].subclass[sc].name, proto);
+ for (p := 0; p < len classes[c].subclass[sc].proto; p++)
+ if (classes[c].subclass[sc].proto[p].proto == proto)
+ break;
+ if (p >= len classes[c].subclass[sc].proto)
+ return sys->sprint("%s.%s.%d", classes[c].name, classes[c].subclass[sc].name, proto);
+ return sys->sprint("%s.%s.%s", classes[c].name, classes[c].subclass[sc].name,
+ classes[c].subclass[sc].proto[p].name);
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
diff --git a/appl/lib/usb/usbmass.b b/appl/lib/usb/usbmass.b
new file mode 100644
index 00000000..f5140cc3
--- /dev/null
+++ b/appl/lib/usb/usbmass.b
@@ -0,0 +1,465 @@
+#
+# Copyright © 2001 Vita Nuova Holdings Limited.
+#
+implement UsbDriver;
+
+include "sys.m";
+ sys: Sys;
+include "usb.m";
+ usb: Usb;
+ Endpt, RD2H, RH2D: import Usb;
+
+ENDPOINT_STALL: con 0; # TO DO: should be in usb.m
+
+readerpid: int;
+setupfd, ctlfd: ref Sys->FD;
+infd, outfd: ref Sys->FD;
+inep, outep: ref Endpt;
+cbwseq := 0;
+capacity: big;
+debug := 0;
+
+lun: int;
+blocksize: int;
+
+kill(pid: int)
+{
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if (fd != nil)
+ sys->fprint(fd, "kill");
+}
+
+reader(pidc: chan of int, fileio: ref Sys->FileIO)
+{
+ pid := sys->pctl(0, nil);
+ pidc <-= pid;
+ for(;;) alt{
+ (offset, count, nil, rc) := <-fileio.read =>
+ if (rc != nil) {
+ if (offset%blocksize || count%blocksize) {
+ rc <- = (nil, "unaligned read");
+ continue;
+ }
+ offset /= blocksize;
+ count /= blocksize;
+ buf := array [count * blocksize] of byte;
+ if (scsiread10(lun, offset, count, buf) < 0) {
+ scsirequestsense(lun);
+ rc <- = (nil, "read error");
+ continue;
+ }
+ rc <- = (buf, nil);
+ }
+ (offset, data, nil, wc) := <-fileio.write =>
+ if(wc != nil){
+ count := len data;
+ if(offset%blocksize || count%blocksize){
+ wc <-= (0, "unaligned write");
+ continue;
+ }
+ offset /= blocksize;
+ count /= blocksize;
+ if(scsiwrite10(lun, offset, count, data) < 0){
+ scsirequestsense(lun);
+ wc <-= (0, "write error");
+ continue;
+ }
+ wc <-= (len data, nil);
+ }
+ }
+ readerpid = -1;
+}
+
+massstoragereset(): int
+{
+ if (usb->setup(setupfd, Usb->RH2D | Usb->Rclass | Usb->Rinterface, 255, 0, 0, nil, nil) < 0) {
+ sys->print("usbmass: storagereset failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+getmaxlun(): int
+{
+ buf := array[1] of byte;
+ if (usb->setup(setupfd, Usb->RD2H | Usb->Rclass | Usb->Rinterface, 254, 0, 0, nil, buf) < 0) {
+ sys->print("usbmass: getmaxlun failed\n");
+ return -1;
+ }
+ return int buf[0];
+}
+
+#
+# CBW:
+# sig[4]="USBC" tag[4] datalen[4] flags[1] lun[1] len[1] cmd[len]
+#
+sendcbw(dtl: int, outdir: int, lun: int, cmd: array of byte): int
+{
+ cbw := array [31] of byte;
+ cbw[0] = byte 'U';
+ cbw[1] = byte 'S';
+ cbw[2] = byte 'B';
+ cbw[3] = byte 'C';
+ usb->put4(cbw[4:], ++cbwseq);
+ usb->put4(cbw[8:], dtl);
+ if (outdir)
+ cbw[12] = byte RH2D;
+ else
+ cbw[12] = byte RD2H;
+ cbw[13] = byte lun;
+ cbw[14] = byte len cmd;
+ cbw[15:] = cmd;
+ rv := sys->write(outfd, cbw, len cbw);
+ if (rv < 0) {
+ sys->print("sendcbw: failed: %r\n");
+ return -1;
+ }
+ if (rv != len cbw) {
+ sys->print("sendcbw: truncated send\n");
+ return -1;
+ }
+ return 0;
+}
+
+#
+# CSW:
+# sig[4]="USBS" tag[4] residue[4] status[1]
+#
+
+recvcsw(tag: int): (int, int)
+{
+ if(debug)
+ sys->print("recvcsw\n");
+ buf := array [13] of byte;
+ if (sys->read(infd, buf, len buf) != len buf) {
+ sys->print("recvcsw: read failed: %r\n");
+ return (-1, -1);
+ }
+ if (usb->get4(buf) != (('S'<<24)|('B'<<16)|('S'<<8)|'U')) {
+ sys->print("recvcsw: signature wrong\n");
+ return (-1, -1);
+ }
+ recvtag := usb->get4(buf[4:]);
+ if (recvtag != tag) {
+ sys->print("recvcsw: tag does not match: sent %d recved %d\n", tag, recvtag);
+ return (-1, -1);
+ }
+ residue := usb->get4(buf[8:]);
+ status := int buf[12];
+ if(debug)
+ sys->print("recvcsw: residue %d status %d\n", residue, status);
+ return (residue, status);
+}
+
+unstall(ep: ref Endpt)
+{
+ if(debug)
+ sys->print("unstalling bulk %x\n", ep.addr);
+ x := ep.addr & 16rF;
+ sys->fprint(ctlfd, "unstall %d", x);
+ sys->fprint(ctlfd, "data %d 0", x);
+ if (usb->setclear_feature(setupfd, Usb->Rendpt, ENDPOINT_STALL, ep.addr, 0) < 0) {
+ sys->print("unstall: clear_feature() failed: %r\n");
+ return;
+ }
+}
+
+warnfprint(fd: ref Sys->FD, s: string)
+{
+ if (sys->fprint(fd, "%s", s) != len s)
+ sys->print("warning: writing %s failed: %r\n", s);
+}
+
+bulkread(lun: int, cmd: array of byte, buf: array of byte, dump: int): int
+{
+ if (sendcbw(len buf, 0, lun, cmd) < 0)
+ return -1;
+ got := 0;
+ if (buf != nil) {
+ while (got < len buf) {
+ rv := sys->read(infd, buf[got:], len buf - got);
+ if (rv < 0) {
+ sys->print("bulkread: read failed: %r\n");
+ break;
+ }
+ if(debug)
+ sys->print("read %d\n", rv);
+ got += rv;
+ break;
+ }
+ if (dump) {
+ for (i := 0; i < got; i++)
+ sys->print("%.2ux", int buf[i]);
+ sys->print("\n");
+ }
+ if (got == 0)
+ unstall(inep);
+ }
+ (residue, status) := recvcsw(cbwseq);
+ if (residue < 0) {
+ unstall(inep);
+ (residue, status) = recvcsw(cbwseq);
+ if (residue < 0)
+ return -1;
+ }
+ if (status != 0)
+ return -1;
+ return got;
+}
+
+bulkwrite(lun: int, cmd: array of byte, buf: array of byte): int
+{
+ if (sendcbw(len buf, 1, lun, cmd) < 0)
+ return -1;
+ got := 0;
+ if (buf != nil) {
+ while (got < len buf) {
+ rv := sys->write(outfd, buf[got:], len buf - got);
+ if (rv < 0) {
+ sys->print("bulkwrite: write failed: %r\n");
+ break;
+ }
+ if(debug)
+ sys->print("write %d\n", rv);
+ got += rv;
+ break;
+ }
+ if (got == 0)
+ unstall(outep);
+ }
+ (residue, status) := recvcsw(cbwseq);
+ if (residue < 0) {
+ unstall(inep);
+ (residue, status) = recvcsw(cbwseq);
+ if (residue < 0)
+ return -1;
+ }
+ if (status != 0)
+ return -1;
+ return got;
+}
+
+scsiinquiry(lun: int): int
+{
+ buf := array [36] of byte; # don't use 255, many devices can't cope
+ cmd := array [6] of byte;
+ cmd[0] = byte 16r12;
+ cmd[1] = byte (lun << 5);
+ cmd[2] = byte 0;
+ cmd[3] = byte 0;
+ cmd[4] = byte len buf;
+ cmd[5] = byte 0;
+ got := bulkread(lun, cmd, buf, 0);
+ if (got < 0)
+ return -1;
+ if (got < 36) {
+ sys->print("scsiinquiry: too little data\n");
+ return -1;
+ }
+ t := int buf[0] & 16r1f;
+ if(debug)
+ sys->print("scsiinquiry: type %d/%s\n", t, string buf[8:35]);
+ if (t != 0)
+ return -1;
+ return 0;
+}
+
+scsireadcapacity(lun: int): int
+{
+# warnfprint(ctlfd, "debug 0 1");
+# warnfprint(ctlfd, "debug 1 1");
+# warnfprint(ctlfd, "debug 2 1");
+ buf := array [8] of byte;
+ cmd := array [10] of byte;
+ cmd[0] = byte 16r25;
+ cmd[1] = byte (lun << 5);
+ cmd[2] = byte 0;
+ cmd[3] = byte 0;
+ cmd[4] = byte 0;
+ cmd[5] = byte 0;
+ cmd[6] = byte 0;
+ cmd[7] = byte 0;
+ cmd[8] = byte 0;
+ cmd[9] = byte 0;
+ got := bulkread(lun, cmd, buf, 0);
+ if (got < 0)
+ return -1;
+ if (got != len buf) {
+ sys->print("scsireadcapacity: returned data not right size\n");
+ return -1;
+ }
+ blocksize = usb->bigget4(buf[4:]);
+ lba := big usb->bigget4(buf[0:]) & 16rFFFFFFFF;
+ capacity = big blocksize * (lba+big 1);
+ if(debug)
+ sys->print("block size %d lba %bd cap %bd\n", blocksize, lba, capacity);
+ return 0;
+}
+
+scsirequestsense(lun: int): int
+{
+# warnfprint(ctlfd, "debug 0 1");
+# warnfprint(ctlfd, "debug 1 1");
+# warnfprint(ctlfd, "debug 2 1");
+ buf := array [18] of byte;
+ cmd := array [6] of byte;
+ cmd[0] = byte 16r03;
+ cmd[1] = byte (lun << 5);
+ cmd[2] = byte 0;
+ cmd[3] = byte 0;
+ cmd[4] = byte len buf;
+ cmd[5] = byte 0;
+ got := bulkread(lun, cmd, buf, 1);
+ if (got < 0)
+ return -1;
+ return 0;
+}
+
+scsiread10(lun: int, offset, count: int, buf: array of byte): int
+{
+ cmd := array [10] of byte;
+ cmd[0] = byte 16r28;
+ cmd[1] = byte (lun << 5);
+ usb->bigput4(cmd[2:], offset);
+ cmd[6] = byte 0;
+ usb->bigput2(cmd[7:], count);
+ cmd[9] = byte 0;
+ got := bulkread(lun, cmd, buf, 0);
+ if (got < 0)
+ return -1;
+ return 0;
+}
+
+scsiwrite10(lun: int, offset, count: int, buf: array of byte): int
+{
+ cmd := array [10] of byte;
+ cmd[0] = byte 16r2A;
+ cmd[1] = byte (lun << 5);
+ usb->bigput4(cmd[2:], offset);
+ cmd[6] = byte 0;
+ usb->bigput2(cmd[7:], count);
+ cmd[9] = byte 0;
+ got := bulkwrite(lun, cmd, buf);
+ if (got < 0)
+ return -1;
+ return 0;
+}
+
+scsistartunit(lun: int, start: int): int
+{
+# warnfprint(ctlfd, "debug 0 1");
+# warnfprint(ctlfd, "debug 1 1");
+# warnfprint(ctlfd, "debug 2 1");
+ cmd := array [6] of byte;
+ cmd[0] = byte 16r1b;
+ cmd[1] = byte (lun << 5);
+ cmd[2] = byte 0;
+ cmd[3] = byte 0;
+ cmd[4] = byte (start & 1);
+ cmd[5] = byte 0;
+ got := bulkread(lun, cmd, nil, 0);
+ if (got < 0)
+ return -1;
+ return 0;
+}
+
+init(usbmod: Usb, psetupfd, pctlfd: ref Sys->FD,
+ nil: ref Usb->Device,
+ conf: array of ref Usb->Configuration, path: string): int
+{
+ usb = usbmod;
+ setupfd = psetupfd;
+ ctlfd = pctlfd;
+
+ sys = load Sys Sys->PATH;
+ rv := usb->set_configuration(setupfd, conf[0].id);
+ if (rv < 0)
+ return rv;
+ rv = massstoragereset();
+ if (rv < 0)
+ return rv;
+ maxlun := getmaxlun();
+ if (maxlun < 0)
+ return maxlun;
+ lun = 0;
+ if(debug)
+ sys->print("maxlun %d\n", maxlun);
+ inep = outep = nil;
+ epts := (hd conf[0].iface[0].altiface).ep;
+ for(i := 0; i < len epts; i++)
+ if(epts[i].etype == Usb->Ebulk){
+ if(epts[i].d2h){
+ if(inep == nil)
+ inep = epts[i];
+ }else{
+ if(outep == nil)
+ outep = epts[i];
+ }
+ }
+ if(inep == nil || outep == nil){
+ sys->print("can't find endpoints\n");
+ return -1;
+ }
+ isrw := (inep.addr & 16rF) == (outep.addr & 16rF);
+ if(!isrw){
+ infd = openep(path, inep, Sys->OREAD);
+ if(infd == nil)
+ return -1;
+ outfd = openep(path, outep, Sys->OWRITE);
+ if(outfd == nil)
+ return -1;
+ }else{
+ infd = outfd = openep(path, inep, Sys->ORDWR);
+ if(infd == nil)
+ return -1;
+ }
+ if (scsiinquiry(0) < 0)
+ return -1;
+ scsistartunit(lun, 1);
+ if (scsireadcapacity(0) < 0) {
+ scsirequestsense(0);
+ if (scsireadcapacity(0) < 0)
+ return -1;
+ }
+ fileio := sys->file2chan("/chan", "usbdisk");
+ if (fileio == nil) {
+ sys->print("file2chan failed: %r\n");
+ return -1;
+ }
+ setlength("/chan/usbdisk", capacity);
+# warnfprint(ctlfd, "debug 0 1");
+# warnfprint(ctlfd, "debug 1 1");
+# warnfprint(ctlfd, "debug 2 1");
+ pidc := chan of int;
+ spawn reader(pidc, fileio);
+ readerpid = <- pidc;
+ return 0;
+}
+
+shutdown()
+{
+ if (readerpid >= 0)
+ kill(readerpid);
+}
+
+openep(path: string, ep: ref Endpt, mode: int): ref Sys->FD
+{
+ if(debug)
+ sys->print("ep %x maxpkt %d interval %d\n", ep.addr, ep.maxpkt, ep.interval);
+ ms: string;
+ case mode {
+ Sys->OREAD => ms = "r";
+ Sys->OWRITE => ms = "w";
+ * => ms = "rw";
+ }
+ if(sys->fprint(ctlfd, "ep %d bulk %s %d 16", ep.addr&16rF, ms, ep.maxpkt) < 0)
+ return nil;
+ return sys->open(sys->sprint("%s/ep%ddata", path, ep.addr&16rF), mode);
+}
+
+setlength(f: string, size: big)
+{
+ d := sys->nulldir;
+ d.length = size;
+ sys->wstat(f, d); # ignore errors since it fails on older kernels
+}
diff --git a/appl/lib/usb/usbmct.b b/appl/lib/usb/usbmct.b
new file mode 100644
index 00000000..7384cb23
--- /dev/null
+++ b/appl/lib/usb/usbmct.b
@@ -0,0 +1,204 @@
+#
+# Copyright © 2002 Vita Nuova Holdings Limited
+#
+implement UsbDriver;
+
+# MCT RS232 USB driver
+# 'Documentation' mined from NetBSD
+
+include "sys.m";
+ sys: Sys;
+include "usb.m";
+ usb: Usb;
+
+UMCT_SET_REQUEST: con 1640;
+
+REQ_SET_BAUD_RATE: con 5;
+REQ_SET_LCR: con 7;
+
+LCR_SET_BREAK: con 16r40;
+LCR_PARITY_EVEN: con 16r18;
+LCR_PARITY_ODD: con 16r08;
+LCR_PARITY_NONE: con 16r00;
+LCR_DATA_BITS_5, LCR_DATA_BITS_6, LCR_DATA_BITS_7, LCR_DATA_BITS_8: con iota;
+LCR_STOP_BITS_2: con 16r04;
+LCR_STOP_BITS_1: con 16r00;
+
+setupfd: ref Sys->FD;
+debug: con 1;
+
+ioreaderpid, statusreaderpid: int;
+
+kill(pid: int): int
+{
+ fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);
+ if (fd == nil)
+ return -1;
+ if (sys->write(fd, array of byte "kill", 4) != 4)
+ return -1;
+ return 0;
+}
+
+ioreader(pidc: chan of int, fd: ref Sys->FD)
+{
+ pid := sys->pctl(0, nil);
+ pidc <-= pid;
+ buf := array [256] of byte;
+ while ((n := sys->read(fd, buf, len buf)) >= 0)
+ {
+ sys->print("[%d]\n", n);
+ sys->write(sys->fildes(1), buf, n);
+ }
+ ioreaderpid = -1;
+}
+
+statusreader(pidc: chan of int, fd: ref Sys->FD)
+{
+ pid := sys->pctl(0, nil);
+ pidc <-= pid;
+ buf := array [2] of byte;
+ while ((n := sys->read(fd, buf, len buf)) >= 0)
+ {
+ sys->print("S(%d)%.2ux%.2ux\n", n, int buf[0], int buf[1]);
+ }
+ statusreaderpid = -1;
+}
+
+set_baud_rate(baud: int)
+{
+ buf := array [1] of byte;
+ val := 12;
+ case baud {
+ 300 => val = 1;
+ 1200 => val = 3;
+ 2400 => val = 4;
+ 4800 => val = 6;
+ 9600 => val = 8;
+ 19200 => val = 9;
+ 38400 => val = 10;
+ 57600 => val = 11;
+ 115200 => val = 12;
+ }
+ buf[0] = byte val;
+ if (usb->setup(setupfd, UMCT_SET_REQUEST, REQ_SET_BAUD_RATE, 0, 0, buf, nil) < 0) {
+ if (debug)
+ sys->print("usbmct: set_baud_rate failed\n");
+ }
+}
+
+set_lcr(val: int)
+{
+ buf := array [1] of byte;
+ buf[0] = byte val;
+ if (usb->setup(setupfd, UMCT_SET_REQUEST, REQ_SET_LCR, 0, 0, buf, nil) < 0) {
+ if (debug)
+ sys->print("usbmct: set_lcr failed\n");
+ }
+}
+
+init(usbmod: Usb, psetupfd, pctlfd: ref Sys->FD,
+ dev: ref Usb->Device,
+ conf: array of ref Usb->Configuration, path: string): int
+{
+ statusep, inep, outep: ref Usb->Endpt;
+ usb = usbmod;
+ sys = load Sys Sys->PATH;
+ setupfd = psetupfd;
+ # check the device descriptor to see if it really is an MCT doofer
+ if (dev.vid != 16r0711 || dev.did != 16r0230) {
+ if (debug)
+ sys->print("usbmct: wrong device!\n");
+ return -1;
+ }
+ usb->set_configuration(setupfd, conf[0].id);
+ ai := hd conf[0].iface[0].altiface;
+ statusep = nil;
+ inep = nil;
+ outep = nil;
+ for (e := 0; e < len ai.ep; e++) {
+ ep := ai.ep[e];
+ if ((ep.addr & 16r80) != 0 && (ep.attr & 3) == 3 && ep.maxpkt == 2)
+ statusep = ep;
+ else if ((ep.addr & 16r80) != 0 && (ep.attr & 3) == 3)
+ inep = ep;
+ else if ((ep.addr & 16r80) == 0 && (ep.attr & 3) == 2)
+ outep = ep;
+ }
+ if (statusep == nil || outep == nil || inep == nil) {
+ if (debug)
+ sys->print("usbmct: can't find sensible endpoints\n");
+ return -1;
+ }
+ if ((inep.addr & 15) != (outep.addr & 15)) {
+ if (debug)
+ sys->print("usbmct: in and out endpoints not same number\n");
+ return -1;
+ }
+ ioid := inep.addr & 15;
+ statusid := statusep.addr & 15;
+ if (debug)
+ sys->print("ep %d %d r %d 32\n", ioid, inep.maxpkt, inep.interval);
+ if (sys->fprint(pctlfd, "ep %d %d r %d 32", ioid, inep.maxpkt, inep.interval) < 0) {
+ if (debug)
+ sys->print("usbmct: can't create i/o endpoint (i)\n");
+ return -1;
+ }
+# if (debug)
+# sys->print("ep %d %d r bulk 32\n", ioid, inep.maxpkt);
+# if (sys->fprint(pctlfd, "ep %d %d r bulk 32", ioid, inep.maxpkt) < 0) {
+# if (debug)
+# sys->print("usbmct: can't create i/o endpoint (i)\n");
+# return -1;
+# }
+ if (debug)
+ sys->print("ep %d %d w bulk 8\n", ioid, outep.maxpkt);
+ if (sys->fprint(pctlfd, "ep %d %d w bulk 8", ioid, outep.maxpkt) < 0) {
+ if (debug)
+ sys->print("usbmct: can't create i/o endpoint (o)\n");
+ return -1;
+ }
+ iofd := sys->open(path + "ep" + string ioid + "data", Sys->ORDWR);
+ if (iofd == nil) {
+ if (debug)
+ sys->print("usbmct: can't open i/o endpoint\n");
+ return -1;
+ }
+ if (debug)
+ sys->print("ep %d %d r %d 8\n", statusid, statusep.maxpkt, statusep.interval);
+ if (sys->fprint(pctlfd, "ep %d %d r %d 8", statusid, statusep.maxpkt, statusep.interval) < 0) {
+ if (debug)
+ sys->print("usbmct: can't create status endpoint\n");
+ return -1;
+ }
+ statusfd := sys->open(path + "ep" + string statusid + "data", Sys->ORDWR);
+ if (statusfd == nil) {
+ if (debug)
+ sys->print("usbmct: can't open status endpoint\n");
+ return -1;
+ }
+sys->print("setting baud rate\n");
+ set_baud_rate(9600);
+sys->print("setting lcr\n");
+ set_lcr(LCR_PARITY_NONE | LCR_DATA_BITS_8 | LCR_STOP_BITS_1);
+sys->print("launching reader\n");
+ pidc := chan of int;
+ spawn ioreader(pidc, iofd);
+ ioreaderpid = <- pidc;
+ spawn statusreader(pidc, statusfd);
+ statusreaderpid = <- pidc;
+ buf := array[512] of byte;
+ for (x := 0; x < 512; x += 16) {
+ buf[x:] = array of byte sys->sprint("%.2ux", x / 16);
+ buf[x + 2:] = array of byte "-0123456789-\r\n";
+ }
+ sys->write(iofd, buf, 512);
+ return 0;
+}
+
+shutdown()
+{
+ if (ioreaderpid >= 0)
+ kill(ioreaderpid);
+ if (statusreaderpid >= 0)
+ kill(statusreaderpid);
+}
diff --git a/appl/lib/usb/usbmouse.b b/appl/lib/usb/usbmouse.b
new file mode 100644
index 00000000..138f31c1
--- /dev/null
+++ b/appl/lib/usb/usbmouse.b
@@ -0,0 +1,60 @@
+#
+# Copyright © 2002 Vita Nuova Holdings Limited.
+#
+implement UsbDriver;
+
+include "sys.m";
+ sys: Sys;
+include "usb.m";
+ usb: Usb;
+
+readerpid: int;
+
+kill(pid: int): int
+{
+ fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);
+ if (fd == nil)
+ return -1;
+ if (sys->write(fd, array of byte "kill", 4) != 4)
+ return -1;
+ return 0;
+}
+
+reader(pidc: chan of int, fd: ref Sys->FD)
+{
+ pid := sys->pctl(0, nil);
+ pidc <-= pid;
+ buf := array [4] of byte;
+ while ((n := sys->read(fd, buf, len buf)) >= 0)
+ sys->print("%d: %d\n", sys->millisec(), n);
+ readerpid = -1;
+}
+
+init(usbmod: Usb, setupfd, ctlfd: ref Sys->FD,
+ nil: ref Usb->Device,
+ conf: array of ref Usb->Configuration, path: string): int
+{
+ usb = usbmod;
+ sys = load Sys Sys->PATH;
+ rv := usb->set_configuration(setupfd, conf[0].id);
+ if (rv < 0)
+ return rv;
+ ep := (hd conf[0].iface[0].altiface).ep[0];
+ sys->print("maxpkt %d interval %d\n", ep.maxpkt, ep.interval);
+ rv = sys->fprint(ctlfd, "ep 1 %d r %d 32", ep.maxpkt, ep.interval);
+ if (rv < 0)
+ return rv;
+ datafd := sys->open(path + "ep1data", Sys->OREAD);
+ if (datafd == nil)
+ return -1;
+ pidc := chan of int;
+ spawn reader(pidc, datafd);
+ readerpid = <- pidc;
+ return 0;
+}
+
+shutdown()
+{
+ if (readerpid >= 0)
+ kill(readerpid);
+}