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 */ | |
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 | 30 | u_short ubastd[] = { 0776700 }; |
0839bdeb | 31 | |
258a5c40 SL |
32 | char up_gottype[MAXNUBA*8] = { 0 }; |
33 | char up_type[MAXNUBA*8] = { 0 }; | |
258a5c40 SL |
34 | short up_off[] = { 0, 27, 68, -1, -1, -1, -1, 82 }; |
35 | short 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 */ | |
37 | short upam_off[] = { 0, 32, 0, 668, 723, 778, 668, 98 }; | |
0839bdeb | 38 | |
b8ca3ab9 | 39 | struct 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 |
45 | u_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 | 52 | struct dkbad upbad[MAXNUBA*8]; /* bad sector table */ |
b8ca3ab9 | 53 | int sectsiz; /* real sector size */ |
0839bdeb | 54 | |
258a5c40 SL |
55 | upopen(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 | ||
115 | upstrategy(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 | 143 | restart: |
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 | 186 | hard: |
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 | 282 | success: |
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 | 298 | upecc(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 | |
410 | upstart(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*/ |
461 | upioctl(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 | } |