| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | |
| 2 | /* | |
| 3 | * Streamer tape driver for 386bsd and FreeBSD. | |
| 4 | * Supports Archive and Wangtek compatible QIC-02/QIC-36 boards. | |
| 5 | * | |
| 6 | * Copyright (C) 1993 by: | |
| 7 | * Sergey Ryzhkov <sir@kiae.su> | |
| 8 | * Serge Vakulenko <vak@zebub.msk.su> | |
| 9 | * | |
| 10 | * This software is distributed with NO WARRANTIES, not even the implied | |
| 11 | * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 12 | * | |
| 13 | * Authors grant any other persons or organisations permission to use | |
| 14 | * or modify this software as long as this message is kept with the software, | |
| 15 | * all derivative works or modified versions. | |
| 16 | * | |
| 17 | * This driver is derived from the old 386bsd Wangtek streamer tape driver, | |
| 18 | * made by Robert Baron at CMU, based on Intel sources. | |
| 19 | * Authors thank Robert Baron, CMU and Intel and retain here | |
| 20 | * the original CMU copyright notice. | |
| 21 | * | |
| 22 | * Version 1.3, Thu Nov 11 12:09:13 MSK 1993 | |
| 23 | * $FreeBSD: src/sys/i386/isa/wt.c,v 1.57.2.1 2000/08/08 19:49:53 peter Exp $ | |
| 24 | * | |
| 25 | */ | |
| 26 | ||
| 27 | /* | |
| 28 | * Code for MTERASE added by John Lind (john@starfire.mn.org) 95/09/02. | |
| 29 | * This was very easy due to the excellent structure and clear coding | |
| 30 | * of the original driver. | |
| 31 | */ | |
| 32 | ||
| 33 | /* | |
| 34 | * Copyright (c) 1989 Carnegie-Mellon University. | |
| 35 | * All rights reserved. | |
| 36 | * | |
| 37 | * Authors: Robert Baron | |
| 38 | * | |
| 39 | * Permission to use, copy, modify and distribute this software and | |
| 40 | * its documentation is hereby granted, provided that both the copyright | |
| 41 | * notice and this permission notice appear in all copies of the | |
| 42 | * software, derivative works or modified versions, and any portions | |
| 43 | * thereof, and that both notices appear in supporting documentation. | |
| 44 | * | |
| 45 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" | |
| 46 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND | |
| 47 | * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |
| 48 | * | |
| 49 | * Carnegie Mellon requests users of this software to return to | |
| 50 | * | |
| 51 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |
| 52 | * School of Computer Science | |
| 53 | * Carnegie Mellon University | |
| 54 | * Pittsburgh PA 15213-3890 | |
| 55 | * | |
| 56 | * any improvements or extensions that they make and grant Carnegie the | |
| 57 | * rights to redistribute these changes. | |
| 58 | */ | |
| 59 | ||
| 1f2de5d4 | 60 | #include "use_wt.h" |
| 984263bc MD |
61 | |
| 62 | #include <sys/param.h> | |
| 63 | #include <sys/systm.h> | |
| 64 | #include <sys/kernel.h> | |
| 65 | #include <sys/buf.h> | |
| 66 | #include <sys/fcntl.h> | |
| 67 | #include <sys/malloc.h> | |
| 68 | #include <sys/mtio.h> | |
| 69 | #include <sys/conf.h> | |
| d9ed64be | 70 | #include <sys/thread2.h> |
| 6011c5c4 | 71 | #include <sys/bus.h> |
| 984263bc MD |
72 | |
| 73 | #include <machine/clock.h> | |
| 74 | ||
| 21ce0dfa | 75 | #include <bus/isa/isa_device.h> |
| 6011c5c4 | 76 | #include <bus/isa/isavar.h> |
| 1f2de5d4 | 77 | #include "wtreg.h" |
| 984263bc MD |
78 | |
| 79 | ||
| 80 | /* | |
| 81 | * Uncomment this to enable internal device tracing. | |
| 82 | */ | |
| e3869ec7 | 83 | #define TRACE(s) /* kprintf s */ |
| 984263bc | 84 | |
| 984263bc MD |
85 | /* |
| 86 | * Wangtek controller ports | |
| 87 | */ | |
| 88 | #define WT_CTLPORT(base) ((base)+0) /* control, write only */ | |
| 89 | #define WT_STATPORT(base) ((base)+0) /* status, read only */ | |
| 90 | #define WT_CMDPORT(base) ((base)+1) /* command, write only */ | |
| 91 | #define WT_DATAPORT(base) ((base)+1) /* data, read only */ | |
| 92 | #define WT_NPORT 2 /* 2 i/o ports */ | |
| 93 | ||
| 94 | /* status port bits */ | |
| 95 | #define WT_BUSY 0x01 /* not ready bit define */ | |
| 96 | #define WT_NOEXCEP 0x02 /* no exception bit define */ | |
| 97 | #define WT_RESETMASK 0x07 /* to check after reset */ | |
| 98 | #define WT_RESETVAL 0x05 /* state after reset */ | |
| 99 | ||
| 100 | /* control port bits */ | |
| 101 | #define WT_ONLINE 0x01 /* device selected */ | |
| 102 | #define WT_RESET 0x02 /* reset command */ | |
| 103 | #define WT_REQUEST 0x04 /* request command */ | |
| 104 | #define WT_IEN 0x08 /* enable dma */ | |
| 105 | ||
| 106 | /* | |
| 107 | * Archive controller ports | |
| 108 | */ | |
| 109 | #define AV_DATAPORT(base) ((base)+0) /* data, read only */ | |
| 110 | #define AV_CMDPORT(base) ((base)+0) /* command, write only */ | |
| 111 | #define AV_STATPORT(base) ((base)+1) /* status, read only */ | |
| 112 | #define AV_CTLPORT(base) ((base)+1) /* control, write only */ | |
| 113 | #define AV_SDMAPORT(base) ((base)+2) /* start dma */ | |
| 114 | #define AV_RDMAPORT(base) ((base)+3) /* reset dma */ | |
| 115 | #define AV_NPORT 4 /* 4 i/o ports */ | |
| 116 | ||
| 117 | /* status port bits */ | |
| 118 | #define AV_BUSY 0x40 /* not ready bit define */ | |
| 119 | #define AV_NOEXCEP 0x20 /* no exception bit define */ | |
| 120 | #define AV_RESETMASK 0xf8 /* to check after reset */ | |
| 121 | #define AV_RESETVAL 0x50 /* state after reset */ | |
| 122 | ||
| 123 | /* control port bits */ | |
| 124 | #define AV_RESET 0x80 /* reset command */ | |
| 125 | #define AV_REQUEST 0x40 /* request command */ | |
| 126 | #define AV_IEN 0x20 /* enable interrupts */ | |
| 127 | ||
| 128 | enum wttype { | |
| 129 | UNKNOWN = 0, /* unknown type, driver disabled */ | |
| 130 | ARCHIVE, /* Archive Viper SC499, SC402 etc */ | |
| 131 | WANGTEK /* Wangtek */ | |
| 132 | }; | |
| 133 | ||
| 134 | typedef struct { | |
| 135 | unsigned short err; /* code for error encountered */ | |
| 136 | unsigned short ercnt; /* number of error blocks */ | |
| 137 | unsigned short urcnt; /* number of underruns */ | |
| 138 | } wtstatus_t; | |
| 139 | ||
| 140 | typedef struct { | |
| 141 | enum wttype type; /* type of controller */ | |
| 142 | unsigned unit; /* unit number */ | |
| 143 | unsigned port; /* base i/o port */ | |
| 144 | unsigned chan; /* dma channel number, 1..3 */ | |
| 145 | unsigned flags; /* state of tape drive */ | |
| 146 | unsigned dens; /* tape density */ | |
| 147 | int bsize; /* tape block size */ | |
| 148 | void *buf; /* internal i/o buffer */ | |
| 149 | ||
| 150 | void *dmavaddr; /* virtual address of dma i/o buffer */ | |
| 151 | unsigned dmatotal; /* size of i/o buffer */ | |
| 152 | unsigned dmaflags; /* i/o direction, ISADMA_READ or ISADMA_WRITE */ | |
| 153 | unsigned dmacount; /* resulting length of dma i/o */ | |
| 154 | ||
| 155 | wtstatus_t error; /* status of controller */ | |
| 156 | ||
| 157 | unsigned short DATAPORT, CMDPORT, STATPORT, CTLPORT, SDMAPORT, RDMAPORT; | |
| 158 | unsigned char BUSY, NOEXCEP, RESETMASK, RESETVAL; | |
| 159 | unsigned char ONLINE, RESET, REQUEST, IEN; | |
| 79b066dc | 160 | struct callout timeout_ch; |
| 984263bc MD |
161 | } wtinfo_t; |
| 162 | ||
| 163 | static wtinfo_t wttab[NWT]; /* tape info by unit number */ | |
| 164 | ||
| 165 | static int wtwait (wtinfo_t *t, int catch, char *msg); | |
| 166 | static int wtcmd (wtinfo_t *t, int cmd); | |
| 10f3fee5 | 167 | static int wtstart (wtinfo_t *t, unsigned flags, void *vaddr, unsigned len); |
| 984263bc MD |
168 | static void wtdma (wtinfo_t *t); |
| 169 | static timeout_t wtimer; | |
| 170 | static void wtclock (wtinfo_t *t); | |
| 171 | static int wtreset (wtinfo_t *t); | |
| 172 | static int wtsense (wtinfo_t *t, int verb, int ignor); | |
| 173 | static int wtstatus (wtinfo_t *t); | |
| 1b51b0fa | 174 | static void wtintr(void *); |
| 984263bc MD |
175 | static void wtrewind (wtinfo_t *t); |
| 176 | static int wtreadfm (wtinfo_t *t); | |
| 177 | static int wtwritefm (wtinfo_t *t); | |
| 178 | static int wtpoll (wtinfo_t *t, int mask, int bits); | |
| 179 | ||
| 180 | static d_open_t wtopen; | |
| 181 | static d_close_t wtclose; | |
| 182 | static d_ioctl_t wtioctl; | |
| 183 | static d_strategy_t wtstrategy; | |
| 184 | ||
| fef8985e | 185 | static struct dev_ops wt_ops = { |
| 88abd8b5 | 186 | { "wt", 0, 0 }, |
| fef8985e MD |
187 | .d_open = wtopen, |
| 188 | .d_close = wtclose, | |
| 189 | .d_read = physread, | |
| 190 | .d_write = physwrite, | |
| 191 | .d_ioctl = wtioctl, | |
| 192 | .d_strategy = wtstrategy, | |
| 984263bc MD |
193 | }; |
| 194 | ||
| 195 | ||
| 196 | /* | |
| 197 | * Probe for the presence of the device. | |
| 198 | */ | |
| 199 | static int | |
| 200 | wtprobe (struct isa_device *id) | |
| 201 | { | |
| 202 | wtinfo_t *t = wttab + id->id_unit; | |
| 984263bc | 203 | |
| 79b066dc | 204 | callout_init(&t->timeout_ch); |
| 984263bc MD |
205 | t->unit = id->id_unit; |
| 206 | t->chan = id->id_drq; | |
| 207 | t->port = id->id_iobase; | |
| 208 | if (t->chan<1 || t->chan>3) { | |
| e3869ec7 | 209 | kprintf ("wt%d: Bad drq=%d, should be 1..3\n", t->unit, t->chan); |
| 984263bc MD |
210 | return (0); |
| 211 | } | |
| 212 | ||
| 213 | /* Try Wangtek. */ | |
| 214 | t->type = WANGTEK; | |
| 215 | t->CTLPORT = WT_CTLPORT (t->port); t->STATPORT = WT_STATPORT (t->port); | |
| 216 | t->CMDPORT = WT_CMDPORT (t->port); t->DATAPORT = WT_DATAPORT (t->port); | |
| 217 | t->SDMAPORT = 0; t->RDMAPORT = 0; | |
| 218 | t->BUSY = WT_BUSY; t->NOEXCEP = WT_NOEXCEP; | |
| 219 | t->RESETMASK = WT_RESETMASK; t->RESETVAL = WT_RESETVAL; | |
| 220 | t->ONLINE = WT_ONLINE; t->RESET = WT_RESET; | |
| 221 | t->REQUEST = WT_REQUEST; t->IEN = WT_IEN; | |
| 222 | if (wtreset (t)) | |
| 223 | return (WT_NPORT); | |
| 224 | ||
| 225 | /* Try Archive. */ | |
| 226 | t->type = ARCHIVE; | |
| 227 | t->CTLPORT = AV_CTLPORT (t->port); t->STATPORT = AV_STATPORT (t->port); | |
| 228 | t->CMDPORT = AV_CMDPORT (t->port); t->DATAPORT = AV_DATAPORT (t->port); | |
| 229 | t->SDMAPORT = AV_SDMAPORT (t->port); t->RDMAPORT = AV_RDMAPORT (t->port); | |
| 230 | t->BUSY = AV_BUSY; t->NOEXCEP = AV_NOEXCEP; | |
| 231 | t->RESETMASK = AV_RESETMASK; t->RESETVAL = AV_RESETVAL; | |
| 232 | t->ONLINE = 0; t->RESET = AV_RESET; | |
| 233 | t->REQUEST = AV_REQUEST; t->IEN = AV_IEN; | |
| 234 | if (wtreset (t)) | |
| 235 | return (AV_NPORT); | |
| 236 | ||
| 237 | /* Tape controller not found. */ | |
| 238 | t->type = UNKNOWN; | |
| 239 | return (0); | |
| 240 | } | |
| 241 | ||
| 242 | /* | |
| 243 | * Device is found, configure it. | |
| 244 | */ | |
| 245 | static int | |
| 246 | wtattach (struct isa_device *id) | |
| 247 | { | |
| 248 | wtinfo_t *t = wttab + id->id_unit; | |
| 249 | ||
| 1b51b0fa | 250 | id->id_intr = (inthand2_t *)wtintr; |
| 984263bc | 251 | if (t->type == ARCHIVE) { |
| e3869ec7 | 252 | kprintf ("wt%d: type <Archive>\n", t->unit); |
| 984263bc MD |
253 | outb (t->RDMAPORT, 0); /* reset dma */ |
| 254 | } else | |
| e3869ec7 | 255 | kprintf ("wt%d: type <Wangtek>\n", t->unit); |
| 984263bc MD |
256 | t->flags = TPSTART; /* tape is rewound */ |
| 257 | t->dens = -1; /* unknown density */ | |
| 258 | isa_dmainit(t->chan, 1024); | |
| 259 | ||
| fef8985e | 260 | make_dev(&wt_ops, id->id_unit, 0, 0, 0600, "rwt%d", id->id_unit); |
| 984263bc MD |
261 | return (1); |
| 262 | } | |
| 263 | ||
| 264 | struct isa_driver wtdriver = { wtprobe, wtattach, "wt", }; | |
| 265 | ||
| 266 | /* | |
| 267 | * Open routine, called on every device open. | |
| 268 | */ | |
| 269 | static int | |
| fef8985e | 270 | wtopen (struct dev_open_args *ap) |
| 984263bc | 271 | { |
| b13267a5 | 272 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
273 | int u = minor (dev) & T_UNIT; |
| 274 | wtinfo_t *t = wttab + u; | |
| 275 | int error; | |
| 276 | ||
| 277 | if (u >= NWT || t->type == UNKNOWN) | |
| 278 | return (ENXIO); | |
| 279 | ||
| 280 | /* Check that device is not in use */ | |
| 281 | if (t->flags & TPINUSE) | |
| 282 | return (EBUSY); | |
| 283 | ||
| 284 | /* If the tape is in rewound state, check the status and set density. */ | |
| 285 | if (t->flags & TPSTART) { | |
| 286 | /* If rewind is going on, wait */ | |
| 287 | error = wtwait (t, PCATCH, "wtrew"); | |
| 288 | if (error) | |
| 289 | return (error); | |
| 290 | ||
| 291 | /* Check the controller status */ | |
| fef8985e | 292 | if (! wtsense (t, 0, (ap->a_oflags & FWRITE) ? 0 : TP_WRP)) { |
| 984263bc MD |
293 | /* Bad status, reset the controller */ |
| 294 | if (! wtreset (t)) | |
| 295 | return (EIO); | |
| fef8985e | 296 | if (! wtsense (t, 1, (ap->a_oflags & FWRITE) ? 0 : TP_WRP)) |
| 984263bc MD |
297 | return (EIO); |
| 298 | } | |
| 299 | ||
| 300 | /* Set up tape density. */ | |
| 301 | if (t->dens != (minor (dev) & WT_DENSEL)) { | |
| 302 | int d = 0; | |
| 303 | ||
| 304 | switch (minor (dev) & WT_DENSEL) { | |
| 305 | case WT_DENSDFLT: default: break; /* default density */ | |
| 306 | case WT_QIC11: d = QIC_FMT11; break; /* minor 010 */ | |
| 307 | case WT_QIC24: d = QIC_FMT24; break; /* minor 020 */ | |
| 308 | case WT_QIC120: d = QIC_FMT120; break; /* minor 030 */ | |
| 309 | case WT_QIC150: d = QIC_FMT150; break; /* minor 040 */ | |
| 310 | case WT_QIC300: d = QIC_FMT300; break; /* minor 050 */ | |
| 311 | case WT_QIC600: d = QIC_FMT600; break; /* minor 060 */ | |
| 312 | } | |
| 313 | if (d) { | |
| 314 | /* Change tape density. */ | |
| 315 | if (! wtcmd (t, d)) | |
| 316 | return (EIO); | |
| 317 | if (! wtsense (t, 1, TP_WRP | TP_ILL)) | |
| 318 | return (EIO); | |
| 319 | ||
| 320 | /* Check the status of the controller. */ | |
| 321 | if (t->error.err & TP_ILL) { | |
| e3869ec7 | 322 | kprintf ("wt%d: invalid tape density\n", t->unit); |
| 984263bc MD |
323 | return (ENODEV); |
| 324 | } | |
| 325 | } | |
| 326 | t->dens = minor (dev) & WT_DENSEL; | |
| 327 | } | |
| 328 | t->flags &= ~TPSTART; | |
| 329 | } else if (t->dens != (minor (dev) & WT_DENSEL)) | |
| 330 | return (ENXIO); | |
| 331 | ||
| 332 | t->bsize = (minor (dev) & WT_BSIZE) ? 1024 : 512; | |
| efda3bd0 | 333 | t->buf = kmalloc (t->bsize, M_TEMP, M_WAITOK); |
| 984263bc MD |
334 | |
| 335 | if (isa_dma_acquire(t->chan)) | |
| 336 | return(EBUSY); | |
| 337 | ||
| 338 | t->flags = TPINUSE; | |
| 339 | ||
| fef8985e | 340 | if (ap->a_oflags & FREAD) |
| 984263bc | 341 | t->flags |= TPREAD; |
| fef8985e | 342 | if (ap->a_oflags & FWRITE) |
| 984263bc MD |
343 | t->flags |= TPWRITE; |
| 344 | return (0); | |
| 345 | } | |
| 346 | ||
| 347 | /* | |
| 348 | * Close routine, called on last device close. | |
| 349 | */ | |
| 350 | static int | |
| fef8985e | 351 | wtclose (struct dev_close_args *ap) |
| 984263bc | 352 | { |
| b13267a5 | 353 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
354 | int u = minor (dev) & T_UNIT; |
| 355 | wtinfo_t *t = wttab + u; | |
| 356 | ||
| 357 | if (u >= NWT || t->type == UNKNOWN) | |
| 358 | return (ENXIO); | |
| 359 | ||
| 360 | /* If rewind is pending, do nothing */ | |
| 361 | if (t->flags & TPREW) | |
| 362 | goto done; | |
| 363 | ||
| 364 | /* If seek forward is pending and no rewind on close, do nothing */ | |
| 365 | if (t->flags & TPRMARK) { | |
| 366 | if (minor (dev) & T_NOREWIND) | |
| 367 | goto done; | |
| 368 | ||
| 369 | /* If read file mark is going on, wait */ | |
| 370 | wtwait (t, 0, "wtrfm"); | |
| 371 | } | |
| 372 | ||
| 373 | if (t->flags & TPWANY) | |
| 374 | /* Tape was written. Write file mark. */ | |
| 375 | wtwritefm (t); | |
| 376 | ||
| 377 | if (! (minor (dev) & T_NOREWIND)) { | |
| 378 | /* Rewind tape to beginning of tape. */ | |
| 379 | /* Don't wait until rewind, though. */ | |
| 380 | wtrewind (t); | |
| 381 | goto done; | |
| 382 | } | |
| 383 | if ((t->flags & TPRANY) && ! (t->flags & (TPVOL | TPWANY))) | |
| 384 | /* Space forward to after next file mark if no writing done. */ | |
| 385 | /* Don't wait for completion. */ | |
| 386 | wtreadfm (t); | |
| 387 | done: | |
| 388 | t->flags &= TPREW | TPRMARK | TPSTART | TPTIMER; | |
| efda3bd0 | 389 | kfree (t->buf, M_TEMP); |
| 984263bc MD |
390 | isa_dma_release(t->chan); |
| 391 | return (0); | |
| 392 | } | |
| 393 | ||
| 394 | /* | |
| 395 | * Ioctl routine. Compatible with BSD ioctls. | |
| 396 | * There are two possible ioctls: | |
| 397 | * ioctl (int fd, MTIOCGET, struct mtget *buf) -- get status | |
| 398 | * ioctl (int fd, MTIOCTOP, struct mtop *buf) -- do BSD-like op | |
| 399 | */ | |
| 400 | static int | |
| fef8985e | 401 | wtioctl (struct dev_ioctl_args *ap) |
| 984263bc | 402 | { |
| b13267a5 | 403 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 404 | caddr_t arg = ap->a_data; |
| 984263bc MD |
405 | int u = minor (dev) & T_UNIT; |
| 406 | wtinfo_t *t = wttab + u; | |
| 407 | int error, count, op; | |
| 408 | ||
| 409 | if (u >= NWT || t->type == UNKNOWN) | |
| 410 | return (ENXIO); | |
| 411 | ||
| fef8985e | 412 | switch (ap->a_cmd) { |
| 984263bc MD |
413 | default: |
| 414 | return (EINVAL); | |
| 415 | case MTIOCIEOT: /* ignore EOT errors */ | |
| 416 | case MTIOCEEOT: /* enable EOT errors */ | |
| 417 | return (0); | |
| 418 | case MTIOCGET: | |
| 419 | ((struct mtget*)arg)->mt_type = | |
| 420 | t->type == ARCHIVE ? MT_ISVIPER1 : 0x11; | |
| 421 | ((struct mtget*)arg)->mt_dsreg = t->flags; /* status */ | |
| 422 | ((struct mtget*)arg)->mt_erreg = t->error.err; /* errors */ | |
| 423 | ((struct mtget*)arg)->mt_resid = 0; | |
| 424 | ((struct mtget*)arg)->mt_fileno = 0; /* file */ | |
| 425 | ((struct mtget*)arg)->mt_blkno = 0; /* block */ | |
| 426 | return (0); | |
| 427 | case MTIOCTOP: | |
| 428 | break; | |
| 429 | } | |
| 430 | switch ((short) ((struct mtop*)arg)->mt_op) { | |
| 431 | default: | |
| 432 | case MTFSR: /* forward space record */ | |
| 433 | case MTBSR: /* backward space record */ | |
| 434 | case MTBSF: /* backward space file */ | |
| 435 | break; | |
| 436 | case MTNOP: /* no operation, sets status only */ | |
| 437 | case MTCACHE: /* enable controller cache */ | |
| 438 | case MTNOCACHE: /* disable controller cache */ | |
| 439 | return (0); | |
| 440 | case MTREW: /* rewind */ | |
| 441 | case MTOFFL: /* rewind and put the drive offline */ | |
| 442 | if (t->flags & TPREW) /* rewind is running */ | |
| 443 | return (0); | |
| 444 | error = wtwait (t, PCATCH, "wtorew"); | |
| 445 | if (error) | |
| 446 | return (error); | |
| 447 | wtrewind (t); | |
| 448 | return (0); | |
| 449 | case MTFSF: /* forward space file */ | |
| 450 | for (count=((struct mtop*)arg)->mt_count; count>0; --count) { | |
| 451 | error = wtwait (t, PCATCH, "wtorfm"); | |
| 452 | if (error) | |
| 453 | return (error); | |
| 454 | error = wtreadfm (t); | |
| 455 | if (error) | |
| 456 | return (error); | |
| 457 | } | |
| 458 | return (0); | |
| 459 | case MTWEOF: /* write an end-of-file record */ | |
| 460 | if (! (t->flags & TPWRITE) || (t->flags & TPWP)) | |
| 461 | return (EACCES); | |
| 462 | error = wtwait (t, PCATCH, "wtowfm"); | |
| 463 | if (error) | |
| 464 | return (error); | |
| 465 | error = wtwritefm (t); | |
| 466 | if (error) | |
| 467 | return (error); | |
| 468 | return (0); | |
| 469 | case MTRETENS: /* re-tension tape */ | |
| 470 | error = wtwait (t, PCATCH, "wtretens"); | |
| 471 | if (error) | |
| 472 | return (error); | |
| 473 | op = QIC_RETENS; | |
| 474 | goto erase_retens; | |
| 475 | ||
| 476 | case MTERASE: /* erase to EOM */ | |
| 477 | if (! (t->flags & TPWRITE) || (t->flags & TPWP)) | |
| 478 | return (EACCES); | |
| 479 | error = wtwait (t, PCATCH, "wterase"); | |
| 480 | if (error) | |
| 481 | return (error); | |
| 482 | op = QIC_ERASE; | |
| 483 | erase_retens: | |
| 484 | /* ERASE and RETENS operations work like REWIND. */ | |
| 485 | /* Simulate the rewind operation here. */ | |
| 486 | t->flags &= ~(TPRO | TPWO | TPVOL); | |
| 487 | if (! wtcmd (t, op)) | |
| 488 | return (EIO); | |
| 489 | t->flags |= TPSTART | TPREW; | |
| 490 | t->flags |= TPWANY; | |
| 491 | wtclock (t); | |
| 492 | return (0); | |
| 493 | } | |
| 494 | return (EINVAL); | |
| 495 | } | |
| 496 | ||
| 497 | /* | |
| 498 | * Strategy routine. | |
| 499 | */ | |
| fef8985e MD |
500 | static int |
| 501 | wtstrategy (struct dev_strategy_args *ap) | |
| 984263bc | 502 | { |
| b13267a5 | 503 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 504 | struct bio *bio = ap->a_bio; |
| 81b5c339 MD |
505 | struct buf *bp = bio->bio_buf; |
| 506 | int u = minor(dev) & T_UNIT; | |
| 984263bc | 507 | wtinfo_t *t = wttab + u; |
| 10f3fee5 | 508 | unsigned flags; |
| 984263bc MD |
509 | |
| 510 | bp->b_resid = bp->b_bcount; | |
| 511 | if (u >= NWT || t->type == UNKNOWN) { | |
| 512 | bp->b_error = ENXIO; | |
| 513 | goto err2xit; | |
| 514 | } | |
| 515 | ||
| 516 | /* at file marks and end of tape, we just return '0 bytes available' */ | |
| 517 | if (t->flags & TPVOL) | |
| 518 | goto xit; | |
| 519 | ||
| 520 | if (bp->b_bcount % t->bsize != 0) { | |
| 521 | bp->b_error = EINVAL; | |
| 522 | goto err2xit; | |
| 523 | } | |
| 524 | ||
| 10f3fee5 | 525 | if (bp->b_cmd == BUF_CMD_READ) { |
| 984263bc MD |
526 | /* Check read access and no previous write to this tape. */ |
| 527 | if (! (t->flags & TPREAD) || (t->flags & TPWANY)) | |
| 528 | goto errxit; | |
| 529 | ||
| 530 | /* For now, we assume that all data will be copied out */ | |
| 531 | /* If read command outstanding, just skip down */ | |
| 532 | if (! (t->flags & TPRO)) { | |
| 533 | if (! wtsense (t, 1, TP_WRP)) /* clear status */ | |
| 534 | goto errxit; | |
| 535 | if (! wtcmd (t, QIC_RDDATA)) { /* sed read mode */ | |
| 536 | wtsense (t, 1, TP_WRP); | |
| 537 | goto errxit; | |
| 538 | } | |
| 539 | t->flags |= TPRO | TPRANY; | |
| 540 | } | |
| 10f3fee5 | 541 | flags = ISADMA_READ; |
| 984263bc MD |
542 | } else { |
| 543 | /* Check write access and write protection. */ | |
| 544 | /* No previous read from this tape allowed. */ | |
| 545 | if (! (t->flags & TPWRITE) || (t->flags & (TPWP | TPRANY))) | |
| 546 | goto errxit; | |
| 547 | ||
| 548 | /* If write command outstanding, just skip down */ | |
| 549 | if (! (t->flags & TPWO)) { | |
| 550 | if (! wtsense (t, 1, 0)) /* clear status */ | |
| 551 | goto errxit; | |
| 552 | if (! wtcmd (t, QIC_WRTDATA)) { /* set write mode */ | |
| 553 | wtsense (t, 1, 0); | |
| 554 | goto errxit; | |
| 555 | } | |
| 556 | t->flags |= TPWO | TPWANY; | |
| 557 | } | |
| 10f3fee5 | 558 | flags = ISADMA_WRITE; |
| 984263bc MD |
559 | } |
| 560 | ||
| 561 | if (! bp->b_bcount) | |
| 562 | goto xit; | |
| 563 | ||
| 564 | t->flags &= ~TPEXCEP; | |
| d9ed64be | 565 | crit_enter(); |
| 10f3fee5 MD |
566 | if (wtstart (t, flags, bp->b_data, bp->b_bcount)) { |
| 567 | wtwait (t, 0, (flags & ISADMA_READ) ? "wtread" : "wtwrite"); | |
| 984263bc MD |
568 | bp->b_resid -= t->dmacount; |
| 569 | } | |
| d9ed64be | 570 | crit_exit(); |
| 984263bc MD |
571 | |
| 572 | if (t->flags & TPEXCEP) { | |
| 573 | errxit: bp->b_error = EIO; | |
| 574 | err2xit: bp->b_flags |= B_ERROR; | |
| 575 | } | |
| 81b5c339 | 576 | xit: biodone (bio); |
| fef8985e | 577 | return(0); |
| 984263bc MD |
578 | } |
| 579 | ||
| 580 | /* | |
| 581 | * Interrupt routine. | |
| 582 | */ | |
| 583 | static void | |
| 477d3c1c | 584 | wtintr (void *arg) |
| 984263bc | 585 | { |
| 477d3c1c | 586 | int u = (int)arg; |
| 984263bc MD |
587 | wtinfo_t *t = wttab + u; |
| 588 | unsigned char s; | |
| 589 | ||
| 590 | if (u >= NWT || t->type == UNKNOWN) { | |
| 591 | TRACE (("wtintr() -- device not configured\n")); | |
| 592 | return; | |
| 593 | } | |
| 594 | ||
| 595 | s = inb (t->STATPORT); /* get status */ | |
| 596 | TRACE (("wtintr() status=0x%x -- ", s)); | |
| 597 | if ((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP)) { | |
| 598 | TRACE (("busy\n")); | |
| 599 | return; /* device is busy */ | |
| 600 | } | |
| 601 | ||
| 602 | /* | |
| 603 | * Check if rewind finished. | |
| 604 | */ | |
| 605 | if (t->flags & TPREW) { | |
| 606 | TRACE (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ? | |
| 607 | "rewind busy?\n" : "rewind finished\n")); | |
| 608 | t->flags &= ~TPREW; /* Rewind finished. */ | |
| 609 | wtsense (t, 1, TP_WRP); | |
| 610 | wakeup ((caddr_t)t); | |
| 611 | return; | |
| 612 | } | |
| 613 | ||
| 614 | /* | |
| 615 | * Check if writing/reading of file mark finished. | |
| 616 | */ | |
| 617 | if (t->flags & (TPRMARK | TPWMARK)) { | |
| 618 | TRACE (((s & (t->BUSY | t->NOEXCEP)) == (t->BUSY | t->NOEXCEP) ? | |
| 619 | "marker r/w busy?\n" : "marker r/w finished\n")); | |
| 620 | if (! (s & t->NOEXCEP)) /* operation failed */ | |
| 621 | wtsense (t, 1, (t->flags & TPRMARK) ? TP_WRP : 0); | |
| 622 | t->flags &= ~(TPRMARK | TPWMARK); /* operation finished */ | |
| 623 | wakeup ((caddr_t)t); | |
| 624 | return; | |
| 625 | } | |
| 626 | ||
| 627 | /* | |
| 628 | * Do we started any i/o? If no, just return. | |
| 629 | */ | |
| 630 | if (! (t->flags & TPACTIVE)) { | |
| 631 | TRACE (("unexpected interrupt\n")); | |
| 632 | return; | |
| 633 | } | |
| 634 | ||
| 635 | /* | |
| 636 | * Clean up dma. | |
| 637 | */ | |
| 638 | if ((t->dmaflags & ISADMA_READ) && (t->dmatotal - t->dmacount) < t->bsize) { | |
| 639 | /* If reading short block, copy the internal buffer | |
| 640 | * to the user memory. */ | |
| 641 | isa_dmadone (t->dmaflags, t->buf, t->bsize, t->chan); | |
| 642 | bcopy (t->buf, t->dmavaddr, t->dmatotal - t->dmacount); | |
| 643 | } else | |
| 644 | isa_dmadone (t->dmaflags, t->dmavaddr, t->bsize, t->chan); | |
| 645 | ||
| 646 | t->flags &= ~TPACTIVE; | |
| 647 | t->dmacount += t->bsize; | |
| 648 | t->dmavaddr = (char *)t->dmavaddr + t->bsize; | |
| 649 | ||
| 650 | /* | |
| 651 | * On exception, check for end of file and end of volume. | |
| 652 | */ | |
| 653 | if (! (s & t->NOEXCEP)) { | |
| 654 | TRACE (("i/o exception\n")); | |
| 655 | wtsense (t, 1, (t->dmaflags & ISADMA_READ) ? TP_WRP : 0); | |
| 656 | if (t->error.err & (TP_EOM | TP_FIL)) | |
| 657 | t->flags |= TPVOL; /* end of file */ | |
| 658 | else | |
| 659 | t->flags |= TPEXCEP; /* i/o error */ | |
| 660 | wakeup ((caddr_t)t); | |
| 661 | return; | |
| 662 | } | |
| 663 | ||
| 664 | if (t->dmacount < t->dmatotal) { /* continue i/o */ | |
| 665 | wtdma (t); | |
| 666 | TRACE (("continue i/o, %d\n", t->dmacount)); | |
| 667 | return; | |
| 668 | } | |
| 669 | if (t->dmacount > t->dmatotal) /* short last block */ | |
| 670 | t->dmacount = t->dmatotal; | |
| 671 | wakeup ((caddr_t)t); /* wake up user level */ | |
| 672 | TRACE (("i/o finished, %d\n", t->dmacount)); | |
| 673 | } | |
| 674 | ||
| 675 | /* start the rewind operation */ | |
| 676 | static void | |
| 677 | wtrewind (wtinfo_t *t) | |
| 678 | { | |
| 679 | int rwmode = (t->flags & (TPRO | TPWO)); | |
| 680 | ||
| 681 | t->flags &= ~(TPRO | TPWO | TPVOL); | |
| 682 | /* | |
| 683 | * Wangtek strictly follows QIC-02 standard: | |
| 684 | * clearing ONLINE in read/write modes causes rewind. | |
| 685 | * REWIND command is not allowed in read/write mode | |
| 686 | * and gives `illegal command' error. | |
| 687 | */ | |
| 688 | if (t->type==WANGTEK && rwmode) { | |
| 689 | outb (t->CTLPORT, 0); | |
| 690 | } else if (! wtcmd (t, QIC_REWIND)) | |
| 691 | return; | |
| 692 | t->flags |= TPSTART | TPREW; | |
| 693 | wtclock (t); | |
| 694 | } | |
| 695 | ||
| 696 | /* start the `read marker' operation */ | |
| 697 | static int | |
| 698 | wtreadfm (wtinfo_t *t) | |
| 699 | { | |
| 700 | t->flags &= ~(TPRO | TPWO | TPVOL); | |
| 701 | if (! wtcmd (t, QIC_READFM)) { | |
| 702 | wtsense (t, 1, TP_WRP); | |
| 703 | return (EIO); | |
| 704 | } | |
| 705 | t->flags |= TPRMARK | TPRANY; | |
| 706 | wtclock (t); | |
| 707 | /* Don't wait for completion here. */ | |
| 708 | return (0); | |
| 709 | } | |
| 710 | ||
| 711 | /* write marker to the tape */ | |
| 712 | static int | |
| 713 | wtwritefm (wtinfo_t *t) | |
| 714 | { | |
| 377d4740 | 715 | tsleep ((caddr_t)wtwritefm, 0, "wtwfm", hz); /* timeout: 1 second */ |
| 984263bc MD |
716 | t->flags &= ~(TPRO | TPWO); |
| 717 | if (! wtcmd (t, QIC_WRITEFM)) { | |
| 718 | wtsense (t, 1, 0); | |
| 719 | return (EIO); | |
| 720 | } | |
| 721 | t->flags |= TPWMARK | TPWANY; | |
| 722 | wtclock (t); | |
| 723 | return (wtwait (t, 0, "wtwfm")); | |
| 724 | } | |
| 725 | ||
| 726 | /* while controller status & mask == bits continue waiting */ | |
| 727 | static int | |
| 728 | wtpoll (wtinfo_t *t, int mask, int bits) | |
| 729 | { | |
| 730 | int s, i; | |
| 731 | ||
| 732 | /* Poll status port, waiting for specified bits. */ | |
| 733 | for (i=0; i<1000; ++i) { /* up to 1 msec */ | |
| 734 | s = inb (t->STATPORT); | |
| 735 | if ((s & mask) != bits) | |
| 736 | return (s); | |
| 737 | DELAY (1); | |
| 738 | } | |
| 739 | for (i=0; i<100; ++i) { /* up to 10 msec */ | |
| 740 | s = inb (t->STATPORT); | |
| 741 | if ((s & mask) != bits) | |
| 742 | return (s); | |
| 743 | DELAY (100); | |
| 744 | } | |
| 745 | for (;;) { /* forever */ | |
| 746 | s = inb (t->STATPORT); | |
| 747 | if ((s & mask) != bits) | |
| 748 | return (s); | |
| 377d4740 | 749 | tsleep ((caddr_t)wtpoll, 0, "wtpoll", 1); /* timeout: 1 tick */ |
| 984263bc MD |
750 | } |
| 751 | } | |
| 752 | ||
| 753 | /* execute QIC command */ | |
| 754 | static int | |
| 755 | wtcmd (wtinfo_t *t, int cmd) | |
| 756 | { | |
| d9ed64be | 757 | int s; |
| 984263bc MD |
758 | |
| 759 | TRACE (("wtcmd() cmd=0x%x\n", cmd)); | |
| d9ed64be | 760 | crit_enter(); |
| 984263bc MD |
761 | s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */ |
| 762 | if (! (s & t->NOEXCEP)) { /* error */ | |
| d9ed64be | 763 | crit_exit(); |
| 984263bc MD |
764 | return (0); |
| 765 | } | |
| 766 | ||
| 767 | outb (t->CMDPORT, cmd); /* output the command */ | |
| 768 | ||
| 769 | outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ | |
| 770 | wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */ | |
| 771 | outb (t->CTLPORT, t->IEN | t->ONLINE); /* reset request */ | |
| 772 | wtpoll (t, t->BUSY, 0); /* wait for not ready */ | |
| d9ed64be | 773 | crit_exit(); |
| 984263bc MD |
774 | return (1); |
| 775 | } | |
| 776 | ||
| 777 | /* wait for the end of i/o, seeking marker or rewind operation */ | |
| 778 | static int | |
| 779 | wtwait (wtinfo_t *t, int catch, char *msg) | |
| 780 | { | |
| 781 | int error; | |
| 782 | ||
| 783 | TRACE (("wtwait() `%s'\n", msg)); | |
| 784 | while (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)) { | |
| 377d4740 | 785 | error = tsleep ((caddr_t)t, catch, msg, 0); |
| 984263bc MD |
786 | if (error) |
| 787 | return (error); | |
| 788 | } | |
| 789 | return (0); | |
| 790 | } | |
| 791 | ||
| 792 | /* initialize dma for the i/o operation */ | |
| 793 | static void | |
| 794 | wtdma (wtinfo_t *t) | |
| 795 | { | |
| 796 | t->flags |= TPACTIVE; | |
| 797 | wtclock (t); | |
| 798 | ||
| 799 | if (t->type == ARCHIVE) | |
| 800 | outb (t->SDMAPORT, 0); /* set dma */ | |
| 801 | ||
| 802 | if ((t->dmaflags & ISADMA_READ) && (t->dmatotal - t->dmacount) < t->bsize) | |
| 803 | /* Reading short block. Do it through the internal buffer. */ | |
| 804 | isa_dmastart (t->dmaflags, t->buf, t->bsize, t->chan); | |
| 805 | else | |
| 806 | isa_dmastart (t->dmaflags, t->dmavaddr, t->bsize, t->chan); | |
| 807 | } | |
| 808 | ||
| 809 | /* start i/o operation */ | |
| 810 | static int | |
| 811 | wtstart (wtinfo_t *t, unsigned flags, void *vaddr, unsigned len) | |
| 812 | { | |
| d9ed64be | 813 | int s; |
| 984263bc MD |
814 | |
| 815 | TRACE (("wtstart()\n")); | |
| d9ed64be | 816 | crit_enter(); |
| 984263bc MD |
817 | s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */ |
| 818 | if (! (s & t->NOEXCEP)) { | |
| 819 | t->flags |= TPEXCEP; /* error */ | |
| d9ed64be | 820 | crit_exit(); |
| 984263bc MD |
821 | return (0); |
| 822 | } | |
| 823 | t->flags &= ~TPEXCEP; /* clear exception flag */ | |
| 824 | t->dmavaddr = vaddr; | |
| 825 | t->dmatotal = len; | |
| 826 | t->dmacount = 0; | |
| 827 | t->dmaflags = flags; | |
| 828 | wtdma (t); | |
| d9ed64be | 829 | crit_exit(); |
| 984263bc MD |
830 | return (1); |
| 831 | } | |
| 832 | ||
| 833 | /* start timer */ | |
| 834 | static void | |
| 835 | wtclock (wtinfo_t *t) | |
| 836 | { | |
| 837 | if (! (t->flags & TPTIMER)) { | |
| 838 | t->flags |= TPTIMER; | |
| 839 | /* Some controllers seem to lose dma interrupts too often. | |
| 840 | * To make the tape stream we need 1 tick timeout. */ | |
| 79b066dc MD |
841 | callout_reset(&t->timeout_ch, ((t->flags & TPACTIVE) ? 1 : hz), |
| 842 | wtimer, t); | |
| 984263bc MD |
843 | } |
| 844 | } | |
| 845 | ||
| 846 | /* | |
| 847 | * Simulate an interrupt periodically while i/o is going. | |
| 848 | * This is necessary in case interrupts get eaten due to | |
| 849 | * multiple devices on a single IRQ line. | |
| 850 | */ | |
| 851 | static void | |
| 852 | wtimer (void *xt) | |
| 853 | { | |
| 854 | wtinfo_t *t = (wtinfo_t *)xt; | |
| 984263bc MD |
855 | |
| 856 | t->flags &= ~TPTIMER; | |
| 857 | if (! (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK))) | |
| 858 | return; | |
| 859 | ||
| 860 | /* If i/o going, simulate interrupt. */ | |
| d9ed64be | 861 | crit_enter(); |
| 984263bc MD |
862 | if ((inb (t->STATPORT) & (t->BUSY | t->NOEXCEP)) != (t->BUSY | t->NOEXCEP)) { |
| 863 | TRACE (("wtimer() -- ")); | |
| 1b51b0fa | 864 | wtintr ((void *)t->unit); |
| 984263bc | 865 | } |
| d9ed64be | 866 | crit_exit(); |
| 984263bc MD |
867 | |
| 868 | /* Restart timer if i/o pending. */ | |
| 869 | if (t->flags & (TPACTIVE | TPREW | TPRMARK | TPWMARK)) | |
| 870 | wtclock (t); | |
| 871 | } | |
| 872 | ||
| 873 | /* reset the controller */ | |
| 874 | static int | |
| 875 | wtreset (wtinfo_t *t) | |
| 876 | { | |
| 877 | /* Perform QIC-02 and QIC-36 compatible reset sequence. */ | |
| 878 | /* Thanks to Mikael Hybsch <micke@dynas.se>. */ | |
| 879 | int s, i; | |
| 880 | ||
| 881 | outb (t->CTLPORT, t->RESET | t->ONLINE); /* send reset */ | |
| 882 | DELAY (30); | |
| 883 | outb (t->CTLPORT, t->ONLINE); /* turn off reset */ | |
| 884 | DELAY (30); | |
| 885 | ||
| 886 | /* Read the controller status. */ | |
| 887 | s = inb (t->STATPORT); | |
| 888 | if (s == 0xff) /* no port at this address? */ | |
| 889 | return (0); | |
| 890 | ||
| 891 | /* Wait 3 sec for reset to complete. Needed for QIC-36 boards? */ | |
| 892 | for (i=0; i<3000; ++i) { | |
| 893 | if (! (s & t->BUSY) || ! (s & t->NOEXCEP)) | |
| 894 | break; | |
| 895 | DELAY (1000); | |
| 896 | s = inb (t->STATPORT); | |
| 897 | } | |
| 898 | return ((s & t->RESETMASK) == t->RESETVAL); | |
| 899 | } | |
| 900 | ||
| 901 | /* get controller status information */ | |
| 902 | /* return 0 if user i/o request should receive an i/o error code */ | |
| 903 | static int | |
| 904 | wtsense (wtinfo_t *t, int verb, int ignor) | |
| 905 | { | |
| 906 | char *msg = 0; | |
| 907 | int err; | |
| 908 | ||
| 909 | TRACE (("wtsense() ignor=0x%x\n", ignor)); | |
| 910 | t->flags &= ~(TPRO | TPWO); | |
| 911 | if (! wtstatus (t)) | |
| 912 | return (0); | |
| 913 | if (! (t->error.err & TP_ST0)) | |
| 914 | t->error.err &= ~TP_ST0MASK; | |
| 915 | if (! (t->error.err & TP_ST1)) | |
| 916 | t->error.err &= ~TP_ST1MASK; | |
| 917 | t->error.err &= ~ignor; /* ignore certain errors */ | |
| 918 | err = t->error.err & (TP_FIL | TP_BNL | TP_UDA | TP_EOM | TP_WRP | | |
| 919 | TP_USL | TP_CNI | TP_MBD | TP_NDT | TP_ILL); | |
| 920 | if (! err) | |
| 921 | return (1); | |
| 922 | if (! verb) | |
| 923 | return (0); | |
| 924 | ||
| 925 | /* lifted from tdriver.c from Wangtek */ | |
| 926 | if (err & TP_USL) msg = "Drive not online"; | |
| 927 | else if (err & TP_CNI) msg = "No cartridge"; | |
| 928 | else if ((err & TP_WRP) && !(t->flags & TPWP)) { | |
| 929 | msg = "Tape is write protected"; | |
| 930 | t->flags |= TPWP; | |
| 931 | } | |
| 932 | else if (err & TP_FIL) msg = 0 /*"Filemark detected"*/; | |
| 933 | else if (err & TP_EOM) msg = 0 /*"End of tape"*/; | |
| 934 | else if (err & TP_BNL) msg = "Block not located"; | |
| 935 | else if (err & TP_UDA) msg = "Unrecoverable data error"; | |
| 936 | else if (err & TP_NDT) msg = "No data detected"; | |
| 937 | else if (err & TP_ILL) msg = "Illegal command"; | |
| 938 | if (msg) | |
| e3869ec7 | 939 | kprintf ("wt%d: %s\n", t->unit, msg); |
| 984263bc MD |
940 | return (0); |
| 941 | } | |
| 942 | ||
| 943 | /* get controller status information */ | |
| 944 | static int | |
| 945 | wtstatus (wtinfo_t *t) | |
| 946 | { | |
| 947 | char *p; | |
| 984263bc | 948 | |
| d9ed64be | 949 | crit_enter(); |
| 984263bc MD |
950 | wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); /* ready? */ |
| 951 | outb (t->CMDPORT, QIC_RDSTAT); /* send `read status' command */ | |
| 952 | ||
| 953 | outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ | |
| 954 | wtpoll (t, t->BUSY, t->BUSY); /* wait for ready */ | |
| 955 | outb (t->CTLPORT, t->ONLINE); /* reset request */ | |
| 956 | wtpoll (t, t->BUSY, 0); /* wait for not ready */ | |
| 957 | ||
| 958 | p = (char*) &t->error; | |
| 959 | while (p < (char*)&t->error + 6) { | |
| 960 | int s = wtpoll (t, t->BUSY | t->NOEXCEP, t->BUSY | t->NOEXCEP); | |
| 961 | if (! (s & t->NOEXCEP)) { /* error */ | |
| d9ed64be | 962 | crit_exit(); |
| 984263bc MD |
963 | return (0); |
| 964 | } | |
| 965 | ||
| 966 | *p++ = inb (t->DATAPORT); /* read status byte */ | |
| 967 | ||
| 968 | outb (t->CTLPORT, t->REQUEST | t->ONLINE); /* set request */ | |
| 969 | wtpoll (t, t->BUSY, 0); /* wait for not ready */ | |
| 970 | DELAY(20); | |
| 971 | outb (t->CTLPORT, t->ONLINE); /* unset request */ | |
| 972 | } | |
| d9ed64be | 973 | crit_exit(); |
| 984263bc MD |
974 | return (1); |
| 975 | } |