summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordg <dg@FreeBSD.org>1995-10-14 15:41:10 +0000
committerdg <dg@FreeBSD.org>1995-10-14 15:41:10 +0000
commit8d3425ea6285435601f20980b43c3143b05bc8d7 (patch)
tree70f4d924760a26cfd35ea6eb6ac64af1e30cefa4
parenta308e5f9a1ba49753fdbe0e60fd5ee377a5096df (diff)
downloadFreeBSD-src-8d3425ea6285435601f20980b43c3143b05bc8d7.zip
FreeBSD-src-8d3425ea6285435601f20980b43c3143b05bc8d7.tar.gz
Latest fixes from Serge:
I tried to solve the problem of IDE probing compatibility in this version. When compiled without an ATAPI option, the wd driver is fully backward compatible with 2.0.5. With ATAPI option, the wdprobe becomes strictly weaker. That is, if wdprobe works without ATAPI option, it will always work with it too. Another problem was with the CD-ROM drive attached as a slave in the IDE bus, where there is no master. All IDE CD-ROM drives are shipped in slave configuration, and most users just plug them in, never thinking about jumpers. It works fine with ms-dos and ms-windows, and this version of the driver supports it as well. The eject op can now load disks. Just repeat it twice, and the disk will be ejected and then loaded back. The disc cannot be ejected if it is mounted. Submitted by: Serge Vakulenko, <vak@cronyx.ru>
-rw-r--r--sys/i386/i386/conf.c24
-rw-r--r--sys/i386/isa/atapi.c284
-rw-r--r--sys/i386/isa/atapi.h58
-rw-r--r--sys/i386/isa/wcd.c435
-rw-r--r--sys/i386/isa/wd.c54
-rw-r--r--sys/pc98/pc98/atapi.h58
6 files changed, 703 insertions, 210 deletions
diff --git a/sys/i386/i386/conf.c b/sys/i386/i386/conf.c
index 312b5f4..f2ca17f 100644
--- a/sys/i386/i386/conf.c
+++ b/sys/i386/i386/conf.c
@@ -42,7 +42,7 @@
* SUCH DAMAGE.
*
* from: @(#)conf.c 5.8 (Berkeley) 5/12/91
- * $Id: conf.c,v 1.100 1995/10/12 23:28:41 bde Exp $
+ * $Id: conf.c,v 1.101 1995/10/14 05:25:45 bde Exp $
*/
#include <sys/param.h>
@@ -305,13 +305,17 @@ d_psize_t atasize;
#include "wcd.h"
#if NWCD > 0
-d_open_t wcdopen;
-d_close_t wcdclose;
+d_open_t wcdbopen;
+d_open_t wcdropen;
+d_close_t wcdbclose;
+d_close_t wcdrclose;
d_strategy_t wcdstrategy;
d_ioctl_t wcdioctl;
#else
-#define wcdopen nxopen
-#define wcdclose nxclose
+#define wcdbopen nxopen
+#define wcdropen nxopen
+#define wcdbclose nxclose
+#define wcdrclose nxclose
#define wcdstrategy nxstrategy
#define wcdioctl nxioctl
#endif
@@ -445,8 +449,8 @@ struct bdevsw bdevsw[] =
matcddump, matcdsize, 0 },
{ ataopen, ataclose, atastrategy, ataioctl, /*18*/
atadump, atasize, 0 },
- { wcdopen, wcdclose, wcdstrategy, wcdioctl, /*19*/
- nxdump, zerosize, 0 },
+ { wcdbopen, wcdbclose, wcdstrategy, wcdioctl, /*19*/
+ nxdump, zerosize, 0 },
{ odopen, odclose, odstrategy, odioctl, /*20*/
oddump, odsize, 0 },
@@ -1291,9 +1295,9 @@ struct cdevsw cdevsw[] =
{ siopen, siclose, siread, siwrite, /*68*/
siioctl, sistop, sireset, sidevtotty,/* slxos */
ttselect, nxmmap, NULL },
- { wcdopen, wcdclose, rawread, nowrite, /*69*/
- wcdioctl, nostop, nullreset, nodevtotty,/* atapi */
- seltrue, nommap, wcdstrategy },
+ { wcdropen, wcdrclose, rawread, nowrite, /*69*/
+ wcdioctl, nostop, nullreset, nodevtotty,/* atapi */
+ seltrue, nommap, wcdstrategy },
{ odopen, odclose, rawread, rawwrite, /*70*/
odioctl, nostop, nullreset, nodevtotty,/* od */
seltrue, nommap, odstrategy },
diff --git a/sys/i386/isa/atapi.c b/sys/i386/isa/atapi.c
index 540c2bd..6d608fb 100644
--- a/sys/i386/isa/atapi.c
+++ b/sys/i386/isa/atapi.c
@@ -11,7 +11,7 @@
* or modify this software as long as this message is kept with the software,
* all derivative works or modified versions.
*
- * Version 1.5, Thu Sep 21 23:08:11 MSD 1995
+ * Version 1.9, Mon Oct 9 22:34:47 MSK 1995
*/
/*
@@ -112,16 +112,40 @@
#include <sys/malloc.h>
#include <i386/include/cpufunc.h>
#include <i386/include/clock.h>
+
+#ifdef ATAPI_MODULE
+# define ATAPI_STATIC
+#endif
+
#include <i386/isa/atapi.h>
+#ifndef ATAPI_STATIC
+/*
+ * In the case of loadable ATAPI driver we need to store
+ * the probe info for delayed attaching.
+ */
+struct atapidrv atapi_drvtab[4];
+int atapi_ndrv;
+struct atapi *atapi_tab;
+
+int atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent)
+{
+ atapi_drvtab[atapi_ndrv].ctlr = ctlr;
+ atapi_drvtab[atapi_ndrv].unit = unit;
+ atapi_drvtab[atapi_ndrv].port = port;
+ atapi_drvtab[atapi_ndrv].parent = parent;
+ atapi_drvtab[atapi_ndrv].attached = 0;
+ ++atapi_ndrv;
+ return (1);
+}
+#else /* ATAPI_STATIC */
+
#ifdef DEBUG
# define print(s) printf s
#else
# define print(s) {/*void*/}
#endif
-#define MAXCMD (8*NWDC)
-
/*
* ATAPI packet command phase.
*/
@@ -131,32 +155,6 @@
#define PHASE_COMPLETED (ARI_IN | ARI_CMD)
#define PHASE_ABORTED 0 /* nonstandard - for NEC 260 */
-struct atapicmd { /* ATAPI command block */
- struct atapicmd *next; /* next command in queue */
- int busy; /* busy flag */
- u_char cmd[16]; /* command and args */
- int unit; /* drive unit number */
- int count; /* byte count, >0 - read, <0 - write */
- char *addr; /* data to transfer */
- void (*callback) (); /* call when done */
- void *cbarg1; /* callback arg 1 */
- void *cbarg2; /* callback arg 1 */
- struct atapires result; /* resulting error code */
-};
-
-struct atapi { /* ATAPI controller data */
- u_short port; /* i/o port base */
- u_char ctrlr; /* physical controller number */
- u_char debug : 1; /* trace enable flag */
- u_char cmd16 : 1; /* 16-byte command flag */
- u_char intrcmd : 1; /* interrupt before cmd flag */
- u_char slow : 1; /* slow reaction device */
- struct atapicmd *queue; /* queue of commands to perform */
- struct atapicmd *tail; /* tail of queue */
- struct atapicmd *free; /* queue of free command blocks */
- struct atapicmd cmdrq[MAXCMD]; /* pool of command requests */
-};
-
struct atapi atapitab[NWDC];
static struct atapi_params *atapi_probe (int port, int unit);
@@ -167,8 +165,16 @@ static int atapi_start_cmd (struct atapi *ata, struct atapicmd *ac);
static int atapi_wait_cmd (struct atapi *ata, struct atapicmd *ac);
extern int wdstart (int ctrlr);
+extern int wcdattach(struct atapi*, int, struct atapi_params*, int, struct kern_devconf*);
-void atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent)
+/*
+ * Probe the ATAPI device at IDE controller `ctlr', drive `unit'.
+ * Called at splbio().
+ */
+#ifdef ATAPI_MODULE
+static
+#endif
+int atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent)
{
struct atapi *ata = atapitab + ctlr;
struct atapi_params *ap;
@@ -179,7 +185,7 @@ void atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent)
print (("atapi%d.%d at 0x%x: attach called\n", ctlr, unit, port));
ap = atapi_probe (port, unit);
if (! ap)
- return;
+ return (0);
bcopy (ap->model, buf, sizeof(buf)-1);
buf[sizeof(buf)-1] = 0;
@@ -230,14 +236,16 @@ void atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent)
ata->port = port;
ata->ctrlr = ctlr;
+ ata->parent = parent;
+ ata->attached[unit] = 0;
#ifdef DEBUG
ata->debug = 1;
#else
ata->debug = 0;
#endif
/* Initialize free queue. */
- ata->cmdrq[MAXCMD-1].next = 0;
- for (ac = ata->cmdrq+MAXCMD-2; ac >= ata->cmdrq; --ac)
+ ata->cmdrq[15].next = 0;
+ for (ac = ata->cmdrq+14; ac >= ata->cmdrq; --ac)
ac->next = ac+1;
ata->free = ata->cmdrq;
@@ -245,8 +253,12 @@ void atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent)
printf ("wdc%d: unit %d: unknown ATAPI protocol=%d\n",
ctlr, unit, ap->proto);
free (ap, M_TEMP);
- return;
+ return (0);
}
+#ifdef ATAPI_MODULE
+ ata->params[unit] = ap;
+ return (1);
+#else
switch (ap->devtype) {
default:
/* unknown ATAPI device */
@@ -258,14 +270,11 @@ void atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent)
case AT_TYPE_CDROM: /* CD-ROM device */
#if NWCD > 0
/* ATAPI CD-ROM */
- {
- int wcdattach (struct atapi*, int, struct atapi_params*,
- int, struct kern_devconf*);
- if (wcdattach (ata, unit, ap, ata->debug, parent) < 0)
- break;
- }
+ if (wcdattach (ata, unit, ap, ata->debug, parent) < 0)
+ break;
/* Device attached successfully. */
- return;
+ ata->attached[unit] = 1;
+ return (1);
#else
printf ("wdc%d: ATAPI CD-ROMs not configured\n", ctlr);
break;
@@ -276,19 +285,46 @@ void atapi_attach (int ctlr, int unit, int port, struct kern_devconf *parent)
/* Add your driver here */
#else
printf ("wdc%d: ATAPI streaming tapes not supported yet\n", ctlr);
- break;
#endif
+ break;
case AT_TYPE_OPTICAL: /* optical disk */
#if NWMD > 0
/* Add your driver here */
#else
printf ("wdc%d: ATAPI optical disks not supported yet\n", ctlr);
- break;
#endif
+ break;
}
/* Attach failed. */
free (ap, M_TEMP);
+ return (0);
+#endif /* ATAPI_MODULE */
+}
+
+static char *cmdname (u_char cmd)
+{
+ static char buf[8];
+
+ switch (cmd) {
+ case 0x00: return ("TEST_UNIT_READY");
+ case 0x03: return ("REQUEST_SENSE");
+ case 0x1b: return ("START_STOP");
+ case 0x1e: return ("PREVENT_ALLOW");
+ case 0x25: return ("READ_CAPACITY");
+ case 0x28: return ("READ_BIG");
+ case 0x43: return ("READ_TOC");
+ case 0x42: return ("READ_SUBCHANNEL");
+ case 0x55: return ("MODE_SELECT_BIG");
+ case 0x5a: return ("MODE_SENSE");
+ case 0xb4: return ("PLAY_CD");
+ case 0x47: return ("PLAY_MSF");
+ case 0x4b: return ("PAUSE");
+ case 0x48: return ("PLAY_TRACK");
+ case 0xa5: return ("PLAY_BIG");
+ }
+ sprintf (buf, "[0x%x]", cmd);
+ return (buf);
}
static void bswap (char *buf, int len)
@@ -319,6 +355,7 @@ static struct atapi_params *atapi_probe (int port, int unit)
char tb [DEV_BSIZE];
/* Wait for controller not busy. */
+ outb (port + AR_DRIVE, unit ? ARD_DRIVE1 : ARD_DRIVE0);
if (atapi_wait (port, 0) < 0) {
print (("atapiX.%d at 0x%x: controller busy, status=%b\n",
unit, port, inb (port + AR_STATUS), ARS_BITS));
@@ -361,11 +398,10 @@ static struct atapi_params *atapi_probe (int port, int unit)
* Mitsumi and NEC drives don't need this.
*/
if (! ((ap->model[0] == 'N' && ap->model[1] == 'E') ||
- (ap->model[0] == 'F' && ap->model[1] == 'X'))) {
+ (ap->model[0] == 'F' && ap->model[1] == 'X')))
bswap (ap->model, sizeof(ap->model));
- bswap (ap->serial, sizeof(ap->serial));
- bswap (ap->revision, sizeof(ap->revision));
- }
+ bswap (ap->serial, sizeof(ap->serial));
+ bswap (ap->revision, sizeof(ap->revision));
/* Clean up the model name, serial and revision numbers. */
btrim (ap->model, sizeof(ap->model));
@@ -537,17 +573,26 @@ int atapi_wait_cmd (struct atapi *ata, struct atapicmd *ac)
{
/* Wait for DRQ from 50 usec to 3 msec for slow devices */
int cnt = ata->intrcmd ? 10000 : ata->slow ? 3000 : 50;
+ int ireason = 0, phase = 0;
+ /* Wait for command phase. */
for (; cnt>0; cnt-=10) {
+ ireason = inb (ata->port + AR_IREASON);
ac->result.status = inb (ata->port + AR_STATUS);
- if (ac->result.status & ARS_DRQ)
+ phase = (ireason & (ARI_CMD | ARI_IN)) |
+ (ac->result.status & ARS_DRQ);
+ if (phase == PHASE_CMDOUT)
break;
DELAY (10);
}
- if (! (ac->result.status & ARS_DRQ)) {
- printf ("atapi%d.%d: no cmd drq\n", ata->ctrlr, ac->unit);
+
+ if (phase != PHASE_CMDOUT) {
ac->result.code = RES_NODRQ;
ac->result.error = inb (ata->port + AR_ERROR);
+ printf ("atapi%d.%d: invalid command phase, ireason=0x%x, status=%b, error=%b\n",
+ ata->ctrlr, ac->unit, ireason,
+ ac->result.status, ARS_BITS,
+ ac->result.error, AER_BITS);
return (-1);
}
return (0);
@@ -560,11 +605,11 @@ void atapi_send_cmd (struct atapi *ata, struct atapicmd *ac)
{
outsw (ata->port + AR_DATA, ac->cmd, ata->cmd16 ? 8 : 6);
if (ata->debug)
- printf ("atapi%d.%d: send cmd %x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x\n",
- ata->ctrlr, ac->unit, ac->cmd[0], ac->cmd[1],
- ac->cmd[2], ac->cmd[3], ac->cmd[4], ac->cmd[5],
- ac->cmd[6], ac->cmd[7], ac->cmd[8], ac->cmd[9],
- ac->cmd[10], ac->cmd[11], ac->cmd[12],
+ printf ("atapi%d.%d: send cmd %s %x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x-%x\n",
+ ata->ctrlr, ac->unit, cmdname (ac->cmd[0]), ac->cmd[0],
+ ac->cmd[1], ac->cmd[2], ac->cmd[3], ac->cmd[4],
+ ac->cmd[5], ac->cmd[6], ac->cmd[7], ac->cmd[8],
+ ac->cmd[9], ac->cmd[10], ac->cmd[11], ac->cmd[12],
ac->cmd[13], ac->cmd[14], ac->cmd[15]);
}
@@ -853,4 +898,133 @@ struct atapires atapi_request_immediate (struct atapi *ata, int unit,
}
return (ac->result);
}
+#endif /* ATAPI_STATIC */
+
+#ifdef ATAPI_MODULE
+/*
+ * ATAPI loadable driver stubs.
+ */
+#include <sys/exec.h>
+#include <sys/conf.h>
+#include <sys/sysent.h>
+#include <sys/lkm.h>
+
+extern int (*atapi_start_ptr) (int ctrlr);
+extern int (*atapi_intr_ptr) (int ctrlr);
+extern void (*atapi_debug_ptr) (struct atapi *ata, int on);
+extern struct atapires (*atapi_request_wait_ptr) (struct atapi *ata, int unit,
+ u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4,
+ u_char a5, u_char a6, u_char a7, u_char a8, u_char a9,
+ u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15,
+ char *addr, int count);
+extern void (*atapi_request_callback_ptr) (struct atapi *ata, int unit,
+ u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4,
+ u_char a5, u_char a6, u_char a7, u_char a8, u_char a9,
+ u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15,
+ char *addr, int count, void (*done)(), void *x, void *y);
+extern struct atapires (*atapi_request_immediate_ptr) (struct atapi *ata, int unit,
+ u_char cmd, u_char a1, u_char a2, u_char a3, u_char a4,
+ u_char a5, u_char a6, u_char a7, u_char a8, u_char a9,
+ u_char a10, u_char a11, u_char a12, u_char a13, u_char a14, u_char a15,
+ char *addr, int count);
+
+extern void wdintr (int);
+
+/*
+ * Construct lkm_misc structure (see lkm.h).
+ */
+MOD_MISC("atapi")
+
+int atapi_locked;
+
+int atapi_lock (int ctlr)
+{
+ atapi_locked = 1;
+ wakeup (&atapi_locked);
+ return (1);
+}
+
+/*
+ * Function called when loading the driver.
+ */
+int atapi_load (struct lkm_table *lkmtp, int cmd)
+{
+ struct atapidrv *d;
+ int n, x;
+
+ /*
+ * Probe all free IDE units, searching for ATAPI drives.
+ */
+ n = 0;
+ for (d=atapi_drvtab; d<atapi_drvtab+atapi_ndrv && d->port; ++d) {
+ /* Lock the controller. */
+ x = splbio ();
+ atapi_locked = 0;
+ atapi_start_ptr = atapi_lock;
+ wdstart (d->ctlr);
+ while (! atapi_locked)
+ tsleep (&atapi_locked, PRIBIO, "atach", 0);
+
+ /* Probe the drive. */
+ if (atapi_attach (d->ctlr, d->unit, d->port, d->parent)) {
+ d->attached = 1;
+ ++n;
+ }
+
+ /* Unlock the controller. */
+ atapi_start_ptr = 0;
+ wdintr (d->ctlr);
+ splx (x);
+ }
+ if (! n)
+ return ENXIO;
+ atapi_start_ptr = atapi_start;
+ atapi_intr_ptr = atapi_intr;
+ atapi_debug_ptr = atapi_debug;
+ atapi_request_wait_ptr = atapi_request_wait;
+ atapi_request_callback_ptr = atapi_request_callback;
+ atapi_request_immediate_ptr = atapi_request_immediate;
+ atapi_tab = atapitab;
+ return 0;
+}
+
+/*
+ * Function called when unloading the driver.
+ */
+int atapi_unload (struct lkm_table *lkmtp, int cmd)
+{
+ struct atapi *ata;
+ int u;
+
+ for (ata=atapi_tab; ata<atapi_tab+2; ++ata)
+ if (ata->port)
+ for (u=0; u<2; ++u)
+ if (ata->attached[u])
+ return EBUSY;
+ for (ata=atapi_tab; ata<atapi_tab+2; ++ata)
+ if (ata->port)
+ for (u=0; u<2; ++u)
+ if (ata->params[u]) {
+ free (ata->params[u], M_TEMP);
+ ata->params[u] = 0;
+ }
+ atapi_start_ptr = 0;
+ atapi_intr_ptr = 0;
+ atapi_debug_ptr = 0;
+ atapi_request_wait_ptr = 0;
+ atapi_request_callback_ptr = 0;
+ atapi_request_immediate_ptr = 0;
+ atapi_tab = 0;
+ return 0;
+}
+
+/*
+ * Dispatcher function for the module (load/unload/stat).
+ */
+int atapi (struct lkm_table *lkmtp, int cmd, int ver)
+{
+ DISPATCH (lkmtp, cmd, ver, atapi_load, atapi_unload, nosys);
+}
+#endif /* ATAPI_MODULE */
+
#endif /* NWDC && ATAPI */
diff --git a/sys/i386/isa/atapi.h b/sys/i386/isa/atapi.h
index cb46dcc..f22a5a2 100644
--- a/sys/i386/isa/atapi.h
+++ b/sys/i386/isa/atapi.h
@@ -11,7 +11,7 @@
* or modify this software as long as this message is kept with the software,
* all derivative works or modified versions.
*
- * Version 1.8, Thu Sep 28 20:24:38 MSK 1995
+ * Version 1.9, Thu Oct 12 15:53:50 MSK 1995
*/
/*
@@ -190,10 +190,64 @@ struct atapires {
u_char error; /* error register contents */
};
+struct atapidrv { /* delayed attach info */
+ int ctlr; /* IDE controller, 0/1 */
+ int unit; /* drive unit, 0/1 */
+ int port; /* controller base port */
+ int attached; /* the drive is attached */
+ struct kern_devconf *parent; /* the devconf info pattern */
+};
+
+struct atapicmd { /* ATAPI command block */
+ struct atapicmd *next; /* next command in queue */
+ int busy; /* busy flag */
+ u_char cmd[16]; /* command and args */
+ int unit; /* drive unit number */
+ int count; /* byte count, >0 - read, <0 - write */
+ char *addr; /* data to transfer */
+ void (*callback) (); /* call when done */
+ void *cbarg1; /* callback arg 1 */
+ void *cbarg2; /* callback arg 1 */
+ struct atapires result; /* resulting error code */
+};
+
+struct atapi { /* ATAPI controller data */
+ u_short port; /* i/o port base */
+ u_char ctrlr; /* physical controller number */
+ u_char debug : 1; /* trace enable flag */
+ u_char cmd16 : 1; /* 16-byte command flag */
+ u_char intrcmd : 1; /* interrupt before cmd flag */
+ u_char slow : 1; /* slow reaction device */
+ u_char attached[2]; /* units are attached to subdrivers */
+ struct atapi_params *params[2]; /* params for units 0,1 */
+ struct kern_devconf *parent; /* parent configuration pattern */
+ struct atapicmd *queue; /* queue of commands to perform */
+ struct atapicmd *tail; /* tail of queue */
+ struct atapicmd *free; /* queue of free command blocks */
+ struct atapicmd cmdrq[16]; /* pool of command requests */
+};
+
#ifdef KERNEL
struct atapi;
struct kern_devconf;
-void atapi_attach (int ctlr, int unit, int port, struct kern_devconf*);
+
+extern struct atapidrv atapi_drvtab[4]; /* delayed attach info */
+extern int atapi_ndrv; /* the number of potential drives */
+extern struct atapi *atapi_tab; /* the table of atapi controllers */
+
+#ifndef ATAPI_STATIC
+# define atapi_start (*atapi_start_ptr)
+# define atapi_intr (*atapi_intr_ptr)
+# define atapi_debug (*atapi_debug_ptr)
+# define atapi_request_wait (*atapi_request_wait_ptr)
+# define atapi_request_callback (*atapi_request_callback_ptr)
+# define atapi_request_immediate (*atapi_request_immediate_ptr)
+#endif
+
+#ifndef ATAPI_MODULE
+int atapi_attach (int ctlr, int unit, int port, struct kern_devconf*);
+#endif
+
int atapi_start (int ctrlr);
int atapi_intr (int ctrlr);
void atapi_debug (struct atapi *ata, int on);
diff --git a/sys/i386/isa/wcd.c b/sys/i386/isa/wcd.c
index a5a2dff..a64953a 100644
--- a/sys/i386/isa/wcd.c
+++ b/sys/i386/isa/wcd.c
@@ -12,7 +12,7 @@
* or modify this software as long as this message is kept with the software,
* all derivative works or modified versions.
*
- * Version 1.8, Thu Sep 28 21:04:16 MSK 1995
+ * Version 1.9, Mon Oct 9 20:27:42 MSK 1995
*/
#include "wdc.h"
@@ -36,10 +36,9 @@
#define UNIT(d) ((minor(d) >> 3) & 3) /* Unit part of minor device number */
#define SECSIZE 2048 /* CD-ROM sector size in bytes */
-#define F_OPEN 0x0001 /* The drive os opened */
+#define F_BOPEN 0x0001 /* The block device is opened */
#define F_MEDIA_CHANGED 0x0002 /* The media have changed since open */
-#define F_DEBUG 0x0004 /* The media have changed since open */
-#define F_NOPLAYCD 0x0008 /* The PLAY_CD op not supported */
+#define F_DEBUG 0x0004 /* Print debug info */
/*
* Disc table of contents.
@@ -184,6 +183,7 @@ struct wcd {
int unit; /* IDE bus drive unit */
int lun; /* Logical device unit */
int flags; /* Device state flags */
+ int refcnt; /* The number of raw opens */
struct buf queue; /* Queue of i/o requests */
struct atapi_params *param; /* Drive parameters table */
struct toc toc; /* Table of disc contents */
@@ -212,6 +212,7 @@ static int wcd_goaway (struct kern_devconf *kdc, int force);
static void wcd_describe (struct wcd *t);
static int wcd_setchan (struct wcd *t,
u_char c0, u_char c1, u_char c2, u_char c3);
+static int wcd_eject (struct wcd *t);
static struct kern_devconf cftemplate = {
0, 0, 0, "wcd", 0, { MDDT_DISK, 0 },
@@ -244,7 +245,7 @@ static int wcd_goaway (struct kern_devconf *kdc, int force)
return 0;
}
-void wcdattach (struct atapi *ata, int unit, struct atapi_params *ap, int debug,
+int wcdattach (struct atapi *ata, int unit, struct atapi_params *ap, int debug,
struct kern_devconf *parent)
{
struct wcd *t;
@@ -252,12 +253,12 @@ void wcdattach (struct atapi *ata, int unit, struct atapi_params *ap, int debug,
if (wcdnlun >= NUNIT) {
printf ("wcd: too many units\n");
- return;
+ return (0);
}
t = malloc (sizeof (struct wcd), M_TEMP, M_NOWAIT);
if (! t) {
printf ("wcd: out of memory\n");
- return;
+ return (0);
}
wcdtab[wcdnlun] = t;
bzero (t, sizeof (struct wcd));
@@ -266,6 +267,7 @@ void wcdattach (struct atapi *ata, int unit, struct atapi_params *ap, int debug,
t->lun = wcdnlun++;
t->param = ap;
t->flags = F_MEDIA_CHANGED;
+ t->refcnt = 0;
if (debug) {
t->flags |= F_DEBUG;
/* Print params. */
@@ -305,6 +307,7 @@ void wcdattach (struct atapi *ata, int unit, struct atapi_params *ap, int debug,
strncpy (t->description + strlen(t->description),
ap->model, sizeof(ap->model));
dev_attach (&t->cf);
+ return (1);
}
void wcd_describe (struct wcd *t)
@@ -365,89 +368,72 @@ void wcd_describe (struct wcd *t)
printf ("\n");
}
-int wcdopen (dev_t dev, int flags, int fmt, struct proc *p)
+int wcd_open (dev_t dev, int rawflag)
{
int lun = UNIT(dev);
struct wcd *t;
struct atapires result;
- /* Check the unit is legal. */
- if (lun >= wcdnlun)
+ /* Check that the device number is legal
+ * and the ATAPI driver is loaded. */
+ if (lun >= wcdnlun || ! atapi_request_immediate)
return (ENXIO);
t = wcdtab[lun];
- /* If already opened, that's all. */
- if (t->flags & F_OPEN) {
- /* If it's been invalidated, forbid re-entry.
- * (may have changed media) */
- if (t->flags & F_MEDIA_CHANGED)
- return (ENXIO);
- return (0);
- }
-
- /* On the first open: check for the media.
- * Do it twice to avoid the stale media changed state. */
- result = atapi_request_wait (t->ata, t->unit, ATAPI_TEST_UNIT_READY,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ /* On the first open, read the table of contents. */
+ if (! (t->flags & F_BOPEN) && ! t->refcnt) {
+ /* Read table of contents. */
+ if (wcd_read_toc (t) < 0)
+ return (EIO);
- if (result.code == RES_ERR &&
- (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION) {
- t->flags |= F_MEDIA_CHANGED;
- result = atapi_request_wait (t->ata, t->unit,
- ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0);
- }
- if (result.code) {
- wcd_error (t, result);
- return (ENXIO);
+ /* Lock the media. */
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
}
+ if (rawflag)
+ ++t->refcnt;
+ else
+ t->flags |= F_BOPEN;
+ return (0);
+}
- /* Read table of contents. */
- if (wcd_read_toc (t) != 0)
- bzero (&t->toc, sizeof (t->toc));
-
- /* Read disc capacity. */
- if (wcd_request_wait (t, ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0,
- 0, sizeof(t->info), 0, (char*)&t->info, sizeof(t->info)) != 0)
- bzero (&t->info, sizeof (t->info));
- t->info.volsize = ntohl (t->info.volsize);
- t->info.blksize = ntohl (t->info.blksize);
-
- /* Print the disc description string on every disc change.
- * It would help to track the history of disc changes. */
- if (t->info.volsize && t->toc.hdr.ending_track &&
- (t->flags & F_MEDIA_CHANGED) && (t->flags & F_DEBUG)) {
- printf ("wcd%d: ", t->lun);
- if (t->toc.tab[0].control & 4)
- printf ("%ldMB ", t->info.volsize / 512);
- else
- printf ("%ld:%ld audio ", t->info.volsize/75/60,
- t->info.volsize/75%60);
- printf ("(%ld sectors), %d tracks\n", t->info.volsize,
- t->toc.hdr.ending_track - t->toc.hdr.starting_track + 1);
- }
- /* Lock the media. */
- wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
- 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
+int wcdbopen (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ return wcd_open (dev, 0);
+}
- t->flags &= ~F_MEDIA_CHANGED;
- t->flags |= F_OPEN;
- return (0);
+int wcdropen (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ return wcd_open (dev, 1);
}
/*
* Close the device. Only called if we are the LAST
* occurence of an open device.
*/
-int wcdclose (dev_t dev, int flags, int fmt, struct proc *p)
+int wcdbclose (dev_t dev, int flags, int fmt, struct proc *p)
{
int lun = UNIT(dev);
struct wcd *t = wcdtab[lun];
/* If we were the last open of the entire device, release it. */
- wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
- t->flags &= ~F_OPEN;
+ if (! t->refcnt)
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ t->flags &= ~F_BOPEN;
+ return (0);
+}
+
+int wcdrclose (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ int lun = UNIT(dev);
+ struct wcd *t = wcdtab[lun];
+
+ /* If we were the last open of the entire device, release it. */
+ if (! (t->flags & F_BOPEN) && t->refcnt == 1)
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ --t->refcnt;
return (0);
}
@@ -462,15 +448,6 @@ void wcdstrategy (struct buf *bp)
struct wcd *t = wcdtab[lun];
int x;
- /* If the device has been made invalid, error out
- * maybe the media changed. */
- if (t->flags & F_MEDIA_CHANGED) {
- bp->b_error = EIO;
- bp->b_flags |= B_ERROR;
- biodone (bp);
- return;
- }
-
/* Can't ever write to a CD. */
if (! (bp->b_flags & B_READ)) {
bp->b_error = EROFS;
@@ -608,34 +585,6 @@ static int wcd_request_wait (struct wcd *t, u_char cmd, u_char a1, u_char a2,
return (0);
}
-static int wcd_play (struct wcd *t, u_char cmd, u_char a1, u_char a2,
- u_char a3, u_char a4, u_char a5, u_char a6, u_char a7, u_char a8,
- u_char a9, char *addr, int count)
-{
- struct atapires result;
-
- if (! (t->flags & F_NOPLAYCD)) {
- t->cf.kdc_state = DC_BUSY;
- result = atapi_request_wait (t->ata, t->unit, ATAPI_PLAY_CD,
- cmd == ATAPI_PLAY_MSF ? 2 : 0, a2, a3, a4, a5, a6,
- a7, a8, a9, 0, 0, 0, 0, 0, 0, addr, count);
- t->cf.kdc_state = DC_IDLE;
- if (result.code == RES_ERR &&
- (result.error & AER_SKEY) == AER_SK_ILLEGAL_REQUEST) {
- /* Some drives don't support a PLAY_CD command.
- * Remember this and use PLAY_MSF instead. */
- t->flags |= F_NOPLAYCD;
- result.code = 0;
- }
- if (result.code) {
- wcd_error (t, result);
- return (EIO);
- }
- }
- return wcd_request_wait (t, cmd, a1, a2, a3, a4, a5, a6,
- a7, a8, a9, addr, count);
-}
-
static inline void lba2msf (int lba, u_char *m, u_char *s, u_char *f)
{
lba += 150; /* offset of first logical frame */
@@ -650,15 +599,29 @@ static inline void lba2msf (int lba, u_char *m, u_char *s, u_char *f)
* Perform special action on behalf of the user.
* Knows about the internals of this device
*/
-int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
+int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p)
{
int lun = UNIT(dev);
struct wcd *t = wcdtab[lun];
+ struct atapires result;
int error = 0;
- /* If the device is not valid.. abandon ship. */
if (t->flags & F_MEDIA_CHANGED)
- return (EIO);
+ switch (cmd) {
+ case CDIOCSETDEBUG:
+ case CDIOCCLRDEBUG:
+ case CDIOCRESET:
+ /* These ops are media change transparent. */
+ break;
+ default:
+ /* Read table of contents. */
+ wcd_read_toc (t);
+
+ /* Lock the media. */
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
+ break;
+ }
switch (cmd) {
default:
return (ENOTTY);
@@ -708,21 +671,15 @@ int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
case CDIOCEJECT:
- /* Stop the disc. */
- error = wcd_request_wait (t, ATAPI_START_STOP,
- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
- if (error)
- return (error);
- /* Give it some time to stop spinning. */
- tsleep ((caddr_t)&lbolt, PRIBIO, "wcdejct", 0);
- tsleep ((caddr_t)&lbolt, PRIBIO, "wcdejct", 0);
- /* Eject. */
- return wcd_request_wait (t, ATAPI_START_STOP,
- 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0);
+ /* Don't allow eject if the device is opened
+ * by somebody (not us) in block mode. */
+ if ((t->flags & F_BOPEN) && t->refcnt)
+ return (EBUSY);
+ return wcd_eject (t);
case CDIOREADTOCHEADER:
if (! t->toc.hdr.ending_track)
- return (ENODEV);
+ return (EIO);
bcopy (&t->toc.hdr, addr, sizeof t->toc.hdr);
break;
@@ -734,7 +691,7 @@ int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
u_long len;
if (! t->toc.hdr.ending_track)
- return (ENODEV);
+ return (EIO);
if (te->starting_track < toc->hdr.starting_track ||
te->starting_track > toc->hdr.ending_track)
return (EINVAL);
@@ -808,14 +765,14 @@ int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
case CDIOCPLAYMSF: {
struct ioc_play_msf *args = (struct ioc_play_msf*) addr;
- return wcd_play (t, ATAPI_PLAY_MSF, 0, 0,
+ return wcd_request_wait (t, ATAPI_PLAY_MSF, 0, 0,
args->start_m, args->start_s, args->start_f,
args->end_m, args->end_s, args->end_f, 0, 0, 0);
}
case CDIOCPLAYBLOCKS: {
struct ioc_play_blocks *args = (struct ioc_play_blocks*) addr;
- return wcd_play (t, ATAPI_PLAY_BIG, 0,
+ return wcd_request_wait (t, ATAPI_PLAY_BIG, 0,
args->blk >> 24 & 0xff, args->blk >> 16 & 0xff,
args->blk >> 8 & 0xff, args->blk & 0xff,
args->len >> 24 & 0xff, args->len >> 16 & 0xff,
@@ -827,7 +784,7 @@ int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
int t1, t2;
if (! t->toc.hdr.ending_track)
- return (ENODEV);
+ return (EIO);
/* Ignore index fields,
* play from start_track to end_track inclusive. */
@@ -842,7 +799,7 @@ int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
start = t->toc.tab[t1].addr.lba;
len = t->toc.tab[t2].addr.lba - start;
- return wcd_play (t, ATAPI_PLAY_BIG, 0,
+ return wcd_request_wait (t, ATAPI_PLAY_BIG, 0,
start >> 24 & 0xff, start >> 16 & 0xff,
start >> 8 & 0xff, start & 0xff,
len >> 24 & 0xff, len >> 16 & 0xff,
@@ -888,6 +845,9 @@ int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
if (t->flags & F_DEBUG)
wcd_dump (t->lun, "mask", &t->aumask, sizeof t->aumask);
+ /* Sony-55E requires the data length field to be zeroed. */
+ t->au.data_length = 0;
+
t->au.port[0].channels = CHANNEL_0;
t->au.port[1].channels = CHANNEL_1;
t->au.port[0].volume = arg->vol[0] & t->aumask.port[0].volume;
@@ -929,16 +889,40 @@ int wcdioctl (dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
static int wcd_read_toc (struct wcd *t)
{
int ntracks, len, i;
+ struct atapires result;
+
+ bzero (&t->toc, sizeof (t->toc));
+ bzero (&t->info, sizeof (t->info));
+
+ /* Check for the media.
+ * Do it twice to avoid the stale media changed state. */
+ result = atapi_request_wait (t->ata, t->unit, ATAPI_TEST_UNIT_READY,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ if (result.code == RES_ERR &&
+ (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION) {
+ t->flags |= F_MEDIA_CHANGED;
+ result = atapi_request_wait (t->ata, t->unit,
+ ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+ if (result.code) {
+ wcd_error (t, result);
+ return (EIO);
+ }
+ t->flags &= ~F_MEDIA_CHANGED;
/* First read just the header, so we know how long the TOC is. */
len = sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry);
if (wcd_request_wait (t, ATAPI_READ_TOC, 0, 0, 0, 0, 0, 0,
- len >> 8, len & 0xff, 0, (char*)&t->toc, len) != 0)
- return (EIO);
+ len >> 8, len & 0xff, 0, (char*)&t->toc, len) != 0) {
+err: bzero (&t->toc, sizeof (t->toc));
+ return (0);
+ }
ntracks = t->toc.hdr.ending_track - t->toc.hdr.starting_track + 1;
if (ntracks <= 0)
- return (EIO);
+ goto err;
if (ntracks > MAXTRK)
ntracks = MAXTRK;
@@ -947,11 +931,36 @@ static int wcd_read_toc (struct wcd *t)
(ntracks+1) * sizeof(struct cd_toc_entry);
if (wcd_request_wait (t, ATAPI_READ_TOC, 0, 0, 0, 0, 0, 0,
len >> 8, len & 0xff, 0, (char*)&t->toc, len) & 0xff)
- return (EIO);
+ goto err;
t->toc.hdr.len = ntohs (t->toc.hdr.len);
for (i=0; i<=ntracks; i++)
t->toc.tab[i].addr.lba = ntohl (t->toc.tab[i].addr.lba);
+
+ /* Decrement the total length of the disc.
+ * Some drives (e.g. Sony-55E) have this value too big. */
+ --t->toc.tab[ntracks].addr.lba;
+
+ /* Read disc capacity. */
+ if (wcd_request_wait (t, ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0,
+ 0, sizeof(t->info), 0, (char*)&t->info, sizeof(t->info)) != 0)
+ bzero (&t->info, sizeof (t->info));
+ t->info.volsize = ntohl (t->info.volsize);
+ t->info.blksize = ntohl (t->info.blksize);
+
+ /* Print the disc description string on every disc change.
+ * It would help to track the history of disc changes. */
+ if (t->info.volsize && t->toc.hdr.ending_track &&
+ (t->flags & F_MEDIA_CHANGED) && (t->flags & F_DEBUG)) {
+ printf ("wcd%d: ", t->lun);
+ if (t->toc.tab[0].control & 4)
+ printf ("%ldMB ", t->info.volsize / 512);
+ else
+ printf ("%ld:%ld audio ", t->info.volsize/75/60,
+ t->info.volsize/75%60);
+ printf ("(%ld sectors), %d tracks\n", t->info.volsize,
+ t->toc.hdr.ending_track - t->toc.hdr.starting_track + 1);
+ }
return (0);
}
@@ -973,6 +982,9 @@ static int wcd_setchan (struct wcd *t,
if (t->au.page_code != AUDIO_PAGE)
return (EIO);
+ /* Sony-55E requires the data length field to be zeroed. */
+ t->au.data_length = 0;
+
t->au.port[0].channels = c0;
t->au.port[1].channels = c1;
t->au.port[2].channels = c2;
@@ -981,4 +993,171 @@ static int wcd_setchan (struct wcd *t,
0, 0, 0, 0, 0, sizeof (t->au) >> 8, sizeof (t->au),
0, (char*) &t->au, - sizeof (t->au));
}
+
+static int wcd_eject (struct wcd *t)
+{
+ struct atapires result;
+
+ /* Try to stop the disc. */
+ t->cf.kdc_state = DC_BUSY;
+ result = atapi_request_wait (t->ata, t->unit, ATAPI_START_STOP,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ t->cf.kdc_state = DC_IDLE;
+
+ if (result.code == RES_ERR &&
+ ((result.error & AER_SKEY) == AER_SK_NOT_READY ||
+ (result.error & AER_SKEY) == AER_SK_UNIT_ATTENTION)) {
+ /*
+ * The disc was unloaded.
+ * Load it (close tray).
+ * Read the table of contents.
+ */
+ int err = wcd_request_wait (t, ATAPI_START_STOP,
+ 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0);
+ if (err)
+ return (err);
+
+ /* Read table of contents. */
+ wcd_read_toc (t);
+
+ /* Lock the media. */
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
+
+ return (0);
+ }
+
+ if (result.code) {
+ wcd_error (t, result);
+ return (EIO);
+ }
+
+ /* Give it some time to stop spinning. */
+ tsleep ((caddr_t)&lbolt, PRIBIO, "wcdej1", 0);
+ tsleep ((caddr_t)&lbolt, PRIBIO, "wcdej2", 0);
+
+ /* Unlock. */
+ wcd_request_wait (t, ATAPI_PREVENT_ALLOW,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ /* Eject. */
+ t->flags |= F_MEDIA_CHANGED;
+ return wcd_request_wait (t, ATAPI_START_STOP,
+ 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0);
+}
+
+#ifdef WCD_MODULE
+/*
+ * Loadable ATAPI CD-ROM driver stubs.
+ */
+#include <sys/exec.h>
+#include <sys/sysent.h>
+#include <sys/lkm.h>
+
+/*
+ * Device table entries.
+ * These get copied at modload time into the kernels
+ * lkm dummy device driver entries (see sys/i386/i386/conf.c).
+ */
+#define NOSTOP (d_stop_t*) enodev
+#define NOWRITE (d_rdwr_t*) enodev
+#define NORESET (d_reset_t*) nullop
+#define NODEVTOTTY (d_ttycv_t*) nullop
+#define NOMMAP (d_mmap_t*) enodev
+#define NODUMP (d_dump_t*) enxio
+#define ZEROSIZE (d_psize_t*) 0
+d_rdwr_t rawread;
+struct bdevsw dev_wcd = { wcdbopen, wcdbclose, wcdstrategy, wcdioctl,
+ NODUMP, ZEROSIZE, 0 };
+struct cdevsw dev_rwcd = { wcdropen, wcdrclose, rawread, NOWRITE, wcdioctl,
+ NOSTOP, NORESET, NODEVTOTTY, seltrue, NOMMAP,
+ wcdstrategy };
+/*
+ * Construct lkm_dev structures (see lkm.h).
+ * Our bdevsw/cdevsw slot numbers are 19/69.
+ */
+static struct lkm_dev wcd_module = {
+ LM_DEV, LKM_VERSION, "wcd", 19, LM_DT_BLOCK, { (void*) &dev_wcd } };
+static struct lkm_dev rwcd_module = {
+ LM_DEV, LKM_VERSION, "rwcd", 69, LM_DT_CHAR, { (void*) &dev_rwcd } };
+
+/*
+ * Function called when loading the driver.
+ */
+int wcd_load (struct lkm_table *lkmtp, int cmd)
+{
+ struct atapi *ata;
+ int n, u;
+
+ if (! atapi_start)
+ /* No ATAPI driver available. */
+ return EPROTONOSUPPORT;
+ n = 0;
+ for (ata=atapi_tab; ata<atapi_tab+2; ++ata)
+ if (ata->port)
+ for (u=0; u<2; ++u)
+ /* Probing controller ata->ctrlr, unit u. */
+ if (ata->params[u] && ! ata->attached[u] &&
+ wcdattach (ata, u, ata->params[u],
+ ata->debug, ata->parent) >= 0)
+ {
+ /* Drive found. */
+ ata->attached[u] = 1;
+ ++n;
+ }
+ if (! n)
+ /* No IDE CD-ROMs found. */
+ return ENXIO;
+ return 0;
+}
+
+/*
+ * Function called when unloading the driver.
+ */
+int wcd_unload (struct lkm_table *lkmtp, int cmd)
+{
+ struct wcd **t;
+
+ for (t=wcdtab; t<wcdtab+wcdnlun; ++t)
+ if (((*t)->flags & F_BOPEN) || (*t)->refcnt)
+ /* The device is opened, cannot unload the driver. */
+ return EBUSY;
+ for (t=wcdtab; t<wcdtab+wcdnlun; ++t) {
+ (*t)->ata->attached[(*t)->unit] = 0;
+ free (*t, M_TEMP);
+ }
+ wcdnlun = 0;
+ bzero (wcdtab, sizeof(wcdtab));
+ return 0;
+}
+
+/*
+ * Dispatcher function for the module (load/unload/stat).
+ */
+int wcd (struct lkm_table *lkmtp, int cmd, int ver)
+{
+ int err = 0;
+
+ if (ver != LKM_VERSION)
+ return EINVAL;
+
+ if (cmd == LKM_E_LOAD)
+ err = wcd_load (lkmtp, cmd);
+ else if (cmd == LKM_E_UNLOAD)
+ err = wcd_unload (lkmtp, cmd);
+ if (err)
+ return err;
+
+ /* Register the cdevsw entry. */
+ lkmtp->private.lkm_dev = &rwcd_module;
+ err = lkmdispatch (lkmtp, cmd);
+ if (err)
+ return err;
+
+ /* Register the bdevsw entry. */
+ lkmtp->private.lkm_dev = &wcd_module;
+ return lkmdispatch (lkmtp, cmd);
+}
+#endif /* WCD_MODULE */
+
#endif /* NWCD && NWDC && ATAPI */
diff --git a/sys/i386/isa/wd.c b/sys/i386/isa/wd.c
index 115f275..d3c8ec3 100644
--- a/sys/i386/isa/wd.c
+++ b/sys/i386/isa/wd.c
@@ -34,7 +34,7 @@
* SUCH DAMAGE.
*
* from: @(#)wd.c 7.2 (Berkeley) 5/9/91
- * $Id: wd.c,v 1.85 1995/09/30 00:11:19 jkh Exp $
+ * $Id: wd.c,v 1.86 1995/09/30 15:19:44 davidg Exp $
*/
/* TODO:
@@ -301,11 +301,34 @@ wdprobe(struct isa_device *dvp)
/* check if we have registers that work */
outb(du->dk_port + wd_sdh, WDSD_IBM); /* set unit 0 */
outb(du->dk_port + wd_cyl_lo, 0xa5); /* wd_cyl_lo is read/write */
- if (inb(du->dk_port + wd_cyl_lo) == 0xff) /* XXX too weak */
- goto nodevice;
+ if (inb(du->dk_port + wd_cyl_lo) == 0xff) { /* XXX too weak */
+#ifdef ATAPI
+ /* There is no master, try the ATAPI slave. */
+ outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10);
+ outb(du->dk_port + wd_cyl_lo, 0xa5);
+ if (inb(du->dk_port + wd_cyl_lo) == 0xff)
+#endif
+ goto nodevice;
+ }
- if (wdreset(du) != 0 && (DELAY(RECOVERYTIME), wdreset(du)) != 0)
+ if (wdreset(du) == 0)
+ goto reset_ok;
+#ifdef ATAPI
+ /* test for ATAPI signature */
+ outb(du->dk_port + wd_sdh, WDSD_IBM); /* master */
+ if (inb(du->dk_port + wd_cyl_lo) == 0x14 &&
+ inb(du->dk_port + wd_cyl_hi) == 0xeb)
+ goto reset_ok;
+ du->dk_unit = 1;
+ outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); /* slave */
+ if (inb(du->dk_port + wd_cyl_lo) == 0x14 &&
+ inb(du->dk_port + wd_cyl_hi) == 0xeb)
+ goto reset_ok;
+#endif
+ DELAY(RECOVERYTIME);
+ if (wdreset(du) != 0)
goto nodevice;
+reset_ok:
/* execute a controller only command */
if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0
@@ -657,7 +680,7 @@ loop:
bp = wdtab[ctrlr].b_actf;
if (bp == NULL) {
#ifdef ATAPI
- if (atapi_start (ctrlr))
+ if (atapi_start && atapi_start (ctrlr))
/* mark controller active in ATAPI mode */
wdtab[ctrlr].b_active = 3;
#endif
@@ -891,7 +914,7 @@ wdintr(int unit)
#ifdef ATAPI
if (wdtab[unit].b_active == 3) {
/* process an ATAPI interrupt */
- if (atapi_intr (unit))
+ if (atapi_intr && atapi_intr (unit))
/* ATAPI op continues */
return;
/* controller is free, start new op */
@@ -1916,13 +1939,6 @@ wdreset(struct disk *du)
du->dk_error = inb(wdc + wd_error);
if (du->dk_error != 0x01)
err = 1; /* the drive is incompatible */
- if (err) {
- /* no IDE drive, test for ATAPI signature */
- int lo = inb(wdc + wd_cyl_lo);
- int hi = inb(wdc + wd_cyl_hi);
- if (lo == 0x14 && hi == 0xeb)
- err = 0; /* ATAPI drive detected */
- }
#else
if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0
|| (du->dk_error = inb(wdc + wd_error)) != 0x01)
@@ -2042,6 +2058,18 @@ wdwait(struct disk *du, u_char bits_wanted, int timeout)
min_retries[du->dk_ctrlr] = timeout;
#endif
du->dk_status = status = inb(wdc + wd_status);
+#ifdef ATAPI
+ /*
+ * Atapi drives have a very interesting feature, when attached
+ * as a slave on the IDE bus, and there is no master.
+ * They release the bus after getting the command.
+ * We should reselect the drive here to get the status.
+ */
+ if (status == 0xff) {
+ outb(wdc + wd_sdh, WDSD_IBM | du->dk_unit << 4);
+ du->dk_status = status = inb(wdc + wd_status);
+ }
+#endif
if (!(status & WDCS_BUSY)) {
if (status & WDCS_ERR) {
du->dk_error = inb(wdc + wd_error);
diff --git a/sys/pc98/pc98/atapi.h b/sys/pc98/pc98/atapi.h
index cb46dcc..f22a5a2 100644
--- a/sys/pc98/pc98/atapi.h
+++ b/sys/pc98/pc98/atapi.h
@@ -11,7 +11,7 @@
* or modify this software as long as this message is kept with the software,
* all derivative works or modified versions.
*
- * Version 1.8, Thu Sep 28 20:24:38 MSK 1995
+ * Version 1.9, Thu Oct 12 15:53:50 MSK 1995
*/
/*
@@ -190,10 +190,64 @@ struct atapires {
u_char error; /* error register contents */
};
+struct atapidrv { /* delayed attach info */
+ int ctlr; /* IDE controller, 0/1 */
+ int unit; /* drive unit, 0/1 */
+ int port; /* controller base port */
+ int attached; /* the drive is attached */
+ struct kern_devconf *parent; /* the devconf info pattern */
+};
+
+struct atapicmd { /* ATAPI command block */
+ struct atapicmd *next; /* next command in queue */
+ int busy; /* busy flag */
+ u_char cmd[16]; /* command and args */
+ int unit; /* drive unit number */
+ int count; /* byte count, >0 - read, <0 - write */
+ char *addr; /* data to transfer */
+ void (*callback) (); /* call when done */
+ void *cbarg1; /* callback arg 1 */
+ void *cbarg2; /* callback arg 1 */
+ struct atapires result; /* resulting error code */
+};
+
+struct atapi { /* ATAPI controller data */
+ u_short port; /* i/o port base */
+ u_char ctrlr; /* physical controller number */
+ u_char debug : 1; /* trace enable flag */
+ u_char cmd16 : 1; /* 16-byte command flag */
+ u_char intrcmd : 1; /* interrupt before cmd flag */
+ u_char slow : 1; /* slow reaction device */
+ u_char attached[2]; /* units are attached to subdrivers */
+ struct atapi_params *params[2]; /* params for units 0,1 */
+ struct kern_devconf *parent; /* parent configuration pattern */
+ struct atapicmd *queue; /* queue of commands to perform */
+ struct atapicmd *tail; /* tail of queue */
+ struct atapicmd *free; /* queue of free command blocks */
+ struct atapicmd cmdrq[16]; /* pool of command requests */
+};
+
#ifdef KERNEL
struct atapi;
struct kern_devconf;
-void atapi_attach (int ctlr, int unit, int port, struct kern_devconf*);
+
+extern struct atapidrv atapi_drvtab[4]; /* delayed attach info */
+extern int atapi_ndrv; /* the number of potential drives */
+extern struct atapi *atapi_tab; /* the table of atapi controllers */
+
+#ifndef ATAPI_STATIC
+# define atapi_start (*atapi_start_ptr)
+# define atapi_intr (*atapi_intr_ptr)
+# define atapi_debug (*atapi_debug_ptr)
+# define atapi_request_wait (*atapi_request_wait_ptr)
+# define atapi_request_callback (*atapi_request_callback_ptr)
+# define atapi_request_immediate (*atapi_request_immediate_ptr)
+#endif
+
+#ifndef ATAPI_MODULE
+int atapi_attach (int ctlr, int unit, int port, struct kern_devconf*);
+#endif
+
int atapi_start (int ctrlr);
int atapi_intr (int ctrlr);
void atapi_debug (struct atapi *ata, int on);
OpenPOWER on IntegriCloud