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