#include <sys/param.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_disk.h>
#include <scsi/scsiconf.h>
#include <dev/biovar.h>
#include <dev/ic/gdtreg.h>
#include <dev/ic/gdtvar.h>
#include "bio.h"
#ifdef GDT_DEBUG
int gdt_maxcmds = GDT_MAXCMDS;
#undef GDT_MAXCMDS
#define GDT_MAXCMDS gdt_maxcmds
#endif
#define GDT_DRIVER_VERSION 1
#define GDT_DRIVER_SUBVERSION 2
int gdt_async_event(struct gdt_softc *, int);
void gdt_chain(struct gdt_softc *);
void gdt_clear_events(struct gdt_softc *);
struct scsi_xfer *gdt_dequeue(struct gdt_softc *);
void gdt_enqueue(struct gdt_softc *, struct scsi_xfer *, int);
void gdt_enqueue_ccb(struct gdt_softc *, struct gdt_ccb *);
void gdt_eval_mapping(u_int32_t, int *, int *, int *);
int gdt_exec_ccb(struct gdt_ccb *);
void gdt_ccb_free(void *, void *);
void *gdt_ccb_alloc(void *);
void gdt_internal_cache_cmd(struct scsi_xfer *);
int gdt_internal_cmd(struct gdt_softc *, u_int8_t, u_int16_t,
u_int32_t, u_int32_t, u_int32_t);
#if NBIO > 0
int gdt_ioctl(struct device *, u_long, caddr_t);
int gdt_ioctl_inq(struct gdt_softc *, struct bioc_inq *);
int gdt_ioctl_vol(struct gdt_softc *, struct bioc_vol *);
int gdt_ioctl_disk(struct gdt_softc *, struct bioc_disk *);
int gdt_ioctl_alarm(struct gdt_softc *, struct bioc_alarm *);
int gdt_ioctl_setstate(struct gdt_softc *, struct bioc_setstate *);
#endif
void gdt_scsi_cmd(struct scsi_xfer *);
void gdt_start_ccbs(struct gdt_softc *);
int gdt_sync_event(struct gdt_softc *, int, u_int8_t,
struct scsi_xfer *);
void gdt_timeout(void *);
int gdt_wait(struct gdt_softc *, struct gdt_ccb *, int);
void gdt_watchdog(void *);
struct cfdriver gdt_cd = {
NULL, "gdt", DV_DULL
};
const struct scsi_adapter gdt_switch = {
gdt_scsi_cmd, NULL, NULL, NULL, NULL
};
int gdt_cnt = 0;
u_int8_t gdt_polling;
u_int8_t gdt_from_wait;
struct gdt_softc *gdt_wait_gdt;
int gdt_wait_index;
#ifdef GDT_DEBUG
int gdt_debug = GDT_DEBUG;
#endif
int
gdt_attach(struct gdt_softc *sc)
{
struct scsibus_attach_args saa;
u_int16_t cdev_cnt;
int i, id, drv_cyls, drv_hds, drv_secs, error, nsegs;
gdt_polling = 1;
gdt_from_wait = 0;
if (bus_dmamem_alloc(sc->sc_dmat, GDT_SCRATCH_SZ, PAGE_SIZE, 0,
&sc->sc_scratch_seg, 1, &nsegs, BUS_DMA_NOWAIT))
panic("%s: bus_dmamem_alloc failed", DEVNAME(sc));
if (bus_dmamem_map(sc->sc_dmat, &sc->sc_scratch_seg, 1,
GDT_SCRATCH_SZ, &sc->sc_scratch, BUS_DMA_NOWAIT))
panic("%s: bus_dmamem_map failed", DEVNAME(sc));
gdt_clear_events(sc);
TAILQ_INIT(&sc->sc_free_ccb);
TAILQ_INIT(&sc->sc_ccbq);
SIMPLEQ_INIT(&sc->sc_queue);
mtx_init(&sc->sc_ccb_mtx, IPL_BIO);
scsi_iopool_init(&sc->sc_iopool, sc, gdt_ccb_alloc, gdt_ccb_free);
for (i = 0; i < GDT_MAXCMDS; i++) {
sc->sc_ccbs[i].gc_cmd_index = i + 2;
error = bus_dmamap_create(sc->sc_dmat,
(GDT_MAXOFFSETS - 1) << PGSHIFT, GDT_MAXOFFSETS,
(GDT_MAXOFFSETS - 1) << PGSHIFT, 0,
BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
&sc->sc_ccbs[i].gc_dmamap_xfer);
if (error) {
printf("%s: cannot create ccb dmamap (%d)",
DEVNAME(sc), error);
return (1);
}
(void)gdt_ccb_set_cmd(sc->sc_ccbs + i, GDT_GCF_UNUSED);
TAILQ_INSERT_TAIL(&sc->sc_free_ccb, &sc->sc_ccbs[i],
gc_chain);
}
if (!gdt_internal_cmd(sc, GDT_SCREENSERVICE, GDT_INIT, 0, 0, 0)) {
printf("screen service initialization error %d\n",
sc->sc_status);
return (1);
}
if (!gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_INIT, GDT_LINUX_OS, 0,
0)) {
printf("cache service initialization error %d\n",
sc->sc_status);
return (1);
}
cdev_cnt = (u_int16_t)sc->sc_info;
gdt_enc32(sc->sc_scratch + GDT_IOC_VERSION, GDT_IOC_NEWEST);
sc->sc_scratch[GDT_IOC_LIST_ENTRIES] = GDT_MAXBUS;
sc->sc_scratch[GDT_IOC_FIRST_CHAN] = 0;
sc->sc_scratch[GDT_IOC_LAST_CHAN] = GDT_MAXBUS - 1;
gdt_enc32(sc->sc_scratch + GDT_IOC_LIST_OFFSET, GDT_IOC_HDR_SZ);
if (gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_IOCTL,
GDT_IOCHAN_RAW_DESC, GDT_INVALID_CHANNEL,
GDT_IOC_HDR_SZ + GDT_RAWIOC_SZ)) {
sc->sc_bus_cnt = sc->sc_scratch[GDT_IOC_CHAN_COUNT];
for (i = 0; i < sc->sc_bus_cnt; i++) {
id = sc->sc_scratch[GDT_IOC_HDR_SZ +
i * GDT_RAWIOC_SZ + GDT_RAWIOC_PROC_ID];
sc->sc_bus_id[id] = id < GDT_MAXBUS ? id : 0xff;
}
} else {
gdt_enc32(sc->sc_scratch + GDT_GETCH_CHANNEL_NO, i);
for (i = 0; i < GDT_MAXBUS; i++) {
if (!gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_IOCTL,
GDT_SCSI_CHAN_CNT | GDT_L_CTRL_PATTERN,
GDT_IO_CHANNEL | GDT_INVALID_CHANNEL,
GDT_GETCH_SZ)) {
if (i == 0) {
printf("cannot get channel count, "
"error %d\n", sc->sc_status);
return (1);
}
break;
}
sc->sc_bus_id[i] =
(sc->sc_scratch[GDT_GETCH_SIOP_ID] < GDT_MAXID) ?
sc->sc_scratch[GDT_GETCH_SIOP_ID] : 0xff;
}
sc->sc_bus_cnt = i;
}
if (!gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_IOCTL, GDT_CACHE_INFO,
GDT_INVALID_CHANNEL, GDT_CINFO_SZ)) {
printf("cannot get cache info, error %d\n", sc->sc_status);
return (1);
}
sc->sc_cpar.cp_version =
gdt_dec32(sc->sc_scratch + GDT_CPAR_VERSION);
sc->sc_cpar.cp_state = gdt_dec16(sc->sc_scratch + GDT_CPAR_STATE);
sc->sc_cpar.cp_strategy =
gdt_dec16(sc->sc_scratch + GDT_CPAR_STRATEGY);
sc->sc_cpar.cp_write_back =
gdt_dec16(sc->sc_scratch + GDT_CPAR_WRITE_BACK);
sc->sc_cpar.cp_block_size =
gdt_dec16(sc->sc_scratch + GDT_CPAR_BLOCK_SIZE);
sc->sc_more_proc = 0;
if (gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_IOCTL, GDT_BOARD_INFO,
GDT_INVALID_CHANNEL, GDT_BINFO_SZ)) {
sc->sc_binfo.bi_ser_no =
gdt_dec32(sc->sc_scratch + GDT_BINFO_SER_NO);
bcopy(sc->sc_scratch + GDT_BINFO_OEM_ID,
sc->sc_binfo.bi_oem_id, sizeof sc->sc_binfo.bi_oem_id);
sc->sc_binfo.bi_ep_flags =
gdt_dec16(sc->sc_scratch + GDT_BINFO_EP_FLAGS);
sc->sc_binfo.bi_proc_id =
gdt_dec32(sc->sc_scratch + GDT_BINFO_PROC_ID);
sc->sc_binfo.bi_memsize =
gdt_dec32(sc->sc_scratch + GDT_BINFO_MEMSIZE);
sc->sc_binfo.bi_mem_banks =
sc->sc_scratch[GDT_BINFO_MEM_BANKS];
sc->sc_binfo.bi_chan_type =
sc->sc_scratch[GDT_BINFO_CHAN_TYPE];
sc->sc_binfo.bi_chan_count =
sc->sc_scratch[GDT_BINFO_CHAN_COUNT];
sc->sc_binfo.bi_rdongle_pres =
sc->sc_scratch[GDT_BINFO_RDONGLE_PRES];
sc->sc_binfo.bi_epr_fw_ver =
gdt_dec32(sc->sc_scratch + GDT_BINFO_EPR_FW_VER);
sc->sc_binfo.bi_upd_fw_ver =
gdt_dec32(sc->sc_scratch + GDT_BINFO_UPD_FW_VER);
sc->sc_binfo.bi_upd_revision =
gdt_dec32(sc->sc_scratch + GDT_BINFO_UPD_REVISION);
bcopy(sc->sc_scratch + GDT_BINFO_TYPE_STRING,
sc->sc_binfo.bi_type_string,
sizeof sc->sc_binfo.bi_type_string);
bcopy(sc->sc_scratch + GDT_BINFO_RAID_STRING,
sc->sc_binfo.bi_raid_string,
sizeof sc->sc_binfo.bi_raid_string);
sc->sc_binfo.bi_update_pres =
sc->sc_scratch[GDT_BINFO_UPDATE_PRES];
sc->sc_binfo.bi_xor_pres =
sc->sc_scratch[GDT_BINFO_XOR_PRES];
sc->sc_binfo.bi_prom_type =
sc->sc_scratch[GDT_BINFO_PROM_TYPE];
sc->sc_binfo.bi_prom_count =
sc->sc_scratch[GDT_BINFO_PROM_COUNT];
sc->sc_binfo.bi_dup_pres =
gdt_dec32(sc->sc_scratch + GDT_BINFO_DUP_PRES);
sc->sc_binfo.bi_chan_pres =
gdt_dec32(sc->sc_scratch + GDT_BINFO_CHAN_PRES);
sc->sc_binfo.bi_mem_pres =
gdt_dec32(sc->sc_scratch + GDT_BINFO_MEM_PRES);
sc->sc_binfo.bi_ft_bus_system =
sc->sc_scratch[GDT_BINFO_FT_BUS_SYSTEM];
sc->sc_binfo.bi_subtype_valid =
sc->sc_scratch[GDT_BINFO_SUBTYPE_VALID];
sc->sc_binfo.bi_board_subtype =
sc->sc_scratch[GDT_BINFO_BOARD_SUBTYPE];
sc->sc_binfo.bi_rampar_pres =
sc->sc_scratch[GDT_BINFO_RAMPAR_PRES];
if (gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_IOCTL,
GDT_BOARD_FEATURES, GDT_INVALID_CHANNEL, GDT_BFEAT_SZ)) {
sc->sc_bfeat.bf_chaining =
sc->sc_scratch[GDT_BFEAT_CHAINING];
sc->sc_bfeat.bf_striping =
sc->sc_scratch[GDT_BFEAT_STRIPING];
sc->sc_bfeat.bf_mirroring =
sc->sc_scratch[GDT_BFEAT_MIRRORING];
sc->sc_bfeat.bf_raid =
sc->sc_scratch[GDT_BFEAT_RAID];
sc->sc_more_proc = 1;
}
} else {
}
if (sc->sc_more_proc) {
int bus, j;
gdt_enc32(sc->sc_scratch + GDT_IOC_VERSION, GDT_IOC_NEWEST);
sc->sc_scratch[GDT_IOC_LIST_ENTRIES] = GDT_MAXBUS;
sc->sc_scratch[GDT_IOC_FIRST_CHAN] = 0;
sc->sc_scratch[GDT_IOC_LAST_CHAN] = GDT_MAXBUS - 1;
gdt_enc32(sc->sc_scratch + GDT_IOC_LIST_OFFSET, GDT_IOC_HDR_SZ);
if (gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_IOCTL,
GDT_IOCHAN_DESC, GDT_INVALID_CHANNEL,
GDT_IOC_HDR_SZ + GDT_IOC_SZ * GDT_MAXBUS)) {
GDT_DPRINTF(GDT_D_INFO, ("method 1\n"));
for (bus = 0; bus < sc->sc_bus_cnt; bus++) {
sc->sc_raw[bus].ra_address =
gdt_dec32(sc->sc_scratch +
GDT_IOC_HDR_SZ +
GDT_IOC_SZ * bus +
GDT_IOC_ADDRESS);
sc->sc_raw[bus].ra_local_no =
gdt_dec8(sc->sc_scratch +
GDT_IOC_HDR_SZ +
GDT_IOC_SZ * bus +
GDT_IOC_LOCAL_NO);
GDT_DPRINTF(GDT_D_INFO, (
"bus: %d address: %x local: %x\n",
bus,
sc->sc_raw[bus].ra_address,
sc->sc_raw[bus].ra_local_no));
}
} else {
GDT_DPRINTF(GDT_D_INFO, ("method 2\n"));
for (bus = 0; bus < sc->sc_bus_cnt; bus++) {
sc->sc_raw[bus].ra_address = GDT_IO_CHANNEL;
sc->sc_raw[bus].ra_local_no = bus;
GDT_DPRINTF(GDT_D_INFO, (
"bus: %d address: %x local: %x\n",
bus,
sc->sc_raw[bus].ra_address,
sc->sc_raw[bus].ra_local_no));
}
}
for (bus = 0; bus < sc->sc_bus_cnt; bus++) {
gdt_enc32(sc->sc_scratch + GDT_GETCH_CHANNEL_NO,
sc->sc_raw[bus].ra_local_no);
if (gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_IOCTL,
GDT_SCSI_CHAN_CNT | GDT_L_CTRL_PATTERN,
sc->sc_raw[bus].ra_address | GDT_INVALID_CHANNEL,
GDT_GETCH_SZ)) {
sc->sc_raw[bus].ra_phys_cnt =
gdt_dec32(sc->sc_scratch +
GDT_GETCH_DRIVE_CNT);
GDT_DPRINTF(GDT_D_INFO, ("chan: %d disks: %d\n",
bus, sc->sc_raw[bus].ra_phys_cnt));
}
if (sc->sc_raw[bus].ra_phys_cnt > 0) {
gdt_enc32(sc->sc_scratch +
GDT_GETSCSI_CHAN,
sc->sc_raw[bus].ra_local_no);
gdt_enc32(sc->sc_scratch +
GDT_GETSCSI_CNT,
sc->sc_raw[bus].ra_phys_cnt);
if (gdt_internal_cmd(sc, GDT_CACHESERVICE,
GDT_IOCTL,
GDT_SCSI_DR_LIST | GDT_L_CTRL_PATTERN,
sc->sc_raw[bus].ra_address |
GDT_INVALID_CHANNEL,
GDT_GETSCSI_SZ))
for (j = 0;
j < sc->sc_raw[bus].ra_phys_cnt;
j++) {
sc->sc_raw[bus].ra_id_list[j] =
gdt_dec32(sc->sc_scratch +
GDT_GETSCSI_LIST +
GDT_GETSCSI_LIST_SZ * j);
GDT_DPRINTF(GDT_D_INFO,
(" diskid: %d\n",
sc->sc_raw[bus].ra_id_list[j]));
}
else
sc->sc_raw[bus].ra_phys_cnt = 0;
}
sc->sc_total_disks += sc->sc_raw[bus].ra_phys_cnt;
}
}
if (!gdt_internal_cmd(sc, GDT_SCSIRAWSERVICE, GDT_INIT, 0, 0, 0)) {
printf("raw service initialization error %d\n",
sc->sc_status);
return (1);
}
sc->sc_raw_feat = 0;
if (gdt_internal_cmd(sc, GDT_SCSIRAWSERVICE, GDT_SET_FEAT,
GDT_SCATTER_GATHER, 0, 0))
if (gdt_internal_cmd(sc, GDT_SCSIRAWSERVICE, GDT_GET_FEAT, 0,
0, 0))
sc->sc_raw_feat = sc->sc_info;
sc->sc_cache_feat = 0;
if (gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_SET_FEAT, 0,
GDT_SCATTER_GATHER, 0))
if (gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_GET_FEAT, 0, 0,
0))
sc->sc_cache_feat = sc->sc_info;
sc->sc_ndevs = 0;
for (i = 0; i < cdev_cnt && i < GDT_MAX_HDRIVES; i++)
if (gdt_internal_cmd(sc, GDT_CACHESERVICE, GDT_INFO, i, 0,
0)) {
sc->sc_hdr[i].hd_present = 1;
sc->sc_hdr[i].hd_size = sc->sc_info;
if (sc->sc_hdr[i].hd_size > 0)
sc->sc_ndevs++;
sc->sc_hdr[i].hd_size &= ~GDT_SECS32;
if (sc->sc_info2 == 0)
gdt_eval_mapping(sc->sc_hdr[i].hd_size,
&drv_cyls, &drv_hds, &drv_secs);
else {
drv_hds = sc->sc_info2 & 0xff;
drv_secs = (sc->sc_info2 >> 8) & 0xff;
drv_cyls = sc->sc_hdr[i].hd_size / drv_hds /
drv_secs;
}
sc->sc_hdr[i].hd_heads = drv_hds;
sc->sc_hdr[i].hd_secs = drv_secs;
sc->sc_hdr[i].hd_size = drv_cyls * drv_hds * drv_secs;
if (gdt_internal_cmd(sc, GDT_CACHESERVICE,
GDT_DEVTYPE, i, 0, 0))
sc->sc_hdr[i].hd_devtype = sc->sc_info;
}
printf("dpmem %llx %d-bus %d cache device%s\n",
(long long)sc->sc_dpmembase,
sc->sc_bus_cnt, cdev_cnt, cdev_cnt == 1 ? "" : "s");
printf("%s: ver %x, cache %s, strategy %d, writeback %s, blksz %d\n",
DEVNAME(sc), sc->sc_cpar.cp_version,
sc->sc_cpar.cp_state ? "on" : "off", sc->sc_cpar.cp_strategy,
sc->sc_cpar.cp_write_back ? "on" : "off",
sc->sc_cpar.cp_block_size);
#if 1
printf("%s: raw feat %x cache feat %x\n", DEVNAME(sc),
sc->sc_raw_feat, sc->sc_cache_feat);
#endif
#if NBIO > 0
if (bio_register(&sc->sc_dev, gdt_ioctl) != 0)
panic("%s: controller registration failed", DEVNAME(sc));
#endif
gdt_cnt++;
saa.saa_adapter_softc = sc;
saa.saa_adapter = &gdt_switch;
saa.saa_adapter_buswidth =
(sc->sc_class & GDT_FC) ? GDT_MAXID : GDT_MAX_HDRIVES;
saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
saa.saa_luns = 8;
if (sc->sc_ndevs == 0)
saa.saa_openings = 0;
else
saa.saa_openings = (GDT_MAXCMDS - GDT_CMD_RESERVE) /
sc->sc_ndevs;
saa.saa_pool = &sc->sc_iopool;
saa.saa_quirks = saa.saa_flags = 0;
saa.saa_wwpn = saa.saa_wwnn = 0;
config_found(&sc->sc_dev, &saa, scsiprint);
gdt_polling = 0;
return (0);
}
void
gdt_eval_mapping(u_int32_t size, int *cyls, int *heads, int *secs)
{
*cyls = size / GDT_HEADS / GDT_SECS;
if (*cyls < GDT_MAXCYLS) {
*heads = GDT_HEADS;
*secs = GDT_SECS;
} else {
*cyls = size / GDT_MEDHEADS / GDT_MEDSECS;
if (*cyls < GDT_MAXCYLS) {
*heads = GDT_MEDHEADS;
*secs = GDT_MEDSECS;
} else {
*cyls = size / GDT_BIGHEADS / GDT_BIGSECS;
*heads = GDT_BIGHEADS;
*secs = GDT_BIGSECS;
}
}
}
void
gdt_enqueue(struct gdt_softc *sc, struct scsi_xfer *xs, int infront)
{
if (infront)
SIMPLEQ_INSERT_HEAD(&sc->sc_queue, xs, xfer_list);
else
SIMPLEQ_INSERT_TAIL(&sc->sc_queue, xs, xfer_list);
}
struct scsi_xfer *
gdt_dequeue(struct gdt_softc *sc)
{
struct scsi_xfer *xs;
xs = SIMPLEQ_FIRST(&sc->sc_queue);
if (xs != NULL)
SIMPLEQ_REMOVE_HEAD(&sc->sc_queue, xfer_list);
return (xs);
}
void
gdt_scsi_cmd(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct gdt_softc *sc = link->bus->sb_adapter_softc;
u_int8_t target = link->target;
struct gdt_ccb *ccb;
u_int32_t blockno, blockcnt;
struct scsi_rw *rw;
struct scsi_rw_10 *rw10;
bus_dmamap_t xfer;
int error;
int s;
int polled;
GDT_DPRINTF(GDT_D_CMD, ("gdt_scsi_cmd "));
s = splbio();
xs->error = XS_NOERROR;
if (target >= GDT_MAX_HDRIVES || !sc->sc_hdr[target].hd_present ||
link->lun != 0) {
xs->error = XS_DRIVER_STUFFUP;
scsi_done(xs);
splx(s);
return;
}
if (xs != SIMPLEQ_FIRST(&sc->sc_queue))
gdt_enqueue(sc, xs, 0);
while ((xs = gdt_dequeue(sc)) != NULL) {
xs->error = XS_NOERROR;
ccb = NULL;
link = xs->sc_link;
target = link->target;
polled = ISSET(xs->flags, SCSI_POLL);
if (!gdt_polling && !(xs->flags & SCSI_POLL) &&
sc->sc_test_busy(sc)) {
gdt_enqueue(sc, xs, 1);
break;
}
switch (xs->cmd.opcode) {
case TEST_UNIT_READY:
case REQUEST_SENSE:
case INQUIRY:
case MODE_SENSE:
case START_STOP:
case READ_CAPACITY:
#if 0
case VERIFY:
#endif
gdt_internal_cache_cmd(xs);
scsi_done(xs);
goto ready;
case PREVENT_ALLOW:
GDT_DPRINTF(GDT_D_CMD, ("PREVENT/ALLOW "));
xs->error = XS_NOERROR;
scsi_done(xs);
goto ready;
default:
GDT_DPRINTF(GDT_D_CMD,
("unknown opc %d ", xs->cmd.opcode));
xs->error = XS_DRIVER_STUFFUP;
scsi_done(xs);
goto ready;
case READ_COMMAND:
case READ_10:
case WRITE_COMMAND:
case WRITE_10:
case SYNCHRONIZE_CACHE:
sc->sc_cmd_off = 0;
if (xs->cmd.opcode == SYNCHRONIZE_CACHE) {
blockno = blockcnt = 0;
} else {
if (xs->cmdlen == 6) {
rw = (struct scsi_rw *)&xs->cmd;
blockno = _3btol(rw->addr) &
(SRW_TOPADDR << 16 | 0xffff);
blockcnt =
rw->length ? rw->length : 0x100;
} else {
rw10 = (struct scsi_rw_10 *)&xs->cmd;
blockno = _4btol(rw10->addr);
blockcnt = _2btol(rw10->length);
}
if (blockno >= sc->sc_hdr[target].hd_size ||
blockno + blockcnt >
sc->sc_hdr[target].hd_size) {
printf(
"%s: out of bounds %u-%u >= %u\n",
DEVNAME(sc), blockno,
blockcnt,
sc->sc_hdr[target].hd_size);
xs->error = XS_DRIVER_STUFFUP;
scsi_done(xs);
goto ready;
}
}
ccb = xs->io;
ccb->gc_blockno = blockno;
ccb->gc_blockcnt = blockcnt;
ccb->gc_xs = xs;
ccb->gc_timeout = xs->timeout;
ccb->gc_service = GDT_CACHESERVICE;
ccb->gc_flags = 0;
gdt_ccb_set_cmd(ccb, GDT_GCF_SCSI);
if (xs->cmd.opcode != SYNCHRONIZE_CACHE) {
xfer = ccb->gc_dmamap_xfer;
error = bus_dmamap_load(sc->sc_dmat, xfer,
xs->data, xs->datalen, NULL,
(xs->flags & SCSI_NOSLEEP) ?
BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
if (error) {
printf("%s: gdt_scsi_cmd: ",
DEVNAME(sc));
if (error == EFBIG)
printf(
"more than %d dma segs\n",
GDT_MAXOFFSETS);
else
printf("error %d "
"loading dma map\n",
error);
xs->error = XS_DRIVER_STUFFUP;
scsi_done(xs);
goto ready;
}
bus_dmamap_sync(sc->sc_dmat, xfer, 0,
xfer->dm_mapsize,
(xs->flags & SCSI_DATA_IN) ?
BUS_DMASYNC_PREREAD :
BUS_DMASYNC_PREWRITE);
}
gdt_enqueue_ccb(sc, ccb);
if (gdt_polling || (xs->flags & SCSI_POLL)) {
if (!gdt_wait(sc, ccb, ccb->gc_timeout)) {
printf("%s: command %d timed out\n",
DEVNAME(sc),
ccb->gc_cmd_index);
xs->error = XS_TIMEOUT;
scsi_done(xs);
splx(s);
return;
}
}
}
ready:
if (polled) {
break;
}
}
splx(s);
}
int
gdt_exec_ccb(struct gdt_ccb *ccb)
{
struct scsi_xfer *xs = ccb->gc_xs;
struct scsi_link *link = xs->sc_link;
struct gdt_softc *sc = link->bus->sb_adapter_softc;
u_int8_t target = link->target;
u_int32_t sg_canz;
bus_dmamap_t xfer;
int i;
#if 1
static int __level = 0;
if (__level++ > 0)
panic("level > 0");
#endif
GDT_DPRINTF(GDT_D_CMD, ("gdt_exec_ccb(%p, %p) ", xs, ccb));
sc->sc_cmd_cnt = 0;
if (sc->sc_cmd_cnt == 0)
sc->sc_set_sema0(sc);
gdt_enc32(sc->sc_cmd + GDT_CMD_COMMANDINDEX, ccb->gc_cmd_index);
gdt_enc32(sc->sc_cmd + GDT_CMD_BOARDNODE, GDT_LOCALBOARD);
gdt_enc16(sc->sc_cmd + GDT_CMD_UNION + GDT_CACHE_DEVICENO,
target);
switch (xs->cmd.opcode) {
case PREVENT_ALLOW:
case SYNCHRONIZE_CACHE:
if (xs->cmd.opcode == PREVENT_ALLOW) {
} else {
GDT_DPRINTF(GDT_D_CMD,
("SYNCHRONIZE CACHE tgt %d ", target));
sc->sc_cmd[GDT_CMD_OPCODE] = GDT_FLUSH;
}
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKNO,
1);
sg_canz = 0;
break;
case WRITE_COMMAND:
case WRITE_10:
sc->sc_cmd[GDT_CMD_OPCODE] = GDT_WRITE;
break;
case READ_COMMAND:
case READ_10:
sc->sc_cmd[GDT_CMD_OPCODE] = GDT_READ;
break;
}
if (xs->cmd.opcode != PREVENT_ALLOW &&
xs->cmd.opcode != SYNCHRONIZE_CACHE) {
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKNO,
ccb->gc_blockno);
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKCNT,
ccb->gc_blockcnt);
xfer = ccb->gc_dmamap_xfer;
if (sc->sc_cache_feat & GDT_SCATTER_GATHER) {
gdt_enc32(
sc->sc_cmd + GDT_CMD_UNION + GDT_CACHE_DESTADDR,
0xffffffff);
for (i = 0; i < xfer->dm_nsegs; i++) {
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION +
GDT_CACHE_SG_LST + i * GDT_SG_SZ +
GDT_SG_PTR,
xfer->dm_segs[i].ds_addr);
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION +
GDT_CACHE_SG_LST + i * GDT_SG_SZ +
GDT_SG_LEN,
xfer->dm_segs[i].ds_len);
GDT_DPRINTF(GDT_D_IO,
("#%d pa %lx len %lx\n", i,
xfer->dm_segs[i].ds_addr,
xfer->dm_segs[i].ds_len));
}
sg_canz = xfer->dm_nsegs;
gdt_enc32(
sc->sc_cmd + GDT_CMD_UNION + GDT_CACHE_SG_LST +
sg_canz * GDT_SG_SZ + GDT_SG_LEN, 0);
} else {
gdt_enc32(
sc->sc_cmd + GDT_CMD_UNION + GDT_CACHE_DESTADDR,
xfer->dm_segs[0].ds_addr);
sg_canz = 0;
}
}
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION + GDT_CACHE_SG_CANZ, sg_canz);
sc->sc_cmd_len =
roundup(GDT_CMD_UNION + GDT_CACHE_SG_LST + sg_canz * GDT_SG_SZ,
sizeof (u_int32_t));
if (sc->sc_cmd_cnt > 0 &&
sc->sc_cmd_off + sc->sc_cmd_len + GDT_DPMEM_COMMAND_OFFSET >
sc->sc_ic_all_size) {
printf("%s: DPMEM overflow\n", DEVNAME(sc));
xs->error = XS_BUSY;
#if 1
__level--;
#endif
return (0);
}
sc->sc_copy_cmd(sc, ccb);
sc->sc_release_event(sc, ccb);
xs->error = XS_NOERROR;
xs->resid = 0;
#if 1
__level--;
#endif
return (1);
}
void
gdt_internal_cache_cmd(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct gdt_softc *sc = link->bus->sb_adapter_softc;
struct scsi_inquiry_data inq;
struct scsi_sense_data sd;
struct scsi_read_cap_data rcd;
u_int8_t target = link->target;
GDT_DPRINTF(GDT_D_CMD, ("gdt_internal_cache_cmd "));
switch (xs->cmd.opcode) {
case TEST_UNIT_READY:
case START_STOP:
#if 0
case VERIFY:
#endif
GDT_DPRINTF(GDT_D_CMD, ("opc %d tgt %d ", xs->cmd.opcode,
target));
break;
case REQUEST_SENSE:
GDT_DPRINTF(GDT_D_CMD, ("REQUEST SENSE tgt %d ", target));
bzero(&sd, sizeof sd);
sd.error_code = SSD_ERRCODE_CURRENT;
sd.segment = 0;
sd.flags = SKEY_NO_SENSE;
gdt_enc32(sd.info, 0);
sd.extra_len = 0;
scsi_copy_internal_data(xs, &sd, sizeof(sd));
break;
case INQUIRY:
GDT_DPRINTF(GDT_D_CMD, ("INQUIRY tgt %d devtype %x ", target,
sc->sc_hdr[target].hd_devtype));
bzero(&inq, sizeof inq);
inq.device =
(sc->sc_hdr[target].hd_devtype & 4) ? T_CDROM : T_DIRECT;
inq.dev_qual2 =
(sc->sc_hdr[target].hd_devtype & 1) ? SID_REMOVABLE : 0;
inq.version = SCSI_REV_2;
inq.response_format = SID_SCSI2_RESPONSE;
inq.additional_length = SID_SCSI2_ALEN;
inq.flags |= SID_CmdQue;
strlcpy(inq.vendor, "ICP ", sizeof inq.vendor);
snprintf(inq.product, sizeof inq.product, "Host drive #%02d",
target);
strlcpy(inq.revision, " ", sizeof inq.revision);
scsi_copy_internal_data(xs, &inq, sizeof(inq));
break;
case READ_CAPACITY:
GDT_DPRINTF(GDT_D_CMD, ("READ CAPACITY tgt %d ", target));
bzero(&rcd, sizeof rcd);
_lto4b(sc->sc_hdr[target].hd_size - 1, rcd.addr);
_lto4b(GDT_SECTOR_SIZE, rcd.length);
scsi_copy_internal_data(xs, &rcd, sizeof(rcd));
break;
default:
GDT_DPRINTF(GDT_D_CMD, ("unsupported scsi command %#x tgt %d ",
xs->cmd.opcode, target));
xs->error = XS_DRIVER_STUFFUP;
return;
}
xs->error = XS_NOERROR;
}
void
gdt_clear_events(struct gdt_softc *sc)
{
GDT_DPRINTF(GDT_D_MISC, ("gdt_clear_events(%p) ", sc));
}
int
gdt_async_event(struct gdt_softc *sc, int service)
{
GDT_DPRINTF(GDT_D_INTR, ("gdt_async_event(%p, %d) ", sc, service));
if (service == GDT_SCREENSERVICE) {
} else {
}
return (0);
}
int
gdt_sync_event(struct gdt_softc *sc, int service, u_int8_t index,
struct scsi_xfer *xs)
{
GDT_DPRINTF(GDT_D_INTR,
("gdt_sync_event(%p, %d, %d, %p) ", sc, service, index, xs));
if (service == GDT_SCREENSERVICE) {
GDT_DPRINTF(GDT_D_INTR, ("service == GDT_SCREENSERVICE "));
return (0);
} else {
switch (sc->sc_status) {
case GDT_S_OK:
GDT_DPRINTF(GDT_D_INTR, ("sc_status == GDT_S_OK "));
break;
case GDT_S_BSY:
GDT_DPRINTF(GDT_D_INTR, ("sc_status == GDT_S_BSY "));
return (2);
default:
GDT_DPRINTF(GDT_D_INTR, ("sc_status is %d ",
sc->sc_status));
return (0);
}
}
return (1);
}
int
gdt_intr(void *arg)
{
struct gdt_softc *sc = arg;
struct gdt_intr_ctx ctx;
int chain = 1;
int sync_val = 0;
struct scsi_xfer *xs = NULL;
int prev_cmd;
struct gdt_ccb *ccb;
GDT_DPRINTF(GDT_D_INTR, ("gdt_intr(%p) ", sc));
if (gdt_polling && !gdt_from_wait)
return (0);
ctx.istatus = sc->sc_get_status(sc);
if (!ctx.istatus) {
sc->sc_status = GDT_S_NO_STATUS;
return (0);
}
gdt_wait_index = 0;
ctx.service = ctx.info2 = 0;
sc->sc_intr(sc, &ctx);
sc->sc_status = ctx.cmd_status;
sc->sc_info = ctx.info;
sc->sc_info2 = ctx.info2;
if (gdt_from_wait) {
gdt_wait_gdt = sc;
gdt_wait_index = ctx.istatus;
}
switch (ctx.istatus) {
case GDT_ASYNCINDEX:
gdt_async_event(sc, ctx.service);
goto finish;
case GDT_SPEZINDEX:
printf("%s: uninitialized or unknown service (%d %d)\n",
DEVNAME(sc), ctx.info, ctx.info2);
chain = 0;
goto finish;
}
ccb = &sc->sc_ccbs[ctx.istatus - 2];
xs = ccb->gc_xs;
if (!gdt_polling)
timeout_del(&xs->stimeout);
ctx.service = ccb->gc_service;
prev_cmd = ccb->gc_flags & GDT_GCF_CMD_MASK;
if (xs && xs->cmd.opcode != PREVENT_ALLOW &&
xs->cmd.opcode != SYNCHRONIZE_CACHE) {
bus_dmamap_sync(sc->sc_dmat, ccb->gc_dmamap_xfer, 0,
ccb->gc_dmamap_xfer->dm_mapsize,
(xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD :
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, ccb->gc_dmamap_xfer);
}
switch (prev_cmd) {
case GDT_GCF_UNUSED:
chain = 0;
goto finish;
case GDT_GCF_INTERNAL:
chain = 0;
goto finish;
}
sync_val = gdt_sync_event(sc, ctx.service, ctx.istatus, xs);
finish:
switch (sync_val) {
case 0:
if (xs && gdt_from_wait)
scsi_done(xs);
break;
case 1:
scsi_done(xs);
break;
case 2:
gdt_enqueue(sc, xs, 0);
}
if (chain)
gdt_chain(sc);
return (1);
}
int
gdt_wait(struct gdt_softc *sc, struct gdt_ccb *ccb, int timeout)
{
int s, rslt, rv = 0;
GDT_DPRINTF(GDT_D_MISC,
("gdt_wait(%p, %p, %d) ", sc, ccb, timeout));
gdt_from_wait = 1;
do {
s = splbio();
rslt = gdt_intr(sc);
splx(s);
if (rslt && sc == gdt_wait_gdt &&
ccb->gc_cmd_index == gdt_wait_index) {
rv = 1;
break;
}
DELAY(1000);
} while (--timeout);
gdt_from_wait = 0;
while (sc->sc_test_busy(sc))
DELAY(0);
return (rv);
}
int
gdt_internal_cmd(struct gdt_softc *sc, u_int8_t service, u_int16_t opcode,
u_int32_t arg1, u_int32_t arg2, u_int32_t arg3)
{
int retries, rslt;
struct gdt_ccb *ccb;
GDT_DPRINTF(GDT_D_CMD, ("gdt_internal_cmd(%p, %d, %d, %d, %d, %d) ",
sc, service, opcode, arg1, arg2, arg3));
bzero(sc->sc_cmd, GDT_CMD_SZ);
for (retries = GDT_RETRIES; ; ) {
ccb = scsi_io_get(&sc->sc_iopool, SCSI_NOSLEEP);
if (ccb == NULL) {
printf("%s: no free command index found\n",
DEVNAME(sc));
return (0);
}
ccb->gc_service = service;
ccb->gc_xs = NULL;
ccb->gc_blockno = ccb->gc_blockcnt = 0;
ccb->gc_timeout = ccb->gc_flags = 0;
ccb->gc_service = GDT_CACHESERVICE;
gdt_ccb_set_cmd(ccb, GDT_GCF_INTERNAL);
sc->sc_set_sema0(sc);
gdt_enc32(sc->sc_cmd + GDT_CMD_COMMANDINDEX,
ccb->gc_cmd_index);
gdt_enc16(sc->sc_cmd + GDT_CMD_OPCODE, opcode);
gdt_enc32(sc->sc_cmd + GDT_CMD_BOARDNODE, GDT_LOCALBOARD);
switch (service) {
case GDT_CACHESERVICE:
if (opcode == GDT_IOCTL) {
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION +
GDT_IOCTL_SUBFUNC, arg1);
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION +
GDT_IOCTL_CHANNEL, arg2);
gdt_enc16(sc->sc_cmd + GDT_CMD_UNION +
GDT_IOCTL_PARAM_SIZE, (u_int16_t)arg3);
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION +
GDT_IOCTL_P_PARAM,
sc->sc_scratch_seg.ds_addr);
} else {
gdt_enc16(sc->sc_cmd + GDT_CMD_UNION +
GDT_CACHE_DEVICENO, (u_int16_t)arg1);
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION +
GDT_CACHE_BLOCKNO, arg2);
}
break;
case GDT_SCSIRAWSERVICE:
gdt_enc32(sc->sc_cmd + GDT_CMD_UNION +
GDT_RAW_DIRECTION, arg1);
sc->sc_cmd[GDT_CMD_UNION + GDT_RAW_BUS] =
(u_int8_t)arg2;
sc->sc_cmd[GDT_CMD_UNION + GDT_RAW_TARGET] =
(u_int8_t)arg3;
sc->sc_cmd[GDT_CMD_UNION + GDT_RAW_LUN] =
(u_int8_t)(arg3 >> 8);
}
sc->sc_cmd_len = GDT_CMD_SZ;
sc->sc_cmd_off = 0;
sc->sc_cmd_cnt = 0;
sc->sc_copy_cmd(sc, ccb);
sc->sc_release_event(sc, ccb);
DELAY(20);
rslt = gdt_wait(sc, ccb, GDT_POLL_TIMEOUT);
scsi_io_put(&sc->sc_iopool, ccb);
if (!rslt)
return (0);
if (sc->sc_status != GDT_S_BSY || --retries == 0)
break;
DELAY(1);
}
return (sc->sc_status == GDT_S_OK);
}
void *
gdt_ccb_alloc(void *xsc)
{
struct gdt_softc *sc = xsc;
struct gdt_ccb *ccb;
GDT_DPRINTF(GDT_D_QUEUE, ("gdt_ccb_alloc(%p) ", sc));
mtx_enter(&sc->sc_ccb_mtx);
ccb = TAILQ_FIRST(&sc->sc_free_ccb);
if (ccb != NULL)
TAILQ_REMOVE(&sc->sc_free_ccb, ccb, gc_chain);
mtx_leave(&sc->sc_ccb_mtx);
return (ccb);
}
void
gdt_ccb_free(void *xsc, void *xccb)
{
struct gdt_softc *sc = xsc;
struct gdt_ccb *ccb = xccb;
int wake = 0;
GDT_DPRINTF(GDT_D_QUEUE, ("gdt_ccb_free(%p, %p) ", sc, ccb));
mtx_enter(&sc->sc_ccb_mtx);
TAILQ_INSERT_HEAD(&sc->sc_free_ccb, ccb, gc_chain);
if (TAILQ_NEXT(ccb, gc_chain) == NULL)
wake = 1;
mtx_leave(&sc->sc_ccb_mtx);
if (wake)
wakeup(&sc->sc_free_ccb);
}
void
gdt_enqueue_ccb(struct gdt_softc *sc, struct gdt_ccb *ccb)
{
GDT_DPRINTF(GDT_D_QUEUE, ("gdt_enqueue_ccb(%p, %p) ", sc, ccb));
timeout_set(&ccb->gc_xs->stimeout, gdt_timeout, ccb);
TAILQ_INSERT_TAIL(&sc->sc_ccbq, ccb, gc_chain);
gdt_start_ccbs(sc);
}
void
gdt_start_ccbs(struct gdt_softc *sc)
{
struct gdt_ccb *ccb;
struct scsi_xfer *xs;
GDT_DPRINTF(GDT_D_QUEUE, ("gdt_start_ccbs(%p) ", sc));
while ((ccb = TAILQ_FIRST(&sc->sc_ccbq)) != NULL) {
xs = ccb->gc_xs;
if (ccb->gc_flags & GDT_GCF_WATCHDOG)
timeout_del(&xs->stimeout);
if (gdt_exec_ccb(ccb) == 0) {
ccb->gc_flags |= GDT_GCF_WATCHDOG;
timeout_set(&ccb->gc_xs->stimeout, gdt_watchdog, ccb);
timeout_add_msec(&xs->stimeout, GDT_WATCH_TIMEOUT);
break;
}
TAILQ_REMOVE(&sc->sc_ccbq, ccb, gc_chain);
if ((xs->flags & SCSI_POLL) == 0) {
timeout_set(&ccb->gc_xs->stimeout, gdt_timeout, ccb);
timeout_add_msec(&xs->stimeout, ccb->gc_timeout);
}
}
}
void
gdt_chain(struct gdt_softc *sc)
{
GDT_DPRINTF(GDT_D_INTR, ("gdt_chain(%p) ", sc));
if (!SIMPLEQ_EMPTY(&sc->sc_queue))
gdt_scsi_cmd(SIMPLEQ_FIRST(&sc->sc_queue));
}
void
gdt_timeout(void *arg)
{
struct gdt_ccb *ccb = arg;
struct scsi_link *link = ccb->gc_xs->sc_link;
struct gdt_softc *sc = link->bus->sb_adapter_softc;
int s;
sc_print_addr(link);
printf("timed out\n");
ccb->gc_xs->error = XS_TIMEOUT;
s = splbio();
gdt_enqueue_ccb(sc, ccb);
splx(s);
}
void
gdt_watchdog(void *arg)
{
struct gdt_ccb *ccb = arg;
struct scsi_link *link = ccb->gc_xs->sc_link;
struct gdt_softc *sc = link->bus->sb_adapter_softc;
int s;
s = splbio();
ccb->gc_flags &= ~GDT_GCF_WATCHDOG;
gdt_start_ccbs(sc);
splx(s);
}
#if NBIO > 0
int
gdt_ioctl(struct device *dev, u_long cmd, caddr_t addr)
{
struct gdt_softc *sc = (struct gdt_softc *)dev;
int error = 0;
GDT_DPRINTF(GDT_D_IOCTL, ("%s: ioctl ", DEVNAME(sc)));
switch (cmd) {
case BIOCINQ:
GDT_DPRINTF(GDT_D_IOCTL, ("inq "));
error = gdt_ioctl_inq(sc, (struct bioc_inq *)addr);
break;
case BIOCVOL:
GDT_DPRINTF(GDT_D_IOCTL, ("vol "));
error = gdt_ioctl_vol(sc, (struct bioc_vol *)addr);
break;
case BIOCDISK:
GDT_DPRINTF(GDT_D_IOCTL, ("disk "));
error = gdt_ioctl_disk(sc, (struct bioc_disk *)addr);
break;
case BIOCALARM:
GDT_DPRINTF(GDT_D_IOCTL, ("alarm "));
error = gdt_ioctl_alarm(sc, (struct bioc_alarm *)addr);
break;
case BIOCSETSTATE:
GDT_DPRINTF(GDT_D_IOCTL, ("setstate "));
error = gdt_ioctl_setstate(sc, (struct bioc_setstate *)addr);
break;
default:
GDT_DPRINTF(GDT_D_IOCTL, (" invalid ioctl\n"));
error = ENOTTY;
}
return (error);
}
int
gdt_ioctl_inq(struct gdt_softc *sc, struct bioc_inq *bi)
{
bi->bi_novol = sc->sc_ndevs;
bi->bi_nodisk = sc->sc_total_disks;
strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev));
return (0);
}
int
gdt_ioctl_vol(struct gdt_softc *sc, struct bioc_vol *bv)
{
return (1);
}
int
gdt_ioctl_disk(struct gdt_softc *sc, struct bioc_disk *bd)
{
return (1);
}
int
gdt_ioctl_alarm(struct gdt_softc *sc, struct bioc_alarm *ba)
{
return (1);
}
int
gdt_ioctl_setstate(struct gdt_softc *sc, struct bioc_setstate *bs)
{
return (1);
}
#endif