1# 2# Copyright © 2002 Vita Nuova Holdings Limited 3# 4implement Usb; 5 6include "sys.m"; 7 sys: Sys; 8 9include "usb.m"; 10 11include "string.m"; 12 str: String; 13 14Proto: adt { 15 proto: int; 16 name: string; 17}; 18 19SubClass: adt { 20 subclass: int; 21 name: string; 22 proto: array of Proto; 23}; 24 25Class: adt { 26 class: int; 27 name: string; 28 subclass: array of SubClass; 29}; 30 31classes := array [] of { 32 Class(Usb->CL_AUDIO, "audio", 33 array [] of { 34 SubClass(1, "control", nil), 35 SubClass(2, "stream", nil), 36 SubClass(3, "midi", nil), 37 } 38 ), 39 Class(Usb->CL_COMMS, "comms", 40 array [] of { 41 SubClass(1, "abstract", 42 array [] of { 43 Proto(1, "AT"), 44 } 45 ) 46 } 47 ), 48 Class(Usb->CL_HID, "hid", 49 array [] of { 50 SubClass(1, "boot", 51 array [] of { 52 Proto(1, "kbd"), 53 Proto(2, "mouse"), 54 } 55 ) 56 } 57 ), 58 Class(Usb->CL_PRINTER, "printer", 59 array [] of { 60 SubClass(1, "printer", 61 array [] of { 62 Proto(1, "uni"), 63 Proto(2, "bi"), 64 } 65 ) 66 } 67 ), 68 Class(Usb->CL_HUB, "hub", 69 array [] of { 70 SubClass(1, "hub", nil), 71 } 72 ), 73 Class(Usb->CL_DATA, "data", nil), 74 Class(Usb->CL_MASS, "mass", 75 array [] of { 76 SubClass(1, "rbc", 77 array [] of { 78 Proto(0, "cbi-cc"), 79 Proto(1, "cbi-nocc"), 80 Proto(16r50, "bulkonly"), 81 } 82 ), 83 SubClass(2, "sff-8020i/mmc-2", 84 array [] of { 85 Proto(0, "cbi-cc"), 86 Proto(1, "cbi-nocc"), 87 Proto(16r50, "bulkonly"), 88 } 89 ), 90 SubClass(3, "qic-157", 91 array [] of { 92 Proto(0, "cbi-cc"), 93 Proto(1, "cbi-nocc"), 94 Proto(16r50, "bulkonly"), 95 } 96 ), 97 SubClass(4, "ufi", 98 array [] of { 99 Proto(0, "cbi-cc"), 100 Proto(1, "cbi-nocc"), 101 Proto(16r50, "bulkonly"), 102 } 103 ), 104 SubClass(5, "sff-8070i", 105 array [] of { 106 Proto(0, "cbi-cc"), 107 Proto(1, "cbi-nocc"), 108 Proto(16r50, "bulkonly"), 109 } 110 ), 111 SubClass(6, "scsi", 112 array [] of { 113 Proto(0, "cbi-cc"), 114 Proto(1, "cbi-nocc"), 115 Proto(16r50, "bulkonly"), 116 } 117 ), 118 } 119 ), 120}; 121 122get2(b: array of byte): int 123{ 124 return int b[0] | (int b[1] << 8); 125} 126 127put2(buf: array of byte, v: int) 128{ 129 buf[0] = byte v; 130 buf[1] = byte (v >> 8); 131} 132 133get4(b: array of byte): int 134{ 135 return int b[0] | (int b[1] << 8) | (int b[2] << 16) | (int b[3] << 24); 136} 137 138put4(buf: array of byte, v: int) 139{ 140 buf[0] = byte v; 141 buf[1] = byte (v >> 8); 142 buf[2] = byte (v >> 16); 143 buf[3] = byte (v >> 24); 144} 145 146bigget2(b: array of byte): int 147{ 148 return int b[1] | (int b[0] << 8); 149} 150 151bigput2(buf: array of byte, v: int) 152{ 153 buf[1] = byte v; 154 buf[0] = byte (v >> 8); 155} 156 157bigget4(b: array of byte): int 158{ 159 return int b[3] | (int b[2] << 8) | (int b[1] << 16) | (int b[0] << 24); 160} 161 162bigput4(buf: array of byte, v: int) 163{ 164 buf[3] = byte v; 165 buf[2] = byte (v >> 8); 166 buf[1] = byte (v >> 16); 167 buf[0] = byte (v >> 24); 168} 169 170strtol(s: string, base: int): (int, string) 171{ 172 if (str == nil) 173 str = load String String->PATH; 174 if (base != 0) 175 return str->toint(s, base); 176 if (len s >= 2 && (s[0:2] == "0X" || s[0:2] == "0x")) 177 return str->toint(s[2:], 16); 178 if (len s > 0 && s[0:1] == "0") 179 return str->toint(s[1:], 8); 180 return str->toint(s, 10); 181} 182 183memset(buf: array of byte, v: int) 184{ 185 for (x := 0; x < len buf; x++) 186 buf[x] = byte v; 187} 188 189setupreq(setupfd: ref Sys->FD, typ, req, value, index: int, outbuf: array of byte, count: int): int 190{ 191 additional: int; 192 if (outbuf != nil) { 193 additional = len outbuf; 194 # if there is an outbuf, then the count sent must be length of the payload 195 # this assumes that RH2D is set 196 count = additional; 197 } 198 else 199 additional = 0; 200 buf := array[8 + additional] of byte; 201 buf[0] = byte typ; 202 buf[1] = byte req; 203 put2(buf[2:], value); 204 put2(buf[4:], index); 205 put2(buf[6:], count); 206 if (additional) 207 buf[8:] = outbuf; 208 rv := sys->write(setupfd, buf, len buf); 209 if (rv < 0) 210 return -1; 211 if (rv != len buf) 212 return -1; 213 return rv; 214} 215 216setupreply(setupfd: ref Sys->FD, buf: array of byte): int 217{ 218 nb := sys->read(setupfd, buf, len buf); 219 return nb; 220} 221 222setup(setupfd: ref Sys->FD, typ, req, value, index: int, outbuf: array of byte, inbuf: array of byte): int 223{ 224 count: int; 225 if (inbuf != nil) 226 count = len inbuf; 227 else 228 count = 0; 229 if (setupreq(setupfd, typ, req, value, index, outbuf, count) < 0) 230 return -1; 231 if (count == 0) 232 return 0; 233 return setupreply(setupfd, inbuf); 234} 235 236get_descriptor(fd: ref Sys->FD, rtyp: int, dtyp: int, dindex: int, langid: int, buf: array of byte): int 237{ 238 nr := -1; 239 if (setupreq(fd, RD2H | rtyp | Rdevice, GET_DESCRIPTOR, (dtyp << 8) | dindex, langid, nil, len buf) < 0 240 || (nr = setupreply(fd, buf)) < 1) 241 return -1; 242 return nr; 243} 244 245get_standard_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int 246{ 247 return get_descriptor(fd, Rstandard, dtyp, index, 0, buf); 248} 249 250get_class_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int 251{ 252 return get_descriptor(fd, Rclass, dtyp, index, 0, buf); 253} 254 255get_vendor_descriptor(fd: ref Sys->FD, dtyp: int, index: int, buf: array of byte): int 256{ 257 return get_descriptor(fd, Rvendor, dtyp, index, 0, buf); 258} 259 260get_status(fd: ref Sys->FD, port: int): int 261{ 262 buf := array [4] of byte; 263 if (setupreq(fd, RD2H | Rclass | Rother, GET_STATUS, 0, port, nil, len buf) < 0 264 || setupreply(fd, buf) < len buf) 265 return -1; 266 return get2(buf); 267} 268 269set_address(fd: ref Sys->FD, address: int): int 270{ 271 return setupreq(fd, RH2D | Rstandard | Rdevice, SET_ADDRESS, address, 0, nil, 0); 272} 273 274set_configuration(fd: ref Sys->FD, n: int): int 275{ 276 return setupreq(fd, RH2D | Rstandard | Rdevice, SET_CONFIGURATION, n, 0, nil, 0); 277} 278 279setclear_feature(fd: ref Sys->FD, rtyp: int, value: int, index: int, on: int): int 280{ 281 req: int; 282 if (on) 283 req = SET_FEATURE; 284 else 285 req = CLEAR_FEATURE; 286 return setupreq(fd, RH2D | rtyp, req, value, index, nil, 0); 287} 288 289parse_conf(b: array of byte): ref Configuration 290{ 291 if (len b < DCONFLEN) 292 return nil; 293 conf := ref Configuration; 294 conf.id = int b[5]; 295 conf.iface = array[int b[4]] of Interface; 296 conf.attr = int b[7]; 297 conf.powerma = int b[8] * 2; 298 return conf; 299} 300 301parse_iface(conf: ref Configuration, b: array of byte): ref AltInterface 302{ 303 if (len b < DINTERLEN || conf == nil) 304 return nil; 305 id := int b[2]; 306 if (id >= len conf.iface) 307 return nil; 308 ai := ref AltInterface; 309 ai.id = int b[3]; 310 if (int b[4] != 0) 311 ai.ep = array [int b[4]] of ref Endpt; 312 ai.class = int b[5]; 313 ai.subclass = int b[6]; 314 ai.proto = int b[7]; 315 conf.iface[id].altiface = ai :: conf.iface[id].altiface; 316 return ai; 317} 318 319parse_endpt(conf: ref Configuration, ai: ref AltInterface, b: array of byte): ref Endpt 320{ 321 if (len b < DENDPLEN || conf == nil || ai == nil || ai.ep == nil) 322 return nil; 323 for (i := 0; i < len ai.ep; i++) 324 if (ai.ep[i] == nil) 325 break; 326 if (i >= len ai.ep) 327 return nil; 328 ep := ref Endpt; 329 ai.ep[i] = ep; 330 ep.addr = int b[2]; 331 ep.attr = int b[3]; 332 ep.d2h = ep.addr & 16r80; 333 ep.etype = int b[3] & 3; 334 ep.isotype = (int b[3] >> 2) & 3; 335 ep.maxpkt = get2(b[4:]); 336 ep.interval = int b[6]; 337 return ep; 338} 339 340get_parsed_configuration_descriptor(fd: ref Sys->FD, n: int): ref Configuration 341{ 342 conf: ref Configuration; 343 altiface: ref AltInterface; 344 345 b := array [256] of byte; 346 nr := get_standard_descriptor(fd, CONFIGURATION, n, b); 347 if (nr < 0) 348 return nil; 349 conf = nil; 350 altiface = nil; 351 for (i := 0; nr - i > 2 && b[i] > byte 0 && int b[i] <= nr - i; i += int b[i]) { 352 ni := i + int b[i]; 353 case int b[i + 1] { 354 Usb->CONFIGURATION => 355 conf = parse_conf(b[i: ni]); 356 if (conf == nil) 357 return nil; 358 Usb->INTERFACE => 359 altiface = parse_iface(conf, b[i: ni]); 360 if (altiface == nil) 361 return nil; 362 Usb->ENDPOINT => 363 if (parse_endpt(conf, altiface, b[i: ni]) == nil) 364 return nil; 365 } 366 } 367 if (i < nr) 368 sys->print("usb: residue at end of descriptors\n"); 369 return conf; 370} 371 372get_parsed_device_descriptor(fd: ref Sys->FD): ref Device 373{ 374 b := array [256] of byte; 375 nr := get_standard_descriptor(fd, DEVICE, 0, b); 376 if (nr < DDEVLEN) { 377 if (nr == 8 || nr == 16) { 378 memset(b[nr: DDEVLEN - 1], 0); 379 b[DDEVLEN - 1] = byte 1; 380 nr = DDEVLEN; 381 } 382 else 383 return nil; 384 } 385 dev := ref Device; 386 dev.usbmajor = int b[3]; 387 dev.usbminor = int b[2]; 388 dev.class = int b[4]; 389 dev.subclass = int b[5]; 390 dev.proto = int b[6]; 391 dev.maxpkt0 = int b[7]; 392 dev.vid = get2(b[8:]); 393 dev.did = get2(b[10:]); 394 dev.relmajor = int b[13]; 395 dev.relminor = int b[12]; 396 dev.nconf = int b[17]; 397 return dev; 398} 399 400dump_configuration(fd: ref Sys->FD, conf: ref Configuration) 401{ 402 sys->fprint(fd, "configuration %d attr 0x%.x powerma %d\n", conf.id, conf.attr, conf.powerma); 403 for (i := 0; i < len conf.iface; i++) { 404 sys->fprint(fd, "\tinterface %d\n", i); 405 ail := conf.iface[i].altiface; 406 while (ail != nil) { 407 ai := hd ail; 408 sys->fprint(fd, "\t\t%d class %d subclass %d proto %d [%s]\n", 409 ai.id, ai.class, ai.subclass, ai.proto, 410 sclass(ai.class, ai.subclass, ai.proto)); 411 for (e := 0; e < len ai.ep; e++) { 412 if (ai.ep[e] == nil) { 413 sys->fprint(fd, "\t\t\t missing descriptor\n"); 414 continue; 415 } 416 sys->fprint(fd, "\t\t\t0x%.2ux attr 0x%.x maxpkt %d interval %d\n", 417 ai.ep[e].addr, ai.ep[e].attr, ai.ep[e].maxpkt, ai.ep[e].interval); 418 } 419 ail = tl ail; 420 } 421 } 422sys->fprint(fd, "done dumping\n"); 423} 424 425sclass(class, subclass, proto: int): string 426{ 427 for (c := 0; c < len classes; c++) 428 if (classes[c].class == class) 429 break; 430 if (c >= len classes) 431 return sys->sprint("%d.%d.%d", class, subclass, proto); 432 if (classes[c].subclass == nil) 433 return sys->sprint("%s.%d.%d", classes[c].name, subclass, proto); 434 for (sc := 0; sc < len classes[c].subclass; sc++) 435 if (classes[c].subclass[sc].subclass == subclass) 436 break; 437 if (sc >= len classes[c].subclass) 438 return sys->sprint("%s.%d.%d", classes[c].name, subclass, proto); 439 if (classes[c].subclass[sc].proto == nil) 440 return sys->sprint("%s.%s.%d", classes[c].name, classes[c].subclass[sc].name, proto); 441 for (p := 0; p < len classes[c].subclass[sc].proto; p++) 442 if (classes[c].subclass[sc].proto[p].proto == proto) 443 break; 444 if (p >= len classes[c].subclass[sc].proto) 445 return sys->sprint("%s.%s.%d", classes[c].name, classes[c].subclass[sc].name, proto); 446 return sys->sprint("%s.%s.%s", classes[c].name, classes[c].subclass[sc].name, 447 classes[c].subclass[sc].proto[p].name); 448} 449 450init() 451{ 452 sys = load Sys Sys->PATH; 453} 454