From 9e90d5aab040fc60175aec4b91e7ed6bc2befe0f Mon Sep 17 00:00:00 2001 From: nyan Date: Tue, 27 Feb 2001 12:34:01 +0000 Subject: Added another wd33c93 based SCSI card driver which replaces the bs driver. Now, default is still bs. Submitted by: nyan and non. Obtained from: NetBSD/pc98 --- sys/dev/ct/bshw_machdep.c | 767 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 767 insertions(+) create mode 100644 sys/dev/ct/bshw_machdep.c (limited to 'sys/dev/ct/bshw_machdep.c') diff --git a/sys/dev/ct/bshw_machdep.c b/sys/dev/ct/bshw_machdep.c new file mode 100644 index 0000000..37aebdd --- /dev/null +++ b/sys/dev/ct/bshw_machdep.c @@ -0,0 +1,767 @@ +/* $FreeBSD$ */ +/* $NecBSD: bshw_machdep.c,v 1.8 1999/07/23 20:54:00 honda Exp $ */ +/* $NetBSD$ */ + +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999 + * NetBSD/pc98 porting staff. All rights reserved. + * + * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999 + * Naofumi HONDA. All rights reserved. + * + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. + */ + +#include "opt_ddb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef __NetBSD__ +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#endif /* __FreeBSD__ */ + +/********************************************************* + * GENERIC MACHDEP FUNCTIONS + *********************************************************/ +void +bshw_synch_setup(ct, li) + struct ct_softc *ct; + struct lun_info *li; +{ + struct scsi_low_softc *slp = &ct->sc_sclow; + struct targ_info *ti = slp->sl_nexus; + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + struct ct_targ_info *cti = (void *) ti; + struct bshw_softc *bs = ct->ct_hw; + struct bshw *hw = bs->sc_hw; + + if (hw->sregaddr == 0) + return; + + ct_cr_write_1(bst, bsh, hw->sregaddr + ti->ti_id, cti->cti_syncreg); + if (hw->hw_flags & BSHW_DOUBLE_DMACHAN) + { + ct_cr_write_1(bst, bsh, hw->sregaddr + ti->ti_id + 8, + cti->cti_syncreg); + } +} + +void +bshw_bus_reset(ct) + struct ct_softc *ct; +{ + struct scsi_low_softc *slp = &ct->sc_sclow; + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + struct bshw_softc *bs = ct->ct_hw; + struct bshw *hw = bs->sc_hw; + bus_addr_t offs; + u_int8_t regv; + int i; + + /* open hardware busmaster mode */ + if (hw->dma_init != NULL && ((*hw->dma_init)(ct)) != 0) + { + printf("%s change mode using external DMA (%x)\n", + slp->sl_xname, (u_int)ct_cr_read_1(bst, bsh, 0x37)); + } + + /* clear hardware synch registers */ + offs = hw->sregaddr; + if (offs != 0) + { + for (i = 0; i < 8; i ++, offs ++) + { + ct_cr_write_1(bst, bsh, offs, 0); + if ((hw->hw_flags & BSHW_DOUBLE_DMACHAN) != 0) + ct_cr_write_1(bst, bsh, offs + 8, 0); + } + } + + /* disable interrupt & assert reset */ + regv = ct_cr_read_1(bst, bsh, wd3s_mbank); + regv |= MBR_RST; + regv &= ~MBR_IEN; + ct_cr_write_1(bst, bsh, wd3s_mbank, regv); + + delay(500000); + + /* reset signal off */ + regv &= ~MBR_RST; + ct_cr_write_1(bst, bsh, wd3s_mbank, regv); + + /* interrupt enable */ + regv |= MBR_IEN; + ct_cr_write_1(bst, bsh, wd3s_mbank, regv); +} + +/* probe */ +int +bshw_read_settings(bst, bsh, bs) + bus_space_tag_t bst; + bus_space_handle_t bsh; + struct bshw_softc *bs; +{ + static int irq_tbl[] = { 3, 5, 6, 9, 12, 13 }; + + bs->sc_hostid = (ct_cr_read_1(bst, bsh, wd3s_auxc) & AUXCR_HIDM); + bs->sc_irq = irq_tbl[(ct_cr_read_1(bst, bsh, wd3s_auxc) >> 3) & 7]; + bs->sc_drq = bus_space_read_1(bst, bsh, cmd_port) & 3; + return 0; +} + +/********************************************************* + * DMA PIO TRANSFER (SMIT) + *********************************************************/ +#define LC_SMIT_TIMEOUT 2 /* 2 sec: timeout for a fifo status ready */ +#define LC_SMIT_OFFSET 0x1000 +#define LC_FSZ DEV_BSIZE +#define LC_SFSZ 0x0c +#define LC_REST (LC_FSZ - LC_SFSZ) + +#define BSHW_LC_FSET 0x36 +#define BSHW_LC_FCTRL 0x44 +#define FCTRL_EN 0x01 +#define FCTRL_WRITE 0x02 + +#define SF_ABORT 0x08 +#define SF_RDY 0x10 + +static __inline void bshw_lc_smit_start __P((struct ct_softc *, int, u_int)); +static int bshw_lc_smit_fstat __P((struct ct_softc *, int, int)); +static __inline void bshw_lc_smit_stop __P((struct ct_softc *)); + +static __inline void +bshw_lc_smit_stop(ct) + struct ct_softc *ct; +{ + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + + ct_cr_write_1(bst, bsh, BSHW_LC_FCTRL, 0); + bus_space_write_1(ct->sc_iot, ct->sc_ioh, cmd_port, CMDP_DMER); +} + +static __inline void +bshw_lc_smit_start(ct, count, direction) + struct ct_softc *ct; + int count; + u_int direction; +{ + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + u_int8_t pval, val; + + val = ct_cr_read_1(bst, bsh, BSHW_LC_FSET); + cthw_set_count(bst, bsh, count); + + pval = FCTRL_EN; + if (direction == SCSI_LOW_WRITE) + pval |= (val & 0xe0) | FCTRL_WRITE; + ct_cr_write_1(bst, bsh, BSHW_LC_FCTRL, pval); + ct_cr_write_1(bst, bsh, wd3s_cmd, WD3S_TFR_INFO); +} + +static int +bshw_lc_smit_fstat(ct, wc, read) + struct ct_softc *ct; + int wc, read; +{ + u_int8_t stat; + + while (wc -- > 0) + { + outb(0x5f, 0); + stat = bus_space_read_1(ct->sc_iot, ct->sc_ioh, cmd_port); + if (read == SCSI_LOW_READ) + { + if ((stat & SF_RDY) != 0) + return 0; + if ((stat & SF_ABORT) != 0) + return EIO; + } + else + { + if ((stat & SF_ABORT) != 0) + return EIO; + if ((stat & SF_RDY) != 0) + return 0; + } + } + + printf("%s: SMIT fifo status timeout\n", ct->sc_sclow.sl_xname); + return EIO; +} + +void +bshw_smit_xfer_stop(ct) + struct ct_softc *ct; +{ + struct scsi_low_softc *slp = &ct->sc_sclow; + struct bshw_softc *bs = ct->ct_hw; + struct targ_info *ti; + struct sc_p *sp = &slp->sl_scp; + u_int count; + u_char *s; + + bshw_lc_smit_stop(ct); + + ti = slp->sl_nexus; + if (ti == NULL) + return; + + if (ti->ti_phase == PH_DATA) + { + count = cthw_get_count(ct->sc_iot, ct->sc_ioh); + if (count < (u_int) sp->scp_datalen) + { + sp->scp_data += (sp->scp_datalen - count); + sp->scp_datalen = count; + /* XXX: + * strict double checks! + * target => wd33c93c transfer counts + * wd33c93c => memory transfer counts + */ + if (sp->scp_direction == SCSI_LOW_READ && + count != bs->sc_tdatalen) + { + s = "read count miss"; + goto bad; + } + return; + } + else if (count == (u_int) sp->scp_datalen) + { + return; + } + + s = "strange count"; + } + else + s = "extra smit interrupt"; + +bad: + printf("%s: smit_xfer_end: %s", slp->sl_xname, s); + slp->sl_error |= PDMAERR; +} + +void +bshw_smit_xfer_start(ct) + struct ct_softc *ct; +{ + struct scsi_low_softc *slp = &ct->sc_sclow; + struct bshw_softc *bs = ct->ct_hw; + struct sc_p *sp = &slp->sl_scp; + struct targ_info *ti = slp->sl_nexus; + struct ct_targ_info *cti = (void *) ti; + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + int datalen, count, wc = LC_SMIT_TIMEOUT * 1024 * 1024; + u_int8_t *data; + + data = sp->scp_data; + datalen = sp->scp_datalen; + + ct_cr_write_1(bst, bsh, wd3s_ctrl, ct->sc_creg | CR_DMA); + bshw_lc_smit_start(ct, sp->scp_datalen, sp->scp_direction); + + if (sp->scp_direction == SCSI_LOW_READ) + { + do + { + if (bshw_lc_smit_fstat(ct, wc, SCSI_LOW_READ)) + break; + + count = (datalen > LC_FSZ ? LC_FSZ : datalen); + bus_space_read_region_4(ct->sc_memt, ct->sc_memh, + LC_SMIT_OFFSET, (u_int32_t *) data, count >> 2); + data += count; + datalen -= count; + } + while (datalen > 0); + + bs->sc_tdatalen = datalen; + } + else + { + do + { + if (bshw_lc_smit_fstat(ct, wc, SCSI_LOW_WRITE)) + break; + if (cti->cti_syncreg == 0) + { + /* XXX: + * If async transfer, reconfirm a scsi phase + * again. Unless C bus might hang up. + */ + if (bshw_lc_smit_fstat(ct, wc, SCSI_LOW_WRITE)) + break; + } + + count = (datalen > LC_SFSZ ? LC_SFSZ : datalen); + bus_space_write_region_4(ct->sc_memt, ct->sc_memh, + LC_SMIT_OFFSET, (u_int32_t *) data, count >> 2); + data += count; + datalen -= count; + + if (bshw_lc_smit_fstat(ct, wc, SCSI_LOW_WRITE)) + break; + + count = (datalen > LC_REST ? LC_REST : datalen); + bus_space_write_region_4(ct->sc_memt, ct->sc_memh, + LC_SMIT_OFFSET + LC_SFSZ, + (u_int32_t *) data, count >> 2); + data += count; + datalen -= count; + } + while (datalen > 0); + } +} + +/********************************************************* + * DMA TRANSFER (BS) + *********************************************************/ +static void bshw_dmastart __P((struct ct_softc *)); +static void bshw_dmadone __P((struct ct_softc *)); + +void +bshw_dma_xfer_start(ct) + struct ct_softc *ct; +{ + struct scsi_low_softc *slp = &ct->sc_sclow; + struct sc_p *sp = &slp->sl_scp; + struct bshw_softc *bs = ct->ct_hw; + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + vaddr_t va, endva, phys, nphys; + + ct_cr_write_1(bst, bsh, wd3s_ctrl, ct->sc_creg | CR_DMA); + phys = vtophys((vaddr_t) sp->scp_data); + if (phys >= bs->sc_minphys) + { + /* setup segaddr */ + bs->sc_segaddr = bs->sc_bounce_phys; + /* setup seglen */ + bs->sc_seglen = sp->scp_datalen; + if (bs->sc_seglen > bs->sc_bounce_size) + bs->sc_seglen = bs->sc_bounce_size; + /* setup bufp */ + bs->sc_bufp = bs->sc_bounce_addr; + if (sp->scp_direction == SCSI_LOW_WRITE) + bcopy(sp->scp_data, bs->sc_bufp, bs->sc_seglen); + } + else + { + /* setup segaddr */ + bs->sc_segaddr = (u_int8_t *) phys; + /* setup seglen */ + endva = (vaddr_t)round_page((vaddr_t)(sp->scp_data + + sp->scp_datalen)); + for (va = (vaddr_t) sp->scp_data; ; phys = nphys) + { + if ((va += PAGE_SIZE) >= endva) + { + bs->sc_seglen = sp->scp_datalen; + break; + } + + nphys = vtophys(va); + if (phys + PAGE_SIZE != nphys || + nphys >= bs->sc_minphys) + { + bs->sc_seglen = + (u_int8_t *) trunc_page(va) - sp->scp_data; + break; + } + } + /* setup bufp */ + bs->sc_bufp = NULL; + } + + bshw_dmastart(ct); + cthw_set_count(bst, bsh, bs->sc_seglen); +} + +void +bshw_dma_xfer_stop(ct) + struct ct_softc *ct; +{ + struct scsi_low_softc *slp = &ct->sc_sclow; + struct sc_p *sp = &slp->sl_scp; + struct bshw_softc *bs = ct->ct_hw; + struct targ_info *ti; + u_int count, transbytes; + + bshw_dmadone(ct); + + ti = slp->sl_nexus; + if (ti == NULL) + return; + + if (ti->ti_phase == PH_DATA) + { + count = cthw_get_count(ct->sc_iot, ct->sc_ioh); + if (count < (u_int) bs->sc_seglen) + { + transbytes = bs->sc_seglen - count; + if (bs->sc_bufp != NULL && + sp->scp_direction == SCSI_LOW_READ) + bcopy(bs->sc_bufp, sp->scp_data, transbytes); + + bs->sc_bufp = NULL; + sp->scp_data += transbytes; + sp->scp_datalen -= transbytes; + return; + } + else if (count == (u_int) bs->sc_seglen) + { + bs->sc_bufp = NULL; + return; + } + + printf("%s: port data %x != seglen %x\n", + slp->sl_xname, count, bs->sc_seglen); + } + else + { + printf("%s: extra DMA interrupt\n", slp->sl_xname); + } + + slp->sl_error |= PDMAERR; + bs->sc_bufp = NULL; +} + +static int dmapageport[4] = { 0x27, 0x21, 0x23, 0x25 }; + +/* common dma settings */ +#undef DMA1_SMSK +#define DMA1_SMSK (0x15) +#undef DMA1_MODE +#define DMA1_MODE (0x17) +#undef DMA1_FFC +#define DMA1_FFC (0x19) +#undef DMA1_CHN +#define DMA1_CHN(c) (0x01 + ((c) << 2)) + +#define DMA37SM_SET 0x04 +#define DMA37MD_WRITE 0x04 +#define DMA37MD_READ 0x08 +#define DMA37MD_SINGLE 0x40 + +static void +bshw_dmastart(ct) + struct ct_softc *ct; +{ + struct scsi_low_softc *slp = &ct->sc_sclow; + struct bshw_softc *bs = ct->ct_hw; + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + int chan = bs->sc_drq; + int waport; + u_int8_t *phys = bs->sc_segaddr; + u_int nbytes = bs->sc_seglen; + + /* + * Program one of DMA channels 0..3. These are + * byte mode channels. + */ + /* set dma channel mode, and reset address ff */ +#ifdef __FreeBSD__ + if (need_pre_dma_flush) + wbinvd(); +#else + if (slp->sl_scp.scp_direction == SCSI_LOW_READ) + cpu_cf_preRead(curcpu); + else + cpu_cf_preWrite(curcpu); +#endif + + if (slp->sl_scp.scp_direction == SCSI_LOW_READ) + outb(DMA1_MODE, DMA37MD_SINGLE | DMA37MD_WRITE | chan); + else + outb(DMA1_MODE, DMA37MD_SINGLE | DMA37MD_READ | chan); + outb(DMA1_FFC, 0); + + /* send start address */ + waport = DMA1_CHN(chan); + outb(waport, (u_int) phys); + outb(waport, ((u_int) phys) >> 8); + outb(dmapageport[chan], ((u_int) phys) >> 16); + + /* send count */ + outb(waport + 2, --nbytes); + outb(waport + 2, nbytes >> 8); + + /* vendor unique hook */ + if (bs->sc_hw->dma_start) + (*bs->sc_hw->dma_start)(ct); + + outb(DMA1_SMSK, chan); + bus_space_write_1(bst, bsh, cmd_port, CMDP_DMES); +} + +static void +bshw_dmadone(ct) + struct ct_softc *ct; +{ + struct bshw_softc *bs = ct->ct_hw; + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + + outb(DMA1_SMSK, (bs->sc_drq | DMA37SM_SET)); + bus_space_write_1(bst, bsh, cmd_port, CMDP_DMER); + + /* vendor unique hook */ + if (bs->sc_hw->dma_stop) + (*bs->sc_hw->dma_stop)(ct); + +#ifdef __FreeBSD__ + if (need_post_dma_flush) + invd(); +#else + if (slp->sl_scp.scp_direction == SCSI_LOW_READ) + cpu_cf_postRead(curcpu); + else + cpu_cf_postWrite(curcpu); +#endif +} + +/********************************************** + * VENDOR UNIQUE DMA FUNCS + **********************************************/ +static int bshw_dma_init_sc98 __P((struct ct_softc *)); +static void bshw_dma_start_sc98 __P((struct ct_softc *)); +static void bshw_dma_stop_sc98 __P((struct ct_softc *)); +static int bshw_dma_init_texa __P((struct ct_softc *)); +static void bshw_dma_start_elecom __P((struct ct_softc *)); +static void bshw_dma_stop_elecom __P((struct ct_softc *)); + +static int +bshw_dma_init_texa(ct) + struct ct_softc *ct; +{ + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + u_int8_t regval; + + if ((regval = ct_cr_read_1(bst, bsh, 0x37)) & 0x08) + return 0; + + ct_cr_write_1(bst, bsh, 0x37, regval | 0x08); + regval = ct_cr_read_1(bst, bsh, 0x3f); + ct_cr_write_1(bst, bsh, 0x3f, regval | 0x08); + return 1; +} + +static int +bshw_dma_init_sc98(ct) + struct ct_softc *ct; +{ + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + + if (ct_cr_read_1(bst, bsh, 0x37) & 0x08) + return 0; + + /* If your card is SC98 with bios ver 1.01 or 1.02 under no PCI */ + ct_cr_write_1(bst, bsh, 0x37, 0x1a); + ct_cr_write_1(bst, bsh, 0x3f, 0x1a); +#if 0 + /* only valid for IO */ + ct_cr_write_1(bst, bsh, 0x40, 0xf4); + ct_cr_write_1(bst, bsh, 0x41, 0x9); + ct_cr_write_1(bst, bsh, 0x43, 0xff); + ct_cr_write_1(bst, bsh, 0x46, 0x4e); + + ct_cr_write_1(bst, bsh, 0x48, 0xf4); + ct_cr_write_1(bst, bsh, 0x49, 0x9); + ct_cr_write_1(bst, bsh, 0x4b, 0xff); + ct_cr_write_1(bst, bsh, 0x4e, 0x4e); +#endif + return 1; +} + +static void +bshw_dma_start_sc98(ct) + struct ct_softc *ct; +{ + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + + ct_cr_write_1(bst, bsh, 0x73, 0x32); + ct_cr_write_1(bst, bsh, 0x74, 0x23); +} + +static void +bshw_dma_stop_sc98(ct) + struct ct_softc *ct; +{ + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + + ct_cr_write_1(bst, bsh, 0x73, 0x43); + ct_cr_write_1(bst, bsh, 0x74, 0x34); +} + +static void +bshw_dma_start_elecom(ct) + struct ct_softc *ct; +{ + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + u_int8_t tmp = ct_cr_read_1(bst, bsh, 0x4c); + + ct_cr_write_1(bst, bsh, 0x32, tmp & 0xdf); +} + +static void +bshw_dma_stop_elecom(ct) + struct ct_softc *ct; +{ + bus_space_tag_t bst = ct->sc_iot; + bus_space_handle_t bsh = ct->sc_ioh; + u_int8_t tmp = ct_cr_read_1(bst, bsh, 0x4c); + + ct_cr_write_1(bst, bsh, 0x32, tmp | 0x20); +} + +static struct bshw bshw_generic = { + BSHW_SYNC_RELOAD, + + 0, + + NULL, + NULL, + NULL, +}; + +static struct bshw bshw_sc98 = { + BSHW_DOUBLE_DMACHAN, + + 0x60, + + bshw_dma_init_sc98, + bshw_dma_start_sc98, + bshw_dma_stop_sc98, +}; + +static struct bshw bshw_texa = { + BSHW_DOUBLE_DMACHAN, + + 0x60, + + bshw_dma_init_texa, + NULL, + NULL, +}; + +static struct bshw bshw_elecom = { + 0, + + 0x38, + + NULL, + bshw_dma_start_elecom, + bshw_dma_stop_elecom, +}; + +static struct bshw bshw_lc_smit = { + BSHW_SMFIFO | BSHW_DOUBLE_DMACHAN, + + 0x60, + + NULL, + NULL, + NULL, +}; + +static struct bshw bshw_lha20X = { + BSHW_DOUBLE_DMACHAN, + + 0x60, + + NULL, + NULL, + NULL, +}; + +/* hw tabs */ +static dvcfg_hw_t bshw_hwsel_array[] = { +/* 0x00 */ &bshw_generic, +/* 0x01 */ &bshw_sc98, +/* 0x02 */ &bshw_texa, +/* 0x03 */ &bshw_elecom, +/* 0x04 */ &bshw_lc_smit, +/* 0x05 */ &bshw_lha20X, +}; + +struct dvcfg_hwsel bshw_hwsel = { + DVCFG_HWSEL_SZ(bshw_hwsel_array), + bshw_hwsel_array +}; -- cgit v1.1