add cassette stuff also
[unix-history] / usr / src / sys / vax / stand / up.c
CommitLineData
7522de24 1/* up.c 4.7 83/02/17 */
258a5c40 2
0839bdeb
SL
3/*
4 * UNIBUS peripheral standalone driver
5 * with ECC correction and bad block forwarding.
6 * Also supports header operation and write
7 * check for data and/or header.
8 */
b8ca3ab9 9
258a5c40
SL
10#include "../h/param.h"
11#include "../h/inode.h"
12#include "../h/fs.h"
13#include "../h/dkbad.h"
14#include "../h/vmmac.h"
15
16#include "../vax/pte.h"
17#include "../vaxuba/upreg.h"
18#include "../vaxuba/ubareg.h"
19
0839bdeb 20#include "saio.h"
258a5c40
SL
21#include "savax.h"
22
b8ca3ab9
HS
23#define MAXBADDESC 126 /* max number of bad sectors recorded */
24#define SECTSIZ 512 /* sector size in bytes */
25#define HDRSIZ 4 /* number of bytes in sector header */
26#define MAXECC 5 /* max number of bad bits accepted in
27 * a soft ecc error when F_ECCLM is set */
28#define NUPTYPES 3
29
258a5c40 30u_short ubastd[] = { 0776700 };
0839bdeb 31
258a5c40
SL
32char up_gottype[MAXNUBA*8] = { 0 };
33char up_type[MAXNUBA*8] = { 0 };
258a5c40
SL
34short up_off[] = { 0, 27, 68, -1, -1, -1, -1, 82 };
35short fj_off[] = { 0, 50, 0, -1, -1, -1, -1, 155 };
36/* this is called upam instead of am because hp.c has a similar array */
37short upam_off[] = { 0, 32, 0, 668, 723, 778, 668, 98 };
0839bdeb 38
b8ca3ab9 39struct st upst[NUPTYPES] = {
0839bdeb
SL
40 32, 19, 32*19, 823, up_off, /* 9300/equiv */
41 32, 10, 32*10, 823, fj_off, /* Fuji 160 */
42 32, 16, 32*16, 1024, upam_off, /* Capricorn */
258a5c40 43};
0839bdeb 44
258a5c40
SL
45u_char up_offset[16] = {
46 UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400,
47 UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800,
48 UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200,
49 0, 0, 0, 0
50};
51
0839bdeb 52struct dkbad upbad[MAXNUBA*8]; /* bad sector table */
b8ca3ab9 53int sectsiz; /* real sector size */
0839bdeb 54
258a5c40
SL
55upopen(io)
56 register struct iob *io;
57{
b8ca3ab9 58 register unit = io->i_unit;
0839bdeb 59 register struct updevice *upaddr;
b8ca3ab9 60 register struct st *st = &upst[up_type[unit]];
258a5c40 61
0839bdeb
SL
62 if (io->i_boff < 0 || io->i_boff > 7 || st->off[io->i_boff] == -1)
63 _stop("up bad unit");
b8ca3ab9 64 upaddr = (struct updevice *)ubamem(unit, ubastd[0]);
3ab40097 65 while ((upaddr->upcs1 & UP_DVA) == 0)
258a5c40 66 ;
b8ca3ab9 67 if (up_gottype[unit] == 0) {
0839bdeb
SL
68 register int i;
69 struct iob tio;
70
258a5c40 71 upaddr->uphr = UPHR_MAXTRAK;
0839bdeb
SL
72 for (st = upst; st < &upst[NUPTYPES]; st++)
73 if (upaddr->uphr == st->ntrak - 1) {
b8ca3ab9 74 up_type[unit] = st - upst;
0839bdeb
SL
75 break;
76 }
77 if (st == &upst[NUPTYPES]) {
b8ca3ab9 78 printf("up%d: uphr=%x\n", unit, upaddr->uphr);
0839bdeb
SL
79 _stop("unknown drive type");
80 }
258a5c40
SL
81 upaddr->upcs2 = UPCS2_CLR;
82#ifdef DEBUG
b8ca3ab9 83 printf("Unittype=%d\n",up_type[unit]);
258a5c40 84#endif
258a5c40 85
0839bdeb
SL
86 /*
87 * Read in the bad sector table:
88 * copy the contents of the io structure
89 * to tio for use during the bb pointer
90 * read operation.
91 */
92 tio = *io;
93 tio.i_bn = st->nspc * st->ncyl - st->nsect;
258a5c40 94 tio.i_ma = (char *)&upbad[tio.i_unit];
960ade39 95 tio.i_cc = sizeof (struct dkbad);
0839bdeb
SL
96 tio.i_flgs |= F_RDDATA;
97 for (i = 0; i < 5; i++) {
960ade39 98 if (upstrategy(&tio, READ) == sizeof (struct dkbad))
0839bdeb 99 break;
258a5c40
SL
100 tio.i_bn += 2;
101 }
102 if (i == 5) {
0839bdeb 103 printf("Unable to read bad sector table\n");
b8ca3ab9
HS
104 for (i = 0; i < MAXBADDESC; i++) {
105 upbad[unit].bt_bad[i].bt_cyl = -1;
106 upbad[unit].bt_bad[i].bt_trksec = -1;
258a5c40
SL
107 }
108 }
b8ca3ab9 109 up_gottype[unit] = 1;
258a5c40 110 }
258a5c40 111 io->i_boff = st->off[io->i_boff] * st->nspc;
0839bdeb 112 io->i_flgs &= ~F_TYPEMASK;
258a5c40
SL
113}
114
115upstrategy(io, func)
116 register struct iob *io;
117{
b8ca3ab9
HS
118 int cn, tn, sn;
119 register unit = io->i_unit;
258a5c40
SL
120 daddr_t bn;
121 int recal, info, waitdry;
122 register struct updevice *upaddr =
b8ca3ab9
HS
123 (struct updevice *)ubamem(unit, ubastd[0]);
124 register struct st *st = &upst[up_type[unit]];
258a5c40 125
b8ca3ab9 126 sectsiz = SECTSIZ;
3ab40097 127 if (io->i_flgs & (F_HDR|F_HCHECK))
b8ca3ab9 128 sectsiz += HDRSIZ;
258a5c40
SL
129 upaddr->upcs2 = unit;
130 if ((upaddr->upds & UPDS_VV) == 0) {
131 upaddr->upcs1 = UP_DCLR|UP_GO;
132 upaddr->upcs1 = UP_PRESET|UP_GO;
133 upaddr->upof = UPOF_FMT22;
134 }
3ab40097 135 if ((upaddr->upds & UPDS_DREADY) == 0)
258a5c40
SL
136 _stop("up not ready");
137 info = ubasetup(io, 1);
138 upaddr->upwc = -io->i_cc / sizeof (short);
139 upaddr->upba = info;
3ab40097
SL
140 recal = 0;
141 io->i_errcnt = 0;
142
960ade39 143restart:
3ab40097 144 bn = io->i_bn + (io->i_cc + upaddr->upwc * sizeof(short)) / sectsiz;
0839bdeb
SL
145 while((upaddr->upds & UPDS_DRY) == 0)
146 ;
b8ca3ab9
HS
147 if (upstart(io, bn) != 0) {
148 ubafree(io, info);
258a5c40 149 return (-1);
b8ca3ab9 150 }
258a5c40
SL
151 do {
152 DELAY(25);
153 } while ((upaddr->upcs1 & UP_RDY) == 0);
3ab40097
SL
154 /*
155 * If transfer has completed, free UNIBUS
156 * resources and return transfer size.
157 */
158 if ((upaddr->upds&UPDS_ERR) == 0 && (upaddr->upcs1&UP_TRE) == 0) {
b8ca3ab9 159 ubafree(io, info);
3ab40097 160 return (io->i_cc);
b8ca3ab9 161 }
258a5c40
SL
162#ifdef LOGALLERRS
163 printf("uper: (c,t,s)=(%d,%d,%d) cs2=%b er1=%b er2=%b wc=%x\n",
3ab40097
SL
164 upaddr->updc, upaddr->upda>>8, (upaddr->upda&0x1f-1),
165 upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
166 UPER1_BITS, upaddr->uper2, UPER2_BITS,-upaddr->upwc);
258a5c40
SL
167#endif
168 waitdry = 0;
b8ca3ab9 169 while ((upaddr->upds & UPDS_DRY) == 0 && ++waitdry < sectsiz)
0839bdeb 170 DELAY(5);
3ab40097
SL
171 if (upaddr->uper1&UPER1_WLE) {
172 /*
173 * Give up on write locked devices immediately.
174 */
175 printf("up%d: write locked\n", unit);
176 return (-1);
177 }
258a5c40
SL
178 if (++io->i_errcnt > 27) {
179 /*
180 * After 28 retries (16 without offset, and
181 * 12 with offset positioning) give up.
182 */
0839bdeb 183 io->i_error = EHER;
0839bdeb
SL
184 if (upaddr->upcs2 & UPCS2_WCE)
185 io->i_error = EWCK;
b8ca3ab9 186hard:
3ab40097
SL
187 bn = io->i_bn +
188 (io->i_cc + upaddr->upwc * sizeof (short)) / sectsiz;
258a5c40
SL
189 cn = bn/st->nspc;
190 sn = bn%st->nspc;
191 tn = sn/st->nsect;
192 sn = sn%st->nsect;
3ab40097
SL
193 printf(
194 "up error: (cyl,trk,sec)=(%d,%d,%d) cs2=%b er1=%b er2=%b\n",
195 cn, tn, sn,
196 upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
197 UPER1_BITS, upaddr->uper2, UPER2_BITS);
258a5c40 198 upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
b8ca3ab9 199 io->i_errblk = bn;
3ab40097
SL
200 return (io->i_cc + upaddr->upwc * sizeof(short));
201 }
202 if (upaddr->uper2 & UPER2_BSE) {
203 short wc = upaddr->upwc;
204 if ((io->i_flgs&F_NBSF) == 0 && upecc(io, BSE) == 0) {
205 if (wc != upaddr->upwc)
206 printf("wc %x upwc %x\n", wc, upaddr->upwc);
258a5c40 207 goto success;
258a5c40 208 }
3ab40097
SL
209 io->i_error = EBSE;
210 goto hard;
258a5c40 211 }
3ab40097
SL
212 /*
213 * Retriable error.
214 * If a soft ecc, correct it
215 * Otherwise fall through and retry the transfer
216 */
217 if (upaddr->uper1 & UPER1_DCK) {
258a5c40 218 /*
3ab40097
SL
219 * If a write check command is active, all
220 * ecc errors give UPER1_ECH.
258a5c40 221 */
3ab40097
SL
222 if ((upaddr->uper1 & UPER1_ECH) == 0 ||
223 (upaddr->upcs2 & UPCS2_WCE)) {
224 if (upecc(io, ECC) == 0)
225 goto success;
226 io->i_error = EECC;
227 goto hard;
228 }
229 }
258a5c40
SL
230 /*
231 * Clear drive error and, every eight attempts,
232 * (starting with the fourth)
233 * recalibrate to clear the slate.
234 */
235 upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
960ade39 236 if ((io->i_errcnt&07) == 4 ) {
258a5c40 237 upaddr->upcs1 = UP_RECAL|UP_GO;
960ade39
HS
238 recal = 1;
239 goto restart;
258a5c40
SL
240 }
241 /*
242 * Advance recalibration finite state machine
243 * if recalibrate in progress, through
244 * RECAL
245 * SEEK
246 * OFFSET (optional)
247 * RETRY
248 */
249 switch (recal) {
250
251 case 1:
252 upaddr->updc = cn;
253 upaddr->upcs1 = UP_SEEK|UP_GO;
3ab40097 254 recal = 2;
960ade39 255 goto restart;
0839bdeb 256
258a5c40
SL
257 case 2:
258 if (io->i_errcnt < 16 || (func & READ) == 0)
259 goto donerecal;
260 upaddr->upof = up_offset[io->i_errcnt & 017] | UPOF_FMT22;
261 upaddr->upcs1 = UP_OFFSET|UP_GO;
3ab40097 262 recal = 3;
960ade39 263 goto restart;
0839bdeb 264
258a5c40
SL
265 donerecal:
266 case 3:
267 recal = 0;
258a5c40
SL
268 break;
269 }
270 /*
960ade39
HS
271 * If we were offset positioning,
272 * return to centerline.
258a5c40 273 */
960ade39
HS
274 if (io->i_errcnt >= 16) {
275 upaddr->upof = UPOF_FMT22;
276 upaddr->upcs1 = UP_RTC|UP_GO;
277 while ((upaddr->upds&UPDS_DRY) == 0)
278 DELAY(25);
258a5c40 279 }
960ade39 280 goto restart;
3ab40097 281
258a5c40 282success:
0839bdeb 283 if (upaddr->upwc != 0)
960ade39 284 goto restart;
258a5c40
SL
285 /*
286 * Release unibus
287 */
288 ubafree(io, info);
289 return (io->i_cc);
290}
291
292/*
293 * Correct an ECC error, and restart the i/o to complete
294 * the transfer if necessary. This is quite complicated because
295 * the transfer may be going to an odd memory address base and/or
296 * across a page boundary.
297 */
0839bdeb 298upecc(io, flag)
258a5c40
SL
299 register struct iob *io;
300 int flag;
301{
302 register struct updevice *up =
303 (struct updevice *)ubamem(io->i_unit, ubastd[0]);
b8ca3ab9 304 register struct st *st;
258a5c40
SL
305 register int i;
306 caddr_t addr;
6796f4df 307 int bn, twc, npf, mask, cn, tn, sn;
b8ca3ab9 308 daddr_t bbn;
258a5c40
SL
309
310 /*
311 * Npf is the number of sectors transferred before the sector
312 * containing the ECC error, bn is the current block number
313 */
b8ca3ab9
HS
314 twc = up->upwc;
315 npf = ((twc * sizeof(short)) + io->i_cc)/sectsiz;
258a5c40
SL
316#ifdef UPECCDEBUG
317 printf("npf %d mask 0x%x pos %d wc 0x%x\n",npf,mask,up->upec1,-up->upwc);
318#endif
b8ca3ab9 319 bn = io->i_bn + npf ;
258a5c40 320 st = &upst[up_type[io->i_unit]];
6796f4df
HS
321 cn = bn/st->nspc;
322 sn = bn%st->nspc;
323 tn = sn/st->nsect;
324 sn = sn%st->nsect;
258a5c40
SL
325 /*
326 * action taken depends on the flag
327 */
328 if (flag == ECC) {
b8ca3ab9 329 int bit, byte, ecccnt;
3ab40097 330
b8ca3ab9 331 ecccnt = 0;
258a5c40 332 mask = up->upec2;
960ade39 333 printf("up%d: soft ecc sn%d\n", io->i_unit, bn);
258a5c40
SL
334 /*
335 * Compute the
336 * byte and bit position of the error. The variable i
337 * is the byte offset in the transfer.
338 */
339 i = up->upec1 - 1; /* -1 makes 0 origin */
340 bit = i&07;
341 i = (i&~07)>>3;
342 byte = i;
343 up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
344 /*
345 * Correct while possible bits remain of mask. Since mask
346 * contains 11 bits, we continue while the bit offset is > -11.
347 * Also watch out for end of this block and the end of the whole
348 * transfer.
349 */
b8ca3ab9 350 while (i < sectsiz && (npf*sectsiz)+i < io->i_cc && bit > -11) {
258a5c40 351 /*
0839bdeb 352 * addr = vax base addr + (number of sectors transferred
258a5c40
SL
353 * before the error sector times the sector size)
354 * + byte number
355 */
3ab40097 356 addr = io->i_ma + (npf * sectsiz) + byte;
258a5c40 357#ifdef UPECCDEBUG
b8ca3ab9 358 printf("addr %x old: %x ",addr, (*addr&0xff));
258a5c40 359#endif
b8ca3ab9
HS
360 if ((io->i_flgs & (F_CHECK|F_HCHECK)) == 0)
361 *addr ^= (mask << bit);
258a5c40 362#ifdef UPECCDEBUG
b8ca3ab9 363 printf("new: %x\n", (*addr&0xff));
258a5c40
SL
364#endif
365 byte++;
366 i++;
367 bit -= 8;
3ab40097
SL
368 if ((io->i_flgs&F_ECCLM) && ++ecccnt > MAXECC)
369 return (1);
258a5c40 370 }
3ab40097
SL
371 return (0);
372 }
373 if (flag == BSE) {
258a5c40 374 /*
b8ca3ab9 375 * if not in bad sector table, return 1 (= hard error)
258a5c40 376 */
258a5c40 377 up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
6796f4df 378 if ((bbn = isbad(&upbad[io->i_unit], cn, tn, sn)) < 0)
3ab40097 379 return (1);
258a5c40 380 bbn = st->ncyl * st->nspc -st->nsect - 1 - bbn;
b8ca3ab9 381 twc = up->upwc + sectsiz;
3ab40097 382 up->upwc = - (sectsiz / sizeof (short));
258a5c40
SL
383#ifdef UPECCDEBUG
384 printf("revector to block %d\n", bbn);
385#endif
386 /*
387 * Clear the drive & read the replacement sector.
388 * If this is in the middle of a transfer, then set up the
389 * controller registers in a normal fashion.
390 * The ub-address need not be changed.
391 */
0839bdeb 392 while (up->upcs1 & UP_RDY == 0)
258a5c40 393 ;
0839bdeb 394 if (upstart(io, bbn) != 0)
b8ca3ab9
HS
395 return (1); /* error */
396 io->i_errcnt = 0; /* success */
258a5c40
SL
397 do {
398 DELAY(25);
399 } while ( up->upcs1 & UP_RDY == 0) ;
400 if (up->upds & UPDS_ERR || up->upcs1 & UP_TRE) {
b8ca3ab9
HS
401 up->upwc = twc -sectsiz;
402 return (1);
258a5c40
SL
403 }
404 }
960ade39 405 if (twc)
258a5c40 406 up->upwc = twc;
b8ca3ab9 407 return (0);
258a5c40 408}
258a5c40
SL
409
410upstart(io, bn)
0839bdeb
SL
411 register struct iob *io;
412 daddr_t bn;
258a5c40
SL
413{
414 register struct updevice *upaddr =
0839bdeb 415 (struct updevice *)ubamem(io->i_unit, ubastd[0]);
b8ca3ab9 416 register struct st *st = &upst[up_type[io->i_unit]];
258a5c40
SL
417 int sn, tn;
418
419 sn = bn%st->nspc;
420 tn = sn/st->nsect;
421 sn %= st->nsect;
422 upaddr->updc = bn/st->nspc;
423 upaddr->upda = (tn << 8) + sn;
b8ca3ab9 424 switch (io->i_flgs & F_TYPEMASK) {
0839bdeb
SL
425
426 case F_RDDATA:
427 upaddr->upcs1 = UP_RCOM|UP_GO;
428 break;
429
430 case F_WRDATA:
431 upaddr->upcs1 = UP_WCOM|UP_GO;
258a5c40 432 break;
0839bdeb
SL
433
434 case F_HDR|F_RDDATA:
435 upaddr->upcs1 = UP_RHDR|UP_GO;
436 break;
437
438 case F_HDR|F_WRDATA:
439 upaddr->upcs1 = UP_WHDR|UP_GO;
258a5c40 440 break;
0839bdeb
SL
441
442 case F_CHECK|F_WRDATA:
443 case F_CHECK|F_RDDATA:
258a5c40
SL
444 upaddr->upcs1 = UP_WCDATA|UP_GO;
445 break;
0839bdeb
SL
446
447 case F_HCHECK|F_WRDATA:
448 case F_HCHECK|F_RDDATA:
258a5c40
SL
449 upaddr->upcs1 = UP_WCHDR|UP_GO;
450 break;
0839bdeb 451
258a5c40 452 default:
0839bdeb
SL
453 io->i_error = ECMD;
454 io->i_flgs &= ~F_TYPEMASK;
455 return (1);
258a5c40 456 }
0839bdeb 457 return (0);
258a5c40
SL
458}
459
0839bdeb
SL
460/*ARGSUSED*/
461upioctl(io, cmd, arg)
462 struct iob *io;
463 int cmd;
464 caddr_t arg;
465{
b8ca3ab9
HS
466 struct st *st = &upst[up_type[io->i_unit]], *tmp;
467
468 switch(cmd) {
469
470 case SAIODEVDATA:
471 tmp = (struct st *)arg;
472 *tmp = *st;
3ab40097 473 return (0);
b8ca3ab9 474 }
3ab40097 475 return (ECMD);
b8ca3ab9 476}