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