#include <sys/param.h>
#include <sys/systm.h>
#include <sys/pool.h>
#include <dev/ata/atareg.h>
#include <dev/ata/atavar.h>
#include <dev/ic/wdcreg.h>
#define DEBUG_FUNCS 0x08
#define DEBUG_PROBE 0x10
#ifdef WDCDEBUG
extern int wdcdebug_mask;
#define WDCDEBUG_PRINT(args, level) do { \
if ((wdcdebug_mask & (level)) != 0) \
printf args; \
} while (0)
#else
#define WDCDEBUG_PRINT(args, level)
#endif
#define ATAPARAMS_SIZE 512
int
ata_get_params(struct ata_drive_datas *drvp, u_int8_t flags,
struct ataparams *prms)
{
char *tb;
struct wdc_command wdc_c;
int i;
u_int16_t *p;
int ret;
WDCDEBUG_PRINT(("ata_get_params\n"), DEBUG_FUNCS);
bzero(&wdc_c, sizeof(struct wdc_command));
if (drvp->drive_flags & DRIVE_ATA) {
wdc_c.r_command = WDCC_IDENTIFY;
wdc_c.r_st_bmask = WDCS_DRDY;
wdc_c.r_st_pmask = 0;
wdc_c.timeout = 3000;
} else if (drvp->drive_flags & DRIVE_ATAPI) {
wdc_c.r_command = ATAPI_IDENTIFY_DEVICE;
wdc_c.r_st_bmask = 0;
wdc_c.r_st_pmask = 0;
wdc_c.timeout = 10000;
} else {
WDCDEBUG_PRINT(("ata_get_params: no disks\n"),
DEBUG_FUNCS|DEBUG_PROBE);
return CMD_ERR;
}
tb = dma_alloc(ATAPARAMS_SIZE, PR_NOWAIT | PR_ZERO);
if (tb == NULL)
return CMD_AGAIN;
wdc_c.flags = AT_READ | flags;
wdc_c.data = tb;
wdc_c.bcount = ATAPARAMS_SIZE;
if ((ret = wdc_exec_command(drvp, &wdc_c)) != WDC_COMPLETE) {
WDCDEBUG_PRINT(("%s: wdc_exec_command failed: %d\n",
__func__, ret), DEBUG_PROBE);
dma_free(tb, ATAPARAMS_SIZE);
return CMD_AGAIN;
}
if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
WDCDEBUG_PRINT(("%s: IDENTIFY failed: 0x%x\n", __func__,
wdc_c.flags), DEBUG_PROBE);
dma_free(tb, ATAPARAMS_SIZE);
return CMD_ERR;
} else {
#if BYTE_ORDER == BIG_ENDIAN
swap16_multi((u_int16_t *)tb, 10);
swap16_multi((u_int16_t *)tb + 20, 3);
swap16_multi((u_int16_t *)tb + 47, ATAPARAMS_SIZE / 2 - 47);
#endif
bcopy(tb, prms, sizeof(struct ataparams));
if ((prms->atap_config & WDC_CFG_ATAPI_MASK) ==
WDC_CFG_ATAPI &&
((prms->atap_model[0] == 'N' &&
prms->atap_model[1] == 'E') ||
(prms->atap_model[0] == 'F' &&
prms->atap_model[1] == 'X'))) {
dma_free(tb, ATAPARAMS_SIZE);
return CMD_OK;
}
for (i = 0; i < sizeof(prms->atap_model); i += 2) {
p = (u_short *)(prms->atap_model + i);
*p = swap16(*p);
}
for (i = 0; i < sizeof(prms->atap_serial); i += 2) {
p = (u_short *)(prms->atap_serial + i);
*p = swap16(*p);
}
for (i = 0; i < sizeof(prms->atap_revision); i += 2) {
p = (u_short *)(prms->atap_revision + i);
*p = swap16(*p);
}
dma_free(tb, ATAPARAMS_SIZE);
return CMD_OK;
}
}
int
ata_set_mode(struct ata_drive_datas *drvp, u_int8_t mode, u_int8_t flags)
{
struct wdc_command wdc_c;
WDCDEBUG_PRINT(("%s: mode=0x%x, flags=0x%x\n", __func__,
mode, flags), DEBUG_PROBE);
bzero(&wdc_c, sizeof(struct wdc_command));
wdc_c.r_command = SET_FEATURES;
wdc_c.r_st_bmask = 0;
wdc_c.r_st_pmask = 0;
wdc_c.r_features = WDSF_SET_MODE;
wdc_c.r_count = mode;
wdc_c.flags = flags;
wdc_c.timeout = 1000;
if (wdc_exec_command(drvp, &wdc_c) != WDC_COMPLETE)
return CMD_AGAIN;
WDCDEBUG_PRINT(("%s: after wdc_exec_command() wdc_c.flags=0x%x\n",
__func__, wdc_c.flags), DEBUG_PROBE);
if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
return CMD_ERR;
}
return CMD_OK;
}
void
ata_dmaerr(struct ata_drive_datas *drvp)
{
drvp->n_dmaerrs++;
if (drvp->n_dmaerrs >= NERRS_MAX && drvp->n_xfers <= NXFER) {
wdc_downgrade_mode(drvp);
drvp->n_dmaerrs = NERRS_MAX-1;
drvp->n_xfers = 0;
return;
}
if (drvp->n_xfers > NXFER) {
drvp->n_dmaerrs = 1;
drvp->n_xfers = 1;
}
}
void
ata_perror(struct ata_drive_datas *drvp, int errno, char *buf, size_t buf_len)
{
static char *errstr0_3[] = {"address mark not found",
"track 0 not found", "aborted command", "media change requested",
"id not found", "media changed", "uncorrectable data error",
"bad block detected"};
static char *errstr4_5[] = {"",
"no media/write protected", "aborted command",
"media change requested", "id not found", "media changed",
"uncorrectable data error", "interface CRC error"};
char **errstr;
int i;
char *sep = "";
size_t len = 0;
if (drvp->ata_vers >= 4)
errstr = errstr4_5;
else
errstr = errstr0_3;
if (errno == 0) {
snprintf(buf, buf_len, "error not notified");
}
for (i = 0; i < 8; i++) {
if (errno & (1 << i)) {
snprintf(buf + len, buf_len - len, "%s%s", sep,
errstr[i]);
len = strlen(buf);
sep = ", ";
}
}
}