summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/wfd.c
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>1998-01-27 16:49:35 +0000
committermsmith <msmith@FreeBSD.org>1998-01-27 16:49:35 +0000
commit23fa2f94de89e09e3e22caed55fd3f2798d803dd (patch)
treedbbfdc35cccbdc6f80f69236de7341869cdfb995 /sys/i386/isa/wfd.c
parent3e3506a667ca4ce723231d410251c5b60d25ea9b (diff)
downloadFreeBSD-src-23fa2f94de89e09e3e22caed55fd3f2798d803dd.zip
FreeBSD-src-23fa2f94de89e09e3e22caed55fd3f2798d803dd.tar.gz
Fix operation with the Iomega Zip 100 ATAPI.
All known versions of this drive (firmware 21.* and 23.*) will lock up if presented with a read/write request of > 64 blocks. In the presence of such a unit, I/O requests of > 64 blocks are fragmented to avoid this.
Diffstat (limited to 'sys/i386/isa/wfd.c')
-rw-r--r--sys/i386/isa/wfd.c105
1 files changed, 92 insertions, 13 deletions
diff --git a/sys/i386/isa/wfd.c b/sys/i386/isa/wfd.c
index 41e78c5..264b904 100644
--- a/sys/i386/isa/wfd.c
+++ b/sys/i386/isa/wfd.c
@@ -23,7 +23,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * $Id: wfd.c,v 1.1 1998/01/16 22:13:07 pst Exp $
+ * $Id: wfd.c,v 1.2 1998/01/16 22:43:33 pst Exp $
*/
/*
@@ -131,6 +131,7 @@ struct wfd {
int lun; /* Logical device unit */
int flags; /* Device state flags */
int refcnt; /* The number of raw opens */
+ int maxblks; /* transfer size limit */
struct buf_queue_head buf_queue; /* Queue of i/o requests */
struct atapi_params *param; /* Drive parameters table */
struct cappage cap; /* Capabilities page info */
@@ -238,6 +239,21 @@ wfdattach (struct atapi *ata, int unit, struct atapi_params *ap, int debug)
} else
return -1;
+ /*
+ * The IOMEGA ZIP 100, at firmware 21.* and 23.* at least
+ * is known to lock up if transfers > 64 blocks are
+ * requested.
+ */
+ if (!strcmp(ap->model, "IOMEGA ZIP 100 ATAPI")) {
+ printf("wfd%d: buggy Zip drive, 64-block transfer limit set\n",
+ unit);
+ t->maxblks = 64;
+ } else {
+ t->maxblks = 0; /* no limit */
+ }
+
+
+
#ifdef DEVFS
mynor = dkmakeminor(unit, WHOLE_DISK_SLICE, RAW_PART);
t->bdevs = devfs_add_devswf(&wfd_bdevsw, mynor,
@@ -455,6 +471,9 @@ static void wfd_start (struct wfd *t)
u_long blkno, nblk;
u_char op_code;
long count;
+ int pxcount, pxnblk;
+ u_char *pxdest;
+
/* See if there is a buf to do and we are not already doing one. */
if (! bp)
@@ -470,30 +489,90 @@ static void wfd_start (struct wfd *t)
blkno = bp->b_blkno / (t->cap.sector_size / 512);
nblk = (bp->b_bcount + (t->cap.sector_size - 1)) / t->cap.sector_size;
- if(bp->b_flags & B_READ) {
- op_code = ATAPI_READ_BIG;
- count = bp->b_bcount;
+ if ((t->maxblks == 0) || (nblk <= t->maxblks)) {
+
+ if(bp->b_flags & B_READ) {
+ op_code = ATAPI_READ_BIG;
+ count = bp->b_bcount;
+ } else {
+ op_code = ATAPI_WRITE_BIG;
+ count = -bp->b_bcount;
+ }
+
+ /* only one transfer */
+ (int)bp->b_driver1 = 0;
+ (int)bp->b_driver2 = 0;
+ atapi_request_callback (t->ata, t->unit, op_code, 0,
+ blkno>>24, blkno>>16, blkno>>8, blkno,
+ 0, nblk>>8, nblk, 0, 0,
+ 0, 0, 0, 0, 0,
+ (u_char*) bp->b_data, count,
+ (void*)wfd_done, t, bp);
} else {
- op_code = ATAPI_WRITE_BIG;
- count = -bp->b_bcount;
- }
- atapi_request_callback (t->ata, t->unit, op_code, 0,
- blkno>>24, blkno>>16, blkno>>8, blkno, 0, nblk>>8, nblk, 0, 0,
- 0, 0, 0, 0, 0, (u_char*) bp->b_data, count,
- (void*)wfd_done, t, bp);
+ /*
+ * We can't handle this request in a single
+ * read/write operation. Instead, queue a set of
+ * transfers, and record the number of transfers
+ * and the running residual in the b_driver
+ * fields of the bp.
+ */
+
+ if(bp->b_flags & B_READ) {
+ op_code = ATAPI_READ_BIG;
+ } else {
+ op_code = ATAPI_WRITE_BIG;
+ }
+
+ /* calculate number of transfers */
+ (int)bp->b_driver1 = (nblk - 1) / t->maxblks;
+ (int)bp->b_driver2 = 0;
+
+ pxdest = (u_char *)bp->b_data;
+ pxcount = bp->b_bcount;
+
+ /* construct partial transfer requests */
+ while (nblk > 0) {
+ pxnblk = min(nblk, t->maxblks);
+ count = min(pxcount, t->maxblks * t->cap.sector_size);
+
+ atapi_request_callback(t->ata, t->unit, op_code, 0,
+ blkno>>24, blkno>>16, blkno>>8,
+ blkno, 0, pxnblk>>8, pxnblk,
+ 0, 0, 0, 0, 0, 0, 0,
+ pxdest,
+ (bp->b_flags & B_READ) ?
+ count : -count,
+ (void*)wfd_done, t, bp);
+ nblk -= pxnblk;
+ pxcount -= count;
+ pxdest += count;
+ blkno += pxnblk;
+ }
+ }
}
static void wfd_done (struct wfd *t, struct buf *bp, int resid,
struct atapires result)
{
+
if (result.code) {
wfd_error (t, result);
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
} else
- bp->b_resid = resid;
- biodone (bp);
+ (int)bp->b_driver2 += resid;
+ /*
+ * We can't call biodone until all outstanding
+ * transfer fragments are handled. If one hits
+ * an error, we will be returning an error, but
+ * only when all are complete.
+ */
+ if (((int)bp->b_driver1)-- <= 0) {
+ bp->b_resid = (int)bp->b_driver2;
+ biodone (bp);
+ }
+
wfd_start (t);
}
OpenPOWER on IntegriCloud