diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2006-03-19 11:53:24 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2006-03-19 11:53:24 +0000 |
commit | 28101f2aea019abb7c702ec32539e5c98b0ea101 (patch) | |
tree | f50f451339ee325b893340c7231fcbf3236900e0 | |
parent | 84887a399e3d9bb4a509977968b41cf5d5756dcb (diff) |
this is a really large reworking of how ami works in terms of submitting
commands to the hardware and completing them.
previously the driver was very engineered toward moving io from the
logical disks scsi commands onto the hardware and off. as we built extra
functionality into the driver, this path grew more and more hairy as bits
were tacked on to cope.
this strips it back to dealing with getting the command onto the hardware
and off again. each path that submits commands now has to do all its work
before submitting the ccb, and supply a function for completing the ccb.
both the async and polled paths call the completion routines for commands
now. async commands that fail to get onto the hardware first go are stuck
on a queue and retried out of a timeout. previously this was only done for
scsi commands, now all paths take advantage of it.
-rw-r--r-- | sys/dev/ic/ami.c | 341 | ||||
-rw-r--r-- | sys/dev/ic/amivar.h | 8 |
2 files changed, 160 insertions, 189 deletions
diff --git a/sys/dev/ic/ami.c b/sys/dev/ic/ami.c index 4347ddad5b2..e872eef6e62 100644 --- a/sys/dev/ic/ami.c +++ b/sys/dev/ic/ami.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ami.c,v 1.125 2006/03/18 04:44:52 dlg Exp $ */ +/* $OpenBSD: ami.c,v 1.126 2006/03/19 11:53:23 dlg Exp $ */ /* * Copyright (c) 2001 Michael Shalayeff @@ -125,13 +125,19 @@ void ami_copyhds(struct ami_softc *, const u_int32_t *, const u_int8_t *, const u_int8_t *); struct ami_mem *ami_allocmem(struct ami_softc *, size_t); void ami_freemem(struct ami_softc *, struct ami_mem *); -void ami_stimeout(void *); -int ami_start(struct ami_softc *, struct ami_ccb *); -int ami_start_xs(struct ami_ccb *, int, int); + int ami_poll(struct ami_softc *, struct ami_ccb *); +void ami_start(struct ami_softc *, struct ami_ccb *); int ami_done(struct ami_softc *, int); -int ami_done_ccb(struct ami_softc *, struct ami_ccb *); +void ami_runqueue(void *); + +int ami_start_xs(struct ami_softc *sc, struct ami_ccb *, + struct scsi_xfer *); int ami_done_xs(struct ami_softc *, struct ami_ccb *); +int ami_done_pt(struct ami_softc *, struct ami_ccb *); +void ami_stimeout(void *); + +int ami_done_ccb(struct ami_softc *, struct ami_ccb *); void ami_copy_internal_data(struct scsi_xfer *, void *, size_t); int ami_inquire(struct ami_softc *, u_int8_t); @@ -162,9 +168,9 @@ ami_get_ccb(struct ami_softc *sc) { struct ami_ccb *ccb; - ccb = TAILQ_LAST(&sc->sc_free_ccb, ami_queue_head); + ccb = TAILQ_LAST(&sc->sc_ccb_freeq, ami_queue_head); if (ccb) { - TAILQ_REMOVE(&sc->sc_free_ccb, ccb, ccb_link); + TAILQ_REMOVE(&sc->sc_ccb_freeq, ccb, ccb_link); ccb->ccb_state = AMI_CCB_READY; } @@ -180,7 +186,9 @@ ami_put_ccb(struct ami_ccb *ccb) ccb->ccb_wakeup = 0; ccb->ccb_data = NULL; ccb->ccb_xs = NULL; - TAILQ_INSERT_TAIL(&sc->sc_free_ccb, ccb, ccb_link); + ccb->ccb_flags = 0; + ccb->ccb_done = NULL; + TAILQ_INSERT_TAIL(&sc->sc_ccb_freeq, ccb, ccb_link); } u_int32_t @@ -310,9 +318,10 @@ ami_attach(struct ami_softc *sc) } ccbmem = AMIMEM_KVA(sc->sc_ccbmem_am); - TAILQ_INIT(&sc->sc_ccbq); - TAILQ_INIT(&sc->sc_ccbdone); - TAILQ_INIT(&sc->sc_free_ccb); + TAILQ_INIT(&sc->sc_ccb_freeq); + TAILQ_INIT(&sc->sc_ccb_preq); + TAILQ_INIT(&sc->sc_ccb_runq); + timeout_set(&sc->sc_run_tmo, ami_runqueue, sc); for (i = 0; i < AMI_MAXCMDS; i++) { ccb = &sc->sc_ccbs[i]; @@ -327,7 +336,6 @@ ami_attach(struct ami_softc *sc) } ccb->ccb_sc = sc; - ccb->ccb_state = AMI_CCB_FREE; ccb->ccb_cmd.acc_id = i + 1; ccb->ccb_offset = sizeof(struct ami_ccbmem) * i; @@ -340,7 +348,7 @@ ami_attach(struct ami_softc *sc) ccb->ccb_sglistpa = htole32(AMIMEM_DVA(sc->sc_ccbmem_am) + ccb->ccb_offset + sizeof(struct ami_passthrough)); - TAILQ_INSERT_TAIL(&sc->sc_free_ccb, ccb, ccb_link); + ami_put_ccb(ccb); } (sc->sc_init)(sc); @@ -351,6 +359,7 @@ ami_attach(struct ami_softc *sc) s = splbio(); ccb = ami_get_ccb(sc); + ccb->ccb_done = ami_done_ccb; cmd = &ccb->ccb_cmd; /* try FC inquiry first */ @@ -367,6 +376,7 @@ ami_attach(struct ami_softc *sc) einq->ain_ldstat); ccb = ami_get_ccb(sc); + ccb->ccb_done = ami_done_ccb; cmd = &ccb->ccb_cmd; cmd->acc_cmd = AMI_FCOP; @@ -392,6 +402,7 @@ ami_attach(struct ami_softc *sc) struct ami_inquiry *inq = AMIMEM_KVA(am); ccb = ami_get_ccb(sc); + ccb->ccb_done = ami_done_ccb; cmd = &ccb->ccb_cmd; cmd->acc_cmd = AMI_EINQUIRY; @@ -400,6 +411,7 @@ ami_attach(struct ami_softc *sc) cmd->acc_io.aio_data = pa; if (ami_poll(sc, ccb) != 0) { ccb = ami_get_ccb(sc); + ccb->ccb_done = ami_done_ccb; cmd = &ccb->ccb_cmd; cmd->acc_cmd = AMI_INQUIRY; @@ -446,6 +458,7 @@ ami_attach(struct ami_softc *sc) * we need to read them first before setting them. */ ccb = ami_get_ccb(sc); + ccb->ccb_done = ami_done_ccb; ccb->ccb_data = NULL; cmd = &ccb->ccb_cmd; @@ -459,6 +472,7 @@ ami_attach(struct ami_softc *sc) " failed\n")); } else { ccb = ami_get_ccb(sc); + ccb->ccb_done = ami_done_ccb; ccb->ccb_data = NULL; cmd = &ccb->ccb_cmd; @@ -956,158 +970,112 @@ ami_schwartz_poll(struct ami_softc *sc, struct ami_iocmd *mbox) } int -ami_start_xs(struct ami_ccb *ccb, int flags, int wait) +ami_start_xs(struct ami_softc *sc, struct ami_ccb *ccb, struct scsi_xfer *xs) { - struct ami_softc *sc = ccb->ccb_sc; - int error = 0; + struct timeval tv; - if (wait) - error = ami_poll(sc, ccb); - else - error = ami_start(sc, ccb); + if (xs->flags & SCSI_POLL) { + ami_poll(sc, ccb); + return (COMPLETE); + } - return (error); + timeout_set(&xs->stimeout, ami_stimeout, ccb); + + /* add 5sec for whacky done() loops */ + tv.tv_sec = 5 + xs->timeout / 1000; + tv.tv_usec = 1000 * (xs->timeout % 1000); + timeout_add(&xs->stimeout, tvtohz(&tv)); + + ami_start(sc, ccb); + + return (SUCCESSFULLY_QUEUED); } -int +void ami_start(struct ami_softc *sc, struct ami_ccb *ccb) { - struct ami_iocmd *cmd = &ccb->ccb_cmd; - struct scsi_xfer *xs = ccb->ccb_xs; - int error; - if (ccb->ccb_state != AMI_CCB_READY) { - printf("%s: ccb %d not ready <%d>\n", DEVNAME(sc), - cmd->acc_id, ccb->ccb_state); - return (EINVAL); + printf("%s: ccb %d not ready (%d)\n", DEVNAME(sc), + ccb->ccb_cmd.acc_id, ccb->ccb_state); + ccb->ccb_flags |= AMI_CCB_F_ERR; + ccb->ccb_done(sc, ccb); + return; } - if (xs != NULL) - timeout_set(&xs->stimeout, ami_stimeout, ccb); - - error = sc->sc_exec(sc, cmd); - if (!error) { - ccb->ccb_state = AMI_CCB_QUEUED; - TAILQ_INSERT_TAIL(&sc->sc_ccbq, ccb, ccb_link); - - if (xs) { - struct timeval tv; - /* add 5sec for whacky done() loops */ - tv.tv_sec = 5 + xs->timeout / 1000; - tv.tv_usec = 1000 * (xs->timeout % 1000); - timeout_add(&xs->stimeout, tvtohz(&tv)); - } - - return (0); - } + ccb->ccb_state = AMI_CCB_PREQUEUED; + TAILQ_INSERT_TAIL(&sc->sc_ccb_preq, ccb, ccb_link); - if (xs) { - ccb->ccb_state = AMI_CCB_PREQUEUED; - timeout_add(&xs->stimeout, 1); - return (0); - } + ami_runqueue(sc); +} - /* error path */ - if (ccb->ccb_data) { - bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, - ccb->ccb_dmamap->dm_mapsize, - (ccb->ccb_dir == AMI_CCB_IN) ? - BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); +void +ami_runqueue(void *arg) +{ + struct ami_softc *sc = arg; + struct ami_ccb *ccb; + int s; - bus_dmamap_sync(sc->sc_dmat, - AMIMEM_MAP(sc->sc_ccbmem_am), - ccb->ccb_offset, sizeof(struct ami_ccbmem), - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + s = splbio(); + while ((ccb = TAILQ_FIRST(&sc->sc_ccb_preq)) != NULL) { + if (sc->sc_exec(sc, &ccb->ccb_cmd) != 0) { + timeout_add(&sc->sc_run_tmo, hz/500); + break; + } - bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); + TAILQ_REMOVE(&sc->sc_ccb_preq, ccb, ccb_link); + ccb->ccb_state = AMI_CCB_QUEUED; + TAILQ_INSERT_TAIL(&sc->sc_ccb_runq, ccb, ccb_link); } - - ami_put_ccb(ccb); - - return (error); + splx(s); } int ami_poll(struct ami_softc *sc, struct ami_ccb *ccb) { int error; + int s; + s = splbio(); error = sc->sc_poll(sc, &ccb->ccb_cmd); + splx(s); - if (ccb->ccb_data) { - bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, - ccb->ccb_dmamap->dm_mapsize, - (ccb->ccb_dir == AMI_CCB_IN) ? - BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); - - bus_dmamap_sync(sc->sc_dmat, - AMIMEM_MAP(sc->sc_ccbmem_am), - ccb->ccb_offset, sizeof(struct ami_ccbmem), - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - - bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); - } - if (ccb->ccb_wakeup) - ccb->ccb_wakeup = 0; - else - ami_put_ccb(ccb); + if (error) + ccb->ccb_flags |= AMI_CCB_F_ERR; + ccb->ccb_done(sc, ccb); return (error); } -/* FIXME timeouts should be rethought */ void ami_stimeout(void *v) { struct ami_ccb *ccb = v; struct ami_softc *sc = ccb->ccb_sc; - struct scsi_xfer *xs = ccb->ccb_xs; struct ami_iocmd *cmd = &ccb->ccb_cmd; - volatile struct ami_iocmd *mbox = sc->sc_mbox; int s; - s = splbio(); switch (ccb->ccb_state) { case AMI_CCB_PREQUEUED: - if (mbox->acc_busy) { - timeout_add(&xs->stimeout, 1); - break; - } - - AMI_DPRINTF(AMI_D_CMD, ("requeue(%d) ", cmd->acc_id)); - - ccb->ccb_state = AMI_CCB_READY; - if (ami_start(sc, ccb)) { - AMI_DPRINTF(AMI_D_CMD, ("requeue(%d) again\n", cmd->acc_id)); - ccb->ccb_state = AMI_CCB_PREQUEUED; - timeout_add(&xs->stimeout, 1); - } + /* command never ran, cleanup is easy */ + s = splbio(); + TAILQ_REMOVE(&sc->sc_ccb_preq, ccb, ccb_link); + ccb->ccb_flags |= AMI_CCB_F_ERR; + splx(s); break; case AMI_CCB_QUEUED: - /* XXX need to kill all cmds in the queue and reset the card */ printf("%s: timeout ccb %d\n", DEVNAME(sc), cmd->acc_id); - AMI_DPRINTF(AMI_D_CMD, ("timeout(%d) ", cmd->acc_id)); - if (xs->cmd->opcode != PREVENT_ALLOW && - xs->cmd->opcode != SYNCHRONIZE_CACHE) { - bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, - ccb->ccb_dmamap->dm_mapsize, - (xs->flags & SCSI_DATA_IN) ? - BUS_DMASYNC_POSTREAD : - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); - } - TAILQ_REMOVE(&sc->sc_ccbq, ccb, ccb_link); - ami_put_ccb(ccb); - xs->error = XS_TIMEOUT; - xs->flags |= ITSDONE; - scsi_done(xs); + s = splbio(); + TAILQ_REMOVE(&sc->sc_ccb_runq, ccb, ccb_link); + /* do not reuse the ccb since its still on the hw */ + splx(s); break; - case AMI_CCB_FREE: - case AMI_CCB_READY: - panic("ami_stimeout(%d) botch", cmd->acc_id); + + default: + panic("%s: ami_stimeout(%d) botch", DEVNAME(sc), cmd->acc_id); } - splx(s); + + ccb->ccb_done(sc, ccb); } int @@ -1126,7 +1094,7 @@ ami_done(struct ami_softc *sc, int idx) s = splbio(); ccb->ccb_state = AMI_CCB_READY; - TAILQ_REMOVE(&sc->sc_ccbq, ccb, ccb_link); + TAILQ_REMOVE(&sc->sc_ccb_runq, ccb, ccb_link); splx(s); ccb->ccb_done(sc, ccb); @@ -1135,9 +1103,12 @@ ami_done(struct ami_softc *sc, int idx) } int -ami_done_xs(struct ami_softc *sc, struct ami_ccb *ccb) +ami_done_pt(struct ami_softc *sc, struct ami_ccb *ccb) { struct scsi_xfer *xs = ccb->ccb_xs; + struct scsi_link *link = xs->sc_link; + struct ami_rawsoftc *rsc = link->adapter_softc; + u_int8_t target = link->target, type; int s; if (xs->data != NULL) { @@ -1153,13 +1124,60 @@ ami_done_xs(struct ami_softc *sc, struct ami_ccb *ccb) bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); } + timeout_del(&xs->stimeout); + xs->resid = 0; + xs->flags |= ITSDONE; + + if (ccb->ccb_flags & AMI_CCB_F_ERR) + xs->error = XS_DRIVER_STUFFUP; + else if (xs->flags & SCSI_POLL && xs->cmd->opcode == INQUIRY) { + type = ((struct scsi_inquiry_data *)xs->data)->device & + SID_TYPE; + if (!(type == T_PROCESSOR || type == T_ENCLOSURE)) + xs->error = XS_DRIVER_STUFFUP; + else + rsc->sc_proctarget = target; + } + s = splbio(); ami_put_ccb(ccb); splx(s); + scsi_done(xs); + + return (0); +} + +int +ami_done_xs(struct ami_softc *sc, struct ami_ccb *ccb) +{ + struct scsi_xfer *xs = ccb->ccb_xs; + int s; + + if (xs->data != NULL) { + bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, + ccb->ccb_dmamap->dm_mapsize, + (xs->flags & SCSI_DATA_IN) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + + bus_dmamap_sync(sc->sc_dmat, AMIMEM_MAP(sc->sc_ccbmem_am), + ccb->ccb_offset, sizeof(struct ami_ccbmem), + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); + } + timeout_del(&xs->stimeout); xs->resid = 0; xs->flags |= ITSDONE; + + if (ccb->ccb_flags & AMI_CCB_F_ERR) + xs->error = XS_DRIVER_STUFFUP; + + s = splbio(); + ami_put_ccb(ccb); + splx(s); + scsi_done(xs); return (0); @@ -1227,9 +1245,7 @@ ami_scsi_raw_cmd(struct scsi_xfer *xs) u_int8_t channel = rsc->sc_channel, target = link->target; struct device *dev = link->device_softc; struct ami_ccb *ccb; - int error; int s; - char type; AMI_DPRINTF(AMI_D_CMD, ("ami_scsi_raw_cmd ")); @@ -1262,10 +1278,7 @@ ami_scsi_raw_cmd(struct scsi_xfer *xs) memset(ccb->ccb_pt, 0, sizeof(struct ami_passthrough)); ccb->ccb_xs = xs; - ccb->ccb_len = xs->datalen; - ccb->ccb_data = xs->data; - ccb->ccb_dir = (xs->flags & SCSI_DATA_IN) ? AMI_CCB_IN : AMI_CCB_OUT; - ccb->ccb_done = ami_done_xs; + ccb->ccb_done = ami_done_pt; ccb->ccb_cmd.acc_cmd = AMI_PASSTHRU; ccb->ccb_cmd.acc_passthru.apt_data = ccb->ccb_ptpa; @@ -1289,34 +1302,7 @@ ami_scsi_raw_cmd(struct scsi_xfer *xs) return (COMPLETE); } - s = splbio(); - error = ami_start_xs(ccb, (xs->flags & SCSI_NOSLEEP) ? - BUS_DMA_NOWAIT : BUS_DMA_WAITOK, xs->flags & SCSI_POLL); - splx(s); - if (error) { - xs->error = XS_DRIVER_STUFFUP; - scsi_done(xs); - return (COMPLETE); - } - - if (xs->flags & SCSI_POLL) { - if (xs->cmd->opcode == INQUIRY) { - type = ((struct scsi_inquiry_data *)xs->data)->device - & SID_TYPE; - - if (!(type == T_PROCESSOR || type == T_ENCLOSURE)) - xs->error = XS_DRIVER_STUFFUP; - else - rsc->sc_proctarget = target; /* save target */ - } - - scsi_done(xs); - } - - if (xs->flags & SCSI_POLL) - return (COMPLETE); - else - return (SUCCESSFULLY_QUEUED); + return (ami_start_xs(sc, ccb, xs)); } int @@ -1531,9 +1517,6 @@ ami_scsi_cmd(struct scsi_xfer *xs) } ccb->ccb_xs = xs; - ccb->ccb_len = xs->datalen; - ccb->ccb_data = xs->data; - ccb->ccb_dir = (xs->flags & SCSI_DATA_IN) ? AMI_CCB_IN : AMI_CCB_OUT; ccb->ccb_done = ami_done_xs; cmd = &ccb->ccb_cmd; @@ -1583,20 +1566,7 @@ ami_scsi_cmd(struct scsi_xfer *xs) ccb->ccb_dmamap->dm_mapsize, (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); - s = splbio(); - error = ami_start_xs(ccb, (xs->flags & SCSI_NOSLEEP) ? - BUS_DMA_NOWAIT : BUS_DMA_WAITOK, xs->flags & SCSI_POLL); - splx(s); - if (error) { - xs->error = XS_DRIVER_STUFFUP; - scsi_done(xs); - return (COMPLETE); - } - - if (xs->flags & SCSI_POLL) - return (COMPLETE); - else - return (SUCCESSFULLY_QUEUED); + return (ami_start_xs(sc, ccb, xs)); } int @@ -1607,7 +1577,7 @@ ami_intr(void *v) int i, rv = 0; int s; - if (TAILQ_EMPTY(&sc->sc_ccbq)) + if (TAILQ_EMPTY(&sc->sc_ccb_runq)) return (0); AMI_DPRINTF(AMI_D_INTR, ("intr ")); @@ -1751,8 +1721,7 @@ ami_drv_inq(struct ami_softc *sc, u_int8_t ch, u_int8_t tg, u_int8_t page, return (ENOMEM); } - if (ami_start(sc, ccb) != 0) - return (EIO); + ami_start(sc, ccb); while (ccb->ccb_wakeup) tsleep(ccb, PRIBIO, "ami_drv_inq", 0); @@ -1776,7 +1745,7 @@ ami_mgmt(struct ami_softc *sc, u_int8_t opcode, u_int8_t par1, u_int8_t par2, struct ami_iocmd *cmd; struct ami_mem *am = NULL; char *idata = NULL; - int error = 0; + int error = EINVAL; ccb = ami_get_ccb(sc); if (ccb == NULL) @@ -1818,19 +1787,19 @@ ami_mgmt(struct ami_softc *sc, u_int8_t opcode, u_int8_t par1, u_int8_t par2, cmd->acc_io.aio_data = am ? htole32(AMIMEM_DVA(am)) : 0; - if (ami_start(sc, ccb) == 0) { - while (ccb->ccb_wakeup) - tsleep(ccb, PRIBIO,"ami_mgmt", 0); + ami_start(sc, ccb); - ami_put_ccb(ccb); + while (ccb->ccb_wakeup) + tsleep(ccb, PRIBIO,"ami_mgmt", 0); - /* XXX how do commands fail? */ - + if (!(ccb->ccb_flags & AMI_CCB_F_ERR)) { if (buffer && size) memcpy(buffer, idata, size); - } else - error = EINVAL; + error = 0; + } + + ami_put_ccb(ccb); if (am) ami_freemem(sc, am); diff --git a/sys/dev/ic/amivar.h b/sys/dev/ic/amivar.h index 9b1027c139e..944eabcb985 100644 --- a/sys/dev/ic/amivar.h +++ b/sys/dev/ic/amivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: amivar.h,v 1.39 2006/03/17 13:34:23 dlg Exp $ */ +/* $OpenBSD: amivar.h,v 1.40 2006/03/19 11:53:23 dlg Exp $ */ /* * Copyright (c) 2001 Michael Shalayeff @@ -75,6 +75,8 @@ struct ami_ccb { AMI_CCB_QUEUED, AMI_CCB_PREQUEUED } ccb_state; + int ccb_flags; +#define AMI_CCB_F_ERR (1<<0) TAILQ_ENTRY(ami_ccb) ccb_link; }; @@ -119,12 +121,12 @@ struct ami_softc { paddr_t sc_mbox_pa; struct ami_ccb sc_ccbs[AMI_MAXCMDS]; - ami_queue_head sc_free_ccb, sc_ccbq, sc_ccbdone; + ami_queue_head sc_ccb_freeq, sc_ccb_preq, sc_ccb_runq; struct ami_mem *sc_ccbmem_am; int sc_timeout; - struct timeout sc_requeue_tmo; + struct timeout sc_run_tmo; int sc_dis_poll; char sc_fwver[16]; |