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