summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorConstantine Sapuntzakis <csapuntz@cvs.openbsd.org>2002-03-16 17:12:10 +0000
committerConstantine Sapuntzakis <csapuntz@cvs.openbsd.org>2002-03-16 17:12:10 +0000
commit62752d4d2ed70ae39b46c72b74b13d16b8f1aa7e (patch)
tree300c14808cfdd49f2d3dec0f265df6600af9e1cd /sys
parent822ddc908ff7f50a397fb0d8cb206ec008678708 (diff)
The new code logs the control signals on the IDE bus. You can extract the log
using atactl <device> dump. The log will help us support more devices. Send logs to csapuntz@
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/ic/wdc.c184
-rw-r--r--sys/dev/ic/wdcevent.h95
-rw-r--r--sys/dev/ic/wdcvar.h7
3 files changed, 273 insertions, 13 deletions
diff --git a/sys/dev/ic/wdc.c b/sys/dev/ic/wdc.c
index c7b7c984c89..8ecb2fd1ee6 100644
--- a/sys/dev/ic/wdc.c
+++ b/sys/dev/ic/wdc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: wdc.c,v 1.47 2002/03/14 01:26:55 millert Exp $ */
+/* $OpenBSD: wdc.c,v 1.48 2002/03/16 17:12:09 csapuntz Exp $ */
/* $NetBSD: wdc.c,v 1.68 1999/06/23 19:00:17 bouyer Exp $ */
@@ -86,6 +86,7 @@
#include <dev/ata/atareg.h>
#include <dev/ic/wdcreg.h>
#include <dev/ic/wdcvar.h>
+#include <dev/ic/wdcevent.h>
#include "atapiscsi.h"
@@ -149,6 +150,128 @@ struct channel_softc_vtbl wdc_default_vtbl = {
wdc_default_write_raw_multi_4
};
+static char *wdc_log_buf = NULL;
+static int wdc_tail = 0;
+static int wdc_head = 0;
+static int wdc_size = 16 * 1024;
+static int chp_idx = 1;
+
+void
+wdc_log(struct channel_softc *chp, int type,
+ unsigned int size, char val[])
+{
+ unsigned int request_size;
+ int idx;
+ char *ptr;
+ int nbytes;
+
+ if (wdc_head < 0 || wdc_head > wdc_size ||
+ wdc_tail < 0 || wdc_tail > wdc_size) {
+ printf ("wdc_log: wdc_head %x wdc_tail %x\n", wdc_head, wdc_tail);
+ return;
+ }
+
+ if (size > wdc_size / 2) {
+ printf ("wdc_log: type %d size %x\n", type, size);
+ }
+
+ if (wdc_log_buf == NULL) {
+ wdc_log_buf = malloc(wdc_size, M_DEVBUF, M_NOWAIT);
+ if (wdc_log_buf == NULL)
+ return;
+ }
+ if (chp->ch_log_idx == 0)
+ chp->ch_log_idx = chp_idx++;
+
+ request_size = size + 2;
+
+ /* Check how many bytes are left */
+ nbytes = wdc_head - wdc_tail;
+ if (nbytes < 0) nbytes += wdc_size;
+
+ if (nbytes + request_size >= wdc_size) {
+ wdc_tail = (wdc_tail + request_size * 2) % wdc_size;
+ }
+
+ /* Avoid wrapping in the middle of a request */
+ if (wdc_head + request_size >= wdc_size) {
+ memset(&wdc_log_buf[wdc_head], 0, wdc_size - wdc_head);
+ wdc_head = 0;
+ }
+
+ ptr = &wdc_log_buf[wdc_head];
+ *ptr++ = type & 0xff;
+ *ptr++ = ((chp->ch_log_idx & 0x7) << 5) | (size & 0x1f);
+
+ idx = 0;
+ while (size--) {
+ *ptr++ = val[idx];
+ idx++;
+ }
+ wdc_head += request_size;
+}
+
+char *wdc_get_log(unsigned int *, unsigned int *);
+
+char *
+wdc_get_log(unsigned int * size, unsigned int *left)
+{
+ int bytes = (wdc_head - wdc_tail);
+ char *retbuf;
+ int ot, c1, c2;
+
+ if (left != NULL)
+ *left = 0;
+
+ if (bytes < 0) bytes += wdc_size;
+ if (bytes > *size) {
+ if (left != NULL) {
+ *left = bytes - *size;
+ }
+ bytes = *size;
+ }
+
+ if (wdc_log_buf == NULL) {
+ *size = 0;
+ *left = 0;
+ return (NULL);
+ }
+
+ if (wdc_head < 0 || wdc_head > wdc_size ||
+ wdc_tail < 0 || wdc_tail > wdc_size) {
+ printf ("wdc_log: wdc_head %x wdc_tail %x\n", wdc_head, wdc_tail);
+ *size = 0;
+ *left = 0;
+ return (NULL);
+ }
+
+ retbuf = malloc(bytes, M_TEMP, M_NOWAIT);
+ if (retbuf == NULL) {
+ *size = 0;
+ *left = bytes;
+ return (NULL);
+ }
+
+ *size = bytes;
+
+ ot = wdc_tail;
+ wdc_tail += bytes;
+ if (wdc_tail > wdc_size) {
+ wdc_tail -= wdc_size;
+ c2 = wdc_tail;
+ c1 = bytes - wdc_tail;
+ } else {
+ c1 = bytes;
+ c2 = 0;
+ }
+
+ memcpy(retbuf, &wdc_log_buf[ot], c1);
+ memcpy(&retbuf[c1], &wdc_log_buf[0], c2);
+
+ return (retbuf);
+}
+
+
u_int8_t
wdc_default_read_reg(chp, reg)
struct channel_softc *chp;
@@ -316,6 +439,13 @@ wdc_enable_intr(chp)
CHP_WRITE_REG(chp, wdr_ctlr, WDCTL_4BIT);
}
+void
+wdc_set_drive(struct channel_softc *chp, int drive)
+{
+ CHP_WRITE_REG(chp, wdr_sdh, (drive << 4) | WDSD_IBM);
+ WDC_LOG_SET_DRIVE(chp, drive);
+}
+
int
wdc_floating_bus(chp, drive)
struct channel_softc *chp;
@@ -325,7 +455,7 @@ wdc_floating_bus(chp, drive)
u_int8_t cumulative_status, status;
int iter;
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4));
+ wdc_set_drive(chp, drive);
delay(10);
/* Stolen from Phoenix BIOS Drive Autotyping document */
@@ -365,7 +495,7 @@ wdc_preata_drive(chp, drive)
return 0;
}
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4));
+ wdc_set_drive(chp, drive);
delay(100);
if (wdcwait(chp, WDCS_DRDY | WDCS_DRQ, WDCS_DRDY, 10000) != 0) {
WDCDEBUG_PRINT(("%s:%d:%d: not ready\n",
@@ -375,6 +505,7 @@ wdc_preata_drive(chp, drive)
}
CHP_WRITE_REG(chp, wdr_command, WDCC_RECAL);
+ WDC_LOG_ATA_CMDSHORT(chp, WDCC_RECAL);
if (wdcwait(chp, WDCS_DRDY | WDCS_DRQ, WDCS_DRDY, 10000) != 0) {
WDCDEBUG_PRINT(("%s:%d:%d: WDCC_RECAL failed\n",
chp->wdc->sc_dev.dv_xname,
@@ -393,7 +524,7 @@ wdc_ata_present(chp, drive)
int time_to_done;
int retry_cnt = 0;
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4));
+ wdc_set_drive(chp, drive);
delay(10);
retry:
@@ -488,12 +619,14 @@ wdcprobe(chp)
if (chp->wdc == NULL ||
(chp->wdc->cap & WDC_CAPABILITY_NO_EXTRA_RESETS) == 0) {
/* Sample the statuses of drive 0 and 1 into st0 and st1 */
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM);
+ wdc_set_drive(chp, 0);
delay(10);
st0 = CHP_READ_REG(chp, wdr_status);
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | 0x10);
+ WDC_LOG_STATUS(chp, st0);
+ wdc_set_drive(chp, 1);
delay(10);
st1 = CHP_READ_REG(chp, wdr_status);
+ WDC_LOG_STATUS(chp, st1);
WDCDEBUG_PRINT(("%s:%d: before reset, st0=0x%x, st1=0x%x\n",
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe",
@@ -531,7 +664,7 @@ wdcprobe(chp)
for (drive = 0; drive < 2; drive++) {
if ((ret_value & (0x01 << drive)) == 0)
continue;
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4));
+ wdc_set_drive(chp, drive);
delay(10);
/* Save registers contents */
st0 = CHP_READ_REG(chp, wdr_status);
@@ -539,6 +672,7 @@ wdcprobe(chp)
sn = CHP_READ_REG(chp, wdr_sector);
cl = CHP_READ_REG(chp, wdr_cyl_lo);
ch = CHP_READ_REG(chp, wdr_cyl_hi);
+ WDC_LOG_REG(chp, wdr_cyl_lo, (ch << 8) | cl);
WDCDEBUG_PRINT(("%s:%d:%d: after reset, st=0x%x, sc=0x%x"
" sn=0x%x cl=0x%x ch=0x%x\n",
@@ -925,10 +1059,10 @@ __wdcwait_reset(chp, drv_mask)
/* wait for BSY to deassert */
for (timeout = 0; timeout < WDCNDELAY_RST;timeout++) {
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM); /* master */
+ wdc_set_drive(chp, 0);
delay(10);
st0 = CHP_READ_REG(chp, wdr_status);
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | 0x10); /* slave */
+ wdc_set_drive(chp, 1);
delay(10);
st1 = CHP_READ_REG(chp, wdr_status);
@@ -986,11 +1120,13 @@ wdc_wait_for_status(chp, mask, bits, timeout)
for (;;) {
chp->ch_status = status = CHP_READ_REG(chp, wdr_status);
+ WDC_LOG_STATUS(chp, chp->ch_status);
if (status == 0xff && (chp->ch_flags & WDCF_ONESLAVE)) {
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | 0x10);
+ wdc_set_drive(chp, 1);
chp->ch_status = status =
CHP_READ_REG(chp, wdr_status);
+ WDC_LOG_STATUS(chp, chp->ch_status);
}
if ((status & WDCS_BSY) == 0 && (status & mask) == bits)
break;
@@ -1005,6 +1141,8 @@ wdc_wait_for_status(chp, mask, bits, timeout)
}
if (status & WDCS_ERR) {
chp->ch_error = CHP_READ_REG(chp, wdr_error);
+ WDC_LOG_ERROR(chp, chp->ch_error);
+
WDCDEBUG_PRINT(("wdcwait: error %x\n", chp->ch_error),
DEBUG_STATUSX | DEBUG_STATUS);
}
@@ -1512,7 +1650,7 @@ __wdccommand_start(chp, xfer)
wdc_disable_intr(chp);
}
- CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4));
+ wdc_set_drive(chp, drive);
DELAY(1);
/*
@@ -1640,6 +1778,8 @@ wdccommand(chp, drive, command, cylin, head, sector, count, precomp)
"sector=%d count=%d precomp=%d\n", chp->wdc->sc_dev.dv_xname,
chp->channel, drive, command, cylin, head, sector, count, precomp),
DEBUG_FUNCS);
+ WDC_LOG_ATA_CMDLONG(chp, head, precomp, cylin, cylin >> 8, sector,
+ count, command);
/* Select drive, head, and addressing mode. */
CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4) | head);
@@ -1670,6 +1810,7 @@ wdccommandshort(chp, drive, command)
WDCDEBUG_PRINT(("wdccommandshort %s:%d:%d command 0x%x\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drive, command),
DEBUG_FUNCS);
+ WDC_LOG_ATA_CMDSHORT(chp, command);
/* Select drive. */
CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4));
@@ -1993,6 +2134,27 @@ wdc_ioctl(drvp, xfer, addr, flag, p)
int error = 0;
switch (xfer) {
+ case ATAIOGETTRACE: {
+ struct atagettrace *agt = (struct atagettrace *)addr;
+ unsigned int size = 0;
+ char *log_to_copy;
+
+ size = agt->buf_size;
+ if (size > 65536) {
+ size = 65536;
+ }
+
+ log_to_copy = wdc_get_log(&size, &agt->bytes_left);
+
+ if (log_to_copy != NULL) {
+ error = copyout(log_to_copy, agt->buf, size);
+ free(log_to_copy, M_TEMP);
+ }
+
+ agt->bytes_copied = size;
+ break;
+ }
+
case ATAIOCCOMMAND:
/*
* Make sure this command is (relatively) safe first
diff --git a/sys/dev/ic/wdcevent.h b/sys/dev/ic/wdcevent.h
new file mode 100644
index 00000000000..7cd08fd5311
--- /dev/null
+++ b/sys/dev/ic/wdcevent.h
@@ -0,0 +1,95 @@
+/* $OpenBSD: wdcevent.h,v 1.1 2002/03/16 17:12:09 csapuntz Exp $ */
+
+/*
+ * Copyright (c) 2001 Constantine Sapuntzakis
+ *
+ * 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.
+ * 2. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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.
+ *
+ */
+
+#ifndef WDCEVENT_H
+#define WDCEVENT_H
+
+void wdc_log(struct channel_softc *chp, int type,
+ unsigned int size, char val[]);
+
+static __inline void WDC_LOG_STATUS(struct channel_softc *chp,
+ u_int8_t status) {
+ if (chp->ch_prev_log_status == status)
+ return;
+
+ chp->ch_prev_log_status = status;
+ wdc_log(chp, 1, 1, &status);
+}
+
+static __inline void WDC_LOG_ERROR(struct channel_softc *chp,
+ u_int8_t error) {
+ wdc_log(chp, 2, 1, &error);
+}
+
+static __inline void WDC_LOG_ATAPI_CMD(struct channel_softc *chp, int drive,
+ int flags, int len, void *cmd) {
+ u_int8_t record[20];
+
+ record[0] = (flags >> 8);
+ record[1] = flags & 0xff;
+ bcopy(cmd, &record[2], len);
+
+ wdc_log(chp, 3, len + 2, record);
+}
+
+static __inline void WDC_LOG_ATAPI_DONE(struct channel_softc *chp, int drive,
+ int flags, u_int8_t error) {
+ char record[3] = {flags >> 8, flags & 0xff, error};
+ wdc_log(chp, 4, 3, record);
+}
+
+static __inline void WDC_LOG_ATA_CMDSHORT(struct channel_softc *chp, u_int8_t cmd) {
+ wdc_log(chp, 5, 1, &cmd);
+}
+
+static __inline void WDC_LOG_ATA_CMDLONG(struct channel_softc *chp,
+ u_int8_t head, u_int8_t precomp, u_int8_t cylinhi, u_int8_t cylinlo,
+ u_int8_t sector, u_int8_t count, u_int8_t command) {
+ char record[8] = { head, precomp, cylinhi, cylinlo,
+ sector, count, command };
+
+ wdc_log(chp, 6, 7, record);
+}
+
+static __inline void WDC_LOG_SET_DRIVE(struct channel_softc *chp,
+ u_int8_t drive) {
+ wdc_log(chp, drive ? 7 : 8, 0, NULL);
+}
+
+static __inline void WDC_LOG_REG(struct channel_softc *chp,
+ enum wdc_regs reg, u_int16_t val) {
+ char record[3];
+
+ record[0] = reg;
+ record[1] = (val >> 8);
+ record[2] = val & 0xff;
+
+ wdc_log(chp, 9, 3, record);
+}
+
+#endif
diff --git a/sys/dev/ic/wdcvar.h b/sys/dev/ic/wdcvar.h
index a6ab5d29aa7..09a4206f19f 100644
--- a/sys/dev/ic/wdcvar.h
+++ b/sys/dev/ic/wdcvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: wdcvar.h,v 1.23 2002/03/14 03:16:05 millert Exp $ */
+/* $OpenBSD: wdcvar.h,v 1.24 2002/03/16 17:12:09 csapuntz Exp $ */
/* $NetBSD: wdcvar.h,v 1.17 1999/04/11 20:50:29 bouyer Exp $ */
/*-
@@ -70,6 +70,8 @@ struct channel_softc { /* Per channel data */
#define WDCF_IRQ_WAIT 0x10 /* controller is waiting for irq */
#define WDCF_VERBOSE_PROBE 0x40 /* verbose probe */
u_int8_t ch_status; /* copy of status register */
+ u_int8_t ch_prev_log_status; /* previous logged value of status reg */
+ u_int8_t ch_log_idx;
u_int8_t ch_error; /* copy of error register */
/* per-drive infos */
struct ata_drive_datas ch_drive[2];
@@ -283,10 +285,11 @@ int atapi_print(void *, const char *);
void wdc_disable_intr(struct channel_softc *);
void wdc_enable_intr(struct channel_softc *);
int wdc_select_drive(struct channel_softc *, int, int);
-
+void wdc_set_drive(struct channel_softc *, int drive);
void wdc_output_bytes(struct ata_drive_datas *drvp, void *, unsigned int);
void wdc_input_bytes(struct ata_drive_datas *drvp, void *, unsigned int);
void wdc_print_current_modes(struct channel_softc *);
int wdc_ioctl(struct ata_drive_datas *, u_long, caddr_t, int, struct proc *);
+