summaryrefslogtreecommitdiff
path: root/sys/dev/ic/ami.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ic/ami.c')
-rw-r--r--sys/dev/ic/ami.c481
1 files changed, 474 insertions, 7 deletions
diff --git a/sys/dev/ic/ami.c b/sys/dev/ic/ami.c
index f93c91e0c44..49e98cbbb3b 100644
--- a/sys/dev/ic/ami.c
+++ b/sys/dev/ic/ami.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ami.c,v 1.46 2005/07/03 22:31:27 krw Exp $ */
+/* $OpenBSD: ami.c,v 1.47 2005/07/18 01:29:01 marco Exp $ */
/*
* Copyright (c) 2001 Michael Shalayeff
@@ -45,7 +45,7 @@
* Theo de Raadt.
*/
-/*#define AMI_DEBUG */
+/*#define AMI_DEBUG */
#include <sys/param.h>
#include <sys/systm.h>
@@ -76,11 +76,11 @@
#define AMI_D_DMA 0x0008
#define AMI_D_IOCTL 0x0010
int ami_debug = 0
- | AMI_D_CMD
- | AMI_D_INTR
- | AMI_D_MISC
-/* | AMI_D_DMA */
-/* | AMI_D_IOCTL */
+/* | AMI_D_CMD*/
+/* | AMI_D_INTR*/
+/* | AMI_D_MISC*/
+/* | AMI_D_DMA*/
+ | AMI_D_IOCTL
;
#else
#define AMI_DPRINTF(m,a) /* m, a */
@@ -136,11 +136,19 @@ void ami_copy_internal_data(struct scsi_xfer *xs, void *v, size_t size);
int ami_inquire(struct ami_softc *sc, u_int8_t op);
#if NBIO > 0
+int ami_mgmt(struct ami_softc *, u_int8_t, u_int8_t, u_int8_t,
+ size_t, void *);
+int ami_drv_inq(struct ami_softc *, u_int8_t, u_int8_t, void *);
int ami_ioctl(struct device *, u_long, caddr_t);
+int ami_ioctl_inq(struct ami_softc *, bioc_inq *);
+int ami_ioctl_vol(struct ami_softc *, bioc_vol *);
+int ami_ioctl_disk(struct ami_softc *, bioc_disk *);
+#if 0
int ami_ioctl_alarm(struct ami_softc *, bioc_alarm *);
int ami_ioctl_startstop( struct ami_softc *, bioc_startstop *);
int ami_ioctl_status( struct ami_softc *, bioc_status *);
int ami_ioctl_passthru(struct ami_softc *, bioc_scsicmd *);
+#endif
#endif /* NBIO > 0 */
struct ami_ccb *
@@ -582,6 +590,7 @@ ami_attach(sc)
sc->sc_link.adapter = &ami_switch;
sc->sc_link.adapter_target = sc->sc_maxunits;
sc->sc_link.adapter_buswidth = sc->sc_maxunits;
+ sc->sc_state_ch = 1; /* mark state as changed */
#ifdef AMI_DEBUG
printf(": FW %s, BIOS v%s, %dMB RAM\n"
@@ -1680,6 +1689,7 @@ ami_scsi_ioctl(struct scsi_link *link, u_long cmd,
caddr_t addr, int flag, struct proc *p)
{
struct ami_softc *sc = (struct ami_softc *)link->adapter_softc;
+ /* u_int8_t target = link->target; */
if (sc->sc_ioctl)
return (sc->sc_ioctl(link->adapter_softc, cmd, addr));
@@ -1688,6 +1698,461 @@ ami_scsi_ioctl(struct scsi_link *link, u_long cmd,
}
#if NBIO > 0
+struct disk {
+ SLIST_ENTRY(disk) next;
+ bioc_disk bd;
+
+ int ch;
+ int tg;
+};
+
+struct volume {
+ SLIST_ENTRY(volume) next;
+ bioc_vol bv;
+};
+
+SLIST_HEAD(disk_list, disk);
+struct disk_list disks = SLIST_HEAD_INITIALIZER(disk);
+
+SLIST_HEAD(vol_list, volume);
+struct vol_list volumes = SLIST_HEAD_INITIALIZER(volume);
+
+int
+ami_ioctl(dev, cmd, addr)
+ struct device *dev;
+ u_long cmd;
+ caddr_t addr;
+{
+ struct ami_softc *sc = (struct ami_softc *)dev;
+ ami_lock_t lock;
+ int error = 0;
+
+ AMI_DPRINTF(AMI_D_IOCTL, ("%s: ioctl()\n", sc->sc_dev.dv_xname));
+
+ if (sc->sc_flags & AMI_BROKEN)
+ return ENODEV; /* can't do this to broken device for now */
+
+ lock = AMI_LOCK_AMI(sc);
+ if (sc->sc_flags & AMI_CMDWAIT) {
+ AMI_UNLOCK_AMI(sc, lock);
+ return EBUSY;
+ }
+
+ switch (cmd) {
+ case BIOCINQ:
+ case BIOCVOL:
+ case BIOCDISK:
+ sc->sc_flags |= AMI_CMDWAIT;
+ while (!TAILQ_EMPTY(&sc->sc_ccbq))
+ if (tsleep(&sc->sc_free_ccb, PRIBIO, "ami_ioctl",
+ 100 * 60) == EWOULDBLOCK)
+ return EWOULDBLOCK;
+ }
+
+ switch (cmd) {
+ case BIOCINQ:
+ AMI_DPRINTF(AMI_D_IOCTL, ("inquiry\n"));
+ error = ami_ioctl_inq(sc, (bioc_inq *)addr);
+ break;
+
+ case BIOCVOL:
+ AMI_DPRINTF(AMI_D_IOCTL, ("volume\n"));
+ error = ami_ioctl_vol(sc, (bioc_vol *)addr);
+ break;
+
+ case BIOCDISK:
+ AMI_DPRINTF(AMI_D_IOCTL, ("disk\n"));
+ error = ami_ioctl_disk(sc, (bioc_disk *)addr);
+ break;
+
+ default:
+ AMI_DPRINTF(AMI_D_IOCTL, ("%s: invalid ioctl\n",
+ sc->sc_dev.dv_xname));
+ error = EINVAL;
+ }
+
+ sc->sc_flags &= ~AMI_CMDWAIT;
+ wakeup(&sc->sc_ccbq);
+
+ AMI_UNLOCK_AMI(sc, lock);
+
+ return (error);
+}
+
+int
+ami_drv_inq(sc, ch, tg, inqbuf)
+ struct ami_softc *sc;
+ u_int8_t ch;
+ u_int8_t tg;
+ void *inqbuf;
+{
+ struct ami_ccb *ccb;
+ struct ami_iocmd *cmd;
+ struct ami_passthrough *ps;
+ struct scsi_inquiry_data *pp;
+ void *idata;
+ bus_dmamap_t idatamap;
+ bus_dma_segment_t idataseg[1];
+ paddr_t pa;
+ int error = 0;
+
+ if (!(idata = ami_allocmem(sc->dmat, &idatamap, idataseg, NBPG, 1,
+ "ami mgmt"))) {
+ error = ENOMEM;
+ goto bail;
+ }
+
+ pa = idataseg[0].ds_addr;
+ ps = idata;
+ pp = idata + sizeof *ps;
+
+ ccb = ami_get_ccb(sc);
+ ccb->ccb_data = NULL;
+ cmd = ccb->ccb_cmd;
+
+ cmd->acc_cmd = AMI_PASSTHRU;
+ cmd->acc_passthru.apt_data = htole32(pa);
+
+ memset(ps, 0, sizeof *ps);
+
+ ps->apt_channel = ch;
+ ps->apt_target = tg;
+ ps->apt_ncdb = sizeof(struct scsi_inquiry);
+ ps->apt_nsense = sizeof(struct scsi_sense_data);
+
+ ps->apt_cdb[0] = INQUIRY;
+ ps->apt_cdb[1] = 0;
+ ps->apt_cdb[2] = 0;
+ ps->apt_cdb[3] = 0;
+ ps->apt_cdb[4] = sizeof(struct scsi_inquiry_data); /* INQUIRY length */
+ ps->apt_cdb[5] = 0;
+
+ ps->apt_data = htole32(pa + sizeof *ps);
+ ps->apt_datalen = sizeof(struct scsi_inquiry_data);
+
+ AMI_DPRINTF(AMI_D_IOCTL, ("%s: ami_drv_inq(%p, %02x, %02x, ,%p) ",
+ sc->sc_dev.dv_xname, sc, ch, tg, inqbuf));
+
+ if (ami_cmd(ccb, BUS_DMA_WAITOK, 1) == 0) {
+ AMI_DPRINTF(AMI_D_IOCTL, ("completed\n"));
+
+ memcpy(inqbuf, pp, sizeof(struct scsi_inquiry_data));
+ }
+ else {
+ AMI_DPRINTF(AMI_D_IOCTL, ("failed\n"));
+
+ error = EINVAL;
+ }
+
+bail:
+ ami_freemem(sc->dmat, &idatamap, idataseg, NBPG, 1, "ami mgmt");
+
+ return (error);
+}
+
+int
+ami_mgmt(sc, opcode, par1, par2, size, buffer)
+ struct ami_softc *sc;
+ u_int8_t opcode;
+ u_int8_t par1;
+ u_int8_t par2;
+ size_t size;
+ void *buffer;
+{
+ struct ami_ccb *ccb;
+ struct ami_iocmd *cmd;
+ void *idata;
+ bus_dmamap_t idatamap;
+ bus_dma_segment_t idataseg[1];
+ paddr_t pa;
+ int error = 0;
+
+ if (!(idata = ami_allocmem(sc->dmat, &idatamap, idataseg, NBPG,
+ (size / NBPG) + 1, "ami mgmt"))) {
+ error = ENOMEM;
+ goto bail;
+ }
+
+ pa = idataseg[0].ds_addr;
+
+ ccb = ami_get_ccb(sc);
+ ccb->ccb_data = NULL;
+ cmd = ccb->ccb_cmd;
+
+ cmd->acc_cmd = opcode;
+ cmd->acc_io.aio_channel = par1;
+ cmd->acc_io.aio_param = par2;
+ cmd->acc_io.aio_data = htole32(pa);
+
+ AMI_DPRINTF(AMI_D_IOCTL, ("%s: ami_mgmt(%p, %02x, %02x, %02x, %d,%p) ",
+ sc->sc_dev.dv_xname, sc, opcode, par1, par2, size, buffer));
+
+ if (ami_cmd(ccb, BUS_DMA_WAITOK, 1) == 0) {
+ AMI_DPRINTF(AMI_D_IOCTL, ("completed\n"));
+
+ memcpy(buffer, idata, size);
+ }
+ else {
+ AMI_DPRINTF(AMI_D_IOCTL, ("failed\n"));
+
+ error = EINVAL;
+ }
+
+bail:;
+ ami_freemem(sc->dmat, &idatamap, idataseg, NBPG, (size / NBPG) + 1,
+ "ami mgmt");
+
+ return (error);
+}
+
+int
+ami_ioctl_inq(sc, bi)
+ struct ami_softc *sc;
+ bioc_inq *bi;
+{
+ struct scsi_inquiry_data inqbuf;
+ struct ami_big_diskarray *p; /* struct too large for stack */
+ struct volume *vol;
+ struct disk *dk;
+ int i, s, t, c;
+ int off;
+ int error = 0;
+
+ if (!sc->sc_state_ch) {
+ bi->novol = 0;
+ bi->nodisk = 0;
+ SLIST_FOREACH(vol, &volumes, next) {
+ bi->novol++;
+ bi->nodisk += vol->bv.nodisk;
+ }
+
+ return 0;
+ }
+
+ AMI_DPRINTF(AMI_D_IOCTL, ("%s: biocinq", sc->sc_dev.dv_xname));
+
+ p = malloc(sizeof *p, M_DEVBUF, M_NOWAIT);
+ if (!p) {
+ printf("%s: no memory for raw interface\n", sc->sc_dev.dv_xname);
+ return (ENOMEM);
+ }
+
+ bi->nodisk = 0;
+
+ for (c = 0; c < 4; c++) { /* FIXME get max channel from inq3 */
+ for (t = 0; t < AMI_MAX_TARGET; t++) {
+ if (!ami_drv_inq(sc, c, t, &inqbuf)) {
+ if ((inqbuf.device & SID_TYPE) != T_DIRECT)
+ /* XXX save c:t if PROC for later use */
+ continue; /* we only care about disk */
+
+ dk = malloc(sizeof(struct disk), M_DEVBUF,
+ M_NOWAIT);
+ if (!dk) {
+ /* FIXME */
+ panic("not enough memory.");
+ }
+
+ dk->ch = c;
+ dk->tg = t;
+ dk->bd.diskid = bi->nodisk;
+ dk->bd.status = 0; /* XXX xlate */
+ dk->bd.volid = -1; /* not yet claimed */
+ dk->bd.size = 0; /* GETCAP or get in 2nd pass */
+ strlcpy(dk->bd.vendor, inqbuf.vendor,
+ 8 + 16 + 4 + 1); /* vendor prod rev zero */
+
+ bi->nodisk++;
+
+ SLIST_INSERT_HEAD(&disks, dk, next);
+ }
+ else {
+ AMI_DPRINTF(AMI_D_IOCTL, ("c: %d t: %d "
+ "fail\n", c, t));
+ }
+ }
+ }
+
+ if (!ami_mgmt(sc, AMI_FCOP, AMI_FC_RDCONF, 0, sizeof *p, p)) {
+ bi->novol = p->ada_nld;
+
+ AMI_DPRINTF(AMI_D_IOCTL, ("novol: %d nodisk: %d p: %p\n",
+ bi->novol, bi->nodisk, &p));
+
+ /* setup volume list */
+ for (i = 0; i < p->ada_nld; i++) {
+ vol = malloc(sizeof(struct volume), M_DEVBUF, M_NOWAIT);
+ if (!vol) {
+ /* FIXME */
+ panic("not enough memory.");
+ }
+
+ vol->bv.volid = i;
+ vol->bv.status = 0; /* XXX translate ami status */
+ vol->bv.size = 0;
+ vol->bv.level = p->ada_ldrv[i].adl_raidlvl;
+ vol->bv.nodisk = 0;
+ /* do string ops here, question is how */
+
+ for (s = 0; s < p->ada_ldrv[i].adl_spandepth; s++) {
+ for (t = 0; t < p->ada_ldrv[i].adl_nstripes; t++) {
+ vol->bv.nodisk++;
+
+ off = p->ada_ldrv[i].adl_spans[s].ads_devs[t].add_channel * AMI_MAX_TARGET + p->ada_ldrv[i].adl_spans[s].ads_devs[t].add_target;
+ /* walk disk list and add size */
+ SLIST_FOREACH(dk, &disks, next) {
+ if (dk->ch != (p->ada_ldrv[i].adl_spans[s].ads_devs[t].add_target >> 4) || dk->tg != (p->ada_ldrv[i].adl_spans[s].ads_devs[t].add_target & 0x0f))
+ continue;
+
+ dk->bd.size = (quad_t)p->ada_pdrv[off].adp_size;
+ dk->bd.volid = i;
+
+ AMI_DPRINTF(AMI_D_IOCTL, ("\tdiskid: %d\n", dk->bd.diskid));
+ AMI_DPRINTF(AMI_D_IOCTL, ("\tstatus: %d\n", dk->bd.status));
+ AMI_DPRINTF(AMI_D_IOCTL, ("\tvolid : %d\n", dk->bd.volid));
+ AMI_DPRINTF(AMI_D_IOCTL, ("\tsize : %d\n", dk->bd.size));
+ AMI_DPRINTF(AMI_D_IOCTL, ("\tvendor: %s\n", dk->bd.vendor));
+ AMI_DPRINTF(AMI_D_IOCTL, ("\tchan : %d\n", dk->ch));
+ AMI_DPRINTF(AMI_D_IOCTL, ("\ttarget: %d\n", dk->tg));
+
+ }
+ }
+
+ switch (vol->bv.level) {
+ case 0:
+ vol->bv.size += p->ada_ldrv[i].adl_spans[s].ads_length * p->ada_ldrv[i].adl_nstripes;
+ break;
+
+ case 1:
+ vol->bv.size += p->ada_ldrv[i].adl_spans[s].ads_length;
+ break;
+
+ case 5:
+ vol->bv.size += p->ada_ldrv[i].adl_spans[s].ads_length * (p->ada_ldrv[i].adl_nstripes - 1);
+ break;
+ }
+
+ }
+
+ if (p->ada_ldrv[i].adl_spandepth > 1)
+ vol->bv.level *= 10;
+
+ vol->bv.size *= (quad_t)512;
+
+ AMI_DPRINTF(AMI_D_IOCTL, ("voldid: %d\n",
+ vol->bv.volid));
+ AMI_DPRINTF(AMI_D_IOCTL, ("status: %d\n",
+ vol->bv.status));
+ AMI_DPRINTF(AMI_D_IOCTL, ("size : %lld\n",
+ vol->bv.size));
+ AMI_DPRINTF(AMI_D_IOCTL, ("level : %d\n",
+ vol->bv.level));
+ AMI_DPRINTF(AMI_D_IOCTL, ("nodisk: %d\n",
+ vol->bv.nodisk));
+
+ SLIST_INSERT_HEAD(&volumes, vol, next);
+ }
+
+#if 0
+ printf("LD: %d %d\n", p->ada_nld, sizeof p);
+
+ for (i = 0; i < p->ada_nld; i++) {
+ printf("\tspandepth: %d\n", p->ada_ldrv[i].adl_spandepth);
+ printf("\traidlvl : %d\n", p->ada_ldrv[i].adl_raidlvl);
+ printf("\trd ahead : %d\n", p->ada_ldrv[i].adl_rdahead);
+ printf("\tstrp size: %d\n", p->ada_ldrv[i].adl_stripesz);
+ printf("\tstatus : %d\n", p->ada_ldrv[i].adl_status);
+ printf("\twr policy: %d\n", p->ada_ldrv[i].adl_wrpolicy);
+ printf("\tdirectio : %d\n", p->ada_ldrv[i].adl_directio);
+ printf("\tnr stripe: %d\n", p->ada_ldrv[i].adl_nstripes);
+ for (s = 0; s < p->ada_ldrv[i].adl_spandepth; s++) {
+ for (t = 0; t < p->ada_ldrv[i].adl_nstripes; t++) {
+ off = p->ada_ldrv[i].adl_spans[s].ads_devs[t].add_channel *
+ AMI_MAX_TARGET +
+ p->ada_ldrv[i].adl_spans[s].ads_devs[t].add_target;
+
+ printf("\t\tspan : %d\n", s);
+ printf("\t\tstart : %d\n",
+ (u_int32_t)p->ada_ldrv[i].adl_spans[s].ads_start);
+ printf("\t\tlength : %d\n",
+ (u_int32_t)p->ada_ldrv[i].adl_spans[s].ads_length);
+
+ printf("\t\t\tchannel : %02x\n",
+ p->ada_ldrv[i].adl_spans[s].ads_devs[t].add_channel);
+ printf("\t\t\ttarget : %02x\n",
+ p->ada_ldrv[i].adl_spans[s].ads_devs[t].add_target);
+ printf("\t\t\toff : %d\n");
+
+ printf("\t\t\ttype : %d\n",
+ p->ada_pdrv[off].adp_type);
+ printf("\t\t\tstatus : %d\n",
+ p->ada_pdrv[off].adp_ostatus);
+ printf("\t\t\tdepth : %d\n",
+ p->ada_pdrv[off].adp_tagdepth);
+ printf("\t\t\tsneg : %d\n",
+ p->ada_pdrv[off].adp_sneg);
+ printf("\t\t\tsize : %d\n\n",
+ (u_int32_t)p->ada_pdrv[off].adp_size);
+ }
+ }
+ }
+#endif
+ }
+ else {
+ AMI_DPRINTF(AMI_D_IOCTL, ("failed\n"));
+ error = EINVAL;
+ }
+
+ free(p, M_DEVBUF);
+
+ sc->sc_state_ch = 0; /* state normalized */
+
+ return (error);
+}
+
+int
+ami_ioctl_vol(sc, bv)
+ struct ami_softc *sc;
+ bioc_vol *bv;
+{
+ struct volume *vol;
+ int error = EINVAL;
+
+ AMI_DPRINTF(AMI_D_IOCTL, ("%s: biocvol\n", sc->sc_dev.dv_xname));
+
+ SLIST_FOREACH(vol, &volumes, next) {
+ if (bv->volid != vol->bv.volid)
+ continue;
+
+ memcpy(bv, &vol->bv, sizeof *bv);
+ error = 0;
+ }
+
+ return (error);
+}
+
+int
+ami_ioctl_disk(sc, bd)
+ struct ami_softc *sc;
+ bioc_disk *bd;
+{
+ struct disk *dk;
+ int error = EINVAL;
+
+ AMI_DPRINTF(AMI_D_IOCTL, ("%s: biocdisk\n", sc->sc_dev.dv_xname));
+
+ SLIST_FOREACH(dk, &disks, next) {
+ if (bd->diskid != dk->bd.diskid)
+ continue;
+
+ memcpy(bd, &dk->bd, sizeof *bd);
+ error = 0;
+ }
+
+ return (error);
+}
+
+#if 0
int
ami_ioctl(dev, cmd, addr)
struct device *dev;
@@ -2072,6 +2537,8 @@ ami_ioctl_passthru(sc, bp)
return (error);
}
+#endif
+
#endif /* NBIO > 0 */
#ifdef AMI_DEBUG