summaryrefslogtreecommitdiffstats
path: root/sys/dev/ata/atapi-fd.c
diff options
context:
space:
mode:
authorsos <sos@FreeBSD.org>1999-03-03 21:10:29 +0000
committersos <sos@FreeBSD.org>1999-03-03 21:10:29 +0000
commitfb974f8ed94734f43dfc82e27445b482593a491d (patch)
treed5601f3a4d38e27cf2213659e87de8c365373d5a /sys/dev/ata/atapi-fd.c
parentee38b913245110a5902f02f489c58c5b31c1cb92 (diff)
downloadFreeBSD-src-fb974f8ed94734f43dfc82e27445b482593a491d.zip
FreeBSD-src-fb974f8ed94734f43dfc82e27445b482593a491d.tar.gz
Added driver to support ATAPI floppies ie LS-120 & ZIP drives.
Added "options ATA_STATIC_ID" that wires ATA disks like the old wd driver. Fixed problems: Dont use more sectors/intr than the drive supports. Fix announce of > 8.4G disks. Dont call ad_interrupt/ad_transfer when no disks config'd. Use the right page# for CDR write mode params. Fix breakage when no PCI support in kernel. Implement DEVFS stuff. General code clenaup.
Diffstat (limited to 'sys/dev/ata/atapi-fd.c')
-rw-r--r--sys/dev/ata/atapi-fd.c434
1 files changed, 434 insertions, 0 deletions
diff --git a/sys/dev/ata/atapi-fd.c b/sys/dev/ata/atapi-fd.c
new file mode 100644
index 0000000..6aa18db
--- /dev/null
+++ b/sys/dev/ata/atapi-fd.c
@@ -0,0 +1,434 @@
+/*-
+ * Copyright (c) 1998,1999 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (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$
+ */
+
+#include "ata.h"
+#include "atapifd.h"
+#include "opt_devfs.h"
+
+#if NATA > 0 && NATAPIFD > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+#include <sys/devicestat.h>
+#include <sys/cdio.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#ifdef DEVFS
+#include <sys/devfsext.h>
+#endif
+#include <dev/ata/ata-all.h>
+#include <dev/ata/atapi-all.h>
+#include <dev/ata/atapi-fd.h>
+
+static d_open_t afdopen;
+static d_close_t afdclose;
+static d_read_t afdread;
+static d_write_t afdwrite;
+static d_ioctl_t afdioctl;
+static d_strategy_t afdstrategy;
+
+#define CDEV_MAJOR 87
+#define BDEV_MAJOR 1
+
+static struct cdevsw afd_cdevsw = {
+ afdopen, afdclose, afdread, afdwrite,
+ afdioctl, nostop, nullreset, nodevtotty,
+ seltrue, nommap, afdstrategy, "afd",
+ NULL, -1 };
+
+#define NUNIT 8
+#define UNIT(d) ((minor(d) >> 3) & 3)
+
+#define F_OPEN 0x0001 /* The device is opened */
+#define F_MEDIA_CHANGED 0x0002 /* The media have changed */
+
+static struct afd_softc *afdtab[NUNIT]; /* Drive info by unit number */
+static int32_t afdnlun = 0; /* Number of config'd drives */
+
+int32_t afdattach(struct atapi_softc *);
+static int32_t afd_sense(struct afd_softc *);
+static void afd_describe(struct afd_softc *);
+static void afd_strategy(struct buf *);
+static void afd_start(struct afd_softc *);
+static void afd_done(struct atapi_request *);
+static int32_t afd_start_device(struct afd_softc *, int32_t);
+static int32_t afd_lock_device(struct afd_softc *, int32_t);
+static int32_t afd_eject(struct afd_softc *, int32_t);
+static void afd_drvinit(void *);
+
+int32_t
+afdattach(struct atapi_softc *atp)
+{
+ struct afd_softc *fdp;
+
+ if (afdnlun >= NUNIT) {
+ printf("afd: too many units\n");
+ return -1;
+ }
+ fdp = malloc(sizeof(struct afd_softc), M_TEMP, M_NOWAIT);
+ if (!fdp) {
+ printf("afd: out of memory\n");
+ return -1;
+ }
+ bzero(fdp, sizeof(struct afd_softc));
+ bufq_init(&fdp->buf_queue);
+ fdp->atp = atp;
+ fdp->lun = afdnlun;
+ fdp->flags = F_MEDIA_CHANGED;
+
+ if (afd_sense(fdp)) {
+ free(fdp, M_TEMP);
+ return -1;
+ }
+
+ afd_describe(fdp);
+ afdtab[afdnlun++] = fdp;
+ devstat_add_entry(&fdp->stats, "afd", fdp->lun, DEV_BSIZE,
+ DEVSTAT_NO_ORDERED_TAGS,
+ DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE,
+ 0x178);
+#ifdef DEVFS
+ fdp->cdevs_token = devfs_add_devswf(&afd_cdevsw, dkmakeminor(fdp->lun, 0,0),
+ DV_CHR, UID_ROOT, GID_OPERATOR,
+ 0640, "rafd%d", fdp->lun);
+ fdp->bdevs_token = devfs_add_devswf(&afd_cdevsw, dkmakeminor(fdp->lun, 0,0),
+ DV_BLK, UID_ROOT, GID_OPERATOR,
+ 0640, "afd%d", fdp->lun);
+#endif
+ return 0;
+}
+
+static int32_t
+afd_sense(struct afd_softc *fdp)
+{
+ int32_t error, count;
+ int8_t buffer[256];
+ int8_t ccb[16] = { ATAPI_MODE_SENSE, 0, ATAPI_REWRITEABLE_CAP_PAGE,
+ 0, 0, 0, 0, sizeof(buffer)>>8, sizeof(buffer) & 0xff,
+ 0, 0, 0, 0, 0, 0, 0 };
+
+ bzero(buffer, sizeof(buffer));
+ /* Get drive capabilities, some drives needs this repeated */
+ for (count = 0 ; count < 5 ; count++) {
+ if (!(error = atapi_queue_cmd(fdp->atp, ccb, buffer, sizeof(buffer),
+ A_READ, NULL, NULL, NULL)))
+ break;
+ }
+#ifdef AFD_DEBUG
+ atapi_dump("afd: sense", buffer, sizeof(buffer));
+#endif
+ if (error)
+ return error;
+ bcopy(buffer, &fdp->header, sizeof(struct afd_header));
+ bcopy(buffer+sizeof(struct afd_header), &fdp->cap,
+ sizeof(struct afd_cappage));
+ if (fdp->cap.page_code != ATAPI_REWRITEABLE_CAP_PAGE)
+ return 1;
+ fdp->cap.cylinders = ntohs(fdp->cap.cylinders);
+ fdp->cap.sector_size = ntohs(fdp->cap.sector_size);
+ return 0;
+}
+
+static void
+afd_describe(struct afd_softc *fdp)
+{
+ int8_t model_buf[40+1];
+ int8_t revision_buf[8+1];
+
+ bpack(fdp->atp->atapi_parm->model, model_buf, sizeof(model_buf));
+ bpack(fdp->atp->atapi_parm->revision, revision_buf, sizeof(revision_buf));
+ printf("afd%d: <%s/%s> rewriteable drive at ata%d as %s\n",
+ fdp->lun, model_buf, revision_buf,
+ fdp->atp->controller->unit,
+ (fdp->atp->unit == ATA_MASTER) ? "master" : "slave ");
+ printf("afd%d: %luMB (%u sectors), %u cyls, %u heads, %u S/T, %u B/S\n",
+ afdnlun,
+ (fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors) /
+ ((1024L * 1024L) / fdp->cap.sector_size),
+ fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors,
+ fdp->cap.cylinders, fdp->cap.heads, fdp->cap.sectors,
+ fdp->cap.sector_size);
+ printf("afd%d: ", fdp->lun);
+ switch (fdp->header.medium_type) {
+ default: printf("Unknown media (0x%x)", fdp->header.medium_type);
+ }
+ if (fdp->header.wp) printf(", writeprotected");
+ printf("\n");
+}
+
+static int32_t
+afdopen(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
+{
+ struct afd_softc *fdp;
+ struct disklabel label;
+ int32_t lun = UNIT(dev);
+
+ if (lun >= afdnlun || !(fdp = afdtab[lun]))
+ return ENXIO;
+
+ fdp->flags &= ~F_MEDIA_CHANGED;
+ afd_lock_device(fdp, 1);
+ if (afd_sense(fdp))
+ printf("afd%d: sense media type failed\n", fdp->lun);
+
+ /* build disklabel and initilize slice tables */
+ bzero(&label, sizeof label);
+ label.d_secsize = fdp->cap.sector_size;
+ label.d_nsectors = fdp->cap.sectors;
+ label.d_ntracks = fdp->cap.heads;
+ label.d_ncylinders = fdp->cap.cylinders;
+ label.d_secpercyl = fdp->cap.heads * fdp->cap.sectors;
+ label.d_secperunit = fdp->cap.heads * fdp->cap.sectors * fdp->cap.cylinders;
+
+ /* Initialize slice tables. */
+ return dsopen("afd", dev, fmt, 0, &fdp->slices, &label, afd_strategy,
+ (ds_setgeom_t *)NULL, &afd_cdevsw);
+}
+
+static int32_t
+afdclose(dev_t dev, int32_t flags, int32_t fmt, struct proc *p)
+{
+ int32_t lun = UNIT(dev);
+ struct afd_softc *fdp;
+
+ if (lun >= afdnlun || !(fdp = afdtab[lun]))
+ return ENXIO;
+
+ dsclose(dev, fmt, fdp->slices);
+ if(!dsisopen(fdp->slices))
+ afd_lock_device(fdp, 0);
+ return 0;
+}
+
+static int32_t
+afdread(dev_t dev, struct uio *uio, int32_t ioflag)
+{
+ return physio(afdstrategy, NULL, dev, 1, minphys, uio);
+}
+
+static int32_t
+afdwrite(dev_t dev, struct uio *uio, int32_t ioflag)
+{
+ return physio(afdstrategy, NULL, dev, 0, minphys, uio);
+}
+
+static int32_t
+afdioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
+{
+ int32_t lun = UNIT(dev);
+ int32_t error = 0;
+ struct afd_softc *fdp;
+
+ if (lun >= afdnlun || !(fdp = afdtab[lun]))
+ return ENXIO;
+
+ switch (cmd) {
+
+ case CDIOCEJECT:
+ if ((fdp->flags & F_OPEN) && fdp->refcnt)
+ return EBUSY;
+ return afd_eject(fdp, 0);
+
+ case CDIOCCLOSE:
+ if ((fdp->flags & F_OPEN) && fdp->refcnt)
+ return 0;
+ return afd_eject(fdp, 1);
+
+ default:
+ return ENOTTY;
+ }
+ return error;
+}
+
+static void
+afdstrategy(struct buf *bp)
+{
+ int32_t lun = UNIT(bp->b_dev);
+ struct afd_softc *fdp = afdtab[lun];
+ int32_t x;
+
+ if (bp->b_bcount == 0) {
+ bp->b_resid = 0;
+ biodone(bp);
+ return;
+ }
+ if (dscheck(bp, fdp->slices) <= 0) {
+ x = splbio();
+ biodone(bp);
+ splx(x);
+ return;
+ }
+ x = splbio();
+ bufq_insert_tail(&fdp->buf_queue, bp);
+ afd_start(fdp);
+ splx(x);
+}
+
+static void
+afd_strategy(struct buf *bp)
+{
+ afdstrategy(bp);
+}
+
+static void
+afd_start(struct afd_softc *fdp)
+{
+ struct buf *bp = bufq_first(&fdp->buf_queue);
+ u_int32_t lba, count;
+ int8_t ccb[16];
+
+ if (!bp)
+ return;
+ bzero(ccb, sizeof(ccb));
+ bufq_remove(&fdp->buf_queue, bp);
+
+ /* Should reject all queued entries if media have changed. */
+ if (fdp->flags & F_MEDIA_CHANGED) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ biodone(bp);
+ return;
+ }
+
+ lba = bp->b_blkno / (fdp->cap.sector_size / DEV_BSIZE);
+ count = (bp->b_bcount + (fdp->cap.sector_size - 1)) / fdp->cap.sector_size;
+
+ if (bp->b_flags & B_READ)
+ ccb[0] = ATAPI_READ_BIG;
+ else
+ ccb[0] = ATAPI_WRITE_BIG;
+
+ ccb[1] = 0;
+ ccb[2] = lba>>24;
+ ccb[3] = lba>>16;
+ ccb[4] = lba>>8;
+ ccb[5] = lba;
+ ccb[7] = count>>8;
+ ccb[8] = count;
+
+ devstat_start_transaction(&fdp->stats);
+
+ atapi_queue_cmd(fdp->atp, ccb, bp->b_data, bp->b_bcount,
+ (bp->b_flags & B_READ) ? A_READ : 0, afd_done, fdp, bp);
+}
+
+static void
+afd_done(struct atapi_request *request)
+{
+ struct buf *bp = request->bp;
+ struct afd_softc *fdp = request->driver;
+
+ devstat_end_transaction(&fdp->stats, bp->b_bcount-request->bytecount,
+ DEVSTAT_TAG_NONE,
+ (bp->b_flags&B_READ) ? DEVSTAT_READ:DEVSTAT_WRITE);
+
+ if (request->result) {
+ printf("afd_done: ");
+ atapi_error(request->device, request->result);
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ }
+ else
+ bp->b_resid = request->bytecount;
+ biodone(bp);
+ afd_start(fdp);
+}
+
+static int32_t
+afd_start_device(struct afd_softc *fdp, int32_t start)
+{
+ int8_t ccb[16] = { ATAPI_START_STOP, 0, 0, 0, start,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return atapi_queue_cmd(fdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+}
+
+static int32_t
+afd_lock_device(struct afd_softc *fdp, int32_t lock)
+{
+ int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ return atapi_queue_cmd(fdp->atp, ccb, NULL, 0, 0, NULL, NULL, NULL);
+}
+
+static int32_t
+afd_eject(struct afd_softc *fdp, int32_t close)
+{
+ int32_t error;
+
+ error = afd_start_device(fdp, 0);
+ if ((error & ATAPI_SK_MASK) &&
+ ((error & ATAPI_SK_MASK) == ATAPI_SK_NOT_READY ||
+ (error & ATAPI_SK_MASK) == ATAPI_SK_UNIT_ATTENTION)) {
+
+ if (!close)
+ return 0;
+ if ((error = afd_start_device(fdp, 3)))
+ return error;
+ afd_lock_device(fdp, 1);
+ return 0;
+ }
+ if (error) {
+ atapi_error(fdp->atp, error);
+ return EIO;
+ }
+ if (close)
+ return 0;
+
+ tsleep((caddr_t) &lbolt, PRIBIO, "afdej1", 0);
+ tsleep((caddr_t) &lbolt, PRIBIO, "afdej2", 0);
+ afd_lock_device(fdp, 0);
+ fdp->flags |= F_MEDIA_CHANGED;
+ return afd_start_device(fdp, 2);
+}
+
+
+static void
+afd_drvinit(void *unused)
+{
+ static afd_devsw_installed = 0;
+
+ if (!afd_devsw_installed) {
+ cdevsw_add_generic(BDEV_MAJOR, CDEV_MAJOR, &afd_cdevsw);
+ afd_devsw_installed = 1;
+ }
+}
+
+SYSINIT(afddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, afd_drvinit, NULL)
+#endif /* NATA & NATAPIFD */
OpenPOWER on IntegriCloud