/*
 * Copyright (c) 1995,1996,1997 Jun-ichiro Itoh <itojun@itojun.org>
 * 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 <riho-m@sfc.wide.ad.jp>
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <machine/cpu.h>

#include <i386/isa/icu.h>
#include <i386/isa/isa.h>
#ifdef __bsdi__
#include <i386/isa/isavar.h>
#endif /*__bsdi__*/
#ifdef __FreeBSD__
#include <sys/kernel.h>
#endif /*__FreeBSD__*/

/* OS dependent part */
#ifdef __bsdi__
#include "cs.h"
#if NCS > 0
#define PCCARD_BSDI
#endif
#endif /*__bsdi__*/
#ifdef __FreeBSD__
#include "crd.h"
#if NCRD > 0
#define PCCARD_FREEBSD
#endif
#endif /*__FreeBSD__*/

#ifdef PCCARD_BSDI
#include <i386/pcmcia/pccs_meta.h>
#include <i386/pcmcia/cs_handle.h>
#include <i386/pcmcia/cs_cisutil.h>
#include <i386/pcmcia/cs_irq.h>
#include <i386/pcmcia/cs_errno.h>
#include <i386/pcmcia/cs_tplsvc.h>
#include <i386/pcmcia/cs_event.h>
#include <i386/pcmcia/cs_iop.h>
#include <i386/pcmcia/cs_rio.h>
#include <i386/pcmcia/cs_cli.h>
#include <i386/pcmcia/cs_conf.h>
#include <i386/pcmcia/cs_acccfg.h>
#include <i386/pcmcia/cs_stat.h>
#endif /*PCCARD_BSDI*/
#ifdef PCCARD_FREEBSD
#include <pccard/card.h>
#include <pccard/driver.h>
#include <pccard/slot.h>
#endif /*PCCARD_FREEBSD*/

#ifdef __bsdi__
#include "hssvar.h"
#endif
#ifdef __FreeBSD__
#include <i386/isa/hssvar.h>
#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 <sys/select.h>
#include <pccard/card.h>
#include <pccard/driver.h>
#include <pccard/slot.h>

/*
 * PC-Card (PCMCIA) specific code.
 */
static int card_intr __P((struct pccard_dev *));
static void hss_unload __P((struct pccard_dev *));
static void hss_suspend __P((struct pccard_dev *));
static int hss_pccard_init __P((struct pccard_dev *, int));
static int hss_pccard_attach  __P((struct pccard_dev *));

static struct pccard_drv hss_info = {
	"hss",
	card_intr,
	hss_unload,
	hss_suspend,
	hss_pccard_init,
	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(dp, 0). */
static void
hss_suspend(dp)
	struct pccard_dev *dp;
{
	struct hss_softc *sc = hss_softc[dp->isahd.id_unit];

	printf("hss%d: suspending\n", dp->isahd.id_unit);
	sc->hss_configured = 0;
}

/*
 * 
 */
static int
hss_pccard_init(dp, first)
	struct pccard_dev *dp;
	int first;
{
	struct isa_device *is = &dp->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(dp)
	struct pccard_dev *dp;
{
	struct isa_device *is = &dp->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(dp)
	struct pccard_dev *dp;
{
	struct isa_device *is = &dp->isahd;
	struct hss_softc *sc = hss_softc[dp->isahd.id_unit];

	if (NHSS <= is->id_unit)
		return;
	if (!sc->hss_configured) {
		printf("hss%d: already unloaded\n", dp->isahd.id_unit);
		return;
	}
	sc->hss_configured = 0;
	printf("hss%d: unload\n", dp->isahd.id_unit);
}

/*
 * card_intr - Shared interrupt called from front end of PC-Card handler.
 */
static int
card_intr(dp)
	struct pccard_dev *dp;
{
	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
