Commit | Line | Data |
---|---|---|
df30aeb2 AM |
1 | /* |
2 | * Copyright (c) 1993 Steve Gerakines | |
3 | * | |
4 | * This is freely redistributable software. You may do anything you | |
5 | * wish with it, so long as the above notice stays intact. | |
6 | * | |
7 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS | |
8 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
9 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
10 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, | |
11 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
12 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
13 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
14 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
15 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
16 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
17 | * POSSIBILITY OF SUCH DAMAGE. | |
18 | * | |
19 | * ft.c - QIC-40/80 floppy tape driver | |
ee874879 | 20 | * $Id: ft.c,v 1.4 1994/02/14 22:24:28 nate Exp $ |
4af7e30c | 21 | * |
df30aeb2 AM |
22 | * |
23 | * 01/26/94 v0.3b - Jim Babb | |
24 | * Got rid of the hard coded device selection. Moved (some of) the | |
25 | * static variables into a structure for support of multiple devices. | |
26 | * ( still has a way to go for 2 controllers - but closer ) | |
27 | * Changed the interface with fd.c so we no longer 'steal' it's | |
28 | * driver routine vectors. | |
29 | * | |
30 | * 10/30/93 v0.3 | |
31 | * Fixed a couple more bugs. Reading was sometimes looping when an | |
32 | * an error such as address-mark-missing was encountered. Both | |
33 | * reading and writing was having more backup-and-retries than was | |
34 | * necessary. Added support to get hardware info. Updated for use | |
35 | * with FreeBSD. | |
36 | * | |
37 | * 09/15/93 v0.2 pl01 | |
38 | * Fixed a bunch of bugs: extra isa_dmadone() in async_write() (shouldn't | |
39 | * matter), fixed double buffering in async_req(), changed tape_end() in | |
40 | * set_fdcmode() to reduce unexpected interrupts, changed end of track | |
41 | * processing in async_req(), protected more of ftreq_rw() with an | |
42 | * splbio(). Changed some of the ftreq_*() functions so that they wait | |
43 | * for inactivity and then go, instead of aborting immediately. | |
44 | * | |
45 | * 08/07/93 v0.2 release | |
46 | * Shifted from ftstrat to ioctl support for I/O. Streaming is now much | |
47 | * more reliable. Added internal support for error correction, QIC-40, | |
48 | * and variable length tapes. Random access of segments greatly | |
49 | * improved. Formatting and verification support is close but still | |
50 | * incomplete. | |
51 | * | |
52 | * 06/03/93 v0.1 Alpha release | |
53 | * Hopefully the last re-write. Many bugs fixed, many remain. | |
54 | */ | |
55 | ||
56 | #include "ft.h" | |
57 | #if NFT > 0 | |
58 | #include "fd.h" | |
59 | ||
60 | #include <sys/param.h> | |
61 | #include <sys/dkbad.h> | |
62 | #include <sys/systm.h> | |
63 | #include <sys/conf.h> | |
64 | #include <sys/file.h> | |
65 | #include <sys/ioctl.h> | |
66 | #include <sys/malloc.h> | |
67 | #include <sys/buf.h> | |
68 | #include <sys/uio.h> | |
69 | #include <sys/ftape.h> | |
70 | #include <machine/pio.h> | |
71 | #include "i386/isa/isa_device.h" | |
72 | #include "i386/isa/fdreg.h" | |
c2ab4ad1 | 73 | #include "i386/isa/fdc.h" |
df30aeb2 AM |
74 | #include "i386/isa/icu.h" |
75 | #include "i386/isa/rtc.h" | |
76 | #include "ftreg.h" | |
77 | ||
78 | /* Enable or disable debugging messages. */ | |
79 | #define FTDBGALL 0 /* everything */ | |
80 | /* #define DPRT(a) printf a */ | |
81 | #define DPRT(a) | |
82 | ||
83 | /* Constants private to the driver */ | |
84 | #define FTPRI (PRIBIO) /* sleep priority */ | |
85 | ||
86 | /* The following items are needed from the fd driver. */ | |
87 | extern int in_fdc(int); /* read fdc registers */ | |
88 | extern int out_fdc(int, int); /* write fdc registers */ | |
89 | ||
90 | extern int hz; /* system clock rate */ | |
91 | ||
92 | /* Type of tape attached */ | |
93 | /* use numbers that don't interfere with the possible floppy types */ | |
94 | #define NO_TYPE 0 /* (same as NO_TYPE in fd.c) */ | |
95 | /* F_TAPE_TYPE must match value in fd.c */ | |
96 | #define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */ | |
97 | #define FT_MOUNTAIN (F_TAPE_TYPE | 1) | |
98 | #define FT_COLORADO (F_TAPE_TYPE | 2) | |
99 | ||
100 | ||
101 | /* Mode FDC is currently in: tape or disk */ | |
102 | enum { FDC_TAPE_MODE, FDC_DISK_MODE }; | |
103 | ||
104 | /* Command we are awaiting completion of */ | |
105 | enum { FTCMD_NONE, FTCMD_RESET, FTCMD_RECAL, FTCMD_SEEK, FTCMD_READID }; | |
106 | ||
107 | /* Tape interrupt status of current request */ | |
108 | enum { FTSTS_NONE, FTSTS_SNOOZE, FTSTS_INTERRUPT, FTSTS_TIMEOUT }; | |
109 | ||
110 | /* Tape I/O status */ | |
111 | enum { | |
112 | FTIO_READY, /* No I/O activity */ | |
113 | FTIO_READING, /* Currently reading blocks */ | |
114 | FTIO_RDAHEAD, /* Currently reading ahead */ | |
115 | FTIO_WRITING /* Buffers are being written */ | |
116 | }; | |
117 | ||
118 | /* Current tape mode */ | |
119 | enum { | |
120 | FTM_PRIMARY, /* Primary mode */ | |
121 | FTM_VERIFY, /* Verify mode */ | |
122 | FTM_FORMAT, /* Format mode */ | |
123 | FTM_DIAG1, /* Diagnostic mode 1 */ | |
124 | FTM_DIAG2 /* Diagnostic mode 2 */ | |
125 | }; | |
126 | ||
127 | /* Tape geometries table */ | |
128 | QIC_Geom ftgtbl[] = { | |
129 | { 0, 0, "Unformatted", "Unknown", 0, 0, 0, 0, 0 }, /* XXX */ | |
130 | { 1, 1, "QIC-40", "205/550", 20, 68, 2176, 128, 21760 }, | |
131 | { 1, 2, "QIC-40", "307.5/550", 20, 102, 3264, 128, 32640 }, | |
132 | { 1, 3, "QIC-40", "295/900", 0, 0, 0, 0, 0 }, /* ??? */ | |
133 | { 1, 4, "QIC-40", "1100/550", 20, 365, 11680, 128, 32512 }, | |
134 | { 1, 5, "QIC-40", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */ | |
135 | { 2, 1, "QIC-80", "205/550", 28, 100, 3200, 128, 19200 }, | |
136 | { 2, 2, "QIC-80", "307.5/550", 28, 150, 4800, 128, 19200 }, | |
137 | { 2, 3, "QIC-80", "295/900", 0, 0, 0, 0, 0 }, /* ??? */ | |
138 | { 2, 4, "QIC-80", "1100/550", 28, 537, 17184, 128, 32512 }, | |
139 | { 2, 5, "QIC-80", "1100/900", 0, 0, 0, 0, 0 }, /* ??? */ | |
140 | { 3, 1, "QIC-500", "205/550", 0, 0, 0, 0, 0 }, /* ??? */ | |
141 | { 3, 2, "QIC-500", "307.5/550", 0, 0, 0, 0, 0 }, /* ??? */ | |
142 | { 3, 3, "QIC-500", "295/900", 0, 0, 0, 0, 0 }, /* ??? */ | |
143 | { 3, 4, "QIC-500", "1100/550", 0, 0, 0, 0, 0 }, /* ??? */ | |
144 | { 3, 5, "QIC-500", "1100/900", 0, 0, 0, 0, 0 } /* ??? */ | |
145 | }; | |
146 | #define NGEOM (sizeof(ftgtbl) / sizeof(QIC_Geom)) | |
147 | ||
148 | QIC_Geom *ftg = NULL; /* Current tape's geometry */ | |
149 | ||
150 | /* | |
151 | * things relating to asynchronous commands | |
152 | */ | |
153 | static int astk_depth; /* async_cmd stack depth */ | |
154 | static int awr_state; /* state of async write */ | |
155 | static int ard_state; /* state of async read */ | |
156 | static int arq_state; /* state of async request */ | |
157 | static int async_retries; /* retries, one per invocation */ | |
158 | static int async_func; /* function to perform */ | |
159 | static int async_state; /* state current function is at */ | |
160 | static int async_arg[5]; /* up to 5 arguments for async cmds */ | |
161 | static int async_ret; /* return value */ | |
162 | ||
163 | /* List of valid async (interrupt driven) tape support functions. */ | |
164 | enum { | |
165 | ACMD_NONE, /* no command */ | |
166 | ACMD_SEEK, /* command seek */ | |
167 | ACMD_STATUS, /* report status */ | |
168 | ACMD_STATE, /* wait for state bits to be true */ | |
169 | ACMD_SEEKSTS, /* perform command and wait for status */ | |
170 | ACMD_READID, /* read id */ | |
171 | ACMD_RUNBLK /* ready tape for I/O on the given block */ | |
172 | }; | |
173 | ||
174 | /* Call another asyncronous command from within async_cmd(). */ | |
175 | #define CALL_ACMD(r,f,a,b,c,d,e) \ | |
176 | astk[astk_depth].over_retries = async_retries; \ | |
177 | astk[astk_depth].over_func = async_func; \ | |
178 | astk[astk_depth].over_state = (r); \ | |
179 | for (i = 0; i < 5; i++) \ | |
180 | astk[astk_depth].over_arg[i] = async_arg[i]; \ | |
181 | async_func = (f); async_state = 0; async_retries = 0; \ | |
182 | async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \ | |
183 | async_arg[3]=(d); async_arg[4]=(e); \ | |
184 | astk_depth++; \ | |
185 | goto restate | |
186 | ||
187 | /* Perform an asyncronous command from outside async_cmd(). */ | |
188 | #define ACMD_FUNC(r,f,a,b,c,d,e) over_async = (r); astk_depth = 0; \ | |
189 | async_func = (f); async_state = 0; async_retries = 0; \ | |
190 | async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \ | |
191 | async_arg[3]=(d); async_arg[4]=(e); \ | |
192 | async_cmd(ftu); \ | |
193 | return | |
194 | ||
195 | /* Various wait channels */ | |
196 | static struct { | |
197 | int buff_avail; | |
198 | int iosts_change; | |
199 | int long_delay; | |
200 | int intr_wait; | |
201 | } ftsem; | |
202 | ||
203 | /***********************************************************************\ | |
204 | * Per controller structure. * | |
205 | \***********************************************************************/ | |
206 | extern struct fdc_data fdc_data[NFDC]; | |
207 | ||
208 | /***********************************************************************\ | |
209 | * Per tape drive structure. * | |
210 | \***********************************************************************/ | |
211 | struct ft_data { | |
212 | struct fdc_data *fdc; /* pointer to controller structure */ | |
213 | int ftsu; /* this units number on this controller */ | |
214 | int type; /* Drive type (Mountain, Colorado) */ | |
215 | /* QIC_Geom *ftg; */ /* pointer to Current tape's geometry */ | |
216 | int flags; | |
217 | int cmd_wait; /* Command we are awaiting completion of */ | |
218 | int sts_wait; /* Tape interrupt status of current request */ | |
219 | int io_sts; /* Tape I/O status */ | |
220 | int mode; | |
221 | int pcn; /* present cylinder number */ | |
222 | int attaching; /* true when ft is attaching */ | |
223 | unsigned char *xptr; /* pointer to buffer blk to xfer */ | |
224 | int xcnt; /* transfer count */ | |
225 | int xblk; /* block number to transfer */ | |
226 | SegReq *curseg; /* Current segment to do I/O on */ | |
227 | SegReq *bufseg; /* Buffered segment to r/w ahead */ | |
228 | /* the next 3 should be defines in 'flags' */ | |
229 | int active; /* TRUE if transfer is active */ | |
230 | int rdonly; /* TRUE if tape is read-only */ | |
231 | int newcart; /* TRUE if new cartridge detected */ | |
232 | int laststs; /* last reported status code */ | |
233 | int lastcfg; /* last reported QIC config */ | |
234 | int lasterr; /* last QIC error code */ | |
235 | int lastpos; /* last known segment number */ | |
236 | int moving; /* TRUE if tape is moving */ | |
237 | int rid[7]; /* read_id return values */ | |
238 | ||
239 | } ft_data[NFT]; | |
240 | ||
241 | /***********************************************************************\ | |
242 | * Throughout this file the following conventions will be used: * | |
243 | * ft is a pointer to the ft_data struct for the drive in question * | |
244 | * fdc is a pointer to the fdc_data struct for the controller * | |
245 | * ftu is the tape drive unit number * | |
246 | * fdcu is the floppy controller unit number * | |
247 | * ftsu is the tape drive unit number on that controller. (sub-unit) * | |
248 | \***********************************************************************/ | |
249 | ||
250 | ||
251 | typedef int ftu_t; | |
252 | typedef int ftsu_t; | |
253 | typedef struct ft_data *ft_p; | |
254 | ||
255 | #define id_physid id_scsiid /* this biotab field doubles as a field */ | |
256 | /* for the physical unit number on the controller */ | |
257 | ||
258 | int ftopen(dev_t, int); | |
259 | int ftclose(dev_t, int); | |
260 | void ftstrategy(struct buf *); | |
261 | int ftioctl(dev_t, int, caddr_t, int, struct proc *); | |
262 | int ftdump(dev_t); | |
263 | int ftsize(dev_t); | |
264 | static void ft_timeout(caddr_t arg1, int arg2); | |
265 | void async_cmd(ftu_t); | |
266 | void async_req(ftu_t, int); | |
267 | void async_read(ftu_t, int); | |
268 | void async_write(ftu_t, int); | |
269 | void tape_start(ftu_t); | |
270 | void tape_end(ftu_t); | |
271 | void tape_inactive(ftu_t); | |
272 | ||
273 | ||
274 | ||
275 | ||
276 | ||
277 | /* | |
278 | * Probe/attach floppy tapes. | |
279 | */ | |
280 | int ftattach(isadev, fdup) | |
281 | struct isa_device *isadev, *fdup; | |
282 | { | |
283 | fdcu_t fdcu = isadev->id_unit; /* fdc active unit */ | |
284 | fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */ | |
285 | ftu_t ftu = fdup->id_unit; | |
c2ab4ad1 | 286 | ft_p ft; |
df30aeb2 AM |
287 | ftsu_t ftsu = fdup->id_physid; |
288 | ||
c2ab4ad1 AM |
289 | if (ftu >= NFT) |
290 | return 0; | |
291 | ft = &ft_data[ftu]; | |
df30aeb2 AM |
292 | /* Probe for tape */ |
293 | ft->attaching = 1; | |
294 | ft->type = NO_TYPE; | |
295 | ft->fdc = fdc; | |
296 | ft->ftsu = ftsu; | |
297 | ||
298 | tape_start(ftu); /* ready controller for tape */ | |
299 | tape_cmd(ftu, QC_COL_ENABLE1); | |
300 | tape_cmd(ftu, QC_COL_ENABLE2); | |
301 | if (tape_status(ftu) >= 0) { | |
302 | ft->type = FT_COLORADO; | |
303 | fdc->flags |= FDC_HASFTAPE; | |
304 | printf(" [%d: ft%d: Colorado tape]", | |
305 | fdup->id_physid, fdup->id_unit ); | |
306 | tape_cmd(ftu, QC_COL_DISABLE); | |
307 | goto out; | |
308 | } | |
309 | ||
310 | tape_start(ftu); /* ready controller for tape */ | |
311 | tape_cmd(ftu, QC_MTN_ENABLE1); | |
312 | tape_cmd(ftu, QC_MTN_ENABLE2); | |
313 | if (tape_status(ftu) >= 0) { | |
314 | ft->type = FT_MOUNTAIN; | |
315 | fdc->flags |= FDC_HASFTAPE; | |
316 | printf(" [%d: ft%d: Mountain tape]", | |
317 | fdup->id_physid, fdup->id_unit ); | |
318 | tape_cmd(ftu, QC_MTN_DISABLE); | |
319 | goto out; | |
320 | } | |
321 | ||
322 | out: | |
323 | tape_end(ftu); | |
324 | ft->attaching = 0; | |
325 | return(ft->type); | |
326 | } | |
327 | ||
328 | ||
329 | /* | |
330 | * Perform common commands asynchronously. | |
331 | */ | |
332 | void async_cmd(ftu_t ftu) { | |
333 | ft_p ft = &ft_data[ftu]; | |
334 | fdcu_t fdcu = ft->fdc->fdcu; | |
335 | int cmd, i, st0, st3, pcn; | |
336 | static int bitn, retval, retpos, nbits, newcn; | |
337 | static struct { | |
338 | int over_func; | |
339 | int over_state; | |
340 | int over_retries; | |
341 | int over_arg[5]; | |
342 | } astk[15]; | |
343 | static int wanttrk, wantblk, wantdir; | |
344 | static int curpos, curtrk, curblk, curdir, curdiff; | |
345 | static int errcnt = 0; | |
346 | ||
347 | restate: | |
348 | #if FTDBGALL | |
349 | DPRT(("async_cmd state: func: %d state: %d\n", async_func, async_state)); | |
350 | #endif | |
351 | switch(async_func) { | |
352 | case ACMD_SEEK: | |
353 | /* | |
354 | * Arguments: | |
355 | * 0 - command to perform | |
356 | */ | |
357 | switch (async_state) { | |
358 | case 0: | |
359 | cmd = async_arg[0]; | |
360 | #if FTDBGALL | |
361 | DPRT(("===>async_seek cmd = %d\n", cmd)); | |
362 | #endif | |
363 | newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd; | |
364 | async_state = 1; | |
365 | i = 0; | |
366 | if (out_fdc(fdcu, NE7CMD_SEEK) < 0) i = 1; | |
367 | if (!i && out_fdc(fdcu, 0x00) < 0) i = 1; | |
368 | if (!i && out_fdc(fdcu, newcn) < 0) i = 1; | |
369 | if (i) { | |
370 | if (++async_retries >= 10) { | |
371 | DPRT(("ft%d: async_cmd command seek failed!!\n", ftu)); | |
372 | goto complete; | |
373 | } | |
374 | DPRT(("ft%d: async_cmd command seek retry...\n",ftu)); | |
375 | async_state = 0; | |
376 | goto restate; | |
377 | } | |
378 | break; | |
379 | case 1: | |
380 | out_fdc(fdcu, NE7CMD_SENSEI); | |
381 | st0 = in_fdc(fdcu); | |
382 | pcn = in_fdc(fdcu); | |
383 | if (st0 < 0 || pcn < 0 || newcn != pcn) { | |
384 | if (++async_retries >= 10) { | |
385 | DPRT(("ft%d: async_cmd seek retries exceeded\n",ftu)); | |
386 | goto complete; | |
387 | } | |
388 | DPRT(("ft%d: async_cmd command bad st0=$%02x pcn=$%02x\n", | |
389 | ftu, st0, pcn)); | |
390 | async_state = 0; | |
391 | timeout(ft_timeout, (caddr_t)ftu, hz/10); | |
392 | break; | |
393 | } | |
394 | if (st0 & 0x20) { /* seek done */ | |
395 | ft->pcn = pcn; | |
396 | } | |
397 | #if FTDBGALL | |
398 | else | |
399 | DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n", | |
400 | ftu, st0, pcn)); | |
401 | #endif | |
402 | if (async_arg[1]) goto complete; | |
403 | async_state = 2; | |
404 | timeout(ft_timeout, (caddr_t)ftu, hz/50); | |
405 | break; | |
406 | case 2: | |
407 | goto complete; | |
408 | /* NOTREACHED */ | |
409 | } | |
410 | break; | |
411 | ||
412 | case ACMD_STATUS: | |
413 | /* | |
414 | * Arguments: | |
415 | * 0 - command to issue report from | |
416 | * 1 - number of bits | |
417 | * modifies: bitn, retval, st3 | |
418 | */ | |
419 | switch (async_state) { | |
420 | case 0: | |
421 | bitn = 0; | |
422 | retval = 0; | |
423 | cmd = async_arg[0]; | |
424 | nbits = async_arg[1]; | |
425 | DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits)); | |
426 | CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); | |
427 | /* NOTREACHED */ | |
428 | case 1: | |
429 | out_fdc(fdcu, NE7CMD_SENSED); | |
430 | out_fdc(fdcu, 0x00); | |
431 | st3 = in_fdc(fdcu); | |
432 | if (st3 < 0) { | |
433 | DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n", | |
434 | ftu,bitn,retval)); | |
435 | async_ret = -1; | |
436 | goto complete; | |
437 | } | |
438 | if ((st3 & 0x10) != 0) retval |= (1 << bitn); | |
439 | bitn++; | |
440 | if (bitn >= (nbits+2)) { | |
441 | if ((retval & 1) && (retval & (1 << (nbits+1)))) { | |
442 | async_ret = (retval & ~(1<<(nbits+1))) >> 1; | |
443 | if (async_arg[0] == QC_STATUS && async_arg[2] == 0 && | |
444 | (async_ret & (QS_ERROR|QS_NEWCART))) { | |
445 | async_state = 2; | |
446 | goto restate; | |
447 | } | |
448 | DPRT(("async status got $%04x ($%04x)\n", async_ret,retval)); | |
449 | } else { | |
450 | DPRT(("ft%d: async_status failed: retval=$%04x nbits=%d\n", | |
451 | ftu, retval,nbits)); | |
452 | async_ret = -2; | |
453 | } | |
454 | goto complete; | |
455 | } | |
456 | CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); | |
457 | /* NOTREACHED */ | |
458 | case 2: | |
459 | if (async_ret & QS_NEWCART) ft->newcart = 1; | |
460 | CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1, 0, 0); | |
461 | case 3: | |
462 | ft->lasterr = async_ret; | |
463 | if ((ft->lasterr & QS_NEWCART) == 0 && ft->lasterr) { | |
464 | DPRT(("ft%d: QIC error %d occurred on cmd %d\n", | |
465 | ftu, ft->lasterr & 0xff, ft->lasterr >> 8)); | |
466 | } | |
467 | cmd = async_arg[0]; | |
468 | nbits = async_arg[1]; | |
469 | CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1, 0, 0); | |
470 | case 4: | |
471 | goto complete; | |
472 | case 5: | |
473 | CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); | |
474 | case 6: | |
475 | CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); | |
476 | case 7: | |
477 | CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); | |
478 | case 8: | |
479 | cmd = async_arg[0]; | |
480 | CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0); | |
481 | } | |
482 | break; | |
483 | ||
484 | case ACMD_STATE: | |
485 | /* | |
486 | * Arguments: | |
487 | * 0 - status bits to check | |
488 | */ | |
489 | switch(async_state) { | |
490 | case 0: | |
491 | CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); | |
492 | case 1: | |
493 | if ((async_ret & async_arg[0]) != 0) goto complete; | |
494 | async_state = 0; | |
495 | if (++async_retries == 360) { /* 90 secs. */ | |
496 | DPRT(("ft%d: acmd_state exceeded retry count\n", ftu)); | |
497 | goto complete; | |
498 | } | |
499 | timeout(ft_timeout, (caddr_t)ftu, hz/4); | |
500 | break; | |
501 | } | |
502 | break; | |
503 | ||
504 | case ACMD_SEEKSTS: | |
505 | /* | |
506 | * Arguments: | |
507 | * 0 - command to perform | |
508 | * 1 - status bits to check | |
509 | * 2 - (optional) seconds to wait until completion | |
510 | */ | |
511 | switch(async_state) { | |
512 | case 0: | |
513 | cmd = async_arg[0]; | |
514 | async_retries = (async_arg[2]) ? (async_arg[2]*4) : 10; | |
515 | CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0); | |
516 | case 1: | |
517 | CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); | |
518 | case 2: | |
519 | if ((async_ret & async_arg[1]) != 0) goto complete; | |
520 | if (--async_retries == 0) { | |
521 | DPRT(("ft%d: acmd_seeksts retries exceeded\n", ftu)); | |
522 | goto complete; | |
523 | } | |
524 | async_state = 1; | |
525 | timeout(ft_timeout, (caddr_t)ftu, hz/4); | |
526 | break; | |
527 | } | |
528 | break; | |
529 | ||
530 | case ACMD_READID: | |
531 | /* | |
532 | * Arguments: (none) | |
533 | */ | |
534 | switch(async_state) { | |
535 | case 0: | |
536 | if (!ft->moving) { | |
537 | CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); | |
538 | /* NOTREACHED */ | |
539 | } | |
540 | async_state = 1; | |
541 | out_fdc(fdcu, 0x4a); /* READ_ID */ | |
542 | out_fdc(fdcu, 0); | |
543 | break; | |
544 | case 1: | |
545 | for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu); | |
546 | async_ret = (ft->rid[3]*ftg->g_fdtrk) + | |
547 | (ft->rid[4]*ftg->g_fdside) + ft->rid[5] - 1; | |
548 | DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n", | |
549 | ft->rid[0], ft->rid[1], ft->rid[2], ft->rid[3], | |
550 | ft->rid[4], ft->rid[5], async_ret)); | |
551 | if ((ft->rid[0] & 0xc0) == 0x40) { | |
552 | if (++errcnt >= 10) { | |
553 | DPRT(("ft%d: acmd_readid errcnt exceeded\n", fdcu)); | |
554 | async_ret = ft->lastpos; | |
555 | errcnt = 0; | |
556 | goto complete; | |
557 | } | |
558 | if (errcnt > 2) { | |
559 | ft->moving = 0; | |
560 | CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); | |
561 | } | |
562 | DPRT(("readid retry...\n")); | |
563 | async_state = 0; | |
564 | goto restate; | |
565 | } | |
566 | if ((async_ret % ftg->g_blktrk) == (ftg->g_blktrk-1)) { | |
567 | DPRT(("acmd_readid detected last block on track\n")); | |
568 | retpos = async_ret; | |
569 | CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0); | |
570 | /* NOTREACHED */ | |
571 | } | |
572 | ft->lastpos = async_ret; | |
573 | errcnt = 0; | |
574 | goto complete; | |
575 | /* NOTREACHED */ | |
576 | case 2: | |
577 | CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); | |
578 | case 3: | |
579 | ft->moving = 0; | |
580 | async_ret = retpos+1; | |
581 | goto complete; | |
582 | case 4: | |
583 | CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); | |
584 | case 5: | |
585 | ft->moving = 1; | |
586 | async_state = 0; | |
587 | timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */ | |
588 | break; | |
589 | } | |
590 | break; | |
591 | ||
592 | case ACMD_RUNBLK: | |
593 | /* | |
594 | * Arguments: | |
595 | * 0 - block number I/O will be performed on | |
596 | * | |
597 | * modifies: curpos | |
598 | */ | |
599 | switch (async_state) { | |
600 | case 0: | |
601 | wanttrk = async_arg[0] / ftg->g_blktrk; | |
602 | wantblk = async_arg[0] % ftg->g_blktrk; | |
603 | wantdir = wanttrk & 1; | |
604 | ft->moving = 0; | |
605 | CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); | |
606 | case 1: | |
607 | curtrk = wanttrk; | |
608 | curdir = curtrk & 1; | |
609 | DPRT(("Changing to track %d\n", wanttrk)); | |
610 | CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0); | |
611 | case 2: | |
612 | cmd = wanttrk+2; | |
613 | CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); | |
614 | case 3: | |
615 | CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); | |
616 | case 4: | |
617 | ft->laststs = async_ret; | |
618 | if (wantblk == 0) { | |
619 | curblk = 0; | |
620 | cmd = (wantdir) ? QC_SEEKEND : QC_SEEKSTART; | |
621 | CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90, 0, 0); | |
622 | } | |
623 | if (ft->laststs & QS_BOT) { | |
624 | DPRT(("Tape is at BOT\n")); | |
625 | curblk = (wantdir) ? 4800 : 0; | |
626 | async_state = 6; | |
627 | goto restate; | |
628 | } | |
629 | if (ft->laststs & QS_EOT) { | |
630 | DPRT(("Tape is at EOT\n")); | |
631 | curblk = (wantdir) ? 0 : 4800; | |
632 | async_state = 6; | |
633 | goto restate; | |
634 | } | |
635 | CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); | |
636 | case 5: | |
637 | curtrk = (async_ret+1) / ftg->g_blktrk; | |
638 | curblk = (async_ret+1) % ftg->g_blktrk; | |
639 | DPRT(("gotid: curtrk=%d wanttrk=%d curblk=%d wantblk=%d\n", | |
640 | curtrk, wanttrk, curblk, wantblk)); | |
641 | if (curtrk != wanttrk) { /* oops! */ | |
642 | DPRT(("oops!! wrong track!\n")); | |
643 | CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); | |
644 | } | |
645 | async_state = 6; | |
646 | goto restate; | |
647 | case 6: | |
648 | DPRT(("curtrk = %d nextblk = %d\n", curtrk, curblk)); | |
649 | if (curblk == wantblk) { | |
650 | ft->lastpos = curblk - 1; | |
651 | async_ret = ft->lastpos; | |
652 | if (ft->moving) goto complete; | |
653 | CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0, 0, 0); | |
654 | } | |
655 | if (curblk > wantblk) { /* passed it */ | |
656 | ft->moving = 0; | |
657 | CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); | |
658 | } | |
659 | if ((wantblk - curblk) <= 96) { /* approaching it */ | |
660 | CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); | |
661 | } | |
662 | /* way up ahead */ | |
663 | ft->moving = 0; | |
664 | CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); | |
665 | break; | |
666 | case 7: | |
667 | ft->moving = 1; | |
668 | CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); | |
669 | break; | |
670 | case 8: | |
671 | async_state = 9; | |
672 | timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */ | |
673 | break; | |
674 | case 9: | |
675 | goto complete; | |
676 | case 10: | |
677 | curdiff = ((curblk - wantblk) / QCV_BLKSEG) + 2; | |
678 | if (curdiff >= ftg->g_segtrk) curdiff = ftg->g_segtrk - 1; | |
679 | DPRT(("pos %d past %d, reverse %d\n", curblk, wantblk, curdiff)); | |
680 | CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0, 0, 0); | |
681 | case 11: | |
682 | DPRT(("reverse 1 done\n")); | |
683 | CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0); | |
684 | case 12: | |
685 | DPRT(("reverse 2 done\n")); | |
686 | CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0); | |
687 | case 13: | |
688 | CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); | |
689 | case 14: | |
690 | curdiff = ((wantblk - curblk) / QCV_BLKSEG) - 2; | |
691 | if (curdiff < 0) curdiff = 0; | |
692 | DPRT(("pos %d before %d, forward %d\n", curblk, wantblk, curdiff)); | |
693 | CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0, 0, 0); | |
694 | case 15: | |
695 | DPRT(("forward 1 done\n")); | |
696 | CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0); | |
697 | case 16: | |
698 | DPRT(("forward 2 done\n")); | |
699 | CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0); | |
700 | } | |
701 | break; | |
702 | } | |
703 | ||
704 | return; | |
705 | ||
706 | complete: | |
707 | if (astk_depth) { | |
708 | astk_depth--; | |
709 | async_retries = astk[astk_depth].over_retries; | |
710 | async_func = astk[astk_depth].over_func; | |
711 | async_state = astk[astk_depth].over_state; | |
712 | for(i = 0; i < 5; i++) | |
713 | async_arg[i] = astk[astk_depth].over_arg[i]; | |
714 | goto restate; | |
715 | } | |
716 | async_func = ACMD_NONE; | |
717 | async_state = 0; | |
718 | switch (ft->io_sts) { | |
719 | case FTIO_READY: | |
720 | async_req(ftu, 2); | |
721 | break; | |
722 | case FTIO_READING: | |
723 | async_read(ftu, 2); | |
724 | break; | |
725 | case FTIO_WRITING: | |
726 | async_write(ftu, 2); | |
727 | break; | |
728 | default: | |
729 | DPRT(("ft%d: bad async_cmd ending I/O state!\n", ftu)); | |
730 | break; | |
731 | } | |
732 | } | |
733 | ||
734 | ||
735 | /* | |
736 | * Entry point for the async request processor. | |
737 | */ | |
738 | void async_req(ftu_t ftu, int from) | |
739 | { | |
740 | ft_p ft = &ft_data[ftu]; | |
741 | SegReq *sp; | |
742 | static int over_async, lastreq, domore; | |
743 | int cmd; | |
744 | ||
745 | if (from == 2) arq_state = over_async; | |
746 | ||
747 | restate: | |
748 | switch (arq_state) { | |
749 | case 0: /* Process segment */ | |
750 | ft->io_sts = ft->curseg->reqtype; | |
751 | if (ft->io_sts == FTIO_WRITING) | |
752 | async_write(ftu, from); | |
753 | else | |
754 | async_read(ftu, from); | |
755 | if (ft->io_sts != FTIO_READY) return; | |
756 | ||
757 | /* Swap buffered and current segment */ | |
758 | lastreq = ft->curseg->reqtype; | |
759 | ft->curseg->reqtype = FTIO_READY; | |
760 | sp = ft->curseg; | |
761 | ft->curseg = ft->bufseg; | |
762 | ft->bufseg = sp; | |
763 | ||
764 | wakeup((caddr_t)&ftsem.buff_avail); | |
765 | ||
766 | /* Detect end of track */ | |
767 | if (((ft->xblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) { | |
768 | domore = (ft->curseg->reqtype != FTIO_READY); | |
769 | ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0); | |
770 | } | |
771 | arq_state = 1; | |
772 | goto restate; | |
773 | ||
774 | case 1: /* Next request */ | |
775 | if (ft->curseg->reqtype != FTIO_READY) { | |
776 | ft->curseg->reqcrc = 0; | |
777 | arq_state = ard_state = awr_state = 0; | |
778 | ft->xblk = ft->curseg->reqblk; | |
779 | ft->xcnt = 0; | |
780 | ft->xptr = ft->curseg->buff; | |
781 | DPRT(("I/O reqblk = %d\n", ft->curseg->reqblk)); | |
782 | goto restate; | |
783 | } | |
784 | if (lastreq == FTIO_READING) { | |
785 | ft->curseg->reqtype = FTIO_RDAHEAD; | |
786 | ft->curseg->reqblk = ft->xblk; | |
787 | ft->curseg->reqcrc = 0; | |
788 | ft->curseg->reqcan = 0; | |
789 | bzero(ft->curseg->buff, QCV_SEGSIZE); | |
790 | arq_state = ard_state = awr_state = 0; | |
791 | ft->xblk = ft->curseg->reqblk; | |
792 | ft->xcnt = 0; | |
793 | ft->xptr = ft->curseg->buff; | |
794 | DPRT(("Processing readahead reqblk = %d\n", ft->curseg->reqblk)); | |
795 | goto restate; | |
796 | } | |
797 | if (ft->moving) { | |
798 | DPRT(("No more I/O.. Stopping.\n")); | |
799 | ACMD_FUNC(7, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); | |
800 | break; | |
801 | } | |
802 | arq_state = 7; | |
803 | goto restate; | |
804 | ||
805 | case 2: /* End of track */ | |
806 | ft->moving = 0; | |
807 | ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); | |
808 | break; | |
809 | ||
810 | case 3: | |
811 | DPRT(("async_req seek head to track %d\n", ft->xblk / ftg->g_blktrk)); | |
812 | ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0); | |
813 | break; | |
814 | ||
815 | case 4: | |
816 | cmd = (ft->xblk / ftg->g_blktrk) + 2; | |
817 | if (domore) { | |
818 | ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); | |
819 | } else { | |
820 | ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); | |
821 | } | |
822 | break; | |
823 | ||
824 | case 5: | |
825 | ft->moving = 1; | |
826 | ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); | |
827 | break; | |
828 | ||
829 | case 6: | |
830 | arq_state = 1; | |
831 | timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */ | |
832 | break; | |
833 | ||
834 | case 7: | |
835 | ft->moving = 0; | |
836 | ||
837 | /* Check one last time to see if a request came in. */ | |
838 | if (ft->curseg->reqtype != FTIO_READY) { | |
839 | DPRT(("async_req: Never say no!\n")); | |
840 | arq_state = 1; | |
841 | goto restate; | |
842 | } | |
843 | ||
844 | /* Time to rest. */ | |
845 | ft->active = 0; | |
846 | wakeup((caddr_t)&ftsem.iosts_change); /* wakeup those who want an i/o chg */ | |
847 | break; | |
848 | } | |
849 | } | |
850 | ||
851 | /* | |
852 | * Entry for async read. | |
853 | */ | |
854 | void async_read(ftu_t ftu, int from) | |
855 | { | |
856 | ft_p ft = &ft_data[ftu]; | |
857 | fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ | |
858 | unsigned long paddr; | |
859 | int i, cmd, newcn, rddta[7]; | |
860 | int st0, pcn, where; | |
861 | static int over_async; | |
862 | ||
863 | if (from == 2) ard_state = over_async; | |
864 | ||
865 | restate: | |
866 | #if FTDBGALL | |
867 | DPRT(("async_read: state: %d from = %d\n", ard_state, from)); | |
868 | #endif | |
869 | switch (ard_state) { | |
870 | case 0: /* Start off */ | |
871 | /* If tape is not at desired position, stop and locate */ | |
872 | if (ft->lastpos != (ft->xblk-1)) { | |
873 | DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n", | |
874 | ftu, ft->lastpos, ft->xblk)); | |
875 | ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0); | |
876 | } | |
877 | ||
878 | /* Tape is in position but stopped. */ | |
879 | if (!ft->moving) { | |
880 | DPRT(("async_read ******STARTING TAPE\n")); | |
881 | ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); | |
882 | } | |
883 | ard_state = 1; | |
884 | goto restate; | |
885 | ||
886 | case 1: /* Start DMA */ | |
887 | /* Tape is now moving and in position-- start DMA now! */ | |
888 | isa_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2); | |
889 | out_fdc(fdcu, 0x66); /* read */ | |
890 | out_fdc(fdcu, 0x00); /* unit */ | |
891 | out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ | |
892 | out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */ | |
893 | out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */ | |
894 | out_fdc(fdcu, 0x03); /* 1K sectors */ | |
895 | out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* count */ | |
896 | out_fdc(fdcu, 0x74); /* gap length */ | |
897 | out_fdc(fdcu, 0xff); /* transfer size */ | |
898 | ard_state = 2; | |
899 | break; | |
900 | ||
901 | case 2: /* DMA completed */ | |
902 | /* Transfer complete, get status */ | |
903 | for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu); | |
904 | isa_dmadone(B_READ, ft->xptr, QCV_BLKSIZE, 2); | |
905 | ||
906 | #if FTDBGALL | |
907 | /* Compute where the controller thinks we are */ | |
908 | where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1; | |
909 | DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", | |
910 | rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], | |
911 | where, ft->xblk)); | |
912 | #endif | |
913 | ||
914 | /* Check for errors */ | |
4af7e30c | 915 | if ((rddta[0] & 0xc0) != 0x00) { |
df30aeb2 AM |
916 | if (rddta[1] & 0x04) { |
917 | /* Probably wrong position */ | |
918 | ft->lastpos = ft->xblk; | |
919 | ard_state = 0; | |
920 | goto restate; | |
921 | } else { | |
922 | /* CRC/Address-mark/Data-mark, et. al. */ | |
923 | DPRT(("ft%d: CRC error on block %d\n", fdcu, ft->xblk)); | |
924 | ft->curseg->reqcrc |= (1 << ft->xcnt); | |
925 | } | |
926 | } | |
927 | ||
928 | /* Otherwise, transfer completed okay. */ | |
929 | ft->lastpos = ft->xblk; | |
930 | ft->xblk++; | |
931 | ft->xcnt++; | |
932 | ft->xptr += QCV_BLKSIZE; | |
933 | if (ft->xcnt < QCV_BLKSEG && ft->curseg->reqcan == 0) { | |
934 | ard_state = 0; | |
935 | goto restate; | |
936 | } | |
937 | DPRT(("Read done.. Cancel = %d\n", ft->curseg->reqcan)); | |
938 | ft->io_sts = FTIO_READY; | |
939 | break; | |
940 | ||
941 | case 3: | |
942 | ft->moving = 1; | |
943 | ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); | |
944 | break; | |
945 | ||
946 | case 4: | |
947 | ard_state = 1; | |
948 | timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */ | |
949 | break; | |
950 | ||
951 | default: | |
952 | DPRT(("ft%d: bad async_read state %d!!\n", ftu, ard_state)); | |
953 | break; | |
954 | } | |
955 | } | |
956 | ||
957 | ||
958 | /* | |
959 | * Entry for async write. If from is 0, this came from the interrupt | |
960 | * routine, if it's 1 then it was a timeout, if it's 2, then an | |
961 | * async_cmd completed. | |
962 | */ | |
963 | void async_write(ftu_t ftu, int from) | |
964 | { | |
965 | ft_p ft = &ft_data[ftu]; | |
966 | fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ | |
967 | unsigned long paddr; | |
968 | int i, cmd, newcn, rddta[7]; | |
969 | int st0, pcn, where; | |
970 | static int over_async; | |
971 | static int retries = 0; | |
972 | ||
973 | if (from == 2) awr_state = over_async; | |
974 | ||
975 | restate: | |
976 | #if FTDBGALL | |
977 | DPRT(("async_write: state: %d from = %d\n", awr_state, from)); | |
978 | #endif | |
979 | switch (awr_state) { | |
980 | case 0: /* Start off */ | |
981 | /* If tape is not at desired position, stop and locate */ | |
982 | if (ft->lastpos != (ft->xblk-1)) { | |
983 | DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n", | |
984 | ftu, ft->lastpos, ft->xblk)); | |
985 | ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0); | |
986 | } | |
987 | ||
988 | /* Tape is in position but stopped. */ | |
989 | if (!ft->moving) { | |
990 | DPRT(("async_write ******STARTING TAPE\n")); | |
991 | ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); | |
992 | } | |
993 | awr_state = 1; | |
994 | goto restate; | |
995 | ||
996 | case 1: /* Start DMA */ | |
997 | /* Tape is now moving and in position-- start DMA now! */ | |
998 | isa_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2); | |
999 | out_fdc(fdcu, 0x45); /* write */ | |
1000 | out_fdc(fdcu, 0x00); /* unit */ | |
1001 | out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ | |
1002 | out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */ | |
1003 | out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */ | |
1004 | out_fdc(fdcu, 0x03); /* 1K sectors */ | |
1005 | out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* count */ | |
1006 | out_fdc(fdcu, 0x74); /* gap length */ | |
1007 | out_fdc(fdcu, 0xff); /* transfer size */ | |
1008 | awr_state = 2; | |
1009 | break; | |
1010 | ||
1011 | case 2: /* DMA completed */ | |
1012 | /* Transfer complete, get status */ | |
1013 | for (i = 0; i < 7; i++) rddta[i] = in_fdc(fdcu); | |
1014 | isa_dmadone(B_WRITE, ft->xptr, QCV_BLKSIZE, 2); | |
1015 | ||
1016 | #if FTDBGALL | |
1017 | /* Compute where the controller thinks we are */ | |
1018 | where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1; | |
1019 | DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", | |
1020 | rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], | |
1021 | where, ft->xblk)); | |
1022 | #endif | |
1023 | ||
1024 | /* Check for errors */ | |
4af7e30c | 1025 | if ((rddta[0] & 0xc0) != 0x00) { |
df30aeb2 AM |
1026 | if (rddta[1] & 0x04) { |
1027 | /* Probably wrong position */ | |
1028 | ft->lastpos = ft->xblk; | |
4af7e30c | 1029 | awr_state = 0; |
df30aeb2 AM |
1030 | goto restate; |
1031 | } else if (retries < 5) { | |
1032 | /* Something happened -- try again */ | |
1033 | ft->lastpos = ft->xblk; | |
4af7e30c | 1034 | awr_state = 0; |
df30aeb2 AM |
1035 | retries++; |
1036 | goto restate; | |
1037 | } else { | |
1038 | /* | |
1039 | * Retries failed. Note the unrecoverable error. | |
1040 | * Marking the block as bad is fairly useless. | |
1041 | */ | |
1042 | printf("ft%d: unrecoverable write error on block %d\n", | |
1043 | ftu, ft->xblk); | |
1044 | ft->curseg->reqcrc |= (1 << ft->xcnt); | |
1045 | } | |
1046 | } | |
1047 | ||
1048 | /* Otherwise, transfer completed okay. */ | |
1049 | retries = 0; | |
1050 | ft->lastpos = ft->xblk; | |
1051 | ft->xblk++; | |
1052 | ft->xcnt++; | |
1053 | ft->xptr += QCV_BLKSIZE; | |
1054 | if (ft->xcnt < QCV_BLKSEG) { | |
1055 | awr_state = 0; /* next block */ | |
1056 | goto restate; | |
1057 | } | |
1058 | #if FTDBGALL | |
1059 | DPRT(("Write done.\n")); | |
1060 | #endif | |
1061 | ft->io_sts = FTIO_READY; | |
1062 | break; | |
1063 | ||
1064 | case 3: | |
1065 | ft->moving = 1; | |
1066 | ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); | |
1067 | break; | |
1068 | ||
1069 | case 4: | |
1070 | awr_state = 1; | |
1071 | timeout(ft_timeout, (caddr_t)ftu, hz/10); /* XXX */ | |
1072 | break; | |
1073 | ||
1074 | default: | |
1075 | DPRT(("ft%d: bad async_write state %d!!\n", ftu, awr_state)); | |
1076 | break; | |
1077 | } | |
1078 | } | |
1079 | ||
1080 | ||
1081 | /* | |
1082 | * Interrupt handler for active tape. Bounced off of fdintr(). | |
1083 | */ | |
1084 | int ftintr(ftu_t ftu) | |
1085 | { | |
1086 | int st0, pcn, i; | |
1087 | ft_p ft = &ft_data[ftu]; | |
1088 | fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ | |
1089 | st0 = 0; | |
1090 | pcn = 0; | |
1091 | ||
1092 | /* I/O segment transfer completed */ | |
1093 | if (ft->active) { | |
1094 | if (async_func != ACMD_NONE) { | |
1095 | async_cmd(ftu); | |
1096 | return(1); | |
1097 | } | |
1098 | #if FTDBGALL | |
1099 | DPRT(("Got request interrupt\n")); | |
1100 | #endif | |
1101 | async_req(ftu, 0); | |
1102 | return(1); | |
1103 | } | |
1104 | ||
1105 | /* Get interrupt status */ | |
1106 | if (ft->cmd_wait != FTCMD_READID) { | |
1107 | out_fdc(fdcu, NE7CMD_SENSEI); | |
1108 | st0 = in_fdc(fdcu); | |
1109 | pcn = in_fdc(fdcu); | |
1110 | } | |
1111 | ||
1112 | if (ft->cmd_wait == FTCMD_NONE || ft->sts_wait != FTSTS_SNOOZE) { | |
1113 | huh_what: | |
1114 | printf("ft%d: unexpected interrupt; st0 = $%02x pcn = %d\n", | |
1115 | ftu, st0, pcn); | |
1116 | return(1); | |
1117 | } | |
1118 | ||
1119 | switch (ft->cmd_wait) { | |
1120 | case FTCMD_RESET: | |
1121 | ft->sts_wait = FTSTS_INTERRUPT; | |
1122 | wakeup((caddr_t)&ftsem.intr_wait); | |
1123 | break; | |
1124 | case FTCMD_RECAL: | |
1125 | case FTCMD_SEEK: | |
1126 | if (st0 & 0x20) { /* seek done */ | |
1127 | ft->sts_wait = FTSTS_INTERRUPT; | |
1128 | ft->pcn = pcn; | |
1129 | wakeup((caddr_t)&ftsem.intr_wait); | |
1130 | } | |
1131 | #if FTDBGALL | |
1132 | else | |
1133 | DPRT(("ft%d: seek error st0 = $%02x pcn = %d\n", | |
1134 | ftu, st0, pcn)); | |
1135 | #endif | |
1136 | break; | |
1137 | case FTCMD_READID: | |
1138 | for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu); | |
1139 | ft->sts_wait = FTSTS_INTERRUPT; | |
1140 | wakeup((caddr_t)&ftsem.intr_wait); | |
1141 | break; | |
1142 | ||
1143 | default: | |
1144 | goto huh_what; | |
1145 | } | |
1146 | ||
1147 | return(1); | |
1148 | } | |
1149 | ||
1150 | /* | |
1151 | * Interrupt timeout routine. | |
1152 | */ | |
1153 | static void ft_timeout(caddr_t arg1, int arg2) | |
1154 | { | |
1155 | int s; | |
1156 | ftu_t ftu = (ftu_t)arg1; | |
1157 | ft_p ft = &ft_data[ftu]; | |
1158 | ||
1159 | s = splbio(); | |
1160 | if (ft->active) { | |
1161 | if (async_func != ACMD_NONE) { | |
1162 | async_cmd(ftu); | |
1163 | splx(s); | |
1164 | return; | |
1165 | } | |
1166 | async_req(ftu, 1); | |
1167 | } else { | |
1168 | ft->sts_wait = FTSTS_TIMEOUT; | |
1169 | wakeup((caddr_t)&ftsem.intr_wait); | |
1170 | } | |
1171 | splx(s); | |
1172 | } | |
1173 | ||
1174 | /* | |
1175 | * Wait for a particular interrupt to occur. ftintr() will wake us up | |
1176 | * if it sees what we want. Otherwise, time out and return error. | |
1177 | * Should always disable ints before trigger is sent and calling here. | |
1178 | */ | |
1179 | int ftintr_wait(ftu_t ftu, int cmd, int ticks) | |
1180 | { | |
1181 | int retries, st0, pcn; | |
1182 | ft_p ft = &ft_data[ftu]; | |
1183 | fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ | |
1184 | ||
1185 | ft->cmd_wait = cmd; | |
1186 | ft->sts_wait = FTSTS_SNOOZE; | |
1187 | ||
1188 | /* At attach time, we can't rely on having interrupts serviced */ | |
1189 | if (ft->attaching) { | |
1190 | switch (cmd) { | |
1191 | case FTCMD_RESET: | |
1192 | DELAY(100); | |
1193 | ft->sts_wait = FTSTS_INTERRUPT; | |
1194 | goto intrdone; | |
1195 | case FTCMD_RECAL: | |
1196 | case FTCMD_SEEK: | |
1197 | for (retries = 0; retries < 10000; retries++) { | |
1198 | out_fdc(fdcu, NE7CMD_SENSEI); | |
1199 | st0 = in_fdc(fdcu); | |
1200 | pcn = in_fdc(fdcu); | |
1201 | if (st0 & 0x20) { | |
1202 | ft->sts_wait = FTSTS_INTERRUPT; | |
1203 | ft->pcn = pcn; | |
1204 | goto intrdone; | |
1205 | } | |
1206 | DELAY(100); | |
1207 | } | |
1208 | break; | |
1209 | } | |
1210 | ft->sts_wait = FTSTS_TIMEOUT; | |
1211 | goto intrdone; | |
1212 | } | |
1213 | ||
1214 | if (ticks) timeout(ft_timeout, (caddr_t)ftu, ticks); | |
ee874879 | 1215 | tsleep((caddr_t)&ftsem.intr_wait, FTPRI, "ftwait", 0); |
df30aeb2 AM |
1216 | |
1217 | intrdone: | |
1218 | if (ft->sts_wait == FTSTS_TIMEOUT) { /* timeout */ | |
1219 | if (ft->cmd_wait != FTCMD_RESET) | |
1220 | DPRT(("ft%d: timeout on command %d\n", ftu, ft->cmd_wait)); | |
1221 | ft->cmd_wait = FTCMD_NONE; | |
1222 | ft->sts_wait = FTSTS_NONE; | |
1223 | return(1); | |
1224 | } | |
1225 | ||
1226 | /* got interrupt */ | |
1227 | if (ft->attaching == 0 && ticks) untimeout(ft_timeout, (caddr_t)ftu); | |
1228 | ft->cmd_wait = FTCMD_NONE; | |
1229 | ft->sts_wait = FTSTS_NONE; | |
1230 | return(0); | |
1231 | } | |
1232 | ||
1233 | /* | |
1234 | * Recalibrate tape drive. Parameter totape is true, if we should | |
1235 | * recalibrate to tape drive settings. | |
1236 | */ | |
1237 | int tape_recal(ftu_t ftu, int totape) | |
1238 | { | |
1239 | int s; | |
1240 | ft_p ft = &ft_data[ftu]; | |
1241 | fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ | |
1242 | ||
1243 | DPRT(("tape_recal start\n")); | |
1244 | ||
1245 | out_fdc(fdcu, NE7CMD_SPECIFY); | |
1246 | out_fdc(fdcu, (totape) ? 0xAD : 0xDF); | |
1247 | out_fdc(fdcu, 0x02); | |
1248 | ||
1249 | s = splbio(); | |
1250 | out_fdc(fdcu, NE7CMD_RECAL); | |
1251 | out_fdc(fdcu, 0x00); | |
1252 | ||
1253 | if (ftintr_wait(ftu, FTCMD_RECAL, hz)) { | |
1254 | splx(s); | |
1255 | DPRT(("ft%d: recalibrate timeout\n", ftu)); | |
1256 | return(1); | |
1257 | } | |
1258 | splx(s); | |
1259 | ||
1260 | out_fdc(fdcu, NE7CMD_SPECIFY); | |
1261 | out_fdc(fdcu, (totape) ? 0xFD : 0xDF); | |
1262 | out_fdc(fdcu, 0x02); | |
1263 | ||
1264 | DPRT(("tape_recal end\n")); | |
1265 | return(0); | |
1266 | } | |
1267 | ||
1268 | static void state_timeout(caddr_t arg1, int arg2) | |
1269 | { | |
1270 | ftu_t ftu = (ftu_t)arg1; | |
1271 | ||
1272 | wakeup((caddr_t)&ftsem.long_delay); | |
1273 | } | |
1274 | ||
1275 | /* | |
1276 | * Wait for a particular tape status to be met. If all is TRUE, then | |
1277 | * all states must be met, otherwise any state can be met. | |
1278 | */ | |
1279 | int tape_state(ftu_t ftu, int all, int mask, int seconds) | |
1280 | { | |
1281 | int r, tries, maxtries; | |
1282 | ||
1283 | maxtries = (seconds) ? (4 * seconds) : 1; | |
1284 | for (tries = 0; tries < maxtries; tries++) { | |
1285 | r = tape_status(ftu); | |
1286 | if (r >= 0) { | |
1287 | if (all && (r & mask) == mask) return(r); | |
1288 | if ((r & mask) != 0) return(r); | |
1289 | } | |
1290 | if (seconds) { | |
1291 | timeout(state_timeout, (caddr_t)ftu, hz/4); | |
ee874879 | 1292 | tsleep((caddr_t)&ftsem.long_delay, FTPRI, "ftstate", 0); |
df30aeb2 AM |
1293 | } |
1294 | } | |
1295 | DPRT(("ft%d: tape_state failed on mask=$%02x maxtries=%d\n", | |
1296 | ftu, mask, maxtries)); | |
1297 | return(-1); | |
1298 | } | |
1299 | ||
1300 | /* | |
1301 | * Send a QIC command to tape drive, wait for completion. | |
1302 | */ | |
1303 | int tape_cmd(ftu_t ftu, int cmd) | |
1304 | { | |
1305 | int newcn; | |
1306 | int retries = 0; | |
1307 | int s; | |
1308 | ft_p ft = &ft_data[ftu]; | |
1309 | fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ | |
1310 | ||
1311 | DPRT(("===> tape_cmd: %d\n",cmd)); | |
1312 | newcn = (cmd <= ft->pcn) ? ft->pcn - cmd : ft->pcn + cmd; | |
1313 | ||
1314 | retry: | |
1315 | ||
1316 | /* Perform seek */ | |
1317 | s = splbio(); | |
1318 | out_fdc(fdcu, NE7CMD_SEEK); | |
1319 | out_fdc(fdcu, 0x00); | |
1320 | out_fdc(fdcu, newcn); | |
1321 | ||
1322 | if (ftintr_wait(ftu, FTCMD_SEEK, hz)) { | |
1323 | DPRT(("ft%d: tape_cmd seek timeout\n", ftu)); | |
1324 | redo: | |
1325 | splx(s); | |
1326 | if (++retries < 5) goto retry; | |
1327 | DPRT(("ft%d: tape_cmd seek failed!\n", ftu)); | |
1328 | return(1); | |
1329 | } | |
1330 | splx(s); | |
1331 | ||
1332 | if (ft->pcn != newcn) { | |
1333 | DPRT(("ft%d: bad seek in tape_cmd; pcn = %d newcn = %d\n", | |
1334 | ftu, ft->pcn, newcn)); | |
1335 | goto redo; | |
1336 | } | |
1337 | DELAY(2500); | |
1338 | return(0); | |
1339 | } | |
1340 | ||
1341 | /* | |
1342 | * Return status of tape drive | |
1343 | */ | |
1344 | int tape_status(ftu_t ftu) | |
1345 | { | |
1346 | int r, err, tries; | |
1347 | ft_p ft = &ft_data[ftu]; | |
1348 | ||
1349 | for (r = -1, tries = 0; r < 0 && tries < 3; tries++) | |
1350 | r = qic_status(ftu, QC_STATUS, 8); | |
1351 | if (tries == 3) return(-1); | |
1352 | DPRT(("tape_status got $%04x\n",r)); | |
1353 | ft->laststs = r; | |
1354 | ||
1355 | if (r & (QS_ERROR|QS_NEWCART)) { | |
1356 | if (r & QS_NEWCART) ft->newcart = 1; | |
1357 | err = qic_status(ftu, QC_ERRCODE, 16); | |
1358 | ft->lasterr = err; | |
1359 | if ((r & QS_NEWCART) == 0 && err && ft->attaching == 0) { | |
1360 | DPRT(("ft%d: QIC error %d occurred on cmd %d\n", | |
1361 | ftu, err & 0xff, err >> 8)); | |
1362 | } | |
1363 | r = qic_status(ftu, QC_STATUS, 8); | |
1364 | ft->laststs = r; | |
1365 | DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r)); | |
1366 | } | |
1367 | ft->rdonly = (r & QS_RDONLY); | |
1368 | return(r); | |
1369 | } | |
1370 | ||
1371 | /* | |
1372 | * Transfer control to tape drive. | |
1373 | */ | |
1374 | void tape_start(ftu_t ftu) | |
1375 | { | |
1376 | ft_p ft = &ft_data[ftu]; | |
1377 | fdc_p fdc = ft->fdc; | |
1378 | int s; | |
1379 | ||
1380 | DPRT(("tape_start start\n")); | |
1381 | ||
1382 | s = splbio(); | |
1383 | ||
1384 | /* reset, dma disable */ | |
1385 | outb(fdc->baseport+fdout, 0x00); | |
1386 | (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); | |
1387 | ||
1388 | /* raise reset, enable DMA */ | |
1389 | outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN); | |
1390 | (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); | |
1391 | ||
1392 | splx(s); | |
1393 | ||
1394 | tape_recal(ftu, 1); | |
1395 | ||
1396 | /* set transfer speed */ | |
1397 | outb(fdc->baseport+fdctl, FDC_500KBPS); | |
1398 | DELAY(10); | |
1399 | ||
1400 | DPRT(("tape_start end\n")); | |
1401 | } | |
1402 | ||
1403 | /* | |
1404 | * Transfer control back to floppy disks. | |
1405 | */ | |
1406 | void tape_end(ftu_t ftu) | |
1407 | { | |
1408 | ft_p ft = &ft_data[ftu]; | |
1409 | fdc_p fdc = ft->fdc; | |
1410 | int s; | |
1411 | ||
1412 | DPRT(("tape_end start\n")); | |
1413 | tape_recal(ftu, 0); | |
1414 | ||
1415 | s = splbio(); | |
1416 | ||
1417 | /* reset, dma disable */ | |
1418 | outb(fdc->baseport+fdout, 0x00); | |
1419 | (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); | |
1420 | ||
1421 | /* raise reset, enable DMA */ | |
1422 | outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN); | |
1423 | (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); | |
1424 | ||
1425 | splx(s); | |
1426 | ||
1427 | /* set transfer speed */ | |
1428 | outb(fdc->baseport+fdctl, FDC_500KBPS); | |
1429 | DELAY(10); | |
1430 | fdc->flags &= ~FDC_TAPE_BUSY; | |
1431 | ||
1432 | DPRT(("tape_end end\n")); | |
1433 | } | |
1434 | ||
1435 | /* | |
1436 | * Wait for the driver to go inactive, cancel readahead if necessary. | |
1437 | */ | |
1438 | void tape_inactive(ftu_t ftu) | |
1439 | { | |
1440 | ft_p ft = &ft_data[ftu]; | |
1441 | ||
1442 | if (ft->curseg->reqtype == FTIO_RDAHEAD) { | |
1443 | ft->curseg->reqcan = 1; /* XXX cancel rdahead */ | |
ee874879 GW |
1444 | while (ft->active) |
1445 | tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftinact", 0); | |
df30aeb2 | 1446 | } |
ee874879 GW |
1447 | while (ft->active) |
1448 | tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftinact", 0); | |
df30aeb2 AM |
1449 | } |
1450 | ||
1451 | /* | |
1452 | * Get the geometry of the tape currently in the drive. | |
1453 | */ | |
1454 | int ftgetgeom(ftu_t ftu) | |
1455 | { | |
1456 | int r, i, tries; | |
1457 | int cfg, qic80, ext; | |
1458 | int sts, fmt, len; | |
1459 | ft_p ft = &ft_data[ftu]; | |
1460 | ||
1461 | r = tape_status(ftu); | |
1462 | ||
1463 | /* XXX fix me when format mode is finished */ | |
1464 | if ((r & QS_CART) == 0 || (r & QS_FMTOK) == 0) { | |
1465 | DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r)); | |
1466 | ftg = NULL; | |
1467 | ft->newcart = 1; | |
1468 | return(0); | |
1469 | } | |
1470 | ||
1471 | /* Report drive configuration */ | |
1472 | for (cfg = -1, tries = 0; cfg < 0 && tries < 3; tries++) | |
1473 | cfg = qic_status(ftu, QC_CONFIG, 8); | |
1474 | if (tries == 3) { | |
1475 | DPRT(("ftgetgeom report config failed\n")); | |
1476 | ftg = NULL; | |
1477 | return(-1); | |
1478 | } | |
1479 | DPRT(("ftgetgeom report config got $%04x\n", cfg)); | |
1480 | ft->lastcfg = cfg; | |
1481 | ||
1482 | qic80 = cfg & QCF_QIC80; | |
1483 | ext = cfg & QCF_EXTRA; | |
1484 | ||
1485 | /* | |
1486 | * XXX - This doesn't seem to work on my Colorado Jumbo 250... | |
1487 | * if it works on your drive, I'd sure like to hear about it. | |
1488 | */ | |
1489 | #if 0 | |
1490 | /* Report drive status */ | |
1491 | for (sts = -1, tries = 0; sts < 0 && tries < 3; tries++) | |
1492 | sts = qic_status(ftu, QC_TSTATUS, 8); | |
1493 | if (tries == 3) { | |
1494 | DPRT(("ftgetgeom report tape status failed\n")); | |
1495 | ftg = NULL; | |
1496 | return(-1); | |
1497 | } | |
1498 | DPRT(("ftgetgeom report tape status got $%04x\n", sts)); | |
1499 | #else | |
1500 | /* | |
1501 | * XXX - Forge a fake tape status based upon the returned | |
1502 | * configuration, since the above command or code is broken | |
1503 | * for my drive and probably other older drives. | |
1504 | */ | |
1505 | sts = 0; | |
1506 | sts = (qic80) ? QTS_QIC80 : QTS_QIC40; | |
1507 | sts |= (ext) ? QTS_LEN2 : QTS_LEN1; | |
1508 | #endif | |
1509 | ||
1510 | fmt = sts & QTS_FMMASK; | |
1511 | len = (sts & QTS_LNMASK) >> 4; | |
1512 | ||
1513 | if (fmt > QCV_NFMT) { | |
1514 | ftg = NULL; | |
1515 | printf("ft%d: unsupported tape format\n", ftu); | |
1516 | return(-1); | |
1517 | } | |
1518 | if (len > QCV_NLEN) { | |
1519 | ftg = NULL; | |
1520 | printf("ft%d: unsupported tape length\n", ftu); | |
1521 | return(-1); | |
1522 | } | |
1523 | ||
1524 | /* Look up geometry in the table */ | |
1525 | for (i = 1; i < NGEOM; i++) | |
1526 | if (ftgtbl[i].g_fmtno == fmt && ftgtbl[i].g_lenno == len) break; | |
1527 | if (i == NGEOM) { | |
1528 | printf("ft%d: unknown tape geometry\n", ftu); | |
1529 | ftg = NULL; | |
1530 | return(-1); | |
1531 | } | |
1532 | ftg = &ftgtbl[i]; | |
1533 | if (!ftg->g_trktape) { | |
1534 | printf("ft%d: unsupported format %s w/len %s\n", | |
1535 | ftu, ftg->g_fmtdesc, ftg->g_lendesc); | |
1536 | ftg = NULL; | |
1537 | return(-1); | |
1538 | } | |
1539 | DPRT(("Tape format is %s, length is %s\n", ftg->g_fmtdesc, ftg->g_lendesc)); | |
1540 | ft->newcart = 0; | |
1541 | return(0); | |
1542 | } | |
1543 | ||
1544 | /* | |
1545 | * Switch between tape/floppy. This will send the tape enable/disable | |
1546 | * codes for this drive's manufacturer. | |
1547 | */ | |
1548 | int set_fdcmode(dev_t dev, int newmode) | |
1549 | { | |
1550 | ftu_t ftu = FDUNIT(minor(dev)); | |
1551 | ft_p ft = &ft_data[ftu]; | |
1552 | fdc_p fdc = ft->fdc; | |
1553 | ||
1554 | static int havebufs = 0; | |
1555 | void *buf; | |
1556 | int r, s, i; | |
1557 | SegReq *sp; | |
1558 | ||
1559 | if (newmode == FDC_TAPE_MODE) { | |
1560 | /* Wake up the tape drive */ | |
1561 | switch (ft->type) { | |
1562 | case NO_TYPE: | |
1563 | fdc->flags &= ~FDC_TAPE_BUSY; | |
1564 | return(ENXIO); | |
1565 | case FT_COLORADO: | |
1566 | tape_start(ftu); | |
1567 | if (tape_cmd(ftu, QC_COL_ENABLE1)) { | |
1568 | tape_end(ftu); | |
1569 | return(EIO); | |
1570 | } | |
1571 | if (tape_cmd(ftu, QC_COL_ENABLE2)) { | |
1572 | tape_end(ftu); | |
1573 | return(EIO); | |
1574 | } | |
1575 | break; | |
1576 | case FT_MOUNTAIN: | |
1577 | tape_start(ftu); | |
1578 | if (tape_cmd(ftu, QC_MTN_ENABLE1)) { | |
1579 | tape_end(ftu); | |
1580 | return(EIO); | |
1581 | } | |
1582 | if (tape_cmd(ftu, QC_MTN_ENABLE2)) { | |
1583 | tape_end(ftu); | |
1584 | return(EIO); | |
1585 | } | |
1586 | break; | |
1587 | default: | |
1588 | DPRT(("ft%d: bad tape type\n", ftu)); | |
1589 | return(ENXIO); | |
1590 | } | |
1591 | if (tape_status(ftu) < 0) { | |
1592 | tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); | |
1593 | tape_end(ftu); | |
1594 | return(EIO); | |
1595 | } | |
1596 | ||
1597 | /* Grab buffers from memory. */ | |
1598 | if (!havebufs) { | |
1599 | ft->curseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT); | |
1600 | if (ft->curseg == NULL) { | |
1601 | printf("ft%d: not enough memory for buffers\n", ftu); | |
1602 | return(ENOMEM); | |
1603 | } | |
1604 | ft->bufseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT); | |
1605 | if (ft->bufseg == NULL) { | |
1606 | free(ft->curseg, M_DEVBUF); | |
1607 | printf("ft%d: not enough memory for buffers\n", ftu); | |
1608 | return(ENOMEM); | |
1609 | } | |
1610 | havebufs = 1; | |
1611 | } | |
1612 | ft->curseg->reqtype = FTIO_READY; | |
1613 | ft->bufseg->reqtype = FTIO_READY; | |
1614 | ft->io_sts = FTIO_READY; /* tape drive is ready */ | |
1615 | ft->active = 0; /* interrupt driver not active */ | |
1616 | ft->moving = 0; /* tape not moving */ | |
1617 | ft->rdonly = 0; /* tape read only */ | |
1618 | ft->newcart = 0; /* a new cart was inserted */ | |
1619 | ft->lastpos = -1; /* tape is rewound */ | |
1620 | tape_state(ftu, 0, QS_READY, 60); | |
1621 | tape_cmd(ftu, QC_RATE); | |
1622 | tape_cmd(ftu, QCF_RT500+2); /* 500K bps */ | |
1623 | tape_state(ftu, 0, QS_READY, 60); | |
1624 | ft->mode = FTM_PRIMARY; | |
1625 | tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */ | |
1626 | tape_state(ftu, 0, QS_READY, 60); | |
1627 | ftg = NULL; /* No geometry yet */ | |
1628 | ftgetgeom(ftu); /* Get tape geometry */ | |
1629 | ftreq_rewind(ftu); /* Make sure tape is rewound */ | |
1630 | } else { | |
1631 | tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); | |
1632 | tape_end(ftu); | |
1633 | ft->newcart = 0; /* clear new cartridge */ | |
1634 | havebufs = 0; | |
1635 | free(ft->curseg, M_DEVBUF); | |
1636 | free(ft->bufseg, M_DEVBUF); | |
1637 | } | |
1638 | return(0); | |
1639 | } | |
1640 | ||
1641 | ||
1642 | /* | |
1643 | * Perform a QIC status function. | |
1644 | */ | |
1645 | int qic_status(ftu_t ftu, int cmd, int nbits) | |
1646 | { | |
1647 | int st3, val, r, i; | |
1648 | ft_p ft = &ft_data[ftu]; | |
1649 | fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ | |
1650 | ||
1651 | if (tape_cmd(ftu, cmd)) { | |
1652 | DPRT(("ft%d: QIC status timeout\n", ftu)); | |
1653 | return(-1); | |
1654 | } | |
1655 | ||
1656 | /* Sense drive status */ | |
1657 | out_fdc(fdcu, NE7CMD_SENSED); | |
1658 | out_fdc(fdcu, 0x00); | |
1659 | st3 = in_fdc(fdcu); | |
1660 | ||
1661 | if ((st3 & 0x10) == 0) { /* track 0 */ | |
1662 | DPRT(("qic_status has dead drive... st3 = $%02x\n", st3)); | |
1663 | return(-1); | |
1664 | } | |
1665 | ||
1666 | for (i = r = 0; i <= nbits; i++) { | |
1667 | if (tape_cmd(ftu, QC_NEXTBIT)) { | |
1668 | DPRT(("ft%d: QIC status bit timed out on %d\n", ftu, i)); | |
1669 | return(-1); | |
1670 | } | |
1671 | ||
1672 | out_fdc(fdcu, NE7CMD_SENSED); | |
1673 | out_fdc(fdcu, 0x00); | |
1674 | st3 = in_fdc(fdcu); | |
1675 | if (st3 < 0) { | |
1676 | DPRT(("ft%d: controller timed out on bit %d r=$%02x\n", | |
1677 | ftu, i, r)); | |
1678 | return(-1); | |
1679 | } | |
1680 | ||
1681 | r >>= 1; | |
1682 | if (i < nbits) | |
1683 | r |= ((st3 & 0x10) ? 1 : 0) << nbits; | |
1684 | else if ((st3 & 0x10) == 0) { | |
1685 | DPRT(("ft%d: qic status stop bit missing at %d, st3=$%02x r=$%04x\n", | |
1686 | ftu,i,st3,r)); | |
1687 | return(-1); | |
1688 | } | |
1689 | } | |
1690 | ||
1691 | DPRT(("qic_status returned $%02x\n", r)); | |
1692 | return(r); | |
1693 | } | |
1694 | ||
1695 | /* | |
1696 | * Open tape drive for use. Bounced off of Fdopen if tape minor is | |
1697 | * detected. | |
1698 | */ | |
1699 | int ftopen(dev_t dev, int arg2) { | |
1700 | ftu_t ftu = FDUNIT(minor(dev)); | |
1701 | int type = FDTYPE(minor(dev)); | |
4af7e30c | 1702 | fdc_p fdc; |
df30aeb2 | 1703 | |
4af7e30c NW |
1704 | /* check bounds */ |
1705 | if (ftu >= NFT) | |
1706 | return(ENXIO); | |
1707 | fdc = ft_data[ftu].fdc; | |
df30aeb2 AM |
1708 | /* check for controller already busy with tape */ |
1709 | if (fdc->flags & FDC_TAPE_BUSY) | |
1710 | return(EBUSY); | |
1711 | /* make sure we found a tape when probed */ | |
1712 | if (!(fdc->flags & FDC_HASFTAPE)) | |
1713 | return(ENODEV); | |
56446ddd | 1714 | fdc->fdu = ftu; |
df30aeb2 AM |
1715 | fdc->flags |= FDC_TAPE_BUSY; |
1716 | return(set_fdcmode(dev, FDC_TAPE_MODE)); /* try to switch to tape */ | |
1717 | } | |
1718 | ||
1719 | /* | |
1720 | * Close tape and return floppy controller to disk mode. | |
1721 | */ | |
1722 | int ftclose(dev_t dev, int flags) | |
1723 | { | |
1724 | int s; | |
1725 | SegReq *sp; | |
1726 | ftu_t ftu = FDUNIT(minor(dev)); | |
1727 | ft_p ft = &ft_data[ftu]; | |
1728 | ||
1729 | /* Wait for any remaining I/O activity to complete. */ | |
1730 | if (ft->curseg->reqtype == FTIO_RDAHEAD) ft->curseg->reqcan = 1; | |
ee874879 GW |
1731 | while (ft->active) |
1732 | tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftclose", 0); | |
df30aeb2 AM |
1733 | |
1734 | ft->mode = FTM_PRIMARY; | |
1735 | tape_cmd(ftu, QC_PRIMARY); | |
1736 | tape_state(ftu, 0, QS_READY, 60); | |
1737 | ftreq_rewind(ftu); | |
1738 | return(set_fdcmode(dev, FDC_DISK_MODE)); /* Otherwise, close tape */ | |
1739 | } | |
1740 | ||
1741 | /* | |
1742 | * Perform strategy on a given buffer (not!). The driver was not | |
1743 | * performing very efficiently using the buffering routines. After | |
1744 | * support for error correction was added, this routine became | |
1745 | * obsolete in favor of doing ioctl's. Ugly, yes. | |
1746 | */ | |
1747 | void ftstrategy(struct buf *bp) | |
1748 | { | |
1749 | return; | |
1750 | } | |
1751 | ||
1752 | /* Read or write a segment. */ | |
1753 | int ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p) | |
1754 | { | |
1755 | int r, i, j; | |
1756 | SegReq *sp; | |
1757 | int s; | |
1758 | long blk, bad; | |
1759 | unsigned char *cp, *cp2; | |
1760 | ft_p ft = &ft_data[ftu]; | |
1761 | ||
1762 | if (!ft->active) { | |
1763 | r = tape_status(ftu); | |
1764 | if ((r & QS_CART) == 0) { | |
1765 | return(ENXIO); /* No cartridge */ | |
1766 | } | |
1767 | if ((r & QS_FMTOK) == 0) { | |
1768 | return(ENXIO); /* Not formatted */ | |
1769 | } | |
1770 | tape_state(ftu, 0, QS_READY, 90); | |
1771 | } | |
1772 | ||
1773 | if (ftg == NULL || ft->newcart) { | |
ee874879 GW |
1774 | while (ft->active) |
1775 | tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftrw", 0); | |
df30aeb2 AM |
1776 | tape_state(ftu, 0, QS_READY, 90); |
1777 | if (ftgetgeom(ftu) < 0) { | |
1778 | return(ENXIO); | |
1779 | } | |
1780 | } | |
1781 | ||
1782 | /* Write not allowed on a read-only tape. */ | |
1783 | if (cmd == QIOWRITE && ft->rdonly) { | |
1784 | return(EROFS); | |
1785 | } | |
1786 | /* Quick check of request and buffer. */ | |
1787 | if (sr == NULL || sr->sg_data == NULL) { | |
1788 | return(EINVAL); | |
1789 | } | |
1790 | if (sr->sg_trk >= ftg->g_trktape || | |
1791 | sr->sg_seg >= ftg->g_segtrk) { | |
1792 | return(EINVAL); | |
1793 | } | |
1794 | blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG; | |
1795 | ||
1796 | s = splbio(); | |
1797 | if (cmd == QIOREAD) { | |
1798 | if (ft->curseg->reqtype == FTIO_RDAHEAD) { | |
1799 | if (blk == ft->curseg->reqblk) { | |
1800 | sp = ft->curseg; | |
1801 | sp->reqtype = FTIO_READING; | |
1802 | sp->reqbad = sr->sg_badmap; | |
1803 | goto rdwait; | |
1804 | } else | |
1805 | ft->curseg->reqcan = 1; /* XXX cancel rdahead */ | |
1806 | } | |
1807 | ||
1808 | /* Wait until we're ready. */ | |
ee874879 GW |
1809 | while (ft->active) |
1810 | tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftrw", 0); | |
df30aeb2 AM |
1811 | |
1812 | /* Set up a new read request. */ | |
1813 | sp = ft->curseg; | |
1814 | sp->reqcrc = 0; | |
1815 | sp->reqbad = sr->sg_badmap; | |
1816 | sp->reqblk = blk; | |
1817 | sp->reqcan = 0; | |
1818 | sp->reqtype = FTIO_READING; | |
1819 | ||
1820 | /* Start the read request off. */ | |
1821 | DPRT(("Starting read I/O chain\n")); | |
1822 | arq_state = ard_state = awr_state = 0; | |
1823 | ft->xblk = sp->reqblk; | |
1824 | ft->xcnt = 0; | |
1825 | ft->xptr = sp->buff; | |
1826 | ft->active = 1; | |
1827 | timeout(ft_timeout, (caddr_t)ftu, 1); | |
1828 | ||
1829 | rdwait: | |
ee874879 | 1830 | tsleep((caddr_t)&ftsem.buff_avail, FTPRI, "ftrw", 0); |
df30aeb2 AM |
1831 | bad = sp->reqbad; |
1832 | sr->sg_crcmap = sp->reqcrc & ~bad; | |
1833 | ||
1834 | /* Copy out segment and discard bad mapped blocks. */ | |
1835 | cp = sp->buff; cp2 = sr->sg_data; | |
1836 | for (i = 0; i < QCV_BLKSEG; cp += QCV_BLKSIZE, i++) { | |
1837 | if (bad & (1 << i)) continue; | |
1838 | copyout(cp, cp2, QCV_BLKSIZE); | |
1839 | cp2 += QCV_BLKSIZE; | |
1840 | } | |
1841 | } else { | |
1842 | if (ft->curseg->reqtype == FTIO_RDAHEAD) { | |
1843 | ft->curseg->reqcan = 1; /* XXX cancel rdahead */ | |
1844 | while (ft->active) | |
ee874879 | 1845 | tsleep((caddr_t)&ftsem.iosts_change, FTPRI, "ftrw", 0); |
df30aeb2 AM |
1846 | } |
1847 | ||
1848 | /* Sleep until a buffer becomes available. */ | |
1849 | while (ft->bufseg->reqtype != FTIO_READY) | |
ee874879 | 1850 | tsleep((caddr_t)&ftsem.buff_avail, FTPRI, "ftrwbuf", 0); |
df30aeb2 AM |
1851 | sp = (ft->curseg->reqtype == FTIO_READY) ? ft->curseg : ft->bufseg; |
1852 | ||
1853 | /* Copy in segment and expand bad blocks. */ | |
1854 | bad = sr->sg_badmap; | |
1855 | cp = sr->sg_data; cp2 = sp->buff; | |
1856 | for (i = 0; i < QCV_BLKSEG; cp2 += QCV_BLKSIZE, i++) { | |
1857 | if (bad & (1 << i)) continue; | |
1858 | copyin(cp, cp2, QCV_BLKSIZE); | |
1859 | cp += QCV_BLKSIZE; | |
1860 | } | |
1861 | ||
1862 | sp->reqblk = blk; | |
1863 | sp->reqcan = 0; | |
1864 | sp->reqtype = FTIO_WRITING; | |
1865 | ||
1866 | if (!ft->active) { | |
1867 | DPRT(("Starting write I/O chain\n")); | |
1868 | arq_state = ard_state = awr_state = 0; | |
1869 | ft->xblk = sp->reqblk; | |
1870 | ft->xcnt = 0; | |
1871 | ft->xptr = sp->buff; | |
1872 | ft->active = 1; | |
1873 | timeout(ft_timeout, (caddr_t)ftu, 1); | |
1874 | } | |
1875 | } | |
1876 | splx(s); | |
1877 | return(0); | |
1878 | } | |
1879 | ||
1880 | ||
1881 | /* Rewind to beginning of tape */ | |
1882 | int ftreq_rewind(ftu_t ftu) | |
1883 | { | |
1884 | ft_p ft = &ft_data[ftu]; | |
1885 | ||
1886 | tape_inactive(ftu); | |
1887 | tape_cmd(ftu, QC_STOP); | |
1888 | tape_state(ftu, 0, QS_READY, 90); | |
1889 | tape_cmd(ftu, QC_SEEKSTART); | |
1890 | tape_state(ftu, 0, QS_READY, 90); | |
1891 | tape_cmd(ftu, QC_SEEKTRACK); | |
1892 | tape_cmd(ftu, 2); | |
1893 | tape_state(ftu, 0, QS_READY, 90); | |
1894 | ft->lastpos = -1; | |
1895 | ft->moving = 0; | |
1896 | return(0); | |
1897 | } | |
1898 | ||
1899 | /* Move to logical beginning or end of track */ | |
1900 | int ftreq_trkpos(ftu_t ftu, int req) | |
1901 | { | |
1902 | int curtrk, r, cmd; | |
1903 | ft_p ft = &ft_data[ftu]; | |
1904 | ||
1905 | tape_inactive(ftu); | |
1906 | tape_cmd(ftu, QC_STOP); | |
1907 | tape_state(ftu, 0, QS_READY, 90); | |
1908 | ||
1909 | r = tape_status(ftu); | |
1910 | if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */ | |
1911 | if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */ | |
1912 | ||
1913 | if (ftg == NULL || ft->newcart) { | |
1914 | if (ftgetgeom(ftu) < 0) return(ENXIO); | |
1915 | } | |
1916 | ||
1917 | curtrk = (ft->lastpos < 0) ? 0 : ft->lastpos / ftg->g_blktrk; | |
1918 | if (req == QIOBOT) | |
1919 | cmd = (curtrk & 1) ? QC_SEEKEND : QC_SEEKSTART; | |
1920 | else | |
1921 | cmd = (curtrk & 1) ? QC_SEEKSTART : QC_SEEKEND; | |
1922 | tape_cmd(ftu, cmd); | |
1923 | tape_state(ftu, 0, QS_READY, 90); | |
1924 | return(0); | |
1925 | } | |
1926 | ||
1927 | /* Seek tape head to a particular track. */ | |
1928 | int ftreq_trkset(ftu_t ftu, int *trk) | |
1929 | { | |
1930 | int curtrk, r, cmd; | |
1931 | ft_p ft = &ft_data[ftu]; | |
1932 | ||
1933 | tape_inactive(ftu); | |
1934 | tape_cmd(ftu, QC_STOP); | |
1935 | tape_state(ftu, 0, QS_READY, 90); | |
1936 | ||
1937 | r = tape_status(ftu); | |
1938 | if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */ | |
1939 | if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */ | |
1940 | if (ftg == NULL || ft->newcart) { | |
1941 | if (ftgetgeom(ftu) < 0) return(ENXIO); | |
1942 | } | |
1943 | ||
1944 | tape_cmd(ftu, QC_SEEKTRACK); | |
1945 | tape_cmd(ftu, *trk + 2); | |
1946 | tape_state(ftu, 0, QS_READY, 90); | |
1947 | return(0); | |
1948 | } | |
1949 | ||
1950 | /* Start tape moving forward. */ | |
1951 | int ftreq_lfwd(ftu_t ftu) | |
1952 | { | |
1953 | tape_inactive(ftu); | |
1954 | tape_cmd(ftu, QC_STOP); | |
1955 | tape_state(ftu, 0, QS_READY, 90); | |
1956 | tape_cmd(ftu, QC_FORWARD); | |
1957 | return(0); | |
1958 | } | |
1959 | ||
1960 | /* Stop the tape */ | |
1961 | int ftreq_stop(ftu_t ftu) | |
1962 | { | |
1963 | tape_inactive(ftu); | |
1964 | tape_cmd(ftu, QC_STOP); | |
1965 | tape_state(ftu, 0, QS_READY, 90); | |
1966 | return(0); | |
1967 | } | |
1968 | ||
1969 | /* Set the particular mode the drive should be in. */ | |
1970 | int ftreq_setmode(ftu_t ftu, int cmd) | |
1971 | { | |
1972 | int r; | |
1973 | ft_p ft = &ft_data[ftu]; | |
1974 | ||
1975 | tape_inactive(ftu); | |
1976 | r = tape_status(ftu); | |
1977 | ||
1978 | switch(cmd) { | |
1979 | case QIOPRIMARY: | |
1980 | ft->mode = FTM_PRIMARY; | |
1981 | tape_cmd(ftu, QC_PRIMARY); | |
1982 | break; | |
1983 | case QIOFORMAT: | |
1984 | if (r & QS_RDONLY) return(ENXIO); | |
1985 | if ((r & QS_BOT) == 0) return(ENXIO); | |
1986 | tape_cmd(ftu, QC_FORMAT); | |
1987 | break; | |
1988 | case QIOVERIFY: | |
1989 | if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */ | |
1990 | tape_cmd(ftu, QC_VERIFY); | |
1991 | break; | |
1992 | } | |
1993 | tape_state(ftu, 0, QS_READY, 60); | |
1994 | return(0); | |
1995 | } | |
1996 | ||
1997 | /* Return drive status bits */ | |
1998 | int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p) | |
1999 | { | |
2000 | ft_p ft = &ft_data[ftu]; | |
2001 | ||
2002 | if (ft->active) | |
2003 | *sts = ft->laststs & ~QS_READY; | |
2004 | else | |
2005 | *sts = tape_status(ftu); | |
2006 | return(0); | |
2007 | } | |
2008 | ||
2009 | /* Return drive configuration bits */ | |
2010 | int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p) | |
2011 | { | |
2012 | int r, tries; | |
2013 | ft_p ft = &ft_data[ftu]; | |
2014 | ||
2015 | if (ft->active) | |
2016 | r = ft->lastcfg; | |
2017 | else { | |
2018 | for (r = -1, tries = 0; r < 0 && tries < 3; tries++) | |
2019 | r = qic_status(ftu, QC_CONFIG, 8); | |
2020 | if (tries == 3) return(ENXIO); | |
2021 | } | |
2022 | *cfg = r; | |
2023 | return(0); | |
2024 | } | |
2025 | ||
2026 | /* Return current tape's geometry. */ | |
2027 | int ftreq_geom(ftu_t ftu, QIC_Geom *g) | |
2028 | { | |
2029 | tape_inactive(ftu); | |
2030 | if (ftg == NULL && ftgetgeom(ftu) < 0) return(ENXIO); | |
2031 | bcopy(ftg, g, sizeof(QIC_Geom)); | |
2032 | return(0); | |
2033 | } | |
2034 | ||
2035 | /* Return drive hardware information */ | |
2036 | int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp) | |
2037 | { | |
2038 | int r, tries; | |
2039 | int rom, vend; | |
2040 | ||
2041 | tape_inactive(ftu); | |
2042 | bzero(hwp, sizeof(QIC_HWInfo)); | |
2043 | ||
2044 | for (rom = -1, tries = 0; rom < 0 && tries < 3; tries++) | |
2045 | rom = qic_status(ftu, QC_VERSION, 8); | |
2046 | if (rom > 0) { | |
2047 | hwp->hw_rombeta = (rom >> 7) & 0x01; | |
2048 | hwp->hw_romid = rom & 0x7f; | |
2049 | } | |
2050 | ||
2051 | for (vend = -1, tries = 0; vend < 0 && tries < 3; tries++) | |
2052 | vend = qic_status(ftu, QC_VENDORID, 16); | |
2053 | if (vend > 0) { | |
2054 | hwp->hw_make = (vend >> 6) & 0x3ff; | |
2055 | hwp->hw_model = vend & 0x3f; | |
2056 | } | |
2057 | ||
2058 | return(0); | |
2059 | } | |
2060 | ||
2061 | /* | |
2062 | * I/O functions. | |
2063 | */ | |
2064 | int ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) | |
2065 | { | |
2066 | ftu_t ftu = FDUNIT(minor(dev)); | |
2067 | ft_p ft = &ft_data[ftu]; | |
2068 | ||
2069 | switch(cmd) { | |
2070 | case QIOREAD: /* Request reading a segment from tape. */ | |
2071 | case QIOWRITE: /* Request writing a segment to tape. */ | |
2072 | return(ftreq_rw(ftu, cmd, (QIC_Segment *)data, p)); | |
2073 | ||
2074 | case QIOREWIND: /* Rewind tape. */ | |
2075 | return(ftreq_rewind(ftu)); | |
2076 | ||
2077 | case QIOBOT: /* Seek to logical beginning of track. */ | |
2078 | case QIOEOT: /* Seek to logical end of track. */ | |
2079 | return(ftreq_trkpos(ftu, cmd)); | |
2080 | ||
2081 | case QIOTRACK: /* Seek tape head to specified track. */ | |
2082 | return(ftreq_trkset(ftu, (int *)data)); | |
2083 | ||
2084 | case QIOSEEKLP: /* Seek load point. */ | |
2085 | goto badreq; | |
2086 | ||
2087 | case QIOFORWARD: /* Move tape in logical forward direction. */ | |
2088 | return(ftreq_lfwd(ftu)); | |
2089 | ||
2090 | case QIOSTOP: /* Causes tape to stop. */ | |
2091 | return(ftreq_stop(ftu)); | |
2092 | ||
2093 | case QIOPRIMARY: /* Enter primary mode. */ | |
2094 | case QIOFORMAT: /* Enter format mode. */ | |
2095 | case QIOVERIFY: /* Enter verify mode. */ | |
2096 | return(ftreq_setmode(ftu, cmd)); | |
2097 | ||
2098 | case QIOWRREF: /* Write reference burst. */ | |
2099 | goto badreq; | |
2100 | ||
2101 | case QIOSTATUS: /* Get drive status. */ | |
2102 | return(ftreq_status(ftu, cmd, (int *)data, p)); | |
2103 | ||
2104 | case QIOCONFIG: /* Get tape configuration. */ | |
2105 | return(ftreq_config(ftu, cmd, (int *)data, p)); | |
2106 | ||
2107 | case QIOGEOM: | |
2108 | return(ftreq_geom(ftu, (QIC_Geom *)data)); | |
2109 | ||
2110 | case QIOHWINFO: | |
2111 | return(ftreq_hwinfo(ftu, (QIC_HWInfo *)data)); | |
2112 | } | |
2113 | badreq: | |
2114 | DPRT(("ft%d: unknown ioctl(%d) request\n", ftu, cmd)); | |
2115 | return(ENXIO); | |
2116 | } | |
2117 | ||
2118 | /* Not implemented */ | |
2119 | int ftdump(dev_t dev) | |
2120 | { | |
2121 | return(EINVAL); | |
2122 | } | |
2123 | ||
2124 | /* Not implemented */ | |
2125 | int ftsize(dev_t dev) | |
2126 | { | |
2127 | return(EINVAL); | |
2128 | } | |
2129 | #endif |