Commit | Line | Data |
---|---|---|
434f5077 KB |
1 | /* |
2 | * Copyright (c) 1988 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Computer Consoles Inc. | |
7 | * | |
8 | * Redistribution and use in source and binary forms are permitted | |
9 | * provided that the above copyright notice and this paragraph are | |
10 | * duplicated in all such forms and that any documentation, | |
11 | * advertising materials, and other materials related to such | |
12 | * distribution and use acknowledge that the software was developed | |
13 | * by the University of California, Berkeley. The name of the | |
14 | * University may not be used to endorse or promote products derived | |
15 | * from this software without specific prior written permission. | |
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
17 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
18 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
19 | * | |
f0f3c469 | 20 | * @(#)vd.c 7.12 (Berkeley) %G% |
434f5077 | 21 | */ |
4006c5e1 | 22 | |
39b72d60 | 23 | /* |
bff99b3e | 24 | * Stand alone driver for the VDDC/SMDE controller |
4006c5e1 | 25 | */ |
39b72d60 SL |
26 | #include "../machine/mtpr.h" |
27 | ||
28 | #include "param.h" | |
29 | #include "inode.h" | |
30 | #include "fs.h" | |
bdfd2fb7 | 31 | #include "buf.h" |
bff99b3e SL |
32 | #include "disklabel.h" |
33 | #include "saio.h" | |
34 | ||
1a9cb196 | 35 | #include "../tahoevba/vdreg.h" |
1a9cb196 | 36 | #include "../tahoevba/vbaparam.h" |
39b72d60 | 37 | |
bff99b3e | 38 | #define COMPAT_42 1 |
39b72d60 | 39 | |
cb69d88e | 40 | #define NVD 4 /* controllers */ |
bff99b3e | 41 | #define NDRIVE 8 /* drives per controller */ |
39b72d60 | 42 | |
bff99b3e SL |
43 | #define VDADDR(ctlr) ((struct vddevice *)vdaddrs[ctlr]) |
44 | long vdaddrs[NVD] = { 0xffff2000, 0xffff2100, 0xffff2200, 0xffff2300 }; | |
39b72d60 | 45 | |
bff99b3e SL |
46 | u_char vdinit[NVD]; /* controller initialized */ |
47 | u_char vdtype[NVD]; /* controller type */ | |
cb69d88e | 48 | u_char dkconfigured[NVD][NDRIVE]; /* unit configured */ |
7d68510b | 49 | u_char dkflags[NVD][NDRIVE]; /* unit flags */ |
39b72d60 | 50 | |
cb69d88e | 51 | static struct disklabel dklabel[NVD][NDRIVE]; /* pack label */ |
7d68510b MK |
52 | static struct mdcb mdcb; |
53 | static struct dcb dcb; | |
54 | static char lbuf[DEV_BSIZE]; | |
39b72d60 SL |
55 | |
56 | vdopen(io) | |
4006c5e1 | 57 | register struct iob *io; |
39b72d60 | 58 | { |
cb69d88e | 59 | register int ctlr = io->i_ctlr; |
bff99b3e | 60 | register struct dkinfo *dk; |
bdfd2fb7 | 61 | register struct disklabel *lp, *dlp; |
bff99b3e | 62 | int error; |
39b72d60 | 63 | |
cb69d88e KB |
64 | if ((u_int)io->i_adapt) |
65 | return (EADAPT); | |
66 | if ((u_int)ctlr >= NVD) | |
67 | return (ECTLR); | |
bff99b3e SL |
68 | if (!vdinit[ctlr] && (error = vdreset_ctlr(ctlr, io->i_unit))) |
69 | return (error); | |
cb69d88e KB |
70 | lp = &dklabel[io->i_ctlr][io->i_unit]; |
71 | if (!dkconfigured[io->i_ctlr][io->i_unit]) { | |
bff99b3e SL |
72 | struct iob tio; |
73 | ||
74 | /* | |
75 | * Read in the pack label. | |
76 | */ | |
7d68510b MK |
77 | lp->d_secsize = 1024; |
78 | lp->d_nsectors = 72; | |
bff99b3e SL |
79 | lp->d_ntracks = 24; |
80 | lp->d_ncylinders = 711; | |
7d68510b | 81 | lp->d_secpercyl = 72*24; |
bff99b3e SL |
82 | if (!vdreset_drive(io)) |
83 | return (ENXIO); | |
84 | tio = *io; | |
85 | tio.i_bn = LABELSECTOR; | |
86 | tio.i_ma = lbuf; | |
87 | tio.i_cc = DEV_BSIZE; | |
88 | tio.i_flgs |= F_RDDATA; | |
cb69d88e KB |
89 | if (vdstrategy(&tio, READ) != DEV_BSIZE) |
90 | return (ERDLAB); | |
bdfd2fb7 | 91 | dlp = (struct disklabel *)(lbuf + LABELOFFSET); |
cb69d88e | 92 | if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) |
bff99b3e | 93 | #ifdef COMPAT_42 |
cb69d88e KB |
94 | { |
95 | printf("dk%d: unlabeled\n", io->i_unit); | |
bff99b3e SL |
96 | if (error = vdmaptype(io)) |
97 | return (error); | |
cb69d88e | 98 | } |
bff99b3e | 99 | #else |
cb69d88e | 100 | return (EUNLAB); |
bff99b3e | 101 | #endif |
cb69d88e | 102 | else { |
bdfd2fb7 | 103 | *lp = *dlp; |
7d68510b MK |
104 | if (!vdreset_drive(io)) |
105 | return (ENXIO); | |
106 | } | |
cb69d88e | 107 | dkconfigured[io->i_ctlr][io->i_unit] = 1; |
39b72d60 | 108 | } |
cb69d88e KB |
109 | if (io->i_part < 0 || io->i_part >= lp->d_npartitions || |
110 | lp->d_partitions[io->i_part].p_size == 0) | |
111 | return (EPART); | |
bff99b3e | 112 | io->i_boff = |
cb69d88e | 113 | (lp->d_partitions[io->i_part].p_offset * lp->d_secsize) / DEV_BSIZE; |
bff99b3e | 114 | return (0); |
39b72d60 SL |
115 | } |
116 | ||
bff99b3e SL |
117 | /* |
118 | * Reset and initialize the controller. | |
119 | */ | |
120 | vdreset_ctlr(ctlr, unit) | |
121 | register int ctlr, unit; | |
39b72d60 | 122 | { |
bff99b3e SL |
123 | register int i; |
124 | register struct vddevice *vdaddr = VDADDR(ctlr); | |
39b72d60 | 125 | |
bff99b3e SL |
126 | if (badaddr(vdaddr, 2)) { |
127 | printf("vd%d: %x: invalid csr\n", ctlr, vdaddr); | |
128 | return (ENXIO); | |
39b72d60 | 129 | } |
bff99b3e SL |
130 | /* probe further to find what kind of controller it is */ |
131 | vdaddr->vdreset = 0xffffffff; | |
39b72d60 | 132 | DELAY(1000000); |
bff99b3e SL |
133 | if (vdaddr->vdreset != 0xffffffff) { |
134 | vdtype[ctlr] = VDTYPE_VDDC; | |
39b72d60 | 135 | DELAY(1000000); |
4006c5e1 | 136 | } else { |
bff99b3e SL |
137 | vdtype[ctlr] = VDTYPE_SMDE; |
138 | vdaddr->vdrstclr = 0; | |
39b72d60 | 139 | DELAY(3000000); |
bff99b3e SL |
140 | vdaddr->vdcsr = 0; |
141 | vdaddr->vdtcf_mdcb = AM_ENPDA; | |
142 | vdaddr->vdtcf_dcb = AM_ENPDA; | |
143 | vdaddr->vdtcf_trail = AM_ENPDA; | |
144 | vdaddr->vdtcf_data = AM_ENPDA; | |
f0f3c469 | 145 | vdaddr->vdccf = CCF_SEN | CCF_DIU | CCF_STS | |
bff99b3e SL |
146 | XMD_32BIT | BSZ_16WRD | |
147 | CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; | |
39b72d60 | 148 | } |
bff99b3e SL |
149 | if (!vdcmd(ctlr, 0, VDOP_INIT, 10) || |
150 | !vdcmd(ctlr, 0, VDOP_DIAG, 10)) { | |
151 | vderror(unit, dcb.opcode == VDOP_INIT ? "init" : "diag", &dcb); | |
152 | return (EIO); | |
39b72d60 | 153 | } |
bff99b3e | 154 | vdinit[ctlr] = 1; |
cb69d88e KB |
155 | for (i = NDRIVE - 1; i >= 0; i--) |
156 | dkconfigured[ctlr][i] = 0; | |
bff99b3e | 157 | return (0); |
39b72d60 SL |
158 | } |
159 | ||
bff99b3e SL |
160 | /* |
161 | * Reset and configure a drive's parameters. | |
162 | */ | |
163 | vdreset_drive(io) | |
4006c5e1 | 164 | register struct iob *io; |
39b72d60 | 165 | { |
cb69d88e KB |
166 | register int ctlr = io->i_ctlr, slave = io->i_unit; |
167 | register struct disklabel *lp = &dklabel[io->i_ctlr][io->i_unit]; | |
bff99b3e SL |
168 | register struct vddevice *vdaddr = VDADDR(ctlr); |
169 | int pass = 0, type = vdtype[ctlr], error; | |
7d68510b | 170 | int devflags = dkflags[ctlr][slave]; /* starts with 0 */ |
4006c5e1 | 171 | |
bff99b3e SL |
172 | again: |
173 | dcb.opcode = VDOP_CONFIG; /* command */ | |
174 | dcb.intflg = DCBINT_NONE; | |
175 | dcb.nxtdcb = (struct dcb *)0; /* end of chain */ | |
176 | dcb.operrsta = 0; | |
7d68510b | 177 | dcb.devselect = slave | devflags; |
bff99b3e SL |
178 | dcb.trail.rstrail.ncyl = lp->d_ncylinders; |
179 | dcb.trail.rstrail.nsurfaces = lp->d_ntracks; | |
180 | if (type == VDTYPE_SMDE) { | |
bdfd2fb7 | 181 | dcb.trailcnt = sizeof (struct treset) / sizeof (long); |
bff99b3e | 182 | dcb.trail.rstrail.nsectors = lp->d_nsectors; |
7d68510b MK |
183 | dcb.trail.rstrail.slip_sec = lp->d_trackskew; |
184 | dcb.trail.rstrail.recovery = VDRF_NORMAL; | |
bff99b3e SL |
185 | } else |
186 | dcb.trailcnt = 2; /* XXX */ | |
187 | mdcb.mdcb_head = &dcb; | |
188 | mdcb.mdcb_status = 0; | |
189 | VDGO(vdaddr, (u_long)&mdcb, type); | |
190 | if (!vdpoll(vdaddr, &dcb, 10, type)) { | |
191 | if (pass++ != 0) { | |
192 | printf(" during drive configuration.\n"); | |
193 | return (0); | |
194 | } | |
195 | VDRESET(vdaddr, type); | |
196 | if (error = vdreset_ctlr(ctlr, io->i_unit)) | |
197 | return (error); | |
198 | goto again; | |
39b72d60 | 199 | } |
7d68510b MK |
200 | if ((dcb.operrsta & VDERR_HARD) == 0) { /* success */ |
201 | dkflags[ctlr][slave] = devflags; | |
bff99b3e | 202 | return (1); |
7d68510b MK |
203 | } |
204 | if (devflags == 0) { | |
205 | devflags = VD_ESDI; | |
206 | goto again; | |
207 | } | |
bff99b3e SL |
208 | if (type == VDTYPE_SMDE && (vdaddr->vdstatus[slave] & STA_US) == 0) { |
209 | printf("dk%d: nonexistent drive\n", io->i_unit); | |
210 | return (0); | |
211 | } | |
212 | if ((dcb.operrsta & (DCBS_OCYL|DCBS_NRDY)) == 0) { | |
213 | vderror(io->i_unit, "config", &dcb); | |
214 | return (0); | |
215 | } | |
7d68510b | 216 | devflags = 0; |
bff99b3e SL |
217 | if (pass++) /* give up */ |
218 | return (0); | |
219 | /* | |
220 | * Try to spin up drive with remote command. | |
221 | */ | |
222 | if (!vdcmd(ctlr, 0, VDOP_START, 62)) { | |
223 | vderror(io->i_unit, "start", &dcb); | |
224 | return (0); | |
39b72d60 | 225 | } |
7f6fe1b4 | 226 | DELAY(62000000); |
bff99b3e | 227 | goto again; |
39b72d60 SL |
228 | } |
229 | ||
bff99b3e SL |
230 | vdcmd(ctlr, unit, cmd, time) |
231 | register int ctlr; | |
232 | int unit, cmd, time; | |
39b72d60 | 233 | { |
bff99b3e | 234 | register struct vddevice *vdaddr = VDADDR(ctlr); |
39b72d60 | 235 | |
bff99b3e SL |
236 | dcb.opcode = cmd; |
237 | dcb.intflg = DCBINT_NONE; | |
238 | dcb.nxtdcb = (struct dcb *)0; /* end of chain */ | |
39b72d60 | 239 | dcb.operrsta = 0; |
7d68510b | 240 | dcb.devselect = unit | dkflags[ctlr][unit]; |
bff99b3e SL |
241 | dcb.trailcnt = 0; |
242 | mdcb.mdcb_head = &dcb; | |
243 | mdcb.mdcb_status = 0; | |
244 | VDGO(vdaddr, (u_long)&mdcb, vdtype[ctlr]); | |
245 | if (!vdpoll(vdaddr, &dcb, time, vdtype[ctlr])) | |
246 | _stop(" during initialization operation.\n"); | |
247 | return ((dcb.operrsta & VDERR_HARD) == 0); | |
39b72d60 SL |
248 | } |
249 | ||
bff99b3e | 250 | vdstrategy(io, cmd) |
4006c5e1 | 251 | register struct iob *io; |
bff99b3e | 252 | int cmd; |
39b72d60 | 253 | { |
bff99b3e | 254 | register struct disklabel *lp; |
7d68510b | 255 | int ctlr, cn, tn, sn, slave, retries = 0; |
bff99b3e SL |
256 | daddr_t bn; |
257 | struct vddevice *vdaddr; | |
4006c5e1 SL |
258 | |
259 | if (io->i_cc == 0 || io->i_cc > 65535) { | |
260 | printf("dk%d: invalid transfer size %d\n", io->i_unit, | |
261 | io->i_cc); | |
d4285b3c | 262 | io->i_error = EIO; |
4006c5e1 | 263 | return (-1); |
39b72d60 | 264 | } |
cb69d88e | 265 | lp = &dklabel[io->i_ctlr][io->i_unit]; |
bff99b3e SL |
266 | bn = io->i_bn * (DEV_BSIZE / lp->d_secsize); |
267 | cn = bn / lp->d_secpercyl; | |
268 | sn = bn % lp->d_secpercyl; | |
269 | tn = sn / lp->d_nsectors; | |
270 | sn = sn % lp->d_nsectors; | |
39b72d60 | 271 | |
7d68510b | 272 | top: |
bff99b3e SL |
273 | dcb.opcode = (cmd == READ ? VDOP_RD : VDOP_WD); |
274 | dcb.intflg = DCBINT_NONE; | |
275 | dcb.nxtdcb = (struct dcb *)0; /* end of chain */ | |
39b72d60 | 276 | dcb.operrsta = 0; |
cb69d88e KB |
277 | ctlr = io->i_ctlr; |
278 | slave = io->i_unit; | |
7d68510b | 279 | dcb.devselect = slave | dkflags[ctlr][slave]; |
bdfd2fb7 MK |
280 | dcb.trailcnt = sizeof (struct trrw) / sizeof (int); |
281 | dcb.trail.rwtrail.memadr = (u_long)io->i_ma; | |
bff99b3e SL |
282 | dcb.trail.rwtrail.wcount = (io->i_cc + 1) / sizeof (short); |
283 | dcb.trail.rwtrail.disk.cylinder = cn; | |
284 | dcb.trail.rwtrail.disk.track = tn; | |
285 | dcb.trail.rwtrail.disk.sector = sn; | |
286 | mdcb.mdcb_head = &dcb; | |
287 | mdcb.mdcb_status = 0; | |
bff99b3e SL |
288 | vdaddr = VDADDR(ctlr); |
289 | VDGO(vdaddr, (u_long)&mdcb, vdtype[ctlr]); | |
290 | if (!vdpoll(vdaddr, &dcb, 60, vdtype[ctlr])) | |
4006c5e1 | 291 | _stop(" during i/o operation.\n"); |
bff99b3e | 292 | if (dcb.operrsta & VDERR_HARD) { |
7d68510b MK |
293 | if (retries++ == 0 && vdreset_ctlr(ctlr, io->i_unit) == 0 && |
294 | vdreset_drive(io)) | |
295 | goto top; | |
bff99b3e SL |
296 | vderror(io->i_unit, cmd == READ ? "read" : "write", &dcb); |
297 | io->i_error = EIO; | |
298 | return (-1); | |
299 | } | |
300 | mtpr(PADC, 0); | |
301 | return (io->i_cc); | |
39b72d60 SL |
302 | } |
303 | ||
bff99b3e SL |
304 | vderror(unit, cmd, dcb) |
305 | int unit; | |
306 | char *cmd; | |
307 | struct dcb *dcb; | |
39b72d60 | 308 | { |
4006c5e1 | 309 | |
bff99b3e SL |
310 | printf("dk%d: %s error; status %b", unit, cmd, |
311 | dcb->operrsta, VDERRBITS); | |
312 | if (dcb->err_code) | |
313 | printf(", code %x", dcb->err_code); | |
39b72d60 | 314 | printf("\n"); |
39b72d60 SL |
315 | } |
316 | ||
1a9cb196 | 317 | /* |
bff99b3e SL |
318 | * Poll controller until operation |
319 | * completes or timeout expires. | |
1a9cb196 | 320 | */ |
bff99b3e SL |
321 | vdpoll(vdaddr, dcb, t, type) |
322 | register struct vddevice *vdaddr; | |
323 | register struct dcb *dcb; | |
1a9cb196 SL |
324 | register int t, type; |
325 | { | |
326 | ||
327 | t *= 1000; | |
d4285b3c | 328 | for (;;) { |
1a9cb196 | 329 | uncache(&dcb->operrsta); |
bff99b3e | 330 | if (dcb->operrsta & (DCBS_DONE|DCBS_ABORT)) |
d4285b3c | 331 | break; |
1a9cb196 SL |
332 | if (--t <= 0) { |
333 | printf("vd: controller timeout"); | |
bff99b3e | 334 | VDABORT(vdaddr, type); |
1a9cb196 SL |
335 | DELAY(30000); |
336 | uncache(&dcb->operrsta); | |
337 | return (0); | |
338 | } | |
d4285b3c | 339 | DELAY(1000); |
1a9cb196 | 340 | } |
bff99b3e | 341 | if (type == VDTYPE_SMDE) { |
d4285b3c | 342 | for (;;) { |
bff99b3e SL |
343 | uncache(&vdaddr->vdcsr); |
344 | if ((vdaddr->vdcsr & CS_GO) == 0) | |
d4285b3c SL |
345 | break; |
346 | DELAY(50); | |
1a9cb196 SL |
347 | } |
348 | DELAY(300); | |
7f6fe1b4 | 349 | uncache(&dcb->err_code); |
1a9cb196 SL |
350 | } |
351 | DELAY(200); | |
352 | uncache(&dcb->operrsta); | |
353 | return (1); | |
354 | } | |
bff99b3e SL |
355 | |
356 | #ifdef COMPAT_42 | |
357 | struct dkcompat { | |
358 | int nsectors; /* sectors per track */ | |
359 | int ntracks; /* tracks per cylinder */ | |
360 | int ncylinders; /* cylinders per drive */ | |
7d68510b | 361 | int secsize; /* sector size */ |
bff99b3e SL |
362 | #define NPART 2 |
363 | int poff[NPART]; /* [a+b] for bootstrapping */ | |
364 | } dkcompat[] = { | |
ef960e6f | 365 | { 64, 20, 842, 512, 0, 61440 }, /* 2361a eagle */ |
7d68510b MK |
366 | { 48, 24, 711, 512, 0, 61056 }, /* xsd */ |
367 | { 44, 20, 842, 512, 0, 52800 }, /* eagle */ | |
368 | { 64, 10, 823, 512, 0, 38400 }, /* fuji 360 */ | |
369 | { 32, 24, 711, 512, 0, 40704 }, /* xfd */ | |
370 | { 32, 19, 823, 512, 0, 40128 }, /* smd */ | |
371 | { 32, 10, 823, 512, 0, 19200 }, /* fsd */ | |
372 | { 18, 15, 1224, 1024, 0, 21600 }, /* mxd */ | |
bff99b3e SL |
373 | }; |
374 | #define NDKCOMPAT (sizeof (dkcompat) / sizeof (dkcompat[0])) | |
375 | ||
376 | /* | |
377 | * Identify and configure drive from above table | |
378 | * by trying to read the last sector until a description | |
379 | * is found for which we're successful. | |
380 | */ | |
381 | vdmaptype(io) | |
382 | struct iob *io; | |
383 | { | |
cb69d88e | 384 | register struct disklabel *lp = &dklabel[io->i_ctlr][io->i_unit]; |
bff99b3e | 385 | register struct dkcompat *dp; |
7d68510b | 386 | int i, ctlr, slave, type; |
bff99b3e SL |
387 | struct vddevice *vdaddr; |
388 | ||
cb69d88e KB |
389 | ctlr = io->i_ctlr; |
390 | slave = io->i_unit; | |
bff99b3e SL |
391 | vdaddr = VDADDR(ctlr); |
392 | type = vdtype[ctlr]; | |
393 | for (dp = dkcompat; dp < &dkcompat[NDKCOMPAT]; dp++) { | |
394 | if (type == VDTYPE_VDDC && dp->nsectors != 32) | |
395 | continue; | |
396 | lp->d_nsectors = dp->nsectors; | |
397 | lp->d_ntracks = dp->ntracks; | |
398 | lp->d_ncylinders = dp->ncylinders; | |
7d68510b | 399 | lp->d_secsize = dp->secsize; |
bff99b3e SL |
400 | if (!vdreset_drive(io)) /* set drive parameters */ |
401 | return (EIO); | |
402 | dcb.opcode = VDOP_RD; | |
403 | dcb.intflg = DCBINT_NONE; | |
404 | dcb.nxtdcb = (struct dcb *)0; /* end of chain */ | |
7d68510b | 405 | dcb.devselect = slave | dkflags[ctlr][slave]; |
bff99b3e | 406 | dcb.operrsta = 0; |
bdfd2fb7 MK |
407 | dcb.trailcnt = sizeof (struct trrw) / sizeof (long); |
408 | dcb.trail.rwtrail.memadr = (u_long)lbuf; | |
7d68510b | 409 | dcb.trail.rwtrail.wcount = lp->d_secsize / sizeof (short); |
bff99b3e SL |
410 | dcb.trail.rwtrail.disk.cylinder = dp->ncylinders - 2; |
411 | dcb.trail.rwtrail.disk.track = dp->ntracks - 1; | |
412 | dcb.trail.rwtrail.disk.sector = dp->nsectors - 1; | |
413 | mdcb.mdcb_head = &dcb; | |
414 | mdcb.mdcb_status = 0; | |
415 | VDGO(vdaddr, (u_long)&mdcb, type); | |
416 | if (!vdpoll(vdaddr, &dcb, 60, type)) | |
417 | _stop(" during i/o operation.\n"); | |
418 | if (dcb.operrsta & VDERR_HARD) | |
419 | continue; | |
420 | /* simulate necessary parts of disk label */ | |
421 | lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; | |
422 | lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; | |
423 | lp->d_npartitions = NPART; | |
424 | for (i = 0; i < NPART; i++) { | |
425 | lp->d_partitions[i].p_offset = dp->poff[i]; | |
426 | lp->d_partitions[i].p_size = | |
427 | lp->d_secperunit - dp->poff[i]; | |
428 | } | |
429 | return (0); | |
430 | } | |
431 | printf("dk%d: unknown drive type\n", io->i_unit); | |
432 | return (ENXIO); | |
433 | } | |
434 | #endif |