xref: /inferno-os/os/mpc/devata.c (revision 7e00430948d8af545f880e82bb30cd3ee50deb04)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"io.h"
7 #include	"../port/error.h"
8 
9 #include	"pcmcia.h"
10 
11 #define DPRINT if(0)print
12 
13 typedef	struct Drive		Drive;
14 typedef	struct Ident		Ident;
15 typedef	struct Controller	Controller;
16 typedef struct Partition	Partition;
17 typedef struct Repl		Repl;
18 
19 enum
20 {
21 	/* ports */
22 	Pbase=		0x1F0,
23 	Pdata=		0,	/* data port (16 bits) */
24 	Perror=		1,	/* error port (read) */
25 	Pprecomp=	1,	/* buffer mode port (write) */
26 	Pcount=		2,	/* sector count port */
27 	Psector=	3,	/* sector number port */
28 	Pcyllsb=	4,	/* least significant byte cylinder # */
29 	Pcylmsb=	5,	/* most significant byte cylinder # */
30 	Pdh=		6,	/* drive/head port */
31 	Pstatus=	7,	/* status port (read) */
32 	 Sbusy=		 (1<<7),
33 	 Sready=	 (1<<6),
34 	 Sdrq=		 (1<<3),
35 	 Serr=		 (1<<0),
36 	Pcmd=		7,	/* cmd port (write) */
37 
38 	/* commands */
39 	Crecal=		0x10,
40 	Cread=		0x20,
41 	Cwrite=		0x30,
42 	Cident=		0xEC,
43 	Cident2=	0xFF,	/* pseudo command for post Cident interrupt */
44 	Csetbuf=	0xEF,
45 	Cinitparam=	0x91,
46 
47 	/* conner specific commands */
48 	Cstandby=	0xE2,
49 	Cidle=		0xE1,
50 	Cpowerdown=	0xE3,
51 
52 	/* disk states */
53 	Sspinning,
54 	Sstandby,
55 	Sidle,
56 	Spowerdown,
57 
58 	/* something we have to or into the drive/head reg */
59 	DHmagic=	0xA0,
60 
61 	/* file types */
62 	Qdir=		0,
63 
64 	Maxxfer=	BY2PG,		/* maximum transfer size/cmd */
65 	Npart=		8+2,		/* 8 sub partitions, disk, and partition */
66 	Nrepl=		64,		/* maximum replacement blocks */
67 };
68 #define PART(x)		((x)&0xF)
69 #define DRIVE(x)	(((x)>>4)&0x7)
70 #define MKQID(d,p)	(((d)<<4) | (p))
71 
72 struct Partition
73 {
74 	ulong	start;
75 	ulong	end;
76 	char	name[NAMELEN+1];
77 };
78 
79 struct Repl
80 {
81 	Partition *p;
82 	int	nrepl;
83 	ulong	blk[Nrepl];
84 };
85 
86 #define PARTMAGIC	"plan9 partitions"
87 #define REPLMAGIC	"block replacements"
88 
89 /*
90  *  an ata drive
91  */
92 struct Drive
93 {
94 	QLock;
95 
96 	Controller *cp;
97 	int	drive;
98 	int	confused;	/* needs to be recalibrated (or worse) */
99 	int	online;
100 	int	npart;		/* number of real partitions */
101 	Partition p[Npart];
102 	Repl	repl;
103 	ulong	usetime;
104 	int	state;
105 	char	vol[NAMELEN];
106 
107 	ulong	cap;		/* total bytes */
108 	int	bytes;		/* bytes/sector */
109 	int	sectors;	/* sectors/track */
110 	int	heads;		/* heads/cyl */
111 	long	cyl;		/* cylinders/drive */
112 
113 	char	lba;		/* true if drive has logical block addressing */
114 	char	multi;		/* non-zero if drive does multiple block xfers */
115 };
116 
117 /*
118  *  a controller for 2 drives
119  */
120 struct Controller
121 {
122 	QLock;			/* exclusive access to the controller */
123 	ISAConf;		/* interface to pcmspecial */
124 
125 	Lock	reglock;	/* exclusive access to the registers */
126 
127 	int	confused;	/* needs to be recalibrated (or worse) */
128 	ulong	pbase;		/* base port (copied from ISAConf) */
129 
130 	/*
131 	 *  current operation
132 	 */
133 	int	cmd;		/* current command */
134 	int	lastcmd;	/* debugging info */
135 	Rendez	r;		/* wait here for command termination */
136 	char	*buf;		/* xfer buffer */
137 	int	nsecs;		/* length of transfer (sectors) */
138 	int	sofar;		/* sectors transferred so far */
139 	int	status;
140 	int	error;
141 	Drive	*dp;		/* drive being accessed */
142 };
143 
144 Controller	*atac;
145 Drive		*ata;
146 static char*	ataerr;
147 static int	nhard;
148 static int	spindowntime;
149 
150 static void	ataintr(Ureg*, void*);
151 static long	ataxfer(Drive*, Partition*, int, long, long, char*);
152 static void	ataident(Drive*);
153 static void	atasetbuf(Drive*, int);
154 static void	ataparams(Drive*);
155 static void	atapart(Drive*);
156 static int	ataprobe(Drive*, int, int, int);
157 
158 static int
159 atagen(Chan *c, Dirtab*, int, int s, Dir *dirp)
160 {
161 	Qid qid;
162 	int drive;
163 	Drive *dp;
164 	Partition *pp;
165 	ulong l;
166 
167 	qid.vers = 0;
168 	drive = s/Npart;
169 	s = s % Npart;
170 	if(drive >= nhard)
171 		return -1;
172 	dp = &ata[drive];
173 
174 	if(dp->online == 0 || s >= dp->npart)
175 		return 0;
176 
177 	pp = &dp->p[s];
178 	sprint(up->genbuf, "%s%s", dp->vol, pp->name);
179 	qid.path = MKQID(drive, s);
180 	l = (pp->end - pp->start) * dp->bytes;
181 	devdir(c, qid, up->genbuf, l, eve, 0660, dirp);
182 	return 1;
183 }
184 
185 static void
186 atainit(void)
187 {
188 	Drive *dp;
189 	Controller *cp;
190 	uchar equip;
191 	int pcmslot;
192 
193 	if (atac)
194 		return;		/* already done */
195 
196 	equip = 0x10;		/* hard coded */
197 
198 	print("ata init\n");
199 	cp = malloc(sizeof(*cp));
200 	if (!cp)
201 		error(Enomem);
202 
203 	cp->port = Pbase;
204 	cp->irq = 14;
205 
206 	if((pcmslot = pcmspecial("SunDisk", cp)) < 0) {
207 		print("No ATA card\n");
208 		free(cp);
209 		ataerr = Enoifc;
210 		return;
211 	}
212 	ata = malloc(2 * sizeof(*ata));
213 	if(ata == nil) {
214 		pcmspecialclose(pcmslot);
215 		free(cp);
216 		error(Enomem);
217 	}
218 
219 	atac = cp;
220 	cp->buf = 0;
221 	cp->lastcmd = cp->cmd;
222 	cp->cmd = 0;
223 	cp->pbase = cp->port;
224 	pcmintrenable(pcmslot, ataintr, cp);
225 
226 	dp = ata;
227 	if(equip & 0xf0){
228 		dp->drive = 0;
229 		dp->online = 0;
230 		dp->cp = cp;
231 		dp++;
232 	}
233 	if((equip & 0x0f)){
234 		dp->drive = 1;
235 		dp->online = 0;
236 		dp->cp = cp;
237 		dp++;
238 	}
239 	nhard = dp - ata;
240 
241 	spindowntime = 1;
242 }
243 
244 
245 /*
246  *  Get the characteristics of each drive.  Mark unresponsive ones
247  *  off line.
248  */
249 static Chan*
250 ataattach(char *spec)
251 {
252 	Drive *dp;
253 
254 	atainit();
255 	if (!ata)
256 		error(ataerr ? ataerr : Enoifc);
257 	for(dp = ata; dp < &ata[nhard]; dp++){
258 		if(waserror()){
259 			dp->online = 0;
260 			qunlock(dp);
261 			continue;
262 		}
263 		qlock(dp);
264 		if(!dp->online){
265 			/*
266 			 * Make sure ataclock() doesn't
267 			 * interfere.
268 			 */
269 			dp->usetime = m->ticks;
270 			ataparams(dp);
271 			dp->online = 1;
272 			atasetbuf(dp, 1);
273 		}
274 
275 		/*
276 		 *  read Plan 9 partition table
277 		 */
278 		atapart(dp);
279 		qunlock(dp);
280 		poperror();
281 	}
282 	return devattach('H', spec);
283 }
284 
285 static int
286 atawalk(Chan *c, char *name)
287 {
288 	return devwalk(c, name, 0, 0, atagen);
289 }
290 
291 static void
292 atastat(Chan *c, char *dp)
293 {
294 	devstat(c, dp, 0, 0, atagen);
295 }
296 
297 static Chan*
298 ataopen(Chan *c, int omode)
299 {
300 	return devopen(c, omode, 0, 0, atagen);
301 }
302 
303 static void
304 ataclose(Chan *c)
305 {
306 	Drive *d;
307 	Partition *p;
308 
309 	if(c->mode != OWRITE && c->mode != ORDWR)
310 		return;
311 
312 	d = &ata[DRIVE(c->qid.path)];
313 	p = &d->p[PART(c->qid.path)];
314 	if(strcmp(p->name, "partition") != 0)
315 		return;
316 
317 	if(waserror()){
318 		qunlock(d);
319 		nexterror();
320 	}
321 	qlock(d);
322 	atapart(d);
323 	qunlock(d);
324 	poperror();
325 }
326 
327 static long
328 ataread(Chan *c, void *a, long n, vlong offset)
329 {
330 	Drive *dp;
331 	long rv, i;
332 	int skip;
333 	uchar *aa = a;
334 	Partition *pp;
335 	char *buf;
336 
337 	if(c->qid.path == CHDIR)
338 		return devdirread(c, a, n, 0, 0, atagen);
339 
340 	buf = smalloc(Maxxfer);
341 	if(waserror()){
342 		free(buf);
343 		nexterror();
344 	}
345 
346 	dp = &ata[DRIVE(c->qid.path)];
347 	pp = &dp->p[PART(c->qid.path)];
348 
349 	skip = offset % dp->bytes;
350 	for(rv = 0; rv < n; rv += i){
351 		i = ataxfer(dp, pp, Cread, offset+rv-skip, n-rv+skip, buf);
352 		if(i == 0)
353 			break;
354 		i -= skip;
355 		if(i > n - rv)
356 			i = n - rv;
357 		memmove(aa+rv, buf + skip, i);
358 		skip = 0;
359 	}
360 
361 	free(buf);
362 	poperror();
363 
364 	return rv;
365 }
366 
367 static long
368 atawrite(Chan *c, void *a, long n, vlong offset)
369 {
370 	Drive *dp;
371 	long rv, i, partial;
372 	uchar *aa = a;
373 	Partition *pp;
374 	char *buf;
375 
376 	if(c->qid.path == CHDIR)
377 		error(Eisdir);
378 
379 	dp = &ata[DRIVE(c->qid.path)];
380 	pp = &dp->p[PART(c->qid.path)];
381 	buf = smalloc(Maxxfer);
382 	if(waserror()){
383 		free(buf);
384 		nexterror();
385 	}
386 
387 	/*
388 	 *  if not starting on a sector boundary,
389 	 *  read in the first sector before writing
390 	 *  it out.
391 	 */
392 	partial = offset % dp->bytes;
393 	if(partial){
394 		ataxfer(dp, pp, Cread, offset-partial, dp->bytes, buf);
395 		if(partial+n > dp->bytes)
396 			rv = dp->bytes - partial;
397 		else
398 			rv = n;
399 		memmove(buf+partial, aa, rv);
400 		ataxfer(dp, pp, Cwrite, offset-partial, dp->bytes, buf);
401 	} else
402 		rv = 0;
403 
404 	/*
405 	 *  write out the full sectors
406 	 */
407 	partial = (n - rv) % dp->bytes;
408 	n -= partial;
409 	for(; rv < n; rv += i){
410 		i = n - rv;
411 		if(i > Maxxfer)
412 			i = Maxxfer;
413 		memmove(buf, aa+rv, i);
414 		i = ataxfer(dp, pp, Cwrite, offset+rv, i, buf);
415 		if(i == 0)
416 			break;
417 	}
418 
419 	/*
420 	 *  if not ending on a sector boundary,
421 	 *  read in the last sector before writing
422 	 *  it out.
423 	 */
424 	if(partial){
425 		ataxfer(dp, pp, Cread, offset+rv, dp->bytes, buf);
426 		memmove(buf, aa+rv, partial);
427 		ataxfer(dp, pp, Cwrite, offset+rv, dp->bytes, buf);
428 		rv += partial;
429 	}
430 
431 	free(buf);
432 	poperror();
433 
434 	return rv;
435 }
436 
437 /*
438  *  did an interrupt happen?
439  */
440 static int
441 cmddone(void *a)
442 {
443 	Controller *cp = a;
444 
445 	return cp->cmd == 0;
446 }
447 
448 /*
449  * Wait for the controller to be ready to accept a command.
450  * This is protected from intereference by ataclock() by
451  * setting dp->usetime before it is called.
452  */
453 static void
454 cmdreadywait(Drive *dp)
455 {
456 	long start;
457 	int period;
458 	Controller *cp = dp->cp;
459 
460 	/* give it 2 seconds to spin down and up */
461 	if(dp->state == Sspinning)
462 		period = 10;
463 	else
464 		period = 2000;
465 
466 	start = m->ticks;
467 	while((inb(cp->pbase+Pstatus) & (Sready|Sbusy)) != Sready)
468 		if(TK2MS(m->ticks - start) > period){
469 			DPRINT("cmdreadywait failed\n");
470 			error(Eio);
471 		}
472 }
473 
474 static void
475 atarepl(Drive *dp, long bblk)
476 {
477 	int i;
478 
479 	if(dp->repl.p == 0)
480 		return;
481 	for(i = 0; i < dp->repl.nrepl; i++){
482 		if(dp->repl.blk[i] == bblk)
483 			DPRINT("found bblk %ld at offset %ld\n", bblk, i);
484 	}
485 }
486 
487 static void
488 atasleep(Controller *cp, int ms)
489 {
490         tsleep(&cp->r, cmddone, cp, ms);
491 	if(cp->cmd && cp->cmd != Cident2){
492 		DPRINT("ata: cmd 0x%uX timeout, status=%lux\n",
493 				cp->cmd, inb(cp->pbase+Pstatus));
494 		error("ata drive timeout");
495 	}
496 }
497 
498 /*
499  *  transfer a number of sectors.  ataintr will perform all the iterative
500  *  parts.
501  */
502 static long
503 ataxfer(Drive *dp, Partition *pp, int cmd, long start, long len, char *buf)
504 {
505 	Controller *cp;
506 	long lblk;
507 	int cyl, sec, head;
508 	int loop, stat;
509 
510 	if(dp->online == 0)
511 		error(Eio);
512 
513 	/*
514 	 *  cut transfer size down to disk buffer size
515 	 */
516 	start = start / dp->bytes;
517 	if(len > Maxxfer)
518 		len = Maxxfer;
519 	len = (len + dp->bytes - 1) / dp->bytes;
520 	if(len == 0)
521 		return 0;
522 
523 	/*
524 	 *  calculate physical address
525 	 */
526 	lblk = start + pp->start;
527 	if(lblk >= pp->end)
528 		return 0;
529 	if(lblk+len > pp->end)
530 		len = pp->end - lblk;
531 	if(dp->lba){
532 		sec = lblk & 0xff;
533 		cyl = (lblk>>8) & 0xffff;
534 		head = (lblk>>24) & 0xf;
535 	} else {
536 		cyl = lblk/(dp->sectors*dp->heads);
537 		sec = (lblk % dp->sectors) + 1;
538 		head = ((lblk/dp->sectors) % dp->heads);
539 	}
540 
541 	DPRINT("<%s %d>", (cmd == Cwrite) ? "W" : "R", lblk);
542 	cp = dp->cp;
543 	qlock(cp);
544 	if(waserror()){
545 		cp->buf = 0;
546 		qunlock(cp);
547 		nexterror();
548 	}
549 
550 	/*
551 	 * Make sure hardclock() doesn't
552 	 * interfere.
553 	 */
554 	dp->usetime = m->ticks;
555 	cmdreadywait(dp);
556 
557 	ilock(&cp->reglock);
558 	cp->sofar = 0;
559 	cp->buf = buf;
560 	cp->nsecs = len;
561 	cp->cmd = cmd;
562 	cp->dp = dp;
563 	cp->status = 0;
564 
565 	outb(cp->pbase+Pcount, cp->nsecs);
566 	outb(cp->pbase+Psector, sec);
567 	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | (dp->lba<<6) | head);
568 	outb(cp->pbase+Pcyllsb, cyl);
569 	outb(cp->pbase+Pcylmsb, cyl>>8);
570 	outb(cp->pbase+Pcmd, cmd);
571 
572 	if(cmd == Cwrite){
573 		loop = 0;
574 		microdelay(1);
575 		while((stat = inb(cp->pbase+Pstatus) & (Serr|Sdrq)) == 0)
576 			if(++loop > 10000)
577 				panic("ataxfer");
578 		outss(cp->pbase+Pdata, cp->buf, dp->bytes/2);
579 	} else
580 		stat = 0;
581 	iunlock(&cp->reglock);
582 
583 	if(stat & Serr)
584 		error(Eio);
585 
586 	/*
587 	 *  wait for command to complete.  if we get a note,
588 	 *  remember it but keep waiting to let the disk finish
589 	 *  the current command.
590 	 */
591 	loop = 0;
592 	while(waserror()){
593 		DPRINT("interrupted ataxfer\n");
594 		if(loop++ > 10){
595 			print("ata disk error\n");
596 			nexterror();
597 		}
598 	}
599 	atasleep(cp, 3000);
600 	dp->state = Sspinning;
601 	dp->usetime = m->ticks;
602 	poperror();
603 	if(loop)
604 		nexterror();
605 
606 	if(cp->status & Serr){
607 		DPRINT("hd%d err: lblk %ld status %lux, err %lux\n",
608 			dp-ata, lblk, cp->status, cp->error);
609 		DPRINT("\tcyl %d, sec %d, head %d\n", cyl, sec, head);
610 		DPRINT("\tnsecs %d, sofar %d\n", cp->nsecs, cp->sofar);
611 		atarepl(dp, lblk+cp->sofar);
612 		error(Eio);
613 	}
614 	cp->buf = 0;
615 	len = cp->sofar*dp->bytes;
616 	qunlock(cp);
617 	poperror();
618 
619 	return len;
620 }
621 
622 /*
623  *  set read ahead mode
624  */
625 static void
626 atasetbuf(Drive *dp, int on)
627 {
628 	Controller *cp = dp->cp;
629 
630 	qlock(cp);
631 	if(waserror()){
632 		qunlock(cp);
633 		nexterror();
634 	}
635 
636 	cmdreadywait(dp);
637 
638 	ilock(&cp->reglock);
639 	cp->cmd = Csetbuf;
640 	outb(cp->pbase+Pprecomp, on ? 0xAA : 0x55);	/* read look ahead */
641 	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
642 	outb(cp->pbase+Pcmd, Csetbuf);
643 	iunlock(&cp->reglock);
644 
645 	atasleep(cp, 5000);
646 
647 /*	if(cp->status & Serr)
648 		DPRINT("hd%d setbuf err: status %lux, err %lux\n",
649 			dp-ata, cp->status, cp->error);/**/
650 
651 	poperror();
652 	qunlock(cp);
653 }
654 
655 /*
656  *  ident sector from drive.  this is from ANSI X3.221-1994
657  */
658 struct Ident
659 {
660 	ushort	config;		/* general configuration info */
661 	ushort	cyls;		/* # of cylinders (default) */
662 	ushort	reserved0;
663 	ushort	heads;		/* # of heads (default) */
664 	ushort	b2t;		/* unformatted bytes/track */
665 	ushort	b2s;		/* unformated bytes/sector */
666 	ushort	s2t;		/* sectors/track (default) */
667 	ushort	reserved1[3];
668 /* 10 */
669 	ushort	serial[10];	/* serial number */
670 	ushort	type;		/* buffer type */
671 	ushort	bsize;		/* buffer size/512 */
672 	ushort	ecc;		/* ecc bytes returned by read long */
673 	ushort	firm[4];	/* firmware revision */
674 	ushort	model[20];	/* model number */
675 /* 47 */
676 	ushort	s2i;		/* number of sectors/interrupt */
677 	ushort	dwtf;		/* double word transfer flag */
678 	ushort	capabilities;
679 	ushort	reserved2;
680 	ushort	piomode;
681 	ushort	dmamode;
682 	ushort	cvalid;		/* (cvald&1) if next 4 words are valid */
683 	ushort	ccyls;		/* current # cylinders */
684 	ushort	cheads;		/* current # heads */
685 	ushort	cs2t;		/* current sectors/track */
686 	ushort	ccap[2];	/* current capacity in sectors */
687 	ushort	cs2i;		/* current number of sectors/interrupt */
688 /* 60 */
689 	ushort	lbasecs[2];	/* # LBA user addressable sectors */
690 	ushort	dmasingle;
691 	ushort	dmadouble;
692 /* 64 */
693 	ushort	reserved3[64];
694 	ushort	vendor[32];	/* vendor specific */
695 	ushort	reserved4[96];
696 };
697 
698 /*
699  *  get parameters from the drive
700  */
701 static void
702 ataident(Drive *dp)
703 {
704 	Controller *cp;
705 	char *buf;
706 	Ident *ip;
707 	char id[21];
708 
709 	cp = dp->cp;
710 	buf = smalloc(Maxxfer);
711 	qlock(cp);
712 	if(waserror()){
713 		cp->buf = 0;
714 		qunlock(cp);
715 		free(buf);
716 		nexterror();
717 	}
718 
719 	cmdreadywait(dp);
720 
721 	ilock(&cp->reglock);
722 	cp->nsecs = 1;
723 	cp->sofar = 0;
724 	cp->cmd = Cident;
725 	cp->dp = dp;
726 	cp->buf = buf;
727 	outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4));
728 	outb(cp->pbase+Pcmd, Cident);
729 	iunlock(&cp->reglock);
730 
731 	atasleep(cp, 5000);
732 	if(cp->status & Serr){
733 		DPRINT("bad disk ident status\n");
734 		error(Eio);
735 	}
736 	ip = (Ident*)buf;
737 
738 	/*
739 	 * this function appears to respond with an extra interrupt after
740 	 * the ident information is read, except on the safari.  The following
741 	 * delay gives this extra interrupt a chance to happen while we are quiet.
742 	 * Otherwise, the interrupt may come during a subsequent read or write,
743 	 * causing a panic and much confusion.
744 	 */
745 	if (cp->cmd == Cident2)
746 		tsleep(&cp->r, return0, 0, 10);
747 
748 	memmove(id, ip->model, sizeof(id)-1);
749 	id[sizeof(id)-1] = 0;
750 
751 	if(ip->capabilities & (1<<9)){
752 		dp->lba = 1;
753 		dp->sectors = (ip->lbasecs[0]) | (ip->lbasecs[1]<<16);
754 		dp->cap = dp->bytes * dp->sectors;
755 /*print("\nata%d model %s with %d lba sectors\n", dp->drive, id, dp->sectors);/**/
756 	} else {
757 		dp->lba = 0;
758 
759 		/* use default (unformatted) settings */
760 		dp->cyl = ip->cyls;
761 		dp->heads = ip->heads;
762 		dp->sectors = ip->s2t;
763 /*print("\nata%d model %s with default %d cyl %d head %d sec\n", dp->drive,
764 			id, dp->cyl, dp->heads, dp->sectors);/**/
765 
766 		if(ip->cvalid&(1<<0)){
767 			/* use current settings */
768 			dp->cyl = ip->ccyls;
769 			dp->heads = ip->cheads;
770 			dp->sectors = ip->cs2t;
771 /*print("\tchanged to %d cyl %d head %d sec\n", dp->cyl, dp->heads, dp->sectors);/**/
772 		}
773 		dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
774 	}
775 	cp->lastcmd = cp->cmd;
776 	cp->cmd = 0;
777 	cp->buf = 0;
778 	free(buf);
779 	poperror();
780 	qunlock(cp);
781 }
782 
783 /*
784  *  probe the given sector to see if it exists
785  */
786 static int
787 ataprobe(Drive *dp, int cyl, int sec, int head)
788 {
789 	Controller *cp;
790 	char *buf;
791 	int rv;
792 
793 	cp = dp->cp;
794 	buf = smalloc(Maxxfer);
795 	qlock(cp);
796 	if(waserror()){
797 		free(buf);
798 		qunlock(cp);
799 		nexterror();
800 	}
801 
802 	cmdreadywait(dp);
803 
804 	ilock(&cp->reglock);
805 	cp->cmd = Cread;
806 	cp->dp = dp;
807 	cp->status = 0;
808 	cp->nsecs = 1;
809 	cp->sofar = 0;
810 
811 	outb(cp->pbase+Pcount, 1);
812 	outb(cp->pbase+Psector, sec+1);
813 	outb(cp->pbase+Pdh, DHmagic | head | (dp->lba<<6) | (dp->drive<<4));
814 	outb(cp->pbase+Pcyllsb, cyl);
815 	outb(cp->pbase+Pcylmsb, cyl>>8);
816 	outb(cp->pbase+Pcmd, Cread);
817 	iunlock(&cp->reglock);
818 
819 	atasleep(cp, 5000);
820 
821 	if(cp->status & Serr)
822 		rv = -1;
823 	else
824 		rv = 0;
825 
826 	cp->buf = 0;
827 	free(buf);
828 	poperror();
829 	qunlock(cp);
830 	return rv;
831 }
832 
833 /*
834  *  figure out the drive parameters
835  */
836 static void
837 ataparams(Drive *dp)
838 {
839 	int i, hi, lo;
840 
841 	/*
842 	 *  first try the easy way, ask the drive and make sure it
843 	 *  isn't lying.
844 	 */
845 	dp->bytes = 512;
846 	ataident(dp);
847 	if(dp->lba){
848 		i = dp->sectors - 1;
849 		if(ataprobe(dp, (i>>8)&0xffff, (i&0xff)-1, (i>>24)&0xf) == 0)
850 			return;
851 	} else {
852 		if(ataprobe(dp, dp->cyl-1, dp->sectors-1, dp->heads-1) == 0)
853 			return;
854 	}
855 
856 	/*
857 	 *  the drive lied, determine parameters by seeing which ones
858 	 *  work to read sectors.
859 	 */
860 	dp->lba = 0;
861 	for(i = 0; i < 32; i++)
862 		if(ataprobe(dp, 0, 0, i) < 0)
863 			break;
864 	dp->heads = i;
865 	for(i = 0; i < 128; i++)
866 		if(ataprobe(dp, 0, i, 0) < 0)
867 			break;
868 	dp->sectors = i;
869 	for(i = 512; ; i += 512)
870 		if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
871 			break;
872 	lo = i - 512;
873 	hi = i;
874 	for(; hi-lo > 1;){
875 		i = lo + (hi - lo)/2;
876 		if(ataprobe(dp, i, dp->sectors-1, dp->heads-1) < 0)
877 			hi = i;
878 		else
879 			lo = i;
880 	}
881 	dp->cyl = lo + 1;
882 	dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
883 }
884 
885 /*
886  *  Read block replacement table.
887  *  The table is just ascii block numbers.
888  */
889 static void
890 atareplinit(Drive *dp)
891 {
892 	char *line[Nrepl+1];
893 	char *field[1];
894 	ulong n;
895 	int i;
896 	char *buf;
897 
898 	/*
899 	 *  check the partition is big enough
900 	 */
901 	if(dp->repl.p->end - dp->repl.p->start < Nrepl+1){
902 		dp->repl.p = 0;
903 		return;
904 	}
905 
906 	buf = smalloc(Maxxfer);
907 	if(waserror()){
908 		free(buf);
909 		nexterror();
910 	}
911 
912 	/*
913 	 *  read replacement table from disk, null terminate
914 	 */
915 	ataxfer(dp, dp->repl.p, Cread, 0, dp->bytes, buf);
916 	buf[dp->bytes-1] = 0;
917 
918 	/*
919 	 *  parse replacement table.
920 	 */
921 	n = getfields(buf, line, Nrepl+1, 1, "\n");
922 	if(strncmp(line[0], REPLMAGIC, sizeof(REPLMAGIC)-1)){
923 		dp->repl.p = 0;
924 	} else {
925 		for(dp->repl.nrepl = 0, i = 1; i < n; i++, dp->repl.nrepl++){
926 			if(getfields(line[i], field, 1, 1, " ") != 1)
927 				break;
928 			dp->repl.blk[dp->repl.nrepl] = strtoul(field[0], 0, 0);
929 			if(dp->repl.blk[dp->repl.nrepl] <= 0)
930 				break;
931 		}
932 	}
933 	free(buf);
934 	poperror();
935 }
936 
937 /*
938  *  read partition table.  The partition table is just ascii strings.
939  */
940 static void
941 atapart(Drive *dp)
942 {
943 	Partition *pp;
944 	char *line[Npart+1];
945 	char *field[3];
946 	ulong n;
947 	int i;
948 	char *buf;
949 
950 	sprint(dp->vol, "hd%d", dp - ata);
951 
952 	/*
953 	 *  we always have a partition for the whole disk
954 	 *  and one for the partition table
955 	 */
956 	pp = &dp->p[0];
957 	strcpy(pp->name, "disk");
958 	pp->start = 0;
959 	pp->end = dp->cap / dp->bytes;
960 	pp++;
961 	strcpy(pp->name, "partition");
962 	pp->start = dp->p[0].end - 1;
963 	pp->end = dp->p[0].end;
964 	pp++;
965 	dp->npart = 2;
966 
967 	/*
968 	 * initialise the bad-block replacement info
969 	 */
970 	dp->repl.p = 0;
971 
972 	buf = smalloc(Maxxfer);
973 	if(waserror()){
974 		free(buf);
975 		nexterror();
976 	}
977 
978 	/*
979 	 *  read last sector from disk, null terminate.  This used
980 	 *  to be the sector we used for the partition tables.
981 	 *  However, this sector is special on some PC's so we've
982 	 *  started to use the second last sector as the partition
983 	 *  table instead.  To avoid reconfiguring all our old systems
984 	 *  we first look to see if there is a valid partition
985 	 *  table in the last sector.  If so, we use it.  Otherwise
986 	 *  we switch to the second last.
987 	 */
988 	ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf);
989 	buf[dp->bytes-1] = 0;
990 	n = getfields(buf, line, Npart+1, 1, "\n");
991 	if(n == 0 || strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1)){
992 		dp->p[0].end--;
993 		dp->p[1].start--;
994 		dp->p[1].end--;
995 		ataxfer(dp, dp->p+1, Cread, 0, dp->bytes, buf);
996 		buf[dp->bytes-1] = 0;
997 		n = getfields(buf, line, Npart+1, 1, "\n");
998 	}
999 
1000 	/*
1001 	 *  parse partition table.
1002 	 */
1003 	if(n > 0 && strncmp(line[0], PARTMAGIC, sizeof(PARTMAGIC)-1) == 0){
1004 		for(i = 1; i < n; i++){
1005 			switch(getfields(line[i], field, 3, 1, " ")) {
1006 			case 2:
1007 				if(strcmp(field[0], "unit") == 0)
1008 					strncpy(dp->vol, field[1], NAMELEN);
1009 				break;
1010 			case 3:
1011 				strncpy(pp->name, field[0], NAMELEN);
1012 				if(strncmp(pp->name, "repl", NAMELEN) == 0)
1013 					dp->repl.p = pp;
1014 				pp->start = strtoul(field[1], 0, 0);
1015 				pp->end = strtoul(field[2], 0, 0);
1016 				if(pp->start > pp->end || pp->end > dp->p[0].end)
1017 					break;
1018 				dp->npart++;
1019 				pp++;
1020 			}
1021 		}
1022 	}
1023 	free(buf);
1024 	poperror();
1025 
1026 	if(dp->repl.p)
1027 		atareplinit(dp);
1028 }
1029 
1030 enum
1031 {
1032 	Maxloop=	10000,
1033 };
1034 
1035 /*
1036  *  we get an interrupt for every sector transferred
1037  */
1038 static void
1039 ataintr(Ureg*, void *arg)
1040 {
1041 	Controller *cp;
1042 	Drive *dp;
1043 	long loop;
1044 	char *addr;
1045 
1046 	cp = arg;
1047 	dp = cp->dp;
1048 
1049 	ilock(&cp->reglock);
1050 
1051 	loop = 0;
1052 	while((cp->status = inb(cp->pbase+Pstatus)) & Sbusy){
1053 		if(++loop > Maxloop) {
1054 			DPRINT("cmd=%lux status=%lux\n",
1055 				cp->cmd, inb(cp->pbase+Pstatus));
1056 			panic("ataintr: wait busy");
1057 		}
1058 	}
1059 
1060 	switch(cp->cmd){
1061 	case Cwrite:
1062 		if(cp->status & Serr){
1063 			cp->lastcmd = cp->cmd;
1064 			cp->cmd = 0;
1065 			cp->error = inb(cp->pbase+Perror);
1066 			wakeup(&cp->r);
1067 			break;
1068 		}
1069 		cp->sofar++;
1070 		if(cp->sofar < cp->nsecs){
1071 			loop = 0;
1072 			while(((cp->status = inb(cp->pbase+Pstatus)) & Sdrq) == 0)
1073 				if(++loop > Maxloop) {
1074 					DPRINT("cmd=%lux status=%lux\n",
1075 						cp->cmd, inb(cp->pbase+Pstatus));
1076 					panic("ataintr: write");
1077 				}
1078 			addr = cp->buf;
1079 			if(addr){
1080 				addr += cp->sofar*dp->bytes;
1081 				outss(cp->pbase+Pdata, addr, dp->bytes/2);
1082 			}
1083 		} else{
1084 			cp->lastcmd = cp->cmd;
1085 			cp->cmd = 0;
1086 			wakeup(&cp->r);
1087 		}
1088 		break;
1089 	case Cread:
1090 	case Cident:
1091 		loop = 0;
1092 		while((cp->status & (Serr|Sdrq)) == 0){
1093 			if(++loop > Maxloop) {
1094 				DPRINT("cmd=%lux status=%lux\n",
1095 					cp->cmd, inb(cp->pbase+Pstatus));
1096 				panic("ataintr: read/ident");
1097 			}
1098 			cp->status = inb(cp->pbase+Pstatus);
1099 		}
1100 		if(cp->status & Serr){
1101 			cp->lastcmd = cp->cmd;
1102 			cp->cmd = 0;
1103 			cp->error = inb(cp->pbase+Perror);
1104 			wakeup(&cp->r);
1105 			break;
1106 		}
1107 		addr = cp->buf;
1108 		if(addr){
1109 			addr += cp->sofar*dp->bytes;
1110 			inss(cp->pbase+Pdata, addr, dp->bytes/2);
1111 		}
1112 		cp->sofar++;
1113 		if(cp->sofar > cp->nsecs)
1114 			print("ataintr %d %d\n", cp->sofar, cp->nsecs);
1115 		if(cp->sofar >= cp->nsecs){
1116 			cp->lastcmd = cp->cmd;
1117 			if (cp->cmd == Cread)
1118 				cp->cmd = 0;
1119 			else
1120 				cp->cmd = Cident2;
1121 			wakeup(&cp->r);
1122 		}
1123 		break;
1124 	case Cinitparam:
1125 	case Csetbuf:
1126 	case Cidle:
1127 	case Cstandby:
1128 	case Cpowerdown:
1129 		cp->lastcmd = cp->cmd;
1130 		cp->cmd = 0;
1131 		wakeup(&cp->r);
1132 		break;
1133 	case Cident2:
1134 		cp->lastcmd = cp->cmd;
1135 		cp->cmd = 0;
1136 		break;
1137 	default:
1138 		print("weird disk interrupt, cmd=%.2ux, lastcmd= %.2ux status=%.2ux\n",
1139 			cp->cmd, cp->lastcmd, cp->status);
1140 		break;
1141 	}
1142 
1143 	iunlock(&cp->reglock);
1144 }
1145 
1146 void
1147 hardclock(void)
1148 {
1149 	int drive;
1150 	Drive *dp;
1151 	Controller *cp;
1152 	int diff;
1153 
1154 	if(spindowntime <= 0)
1155 		return;
1156 
1157 	for(drive = 0; drive < nhard; drive++){
1158 		dp = &ata[drive];
1159 		cp = dp->cp;
1160 
1161 		diff = TK2SEC(m->ticks - dp->usetime);
1162 		if((dp->state == Sspinning) && (diff >= spindowntime)){
1163 			ilock(&cp->reglock);
1164 			cp->cmd = Cstandby;
1165 			outb(cp->pbase+Pcount, 0);
1166 			outb(cp->pbase+Pdh, DHmagic | (dp->drive<<4) | 0);
1167 			outb(cp->pbase+Pcmd, cp->cmd);
1168 			iunlock(&cp->reglock);
1169 			dp->state = Sstandby;
1170 		}
1171 	}
1172 }
1173 
1174 Dev atadevtab = {
1175 	'H',
1176 	"ata",
1177 
1178 	devreset,
1179 	atainit,
1180 	ataattach,
1181 	devdetach,
1182 	devclone,
1183 	atawalk,
1184 	atastat,
1185 	ataopen,
1186 	devcreate,
1187 	ataclose,
1188 	ataread,
1189 	devbread,
1190 	atawrite,
1191 	devbwrite,
1192 	devremove,
1193 	devwstat,
1194 };
1195