/* * Copyright (c) 1995,1996,1997 Jun-ichiro Itoh * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jun-ichiro Itoh. * 4. The name of Jun-ichiro Itoh 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. */ /* * Hitachi microcomputer system Speech Synthesis card (MSSHVPC02E) driver * * To purchasing the card, contact the following address: * Tokyo office, Hitachi microcomputer system co. * J tower 9th floor, 1-1, Higane-machi, * Fuchu, Tokyo 183 JAPAN * (voice) +81-423-51-6600 / (fax) +81-423-51-6601 */ /* * Special thanks to: Yasuhito riho-m Watanabe */ #include #include #include #include #include #include #include #include #include #ifdef __bsdi__ #include #endif /*__bsdi__*/ #ifdef __FreeBSD__ #include #endif /*__FreeBSD__*/ /* OS dependent part */ #ifdef __bsdi__ #include "cs.h" #if NCS > 0 #define PCCARD_BSDI #endif #endif /*__bsdi__*/ #ifdef __FreeBSD__ #include "card.h" #if NCARD > 0 #define PCCARD_FREEBSD #endif #endif /*__FreeBSD__*/ #ifdef PCCARD_BSDI #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /*PCCARD_BSDI*/ #ifdef PCCARD_FREEBSD #include #include #include #endif /*PCCARD_FREEBSD*/ #ifdef __bsdi__ #include "hssvar.h" #endif #ifdef __FreeBSD__ #include #include "hss.h" #endif #if defined(PCCARD_BSDI) || defined(PCCARD_FREEBSD) #define CCPROBE_DEBUG #define CCVERBOSE #define TIMODEBUG #ifdef CCPROBE_DEBUG int hss_ccprobe_debug = 0; # define ccdprintf(x) { if (hss_ccprobe_debug) printf x; } #else # define ccdprintf(x) #endif #ifdef CCVERBOSE int hss_ccverbose = 0; # define ccvprintf(x) { if (hss_ccverbose) printf x; } #else # define ccvprintf(x) #endif #define TIMOMAX (1000000L) #ifdef TIMODEBUG int hss_timodebug = 0; # define timoprintf(x) { if (hss_timodebug) printf x; } #else # define timoprintf(x) #endif #endif /*PCCARD_BSDI || PCCARD_FREEBSD*/ int hssopen __P((dev_t, int, int, struct proc *)); int hssread __P((dev_t, struct uio *, int)); int hsswrite __P((dev_t, struct uio *, int)); int hssclose __P((dev_t, int, int, struct proc *)); #if 0 int hssmap __P((dev_t, int, int)); #endif #ifdef __bsdi__ int hssprobe __P((struct device *, struct cfdata *, void *)); void hssattach __P((struct device *, struct device *, void *)); struct cfdriver hsscd = { NULL, "hss", hssprobe, hssattach, DV_DULL, sizeof(struct hss_softc) }; struct devsw hsssw = { &hsscd, hssopen, hssclose, hssread, hsswrite, noioctl, seltrue, nommap, nostrat, nodump, nopsize, 0, nostop }; #endif /*__bsdi__*/ #ifdef __FreeBSD__ int hss_isa_probe __P((struct isa_device *)); int hss_isa_attach __P((struct isa_device *)); struct isa_driver hssdriver = { hss_isa_probe, hss_isa_attach, "hss", 0 }; #define CDEV_MAJOR 81 static struct cdevsw hsssw = { hssopen, hssclose, hssread, hsswrite, noioctl, nostop, noreset, nodevtotty, seltrue, nommap, NULL, "hss", NULL, -1, }; #endif /*__FreeBSD__*/ #define LBSZ 132 /*small local buffer in hsswrite()*/ #ifdef PCCARD_BSDI /* * PCMCIA Card Service event handler (callback) */ hss_cse_handler(clidata, func, sock, info, mtdreq, buf, misc) void *clidata; int func, sock, info; struct mtdreq *mtdreq; char *buf; int misc; { struct hss_softc *hssp = (struct hss_softc *)clidata; switch (func) { case CSE_CARD_INSERTION: if (hssp->hss_configured || cu_configured(&hssp->hss_clihdl, sock)) break; if (hss_cc_probe(hssp, sock)) { ccdprintf(("hss_cse_handler: probe okay\n")); hss_cc_attach(hssp, sock); } break; case CSE_CARD_REMOVAL: if (hssp->hss_configured && hssp->hss_rcfg.cscfg_Socket == sock) hss_cc_detach(hssp, sock); break; case CSE_CLIENT_INFO: break; default: break; } } /* * probe the card. */ hss_cc_probe(hssp, socket) struct hss_softc *hssp; int socket; { if (cs_spec_lookup(socket, "hss", NULL, 0) != 0) { return 0; } return 1; #if 0 cs_rtpl_t tplreq; int unit = hssp->sc_dev.dv_unit; int err; int i; /* consult CISTPL_VERS_1 */ tplreq.Socket = socket; tplreq.Desired = CISTPL_VERS_1; tplreq.TupleData = hssp->hss_vstr; tplreq.TupleDataMax = sizeof(hssp->hss_vstr); if (err = cs_GetTuple(&tplreq)) { if (err != CSERR_NO_MORE_ITEMS) printf("hss%d: GetTuple(VERS_1) err 0x%x\n", unit, err); return 0; } hssp->hss_vlen = TupleLen(tplreq); #ifdef CCPROBE_DEBUG if (hss_ccprobe_debug) { printf("hss_cc_probe: "); cu_print_version(hssp->hss_vstr, hssp->hss_vlen); printf("\n"); } #endif /* since we don't have FUNCID, we sorely rely upon VERS_1 tuple. */ if (bcmp(hssp->hss_vstr, hss_vstr, sizeof (hss_vstr)) == 0) { #ifdef CCPROBE_DEBUG if (hss_ccprobe_debug) { printf("hss%d: ", unit); cu_print_version(hssp->hss_vstr, hssp->hss_vlen); printf("\n"); } #endif return 1; } return 0; #endif } hss_cc_attach(hssp, socket) struct hss_softc *hssp; int socket; { cs_rtpl_t tplreq; struct tpce tpce; struct tpcc tpcc; int unit = hssp->sc_dev.dv_unit; u_char tplbuf[256]; int err; int i; ccdprintf(("hss_cc_attach: socket %d\n", socket)); /* * CISTPL_CONF */ tplreq.Socket = socket; tplreq.Desired = CISTPL_CONF; tplreq.TupleData = tplbuf; tplreq.TupleDataMax = sizeof(tplbuf); if (err = cs_GetTuple(&tplreq)) { ccdprintf(("hss_cc_attach: no CISTPL_CONF, err 0x%x\n", err)); return 0; } if (err = cu_parse_conf(tplbuf, TupleLen(tplreq), &tpcc)) { ccdprintf(("hss_cc_attach: bad CISTPL_CONF, err 0x%x\n", err)); return 0; } ccdprintf(("hss_cc_attach: radr 0x%x, rmsk 0x%x, last 0x%x\n", tpcc.tpcc_radr, tpcc.tpcc_rmsk[0], tpcc.tpcc_last)); /* * CISTPL_CE (must follow CISTPL_CONF) */ tplreq.Desired = CISTPL_CE; while ((err = cs_GetNextTuple(&tplreq)) == 0) { tplreq.TupleData = tplbuf; tplreq.TupleDataMax = sizeof(tplbuf); if (err = cs_GetTupleData(&tplreq)) { printf("hss_cc_attach: bad read on CISTPL_CE\n"); return 0; } cu_parse_ce(tplbuf, TupleLen(tplreq), &tpce, NULL); #if 0 cu_dump_ce(&tpce); printf("iftype: %d(%d) addrline: %d iocount: %d base: %x len:%x\n", tpce.ce.ce_if_type, TPCE_IF_TYPE_IO, tpce.ce.ce_io_addrline, tpce.ce.ce_io_count, tpce.ce.ce_io[0].tpce_io_base, tpce.ce.ce_io[0].tpce_io_len); #endif if (tpce.ce.ce_if_type != TPCE_IF_TYPE_IO) continue; /* * If the card has proper cis-tuple, "proper version" * However, Version 1.0 card requests 256 (1<<8) I/O * addresses. How stupid it is. */ #if 1 /*screwed up cis-tuple version*/ { static caddr_t baseaddrs[] = { (caddr_t)0x320, (caddr_t)0x300, (caddr_t)0x310, (caddr_t)0x330, (caddr_t)0x340, (caddr_t)0x200, (caddr_t)0x3d0, }; size_t i; for (i = 0; i < sizeof(baseaddrs)/sizeof(baseaddrs[0]); i++) { hssp->hss_rio.csrio_Socket = socket; hssp->hss_rio.csrio_PortAttr1 = 0; hssp->hss_rio.csrio_PortAttr2 = 0; hssp->hss_rio.csrio_BasePort1 = baseaddrs[i]; hssp->hss_rio.csrio_NumPorts1 = 16; ccdprintf(("hss%d: trying 0x%x len 0x%d\n", unit, hssp->hss_rio.csrio_BasePort1, hssp->hss_rio.csrio_NumPorts1)); if ((err = cs_RequestIO(&hssp->hss_clihdl, &hssp->hss_rio)) == 0) goto found; } } #else /*proper version*/ if (tpce.ce.ce_io_addrline) { hssp->hss_rio.csrio_BasePort1 = 0; hssp->hss_rio.csrio_NumPorts1 = (1 << tpce.ce.ce_io_addrline); hssp->hss_rio.csrio_IOAddrLines = tpce.ce.ce_io_addrline; } else if (tpce.ce.ce_io_count == 1) { hssp->hss_rio.csrio_BasePort1 = tpce.ce.ce_io[0].tpce_io_base; hssp->hss_rio.csrio_NumPorts1 = tpce.ce.ce_io[0].tpce_io_len; } else continue; hssp->hss_rio.csrio_Socket = socket; hssp->hss_rio.csrio_PortAttr1 = 0; hssp->hss_rio.csrio_PortAttr2 = 0; ccdprintf(("hss%d: trying 0x%x len 0x%d\n", unit, hssp->hss_rio.csrio_BasePort1, hssp->hss_rio.csrio_NumPorts1)); if ((err = cs_RequestIO(&hssp->hss_clihdl, &hssp->hss_rio)) == 0) goto found; #endif } if (err) { printf("hss_cc_attach: error finding usable CISTPL_CE\n"); return 0; } found:; hssp->hss_io_addr = (caddr_t)hssp->hss_rio.csrio_BasePort1; hssp->hss_io_size = hssp->hss_rio.csrio_NumPorts1; /* * configure the card. should consult CISTPL_DEVICE */ hssp->hss_rcfg.cscfg_Socket = socket; hssp->hss_rcfg.cscfg_Attributes = 0; /* we don't need IRQ */ hssp->hss_rcfg.cscfg_Vcc = 50; /* 5V to Vcc, Vpp1, Vpp2 */ hssp->hss_rcfg.cscfg_Vpp1 = 50; hssp->hss_rcfg.cscfg_Vpp2 = 50; hssp->hss_rcfg.cscfg_IntType = 2; /* fake: I/O card */ hssp->hss_rcfg.cscfg_Present = CREGMAP_COR; hssp->hss_rcfg.cscfg_ConfigBase = tpcc.tpcc_radr; hssp->hss_rcfg.cscfg_ConfigIndex = tpce.ce.ce_index; if (err = cs_RequestConfiguration(&hssp->hss_clihdl, &hssp->hss_rcfg)) { ccdprintf(("hss_cc_attach: RequestConfig err 0x%x\n", err)); cs_ReleaseIO(&hssp->hss_clihdl, &hssp->hss_rio); return 0; } hssp->hss_configured = 1; #if 0 /* * allocate memory window */ rwin.Socket = socket; rwin.Attributes = CSRWIN_ENABLE|CSRWIN_DATA_PATH_16; rwin.Base = 0; /* pick available address */ rwin.Size = 4096; /* map 4Kbytes */ rwin.AccessSpeed = 4; /* 100nsec, based on CISTPL_DEVICE */ if (err = cs_RequestWindow(&hssp->hss_clihdl, &hssp->hss_whdl, &rwin)) { printf("hss_cc_attach: can't alloc 16-bit win. err 0x%x\n", err); goto bad; } /* * map card memory onto the memory window */ rpage.CardOffset = 0; rpage.Page = 0; if (err = cs_MapMemPage(&hssp->hss_clihdl, &hssp->hss_whdl, &rpage)) { printf("hss_cc_attach: can't map 16-bit win. err 0x%x\n", err); goto bad; } hssp->hss_mem_addr = (caddr_t) rwin.Base; hssp->hss_mem_size = rwin.Size; ccdprintf(("hss_cc_attach: got 16-bit win @ 0x%x (phys 0x%x)\n", cs_ptokvm(rwin.Base), rwin.Base)); #endif ccvprintf(("hss%d: <%s> iobase 0x%x\n", unit, cu_EditTuple(hssp->hss_vstr, hssp->hss_vlen, 40), hssp->hss_io_addr)); return 1; bad:; hss_cc_detach(hssp, socket); return 0; } hss_cc_detach(hssp, socket) struct hss_softc *hssp; int socket; { if (hssp->hss_configured) { cs_ReleaseConfiguration(&hssp->hss_clihdl, &hssp->hss_rcfg); cs_ReleaseIO(&hssp->hss_clihdl, &hssp->hss_rio); hssp->hss_configured = 0; } #if 0 if (hssp->hss_whdl) { cs_ReleaseWindow(&hssp->hss_clihdl, &hssp->hss_whdl); hssp->hss_whdl = 0; } #endif } #endif /*PCCARD_BSDI*/ #ifdef PCCARD_FREEBSD #include "apm.h" #include #include #include #include /* * PC-Card (PCMCIA) specific code. */ static int card_intr __P((struct pccard_devinfo *)); static void hss_unload __P((struct pccard_devinfo *)); static void hss_suspend __P((struct pccard_devinfo *)); static int hss_pccard_init __P((struct pccard_devinfo *, int)); static int hss_pccard_attach __P((struct pccard_devinfo *)); static struct pccard_device hss_info = { "hss", hss_pccard_init, hss_unload, /* hss_suspend, */ card_intr, 0, /* Attributes - presently unused */ &bio_imask /* usual device */ }; DATA_SET(pccarddrv_set, hss_info); static struct hss_softc *hss_softc[NHSS]; /* Resume is done by executing hss_pccard_init(devi, 0). */ static void hss_suspend(devi) struct pccard_devinfo *devi; { struct hss_softc *sc = hss_softc[devi->isahd.id_unit]; printf("hss%d: suspending\n", devi->isahd.id_unit); sc->hss_configured = 0; } /* * */ static int hss_pccard_init(devi, first) struct pccard_devinfo *devi; int first; { struct isa_device *is = &devi->isahd; struct hss_softc *sc = hss_softc[is->id_unit]; int i; dev_t dev; if (NHSS <= is->id_unit) return ENXIO; if (!sc) { sc = (struct hss_softc *) malloc(sizeof(struct hss_softc), M_DEVBUF, M_NOWAIT); if (!sc) { printf("hss%d: cannot alloc hss_softc.\n", is->id_unit); return ENXIO; } bzero(sc, sizeof(struct hss_softc)); hss_softc[is->id_unit] = sc; } sc->hss_io_addr = is->id_iobase; sc->hss_io_size = 16; /*XXX*/ sc->hss_configured = 0; /* just for initialize */ if (first) { /* attach routine: we need nothing at all */ printf("hss%d: initialized.\n", is->id_unit); dev = makedev(CDEV_MAJOR, 0); printf("cdevsw_add returns %d\n", cdevsw_add(&dev, &hsssw, NULL)); } else { printf("hss%d: resumed.\n", is->id_unit); } sc->hss_configured = 1; return 0; } static int hss_pccard_attach(devi) struct pccard_devinfo *devi; { struct isa_device *is = &devi->isahd; struct hss_softc *sc = hss_softc[is->id_unit]; if (NHSS <= is->id_unit) return ENXIO; sc->hss_io_addr = is->id_iobase; sc->hss_io_size = 8; return 1; } static void hss_unload(devi) struct pccard_devinfo *devi; { struct isa_device *is = &devi->isahd; struct hss_softc *sc = hss_softc[devi->isahd.id_unit]; if (NHSS <= is->id_unit) return; if (!sc->hss_configured) { printf("hss%d: already unloaded\n", devi->isahd.id_unit); return; } sc->hss_configured = 0; printf("hss%d: unload\n", devi->isahd.id_unit); } /* * card_intr - Shared interrupt called from front end of PC-Card handler. */ static int card_intr(devi) struct pccard_devinfo *devi; { return 0; /*we don't handle interrupts*/ } #endif /*PCCARD_FREEBSD*/ /* * Normal init routine called by configure() code */ #ifdef __bsdi__ /* ARGSUSED */ int hssprobe(parent, cf, aux) struct device *parent; struct cfdata *cf; void *aux; { register struct isa_attach_args *ia = (struct isa_attach_args *)aux; ia->ia_iobase = 0; ia->ia_iosize = 0; ia->ia_irq = IRQNONE; return 1; } /* ARGSUSED */ void hssattach(parent, self, aux) struct device *parent, *self; void *aux; { register struct hss_softc *hssp = (struct hss_softc *)self; cs_rclient_t rcli; int i; #if 0 hssp->hss_whdl = 0; hssp->hss_mem_addr = 0; #endif hssp->hss_configured = 0; /* * initialize as card client */ rcli.Attributes = CSCLI_IO; /*XXX*/ rcli.EventMask = ~CSEM_CD_CHANGE; rcli.CallBack = hss_cse_handler; rcli.CallBackArg = hssp; cs_RegisterClient(&hssp->hss_clihdl, &rcli); isa_establish(&hssp->sc_id, &hssp->sc_dev); printf(": PCCARD_BSDI Speech Synthesizer\n"); } #endif /*__bsdi__*/ #ifdef __FreeBSD__ int hss_isa_probe(id) struct isa_device *id; { return 0; /*always fail*/ } int hss_isa_attach(id) struct isa_device *id; { return 0; /*always fail*/ } #endif /*__FreeBSD__*/ int hssopen(dev, flags, mode, p) dev_t dev; int flags, mode; struct proc *p; { int unit = HSSUNIT(dev); register struct hss_softc *hssp; /* Validate unit number */ #ifdef __bsdi__ if (unit >= hsscd.cd_ndevs || (hssp = hsscd.cd_devs[unit]) == NULL) return (ENXIO); #endif /*__bsdi__*/ #ifdef __FreeBSD__ if (NHSS <= unit || (hssp = hss_softc[unit]) == NULL) return (ENXIO); #endif /*__FreeBSD__*/ #if defined(PCCARD_BSDI) || defined(PCCARD_FREEBSD) if (! hssp->hss_configured) return ENXIO; #endif if (hssp->hss_flags & HSS_OPEN) return (EBUSY); /* * First open. */ hssp->hss_flags |= HSS_OPEN; if (suser(p->p_ucred, &p->p_acflag) == 0) hssp->hss_flags |= HSS_PRIV; return (0); } /*ARGSUSED*/ int hssread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int error; u_char buffer[1]; int unit = HSSUNIT(dev); #ifdef __bsdi__ register struct hss_softc *sc = hsscd.cd_devs[HSSUNIT(dev)]; #endif /*__bsdi__*/ #ifdef __FreeBSD__ register struct hss_softc *sc = hss_softc[HSSUNIT(dev)]; #endif /*__FreeBSD*/ caddr_t base = sc->hss_io_addr; buffer[0] = inb((int)base + HSS_PORTB); error = uiomove(buffer, 1, uio); if (error) return error; return 0; } /*ARGSUSED*/ int hsswrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int n, i, error; u_char s0, s1, s2; long timo; int timeoutcnt; int timeerrcnt; u_char buffer[LBSZ]; int unit = HSSUNIT(dev); #ifdef __bsdi__ register struct hss_softc *sc = hsscd.cd_devs[HSSUNIT(dev)]; #endif /*__bsdi__*/ #ifdef __FreeBSD__ register struct hss_softc *sc = hss_softc[HSSUNIT(dev)]; #endif /*__FreeBSD*/ caddr_t base = sc->hss_io_addr; /* Loop while more data remaining to be written */ while ((n = min(LBSZ, uio->uio_resid)) > 0) { timeoutcnt = 0; timeerrcnt = 0; error = uiomove(buffer, n, uio); if (error) return error; for (i = 0; i < n; i++) { timoprintf(("buf=%d, i=%d, %d chars to go\n", n, i, n-i)); #if 1 /*busywait version*/ s0 = inb((int)base + HSS_PORTB); outb((int)base + HSS_PORTC, buffer[i]); outb((int)base + HSS_SHIRQ, 0); timoprintf(("entering timo1\n")); timo = 0L; do { if (TIMOMAX < ++timo) { timoprintf(("timo1 timeouted\n")); goto timeout; } s1 = s2; s2 = inb((int)base + HSS_PORTB); } while (s0 == s2 || s1 != s2); timoprintf(("timo1=%ld\n", timo)); if (s2 == 0x90) timeerrcnt++; if (0) { timeout:; timeoutcnt++; } #else /*write-and-forget version*/ outb((int)base + HSS_PORTC, buffer[i]); outb((int)base + HSS_HSIRQ, 0); #endif } /* error reporting... */ if (timeerrcnt || timeoutcnt) { printf("hss%d: hsswrite timeerr=%d, timeout=%d in %d chars\n", unit, timeerrcnt, timeoutcnt, n); } } return 0; } /*ARGSUSED*/ int hssclose(dev, flags, mode, p) dev_t dev; int flags, mode; struct proc *p; { #ifdef __bsdi__ struct hss_softc *hssp = hsscd.cd_devs[HSSUNIT(dev)]; #endif /*__bsdi__*/ #ifdef __FreeBSD__ struct hss_softc *hssp = hss_softc[HSSUNIT(dev)]; #endif /*__FreeBSD*/ hssp->hss_flags = HSS_DEAD; return (0); } #if 0 /*ARGSUSED*/ int hssmap(dev, off, prot) dev_t dev; int off, prot; { #ifdef __bsdi__ struct hss_softc *hssp = hsscd.cd_devs[HSSUNIT(dev)]; #endif /*__bsdi__*/ #ifdef __FreeBSD__ struct hss_softc *hssp = hss_softc[HSSUNIT(dev)]; #endif /*__FreeBSD*/ u_int paddr; if (off + NBPG > hssp->hss_mem_size) return (-1); return (((u_int)hssp->hss_mem_addr + off) >> PGSHIFT); } #endif