xref: /plan9/sys/src/cmd/usb/disk/disk.c (revision 60014d6756a98ad10929607ca84a1b7488a16cfc)
1 /*
2  * usb/disk - usb mass storage file server
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <ctype.h>
7 #include <bio.h>
8 #include <fcall.h>
9 #include <thread.h>
10 #include <9p.h>
11 #include "scsireq.h"
12 #include "usb.h"
13 
14 /*
15  * mass storage transport protocols and subclasses,
16  * from usb mass storage class specification overview rev 1.2
17  */
18 enum {
19 	Protocbi =	0,	/* control/bulk/interrupt; mainly floppies */
20 	Protocb =	1,	/*   "  with no interrupt; mainly floppies */
21 	Protobulk =	0x50,	/* bulk only */
22 
23 	Subrbc =	1,	/* reduced blk cmds */
24 	Subatapi =	2,	/* cd/dvd using sff-8020i or mmc-2 cmd blks */
25 	Subqic =	3,	/* QIC-157 tapes */
26 	Subufi =	4,	/* floppy */
27 	Sub8070 =	5,	/* removable media, atapi-like */
28 	Subscsi =	6,	/* scsi transparent cmd set */
29 	Subisd200 =	7,	/* ISD200 ATA */
30 	Subdev =	0xff,	/* use device's value */
31 };
32 
33 enum {
34 	GET_MAX_LUN_T =	RD2H | Rclass | Rinterface,
35 	GET_MAX_LUN =	0xFE,
36 	UMS_RESET_T =	RH2D | Rclass | Rinterface,
37 	UMS_RESET =	0xFF,
38 
39 	MaxIOsize	= 256*1024,	/* max. I/O size */
40 //	Maxlun		= 256,
41 	Maxlun		= 32,
42 };
43 
44 #define PATH(type, n)	((type) | (n)<<8)
45 #define TYPE(path)	((uchar)(path))
46 #define NUM(path)	((uint)(path)>>8)
47 
48 enum {
49 	Qdir = 0,
50 	Qctl,
51 	Qn,
52 	Qraw,
53 	Qdata,
54 
55 	CMreset = 1,
56 
57 	Pcmd = 0,
58 	Pdata,
59 	Pstatus,
60 };
61 
62 static char *subclass[] = {
63 	"?",
64 	"rbc",
65 	"atapi",
66 	"qic tape",
67 	"ufi floppy",
68 	"8070 removable",
69 	"scsi transparent",
70 	"isd200 ata",
71 };
72 
73 typedef struct Dirtab Dirtab;
74 struct Dirtab {
75 	char	*name;
76 	int	mode;
77 };
78 Dirtab dirtab[] = {
79 	".",	DMDIR|0555,
80 	"ctl",	0640,
81 	nil,	DMDIR|0750,
82 	"raw",	0640,
83 	"data",	0640,
84 };
85 
86 Cmdtab cmdtab[] = {
87 	CMreset,	"reset",	1,
88 };
89 
90 /* these are 600 bytes each; ScsiReq is not tiny */
91 typedef struct Umsc Umsc;
92 struct Umsc {
93 	ScsiReq;
94 	ulong	blocks;
95 	vlong	capacity;
96 	uchar 	rawcmd[10];
97 	uchar	phase;
98 	char	*inq;
99 };
100 
101 typedef struct Ums Ums;
102 struct Ums {
103 	Umsc	*lun;
104 	uchar	maxlun;
105 	int	fd2;
106 	int	fd;
107 	int	setupfd;
108 	int	ctlfd;
109 	uchar	epin;
110 	uchar	epout;
111 	char	dev[64];
112 };
113 
114 int exabyte, force6bytecmds, needmaxlun;
115 long starttime;
116 long maxiosize = MaxIOsize;
117 
118 volatile int timedout;
119 
120 char *owner;
121 
122 static Ums ums;
123 static int freakout;		/* flag: if true, drive freaks out if reset */
124 
125 extern int debug;
126 
127 static void umsreset(Ums *umsc, int doinit);
128 
129 /*
130  * USB transparent SCSI devices
131  */
132 typedef struct Cbw Cbw;			/* command block wrapper */
133 struct Cbw {
134 	char	signature[4];		/* "USBC" */
135 	long	tag;
136 	long	datalen;
137 	uchar	flags;
138 	uchar	lun;
139 	uchar	len;
140 	char	command[16];
141 };
142 
143 typedef struct Csw Csw;			/* command status wrapper */
144 struct Csw {
145 	char	signature[4];		/* "USBS" */
146 	long	tag;
147 	long	dataresidue;
148 	uchar	status;
149 };
150 
151 enum {
152 	CbwLen		= 31,
153 	CbwDataIn	= 0x80,
154 	CbwDataOut	= 0x00,
155 	CswLen		= 13,
156 	CswOk		= 0,
157 	CswFailed	= 1,
158 	CswPhaseErr	= 2,
159 };
160 
161 int
162 statuscmd(int fd, int type, int req, int value, int index, char *data,
163 	int count)
164 {
165 	char *wp;
166 
167 	wp = emalloc9p(count + 8);
168 	wp[0] = type;
169 	wp[1] = req;
170 	PUT2(wp + 2, value);
171 	PUT2(wp + 4, index);
172 	PUT2(wp + 6, count);
173 	if(data != nil)
174 		memmove(wp + 8, data, count);
175 	if(write(fd, wp, count + 8) != count + 8){
176 		fprint(2, "%s: statuscmd: %r\n", argv0);
177 		return -1;
178 	}
179 	return 0;
180 }
181 
182 int
183 statusread(int fd, char *buf, int count)
184 {
185 	if(read(fd, buf, count) < 0){
186 		fprint(2, "%s: statusread: %r\n", argv0);
187 		return -1;
188 	}
189 	return 0;
190 }
191 
192 void
193 getmaxlun(Ums *ums)
194 {
195 	uchar max;
196 
197 	if(needmaxlun &&
198 	    statuscmd(ums->setupfd, GET_MAX_LUN_T, GET_MAX_LUN, 0, 0, nil, 0)
199 	    == 0 && statusread(ums->setupfd, (char *)&max, 1) == 0)
200 		fprint(2, "%s: maxlun %d\n", argv0, max);	// DEBUG
201 	else
202 		max = 0;
203 	ums->lun = mallocz((max + 1) * sizeof *ums->lun, 1);
204 	assert(ums->lun);
205 	ums->maxlun = max;
206 }
207 
208 int
209 umsinit(Ums *ums, int epin, int epout)
210 {
211 	uchar data[8], i;
212 	char fin[128];
213 	Umsc *lun;
214 
215 	if(ums->ctlfd == -1) {
216 		snprint(fin, sizeof fin, "%s/ctl", ums->dev);
217 		if((ums->ctlfd = open(fin, OWRITE)) == -1)
218 			return -1;
219 		if(epin == epout) {
220 			if(fprint(ums->ctlfd, "ep %d bulk rw 64 16", epin) < 0)
221 				return -1;
222 		} else {
223 			if(fprint(ums->ctlfd, "ep %d bulk r 64 16", epin) < 0 ||
224 			   fprint(ums->ctlfd, "ep %d bulk w 64 16", epout) < 0)
225 				return -1;
226 		}
227 		snprint(fin, sizeof fin, "%s/ep%ddata", ums->dev, epin);
228 		if((ums->fd = open(fin, OREAD)) == -1)
229 			return -1;
230 		snprint(fin, sizeof fin, "%s/ep%ddata", ums->dev, epout);
231 		if((ums->fd2 = open(fin, OWRITE)) == -1)
232 			return -1;
233 		snprint(fin, sizeof fin, "%s/setup", ums->dev);
234 		if ((ums->setupfd = open(fin, ORDWR)) == -1)
235 			return -1;
236 	}
237 
238 	ums->epin = epin;
239 	ums->epout = epout;
240 	umsreset(ums, 0);
241 	getmaxlun(ums);
242 	for(i = 0; i <= ums->maxlun; i++) {
243 		lun = &ums->lun[i];
244 		lun->lun = i;
245 		lun->umsc = lun;			/* pointer to self */
246 		lun->flags = Fopen | Fusb | Frw10;
247 		if(SRinquiry(lun) == -1)
248 			return -1;
249 		lun->inq = smprint("%.48s", (char *)lun->inquiry+8);
250 		SRstart(lun, 1);
251 		if (SRrcapacity(lun, data) == -1 &&
252 		    SRrcapacity(lun, data) == -1) {
253 			lun->blocks = 0;
254 			lun->capacity = 0;
255 			lun->lbsize = 0;
256 		} else {
257 			lun->lbsize = data[4]<<24|data[5]<<16|data[6]<<8|data[7];
258 			lun->blocks = data[0]<<24|data[1]<<16|data[2]<<8|data[3];
259 			lun->blocks++; /* SRcapacity returns LBA of last block */
260 			lun->capacity = (vlong)lun->blocks * lun->lbsize;
261 		}
262 	}
263 	return 0;
264 }
265 
266 static void
267 unstall(Ums *ums, int ep)
268 {
269 	if(fprint(ums->ctlfd, "unstall %d", ep & 0xF) < 0)
270 		fprint(2, "ctl write failed\n");
271 	if(fprint(ums->ctlfd, "data %d 0", ep & 0xF) < 0)
272 		fprint(2, "ctl write failed\n");
273 
274 	statuscmd(ums->setupfd, RH2D | Rstandard | Rendpt, CLEAR_FEATURE, 0,
275 		0<<8 | ep, nil, 0);
276 }
277 
278 static void
279 umsreset(Ums *umsc, int doinit)
280 {
281 	if (!freakout)
282 		statuscmd(umsc->setupfd, UMS_RESET_T, UMS_RESET, 0, 0, nil, 0);
283 
284 	unstall(umsc, umsc->epin|0x80);
285 	unstall(umsc, umsc->epout);
286 	if(doinit && umsinit(&ums, umsc->epin, umsc->epout) < 0)
287 		sysfatal("device error");
288 }
289 
290 long
291 umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
292 {
293 	Cbw cbw;
294 	Csw csw;
295 	int n;
296 	static int seq = 0;
297 
298 	memcpy(cbw.signature, "USBC", 4);
299 	cbw.tag = ++seq;
300 	cbw.datalen = data->count;
301 	cbw.flags = data->write? CbwDataOut: CbwDataIn;
302 	cbw.lun = umsc->lun;
303 	cbw.len = cmd->count;
304 	memcpy(cbw.command, cmd->p, cmd->count);
305 	memset(cbw.command + cmd->count, 0, sizeof(cbw.command) - cmd->count);
306 
307 	if(debug) {
308 		fprint(2, "cmd:");
309 		for (n = 0; n < cbw.len; n++)
310 			fprint(2, " %2.2x", cbw.command[n]&0xFF);
311 		fprint(2, " datalen: %ld\n", cbw.datalen);
312 	}
313 	if(write(ums.fd2, &cbw, CbwLen) != CbwLen){
314 		fprint(2, "usbscsi: write cmd: %r\n");
315 		goto reset;
316 	}
317 	if(data->count != 0) {
318 		if(data->write)
319 			n = write(ums.fd2, data->p, data->count);
320 		else
321 			n = read(ums.fd, data->p, data->count);
322 		if(n == -1){
323 			if(debug)
324 				fprint(2, "usbscsi: data %sput: %r\n",
325 					data->write? "out": "in");
326 			if(data->write)
327 				unstall(&ums, ums.epout);
328 			else
329 				unstall(&ums, ums.epin | 0x80);
330 		}
331 	}
332 	n = read(ums.fd, &csw, CswLen);
333 	if(n == -1){
334 		unstall(&ums, ums.epin | 0x80);
335 		n = read(ums.fd, &csw, CswLen);
336 	}
337 	if(n != CswLen || strncmp(csw.signature, "USBS", 4) != 0){
338 		fprint(2, "usbscsi: read status: %r\n");
339 		goto reset;
340 	}
341 	if(csw.tag != cbw.tag) {
342 		fprint(2, "usbscsi: status tag mismatch\n");
343 		goto reset;
344 	}
345 	if(csw.status >= CswPhaseErr){
346 		fprint(2, "usbscsi: phase error\n");
347 		goto reset;
348 	}
349 	if(debug) {
350 		fprint(2, "status: %2.2ux residue: %ld\n",
351 			csw.status, csw.dataresidue);
352 		if(cbw.command[0] == ScmdRsense) {
353 			fprint(2, "sense data:");
354 			for (n = 0; n < data->count - csw.dataresidue; n++)
355 				fprint(2, " %2.2x", data->p[n]);
356 			fprint(2, "\n");
357 		}
358 	}
359 
360 	if(csw.status == CswOk)
361 		*status = STok;
362 	else
363 		*status = STcheck;
364 	return data->count - csw.dataresidue;
365 
366 reset:
367 	umsreset(&ums, 0);
368 	*status = STharderr;
369 	return -1;
370 }
371 
372 int
373 findendpoints(Device *d, int *epin, int *epout)
374 {
375 	Endpt *ep;
376 	ulong csp;
377 	int i, addr, nendpt;
378 
379 	*epin = *epout = -1;
380 	nendpt = 0;
381 	if(d->nconf < 1)
382 		return -1;
383 	for(i=0; i<d->nconf; i++) {
384 		if (d->config[i] == nil)
385 			d->config[i] = mallocz(sizeof(*d->config[i]),1);
386 		loadconfig(d, i);
387 	}
388 	for(i = 0; i < Nendpt; i++){
389 		if((ep = d->ep[i]) == nil)
390 			continue;
391 		nendpt++;
392 		csp = ep->csp;
393 		if(!(Class(csp) == CL_STORAGE && (Proto(csp) == 0x50)))
394 			continue;
395 		if(ep->type == Ebulk) {
396 			addr = ep->addr;
397 			if (debug)
398 				print("findendpoints: bulk; ep->addr %ux\n",
399 					ep->addr);
400 			if (ep->dir == Eboth || addr&0x80)
401 				if(*epin == -1)
402 					*epin =  addr&0xF;
403 			if (ep->dir == Eboth || !(addr&0x80))
404 				if(*epout == -1)
405 					*epout = addr&0xF;
406 		}
407 	}
408 	if(nendpt == 0) {
409 		if(*epin == -1)
410 			*epin = *epout;
411 		if(*epout == -1)
412 			*epout = *epin;
413 	}
414 	if (*epin == -1 || *epout == -1)
415 		return -1;
416 	return 0;
417 }
418 
419 int
420 timeoutok(void)
421 {
422 	if (freakout)
423 		return 1;	/* OK; keep trying */
424 	else {
425 		fprint(2, "%s: no response from device.  unplug and replug "
426 			"it and try again with -f\n", argv0);
427 		return 0;	/* die */
428 	}
429 }
430 
431 int
432 notifyf(void *, char *s)
433 {
434 	if(strcmp(s, "alarm") != 0)
435 		return 0;		/* die */
436 	if (!timeoutok()) {
437 		fprint(2, "%s: timed out\n", argv0);
438 		return 0;		/* die */
439 	}
440 	alarm(120*1000);
441 	fprint(2, "%s: resetting alarm\n", argv0);
442 	timedout = 1;
443 	return 1;			/* keep going */
444 }
445 
446 int
447 devokay(int ctlrno, int id)
448 {
449 	int epin = -1, epout = -1;
450 	long time;
451 	Device *d;
452 	static int beenhere;
453 
454 	if (!beenhere) {
455 		atnotify(notifyf, 1);
456 		beenhere = 1;
457 	}
458 	time = alarm(15*1000);
459 	d = opendev(ctlrno, id);
460 	if (describedevice(d) < 0) {
461 		perror("");
462 		closedev(d);
463 		alarm(time);
464 		return 0;
465 	}
466 	if (findendpoints(d, &epin, &epout) < 0) {
467 		fprint(2, "%s: bad usb configuration for ctlr %d id %d\n",
468 			argv0, ctlrno, id);
469 		closedev(d);
470 		alarm(time);
471 		return 0;
472 	}
473 	closedev(d);
474 
475 	snprint(ums.dev, sizeof ums.dev, "/dev/usb%d/%d", ctlrno, id);
476 	if (umsinit(&ums, epin, epout) < 0) {
477 		alarm(time);
478 		fprint(2, "%s: initialisation: %r\n", argv0);
479 		return 0;
480 	}
481 	alarm(time);
482 	return 1;
483 }
484 
485 static char *
486 subclname(int subcl)
487 {
488 	if ((unsigned)subcl < nelem(subclass))
489 		return subclass[subcl];
490 	return "**GOK**";		/* traditional */
491 }
492 
493 static int
494 scanstatus(int ctlrno, int id)
495 {
496 	int winner;
497 	ulong csp;
498 	char *p, *hex;
499 	char buf[64];
500 	Biobuf *f;
501 
502 	/* read port status file */
503 	sprint(buf, "/dev/usb%d/%d/status", ctlrno, id);
504 	f = Bopen(buf, OREAD);
505 	if (f == nil)
506 		sysfatal("can't open %s: %r", buf);
507 	if (debug)
508 		fprint(2, "%s: reading %s\n", argv0, buf);
509 	winner = 0;
510 	while (!winner && (p = Brdline(f, '\n')) != nil) {
511 		p[Blinelen(f)-1] = '\0';
512 		if (debug && *p == 'E')			/* Enabled? */
513 			fprint(2, "%s: %s\n", argv0, p);
514 		for (hex = p; *hex != '\0' && *hex != '0'; hex++)
515 			continue;
516 		csp = atol(hex);
517 
518 		if (Class(csp) == CL_STORAGE && Proto(csp) == Protobulk) {
519 			if (0)
520 				fprint(2, "%s: /dev/usb%d/%d: bulk storage "
521 					"of subclass %s\n", argv0, ctlrno, id,
522 					subclname(Subclass(csp)));
523 			switch (Subclass(csp)) {
524 			case Subatapi:
525 			case Sub8070:
526 			case Subscsi:
527 				winner++;
528 				break;
529 			}
530 		}
531 	}
532 	Bterm(f);
533 	return winner;
534 }
535 
536 static int
537 findums(int *ctlrp, int *idp)
538 {
539 	int ctlrno, id, winner, devfd, ctlrfd, nctlr, nport;
540 	char buf[64];
541 	Dir *ctlrs, *cdp, *ports, *pdp;
542 
543 	*ctlrp = *idp = -1;
544 	winner = 0;
545 
546 	/* walk controllers */
547 	devfd = open("/dev", OREAD);
548 	if (devfd < 0)
549 		sysfatal("can't open /dev: %r");
550 	nctlr = dirreadall(devfd, &ctlrs);
551 	if (nctlr < 0)
552 		sysfatal("can't read /dev: %r");
553 	for (cdp = ctlrs; nctlr-- > 0 && !winner; cdp++) {
554 		if (strncmp(cdp->name, "usb", 3) != 0)
555 			continue;
556 		ctlrno = atoi(cdp->name + 3);
557 
558 		/* walk ports within a controller */
559 		snprint(buf, sizeof buf, "/dev/%s", cdp->name);
560 		ctlrfd = open(buf, OREAD);
561 		if (ctlrfd < 0)
562 			sysfatal("can't open %s: %r", buf);
563 		nport = dirreadall(ctlrfd, &ports);
564 		if (nport < 0)
565 			sysfatal("can't read %s: %r", buf);
566 		for (pdp = ports; nport-- > 0 && !winner; pdp++) {
567 			if (!isdigit(*pdp->name))
568 				continue;
569 			id = atoi(pdp->name);
570 
571 			/* read port status file */
572 			winner = scanstatus(ctlrno, id);
573 			if (winner)
574 				if (devokay(ctlrno, id)) {
575 					*ctlrp = ctlrno;
576 					*idp = id;
577 				} else
578 					winner = 0;
579 		}
580 		free(ports);
581 		close(ctlrfd);
582 	}
583 	free(ctlrs);
584 	close(devfd);
585 	if (!winner)
586 		return -1;
587 	else
588 		return 0;
589 }
590 
591 void
592 rattach(Req *r)
593 {
594 	r->ofcall.qid.path = PATH(Qdir, 0);
595 	r->ofcall.qid.type = dirtab[Qdir].mode >> 24;
596 	r->fid->qid = r->ofcall.qid;
597 	respond(r, nil);
598 }
599 
600 char*
601 rwalk1(Fid *fid, char *name, Qid *qid)
602 {
603 	int i, n;
604 	char buf[32];
605 	ulong path;
606 
607 	path = fid->qid.path;
608 	if(!(fid->qid.type & QTDIR))
609 		return "walk in non-directory";
610 
611 	if(strcmp(name, "..") == 0)
612 		switch(TYPE(path)) {
613 		case Qn:
614 			qid->path = PATH(Qn, NUM(path));
615 			qid->type = dirtab[Qn].mode >> 24;
616 			return nil;
617 		case Qdir:
618 			return nil;
619 		default:
620 			return "bug in rwalk1";
621 		}
622 
623 	for(i = TYPE(path)+1; i < nelem(dirtab); i++) {
624 		if(i==Qn){
625 			n = atoi(name);
626 			snprint(buf, sizeof buf, "%d", n);
627 			if(n <= ums.maxlun && strcmp(buf, name) == 0){
628 				qid->path = PATH(i, n);
629 				qid->type = dirtab[i].mode>>24;
630 				return nil;
631 			}
632 			break;
633 		}
634 		if(strcmp(name, dirtab[i].name) == 0) {
635 			qid->path = PATH(i, NUM(path));
636 			qid->type = dirtab[i].mode >> 24;
637 			return nil;
638 		}
639 		if(dirtab[i].mode & DMDIR)
640 			break;
641 	}
642 	return "directory entry not found";
643 }
644 
645 void
646 dostat(int path, Dir *d)
647 {
648 	Dirtab *t;
649 
650 	memset(d, 0, sizeof(*d));
651 	d->uid = estrdup9p(owner);
652 	d->gid = estrdup9p(owner);
653 	d->qid.path = path;
654 	d->atime = d->mtime = starttime;
655 	t = &dirtab[TYPE(path)];
656 	if(t->name)
657 		d->name = estrdup9p(t->name);
658 	else {
659 		d->name = smprint("%ud", NUM(path));
660 		if(d->name == nil)
661 			sysfatal("out of memory");
662 	}
663 	if(TYPE(path) == Qdata)
664 		d->length = ums.lun[NUM(path)].capacity;
665 	d->qid.type = t->mode >> 24;
666 	d->mode = t->mode;
667 }
668 
669 static int
670 dirgen(int i, Dir *d, void*)
671 {
672 	i += Qdir + 1;
673 	if(i <= Qn) {
674 		dostat(i, d);
675 		return 0;
676 	}
677 	i -= Qn;
678 	if(i <= ums.maxlun) {
679 		dostat(PATH(Qn, i), d);
680 		return 0;
681 	}
682 	return -1;
683 }
684 
685 static int
686 lungen(int i, Dir *d, void *aux)
687 {
688 	int *c;
689 
690 	c = aux;
691 	i += Qn + 1;
692 	if(i <= Qdata){
693 		dostat(PATH(i, NUM(*c)), d);
694 		return 0;
695 	}
696 	return -1;
697 }
698 
699 void
700 rstat(Req *r)
701 {
702 	dostat((long)r->fid->qid.path, &r->d);
703 	respond(r, nil);
704 }
705 
706 void
707 ropen(Req *r)
708 {
709 	ulong path;
710 
711 	path = r->fid->qid.path;
712 	switch(TYPE(path)) {
713 	case Qraw:
714 		ums.lun[NUM(path)].phase = Pcmd;
715 		break;
716 	}
717 	respond(r, nil);
718 }
719 
720 void
721 rread(Req *r)
722 {
723 	int bno, nb, len, offset, n;
724 	ulong path;
725 	uchar i;
726 	char buf[8192], *p;
727 	Umsc *lun;
728 
729 	path = r->fid->qid.path;
730 	switch(TYPE(path)) {
731 	case Qdir:
732 		dirread9p(r, dirgen, 0);
733 		break;
734 	case Qn:
735 		dirread9p(r, lungen, &path);
736 		break;
737 	case Qctl:
738 		n = 0;
739 		for(i = 0; i <= ums.maxlun; i++) {
740 			lun = &ums.lun[i];
741 			n += snprint(buf + n, sizeof buf - n, "%d: ", i);
742 			if(lun->flags & Finqok)
743 				n += snprint(buf + n, sizeof buf - n,
744 					"inquiry %s ", lun->inq);
745 			if(lun->blocks > 0)
746 				n += snprint(buf + n, sizeof buf - n,
747 					"geometry %ld %ld", lun->blocks,
748 					lun->lbsize);
749 			n += snprint(buf + n, sizeof buf - n, "\n");
750 		}
751 		readbuf(r, buf, n);
752 		break;
753 	case Qraw:
754 		lun = &ums.lun[NUM(path)];
755 		if(lun->lbsize <= 0) {
756 			respond(r, "no media on this lun");
757 			return;
758 		}
759 		switch(lun->phase) {
760 		case Pcmd:
761 			respond(r, "phase error");
762 			return;
763 		case Pdata:
764 			lun->data.p = (uchar*)r->ofcall.data;
765 			lun->data.count = r->ifcall.count;
766 			lun->data.write = 0;
767 			n = umsrequest(lun, &lun->cmd, &lun->data, &lun->status);
768 			lun->phase = Pstatus;
769 			if (n == -1) {
770 				respond(r, "IO error");
771 				return;
772 			}
773 			r->ofcall.count = n;
774 			break;
775 		case Pstatus:
776 			n = snprint(buf, sizeof buf, "%11.0ud ", lun->status);
777 			if (r->ifcall.count < n)
778 				n = r->ifcall.count;
779 			memmove(r->ofcall.data, buf, n);
780 			r->ofcall.count = n;
781 			lun->phase = Pcmd;
782 			break;
783 		}
784 		break;
785 	case Qdata:
786 		lun = &ums.lun[NUM(path)];
787 		if(lun->lbsize <= 0) {
788 			respond(r, "no media on this lun");
789 			return;
790 		}
791 		bno = r->ifcall.offset / lun->lbsize;
792 		nb = (r->ifcall.offset + r->ifcall.count + lun->lbsize - 1)
793 			/ lun->lbsize - bno;
794 		if(bno + nb > lun->blocks)
795 			nb = lun->blocks - bno;
796 		if(bno >= lun->blocks || nb == 0) {
797 			r->ofcall.count = 0;
798 			break;
799 		}
800 		if(nb * lun->lbsize > maxiosize)
801 			nb = maxiosize / lun->lbsize;
802 		p = malloc(nb * lun->lbsize);
803 		if (p == 0) {
804 			respond(r, "no mem");
805 			return;
806 		}
807 		lun->offset = r->ifcall.offset / lun->lbsize;
808 		n = SRread(lun, p, nb * lun->lbsize);
809 		if(n == -1) {
810 			free(p);
811 			respond(r, "IO error");
812 			return;
813 		}
814 		len = r->ifcall.count;
815 		offset = r->ifcall.offset % lun->lbsize;
816 		if(offset + len > n)
817 			len = n - offset;
818 		r->ofcall.count = len;
819 		memmove(r->ofcall.data, p + offset, len);
820 		free(p);
821 		break;
822 	}
823 	respond(r, nil);
824 }
825 
826 void
827 rwrite(Req *r)
828 {
829 	int n, bno, nb, len, offset;
830 	ulong path;
831 	char *p;
832 	Cmdbuf *cb;
833 	Cmdtab *ct;
834 	Umsc *lun;
835 
836 	n = r->ifcall.count;
837 	r->ofcall.count = 0;
838 	path = r->fid->qid.path;
839 	switch(TYPE(path)) {
840 	case Qctl:
841 		cb = parsecmd(r->ifcall.data, n);
842 		ct = lookupcmd(cb, cmdtab, nelem(cmdtab));
843 		if(ct == 0) {
844 			respondcmderror(r, cb, "%r");
845 			return;
846 		}
847 		switch(ct->index) {
848 		case CMreset:
849 			umsreset(&ums, 1);
850 		}
851 		break;
852 	case Qraw:
853 		lun = &ums.lun[NUM(path)];
854 		if(lun->lbsize <= 0) {
855 			respond(r, "no media on this lun");
856 			return;
857 		}
858 		n = r->ifcall.count;
859 		switch(lun->phase) {
860 		case Pcmd:
861 			if(n != 6 && n != 10) {
862 				respond(r, "bad command length");
863 				return;
864 			}
865 			memmove(lun->rawcmd, r->ifcall.data, n);
866 			lun->cmd.p = lun->rawcmd;
867 			lun->cmd.count = n;
868 			lun->cmd.write = 1;
869 			lun->phase = Pdata;
870 			break;
871 		case Pdata:
872 			lun->data.p = (uchar*)r->ifcall.data;
873 			lun->data.count = n;
874 			lun->data.write = 1;
875 			n = umsrequest(lun, &lun->cmd, &lun->data, &lun->status);
876 			lun->phase = Pstatus;
877 			if(n == -1) {
878 				respond(r, "IO error");
879 				return;
880 			}
881 			break;
882 		case Pstatus:
883 			lun->phase = Pcmd;
884 			respond(r, "phase error");
885 			return;
886 		}
887 		break;
888 	case Qdata:
889 		lun = &ums.lun[NUM(path)];
890 		if(lun->lbsize <= 0) {
891 			respond(r, "no media on this lun");
892 			return;
893 		}
894 		bno = r->ifcall.offset / lun->lbsize;
895 		nb = (r->ifcall.offset + r->ifcall.count + lun->lbsize-1)
896 			/ lun->lbsize - bno;
897 		if(bno + nb > lun->blocks)
898 			nb = lun->blocks - bno;
899 		if(bno >= lun->blocks || nb == 0) {
900 			r->ofcall.count = 0;
901 			break;
902 		}
903 		if(nb * lun->lbsize > maxiosize)
904 			nb = maxiosize / lun->lbsize;
905 		p = malloc(nb * lun->lbsize);
906 		if(p == 0) {
907 			respond(r, "no mem");
908 			return;
909 		}
910 		offset = r->ifcall.offset % lun->lbsize;
911 		len = r->ifcall.count;
912 		if(offset || (len % lun->lbsize) != 0) {
913 			lun->offset = r->ifcall.offset / lun->lbsize;
914 			n = SRread(lun, p, nb * lun->lbsize);
915 			if(n == -1) {
916 				free(p);
917 				respond(r, "IO error");
918 				return;
919 			}
920 			if(offset + len > n)
921 				len = n - offset;
922 		}
923 		memmove(p+offset, r->ifcall.data, len);
924 		lun->offset = r->ifcall.offset / lun->lbsize;
925 		n = SRwrite(lun, p, nb * lun->lbsize);
926 		if(n == -1) {
927 			free(p);
928 			respond(r, "IO error");
929 			return;
930 		}
931 		if(offset+len > n)
932 			len = n - offset;
933 		r->ofcall.count = len;
934 		free(p);
935 		break;
936 	}
937 	r->ofcall.count = n;
938 	respond(r, nil);
939 }
940 
941 Srv usbssrv = {
942 	.attach = rattach,
943 	.walk1 = rwalk1,
944 	.open =	 ropen,
945 	.read =	 rread,
946 	.write = rwrite,
947 	.stat =	 rstat,
948 };
949 
950 void (*dprinter[])(Device *, int, ulong, void *b, int n) = {
951 	[STRING] pstring,
952 	[DEVICE] pdevice,
953 };
954 
955 void
956 usage(void)
957 {
958 	fprint(2, "usage: %s [-Ddfl] [-m mountpoint] [-s srvname] [ctrno id]\n",
959 		argv0);
960 	exits("usage");
961 }
962 
963 void
964 main(int argc, char **argv)
965 {
966 	int ctlrno, id;
967 	char *srvname, *mntname;
968 
969 	mntname = "/n/disk";
970 	srvname = nil;
971 	ctlrno = 0;
972 	id = 1;
973 
974 	ARGBEGIN{
975 	case 'd':
976 		debug = Dbginfo;
977 		break;
978 	case 'f':
979 		freakout++;
980 		break;
981 	case 'l':
982 		needmaxlun++;
983 		break;
984 	case 'm':
985 		mntname = EARGF(usage());
986 		break;
987 	case 's':
988 		srvname = EARGF(usage());
989 		break;
990 	case 'D':
991 		++chatty9p;
992 		break;
993 	default:
994 		usage();
995 	}ARGEND
996 
997 	ums.ctlfd = ums.setupfd = ums.fd = ums.fd2 = -1;
998 	ums.maxlun = -1;
999 	if (argc == 0) {
1000 		if (findums(&ctlrno, &id) < 0) {
1001 			sleep(5*1000);
1002 			if (findums(&ctlrno, &id) < 0)
1003 				sysfatal("no usb mass storage device found");
1004 		}
1005 	} else if (argc == 2 && isdigit(argv[0][0]) && isdigit(argv[1][0])) {
1006 		ctlrno = atoi(argv[0]);
1007 		id = atoi(argv[1]);
1008 		if (!devokay(ctlrno, id))
1009 			sysfatal("no usable usb mass storage device at %d/%d",
1010 				ctlrno, id);
1011 	} else
1012 		usage();
1013 
1014 	starttime = time(0);
1015 	owner = getuser();
1016 
1017 	postmountsrv(&usbssrv, srvname, mntname, 0);
1018 	exits(0);
1019 }
1020