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.
This commit is contained in:
msmith 1998-01-27 16:49:35 +00:00
parent 3e3506a667
commit 23fa2f94de
2 changed files with 186 additions and 28 deletions

View File

@ -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;
} else {
op_code = ATAPI_WRITE_BIG;
count = -bp->b_bcount;
}
if ((t->maxblks == 0) || (nblk <= t->maxblks)) {
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);
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 {
/*
* 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);
}

View File

@ -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;
} else {
op_code = ATAPI_WRITE_BIG;
count = -bp->b_bcount;
}
if ((t->maxblks == 0) || (nblk <= t->maxblks)) {
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);
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 {
/*
* 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);
}