1 #include <u.h> 2 #include <libc.h> 3 #include <thread.h> 4 #include "usb.h" 5 6 #include "dat.h" 7 #include "fns.h" 8 9 #define STACKSIZE 128*1024 10 11 static int dontfork; 12 static int usbnum; 13 14 Ref busy; 15 16 int debug; 17 18 typedef struct Enum Enum; 19 struct Enum 20 { 21 Hub *hub; 22 int port; 23 }; 24 25 void (*dprinter[])(Device *, int, ulong, void *b, int n) = { 26 [STRING] pstring, 27 [DEVICE] pdevice, 28 [0x29] phub, 29 }; 30 31 static void 32 usage(void) 33 { 34 fprint(2, "usage: usbd [-DfV] [-d mask] [-u root-hub]\n"); 35 threadexitsall("usage"); 36 } 37 38 /* 39 * based on libthread's threadsetname, but drags in less library code. 40 * actually just sets the arguments displayed. 41 */ 42 void 43 procsetname(char *fmt, ...) 44 { 45 int fd; 46 char *cmdname; 47 char buf[32]; 48 va_list arg; 49 50 va_start(arg, fmt); 51 cmdname = vsmprint(fmt, arg); 52 va_end(arg); 53 if (cmdname == nil) 54 return; 55 snprint(buf, sizeof buf, "#p/%d/args", getpid()); 56 if((fd = open(buf, OWRITE)) >= 0){ 57 write(fd, cmdname, strlen(cmdname)+1); 58 close(fd); 59 } 60 free(cmdname); 61 } 62 63 void 64 work(void *a) 65 { 66 int port; 67 char name[100]; 68 Hub *hub; 69 Enum *arg; 70 71 hub = a; 72 snprint(name, sizeof name, "%H", hub); 73 procsetname(name); 74 75 for(port = 1; port <= hub->nport; port++){ 76 if (debug) 77 fprint(2, "enumerate port %H.%d\n", hub, port); 78 arg = emallocz(sizeof(Enum), 1); 79 arg->hub = hub; 80 arg->port = port; 81 incref(&busy); 82 threadcreate(enumerate, arg, STACKSIZE); 83 } 84 85 decref(&busy); 86 for(;;) { 87 yield(); 88 if (busy.ref == 0) 89 sleep(2000); 90 } 91 } 92 93 void 94 realmain(void *) 95 { 96 int i; 97 Hub *h; 98 99 if (!dontfork) { 100 /* don't hold window open */ 101 close(0); 102 close(1); 103 open("/dev/null", OREAD); 104 open("/dev/null", OWRITE); 105 if(!debug && !verbose) { 106 close(2); 107 open("/dev/null", OWRITE); 108 } 109 } 110 if(usbnum < 0){ 111 /* always fork off usb[1—n] */ 112 for(i=1; (h = roothub(i)) != nil; i++) { 113 incref(&busy); 114 proccreate(work, h, STACKSIZE); 115 } 116 usbnum = 0; 117 } 118 /* usb0 might be handled in this proc */ 119 if((h = roothub(usbnum)) != nil){ 120 incref(&busy); 121 if (dontfork) 122 work(h); 123 else 124 proccreate(work, h, STACKSIZE); 125 } 126 if (debug) 127 fprint(2, "done\n"); 128 while (busy.ref) 129 sleep(100); 130 threadexits(nil); 131 } 132 133 void 134 threadmain(int argc, char **argv) 135 { 136 usbnum = -1; 137 ARGBEGIN{ 138 case 'd': 139 debug = atoi(EARGF(usage())); 140 break; 141 case 'D': 142 debugdebug++; 143 break; 144 case 'f': 145 dontfork = 1; 146 break; 147 case 'u': 148 usbnum = atoi(EARGF(usage())); 149 break; 150 case 'V': 151 verbose = 1; 152 break; 153 }ARGEND 154 if(argc) 155 usage(); 156 if(access("/dev/usb0", 0) < 0 && bind("#U", "/dev", MBEFORE) < 0) 157 sysfatal("%s: can't bind -b #U /dev: %r", argv0); 158 159 usbfmtinit(); 160 fmtinstall('H', Hfmt); 161 162 if (dontfork) 163 realmain(nil); 164 else 165 procrfork(realmain, nil, STACKSIZE, RFNOTEG | RFFDG); 166 threadexits(nil); 167 } 168 169 void 170 enumerate(void *v) 171 { 172 int i, port; 173 Device *d; 174 Enum *arg; 175 Hub *h, *nh; 176 177 arg = v; 178 h = arg->hub; 179 port = arg->port; 180 free(arg); 181 182 for(;;){ 183 if((portstatus(h, port) & (1<<PORT_CONNECTION)) == 0){ 184 decref(&busy); 185 if(verbose) 186 fprint(2, "usbd: %H: port %d empty\n", h, port); 187 do{ 188 yield(); 189 if(debugdebug) 190 fprint(2, "usbd: probing %H.%d\n", 191 h, port); 192 sleep(500); 193 }while((portstatus(h, port) & (1<<PORT_CONNECTION)) == 0); 194 incref(&busy); 195 } 196 if(verbose) 197 fprint(2, "usbd: %H: port %d attached\n", h, port); 198 d = configure(h, port); 199 if(d == nil){ 200 if(verbose) 201 fprint(2, "usbd: can't configure port %H.%d\n", h, port); 202 decref(&busy); 203 threadexits("configure"); 204 } 205 if(d->class == Hubclass){ 206 if(debug) 207 fprint(2, "usbd: %H.%d: hub %d attached\n", 208 h, port, d->id); 209 setconfig(d, 1); 210 nh = newhub(h, d); 211 if(nh == nil) { 212 detach(h, port); 213 decref(&busy); 214 threadexits("describehub"); 215 } 216 if(debug) 217 fprint(2, "usbd: traversing hub %H\n", nh); 218 /* TO DO: initialise status endpoint */ 219 for(i=1; i<=nh->nport; i++) 220 portpower(nh, i, 1); 221 sleep(nh->pwrms); 222 for(i=1; i<=nh->nport; i++) { 223 arg = emallocz(sizeof(Enum), 1); 224 arg->hub = nh; 225 arg->port = i; 226 incref(&busy); 227 threadcreate(enumerate, arg, STACKSIZE); 228 } 229 }else{ 230 if(debug) 231 fprint(2, 232 "usbd: %H.%d: %d: not hub, %s speed\n", 233 h, port, d->id, d->ls?"low":"high"); 234 setconfig(d, 1); /* TO DO */ 235 //unconscionable kludge (testing camera) 236 if(d->class == 10) setup0(d, RH2D|Rinterface, SET_INTERFACE, 10, 0, 0); 237 } 238 decref(&busy); 239 while(portstatus(h, port) & (1<<PORT_CONNECTION)) { 240 if (debugdebug) 241 fprint(2, "checking %H.%d\n", h, port); 242 yield(); 243 if (d->state == Detached) { 244 if (verbose) 245 fprint(2, 246 "%H: port %d detached by parent hub\n", 247 h, port); 248 /* parent hub died */ 249 threadexits(nil); 250 } 251 } 252 if(verbose) 253 fprint(2, "%H: port %d detached\n", h, port); 254 detach(h, port); 255 } 256 } 257 258 Device* 259 configure(Hub *h, int port) 260 { 261 Port *p; 262 Device *d; 263 int i, s, maxpkt, ls; 264 265 portenable(h, port, 1); 266 sleep(20); 267 portreset(h, port); 268 sleep(20); 269 s = portstatus(h, port); 270 if (debug) 271 fprint(2, "%H.%d status %#ux\n", h, port, s); 272 if ((s & (1<<PORT_CONNECTION)) == 0) 273 return nil; 274 if ((s & (1<<PORT_SUSPEND)) == 0) { 275 if (debug) 276 fprint(2, "enabling port %H.%d\n", h, port); 277 portenable(h, port, 1); 278 s = portstatus(h, port); 279 if (debug) 280 fprint(2, "%H.%d status now %#ux\n", h, port, s); 281 } 282 283 ls = (s & (1<<PORT_LOW_SPEED)) != 0; 284 devspeed(h->dev0, ls); 285 maxpkt = getmaxpkt(h->dev0); 286 if(debugdebug) 287 fprint(2, "%H.%d maxpkt: %d\n", h, port, maxpkt); 288 if(maxpkt < 0){ 289 Error0: 290 portenable(h, port, 0); 291 return nil; 292 } 293 d = opendev(h->ctlrno, -1); 294 d->ls = ls; 295 d->state = Enabled; 296 d->ep[0]->maxpkt = maxpkt; 297 if(fprint(d->ctl, "maxpkt 0 %d", maxpkt) < 0){ 298 Error1: 299 closedev(d); 300 goto Error0; 301 } 302 if(setaddress(h->dev0, d->id) < 0) 303 goto Error1; 304 d->state = Assigned; 305 devspeed(d, ls); 306 if(describedevice(d) < 0) 307 goto Error1; 308 309 /* read configurations 0 to n */ 310 for(i=0; i<d->nconf; i++){ 311 if(d->config[i] == nil) 312 d->config[i] = mallocz(sizeof(*d->config[i]),1); 313 loadconfig(d, i); 314 } 315 for(i=0; i<16; i++) 316 setdevclass(d, i); 317 p = &h->port[port-1]; 318 p->d = d; 319 return d; 320 } 321 322 void 323 detach(Hub *h, int port) 324 { 325 Port *p; 326 Device *d; 327 328 p = &h->port[port-1]; 329 if(p->hub != nil) { 330 freehub(p->hub); 331 p->hub = nil; 332 } 333 d = p->d; 334 d->state = Detached; /* return i/o error on access */ 335 closedev(d); 336 } 337