code:
/*	$OpenBSD: flashvar.h,v 1.7 2013/11/11 03:03:34 dlg Exp $	*/

/*
 * Copyright (c) 2005 Uwe Stuehler <uwe@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _FLASHVAR_H_
#define _FLASHVAR_H_

#ifdef _KERNEL

/* Flash controller descriptor structure */
struct flash_ctl_tag {
	u_int8_t (*reg8_read)(void *, int);
	int	 (*regx_read_page)(void *, caddr_t, caddr_t);
	void	 (*reg8_write)(void *, int, u_int8_t);
	int	 (*regx_write_page)(void *, caddr_t, caddr_t);
	void	 (*default_disklabel)(void *, dev_t, struct disklabel *);
	int	 (*safe_strategy)(void *, struct buf *);
};

/*
 * Pseudo-registers for a fictitious flash controller
 *
 * Note that logical levels are assumed for CE and WP bits.
 * Signals corresponding to these bits are usually negated.
 */
#define FLASH_REG_DATA		0x00
#define FLASH_REG_COL		0x01
#define FLASH_REG_ROW		0x02
#define FLASH_REG_CMD		0x03
#define FLASH_REG_ALE		0x04
#define FLASH_REG_CLE		0x05
#define FLASH_REG_CE		0x06
#define FLASH_REG_WP		0x07
#define FLASH_REG_READY		0x0f

/* Flash device descriptor structure */
struct flashdev {
	u_int16_t	 id;
	const char	*longname;
	u_long		 pagesize;	/* bytes per page */
	u_long		 oobsize;	/* OOB bytes per page */
	u_long		 blkpages;	/* pages per erasable block */
	u_long		 capacity;	/* pages per device */
};

#define FLASH_DEVICE(v,d)		((FLASH_VENDOR_##v << 8) | (d))

/* Flash device vendors */
#define FLASH_VENDOR_SAMSUNG		0xec

/* Flash devices */
#define FLASH_DEVICE_SAMSUNG_K9F2808U0C FLASH_DEVICE(SAMSUNG, 0x73)
#define FLASH_DEVICE_SAMSUNG_K9F1G08U0A FLASH_DEVICE(SAMSUNG, 0xf1)

/* Maximum sizes for all devices */
#define FLASH_MAXPAGESIZE	2048
#define FLASH_MAXOOBSIZE	64

/*
 * Should-be private softc structure for the generic flash driver.
 */
struct flash_softc {
	struct device		 sc_dev;
	/* Disk device information */
	struct disk		 sc_dk;
	struct bufq		 sc_bufq;
	struct buf		*sc_bp;
	int			 sc_flags;
	/* Flash controller tag */
	struct flash_ctl_tag	*sc_tag;
	void			*sc_cookie;
	/* Flash device characteristics */
	const struct flashdev	*sc_flashdev;
	int			 sc_maxwaitready;
	int			 sc_maxwaitcomplete;
};

/* Values for sc_flags */
#define FDK_LOADED		 0x00000001
#define FDK_SAFE		 0x00000002

/*
 * Similar to vnd(4) devices there are two kinds of flash devices.
 * Both device kinds share the same disklabel.
 *
 * ``Safe'' devices have bit 11 set in the minor number and use the
 * out-of-band page data to implement wear-leveling and transparent
 * management of bad block information. Block erasing and rewriting
 * is also handled transparently; arbitrary pages can be modified.
 *
 * ``Unsafe'' devices provide raw access to the flash pages. Access
 * to OOB page data is possible via ioctl()s only with these devices.
 * Erasing the containing flash block may be necessary before a page
 * can be writting successfully, but the block erase command is only
 * provided as an ioctl().
 */
#define flashsafe(x)	(minor(x) & 0x800)
#define flashunit(x)	DISKUNIT(makedev(major(x), minor(x) & 0x7ff))
#define flashpart(x)	DISKPART(makedev(major(x), minor(x) & 0x7ff))
#define flashlabeldev(x) (MAKEDISKDEV(major(x), flashunit(x), RAW_PART)\
			 |flashsafe(x))

void	 flashattach(struct flash_softc *, struct flash_ctl_tag *, void *);
int	 flashdetach(struct device *, int);

u_int8_t flash_reg8_read(struct flash_softc *, int);
void	 flash_reg8_read_page(struct flash_softc *, caddr_t, caddr_t);
void	 flash_reg8_write(struct flash_softc *, int, u_int8_t);
void	 flash_reg8_write_page(struct flash_softc *, caddr_t, caddr_t);
void	 flash_chip_enable(struct flash_softc *);
void	 flash_chip_disable(struct flash_softc *);
int	 flash_chip_reset(struct flash_softc *);
int	 flash_chip_identify(struct flash_softc *, u_int8_t *, u_int8_t *);
int	 flash_chip_erase_block(struct flash_softc *, long);
int	 flash_chip_read_block(struct flash_softc *, long, caddr_t);
int	 flash_chip_read_page(struct flash_softc *, long, caddr_t, caddr_t);
int	 flash_chip_read_oob(struct flash_softc *, long, caddr_t);
int	 flash_chip_write_block(struct flash_softc *, long, caddr_t, caddr_t);
int	 flash_chip_write_page(struct flash_softc *, long, caddr_t, caddr_t);
int	 flash_chip_verify_block(struct flash_softc *, long, caddr_t, caddr_t);
int	 flash_chip_verify_page(struct flash_softc *, long, caddr_t, caddr_t);

#endif /* _KERNEL */

/* XXX: define ioctl commands for OOB page data access and block erase. */

#endif /* _FLASHVAR_H_ */


code: src/sys/dev/gpio

path: sys/dev/gpio/CVS/Repository

license: unknown


code:
# $OpenBSD: files.gpio,v 1.10 2015/06/07 20:11:52 claudio Exp $

define	gpio {[offset = -1], [mask = 0], [flag = 0]}

device	gpio: gpio
attach	gpio at gpiobus
file	dev/gpio/gpio.c				gpio	needs-flag

# GPIO simulator
device	gpiosim: gpiobus
attach	gpiosim at root
file	dev/gpio/gpiosim.c			gpiosim needs-flag

# I2C bus bit-banging
device	gpioiic: i2cbus, i2c_bitbang
attach	gpioiic at gpio
file	dev/gpio/gpioiic.c			gpioiic

# 1-Wire bus bit-banging
device	gpioow: onewirebus, onewire_bitbang
attach	gpioow at gpio
file	dev/gpio/gpioow.c			gpioow

# DCF77 time signal station receivers
device	gpiodcf
attach	gpiodcf at gpio
file	dev/gpio/gpiodcf.c			gpiodcf


code:
/*	$OpenBSD: gpio.c,v 1.13 2014/07/12 18:48:17 tedu Exp $	*/

/*
 * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
 * Copyright (c) 2004, 2006 Alexander Yurchenko <grange@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * General Purpose Input/Output framework.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#include <sys/gpio.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/queue.h>

#include <dev/gpio/gpiovar.h>

struct gpio_softc {
	struct device sc_dev;

	gpio_chipset_tag_t		 sc_gc;		/* GPIO controller */
	gpio_pin_t			*sc_pins;	/* pins array */
	int				 sc_npins;	/* number of pins */

	int sc_opened;
	LIST_HEAD(, gpio_dev)		 sc_devs;	/* devices */
	LIST_HEAD(, gpio_name) 	 	 sc_names;	/* named pins */
};

int	gpio_match(struct device *, void *, void *);
int	gpio_submatch(struct device *, void *, void *);
void	gpio_attach(struct device *, struct device *, void *);
int	gpio_detach(struct device *, int);
int	gpio_search(struct device *, void *, void *);
int	gpio_print(void *, const char *);
int	gpio_pinbyname(struct gpio_softc *, char *gp_name);

struct cfattach gpio_ca = {
	sizeof (struct gpio_softc),
	gpio_match,
	gpio_attach,
	gpio_detach
};

struct cfdriver gpio_cd = {
	NULL, "gpio", DV_DULL
};

int
gpio_match(struct device *parent, void *match, void *aux)
{
	struct cfdata *cf = match;
	struct gpiobus_attach_args *gba = aux;

	return (strcmp(gba->gba_name, cf->cf_driver->cd_name) == 0);
}

int
gpio_submatch(struct device *parent, void *match, void *aux)
{
	struct cfdata *cf = match;
	struct gpio_attach_args *ga = aux;

	if (strcmp(ga->ga_dvname, cf->cf_driver->cd_name) != 0)
		return (0);

	return ((*cf->cf_attach->ca_match)(parent, match, aux));
}

void
gpio_attach(struct device *parent, struct device *self, void *aux)
{
	struct gpio_softc *sc = (struct gpio_softc *)self;
	struct gpiobus_attach_args *gba = aux;

	sc->sc_gc = gba->gba_gc;
	sc->sc_pins = gba->gba_pins;
	sc->sc_npins = gba->gba_npins;

	printf(": %d pins\n", sc->sc_npins);

	/*
	 * Attach all devices that can be connected to the GPIO pins
	 * described in the kernel configuration file.
	 */
	config_search(gpio_search, self, sc);
}

int
gpio_detach(struct device *self, int flags)
{
	int maj, mn;

	/* Locate the major number */
	for (maj = 0; maj < nchrdev; maj++)
		if (cdevsw[maj].d_open == gpioopen)
			break;

	/* Nuke the vnodes for any open instances (calls close) */
	mn = self->dv_unit;
	vdevgone(maj, mn, mn, VCHR);

	return (0);
}

int
gpio_search(struct device *parent, void *arg, void *aux)
{
	struct cfdata *cf = arg;
	struct gpio_attach_args ga;

	ga.ga_gpio = aux;
	ga.ga_offset = cf->cf_loc[0];
	ga.ga_mask = cf->cf_loc[1];
	ga.ga_flags = cf->cf_loc[2];

	if (cf->cf_attach->ca_match(parent, cf, &ga) > 0)
		config_attach(parent, cf, &ga, gpio_print);

	return (0);
}

int
gpio_print(void *aux, const char *pnp)
{
	struct gpio_attach_args *ga = aux;
	int i;

	printf(" pins");
	for (i = 0; i < 32; i++)
		if (ga->ga_mask & (1 << i))
			printf(" %d", ga->ga_offset + i);

	return (UNCONF);
}

int
gpiobus_print(void *aux, const char *pnp)
{
	struct gpiobus_attach_args *gba = aux;

	if (pnp != NULL)
		printf("%s at %s", gba->gba_name, pnp);

	return (UNCONF);
}

int
gpio_pin_map(void *gpio, int offset, u_int32_t mask, struct gpio_pinmap *map)
{
	struct gpio_softc *sc = gpio;
	int npins, pin, i;

	npins = gpio_npins(mask);
	if (npins > sc->sc_npins)
		return (1);

	for (npins = 0, i = 0; i < 32; i++)
		if (mask & (1 << i)) {
			pin = offset + i;
			if (pin < 0 || pin >= sc->sc_npins)
				return (1);
			if (sc->sc_pins[pin].pin_mapped)
				return (1);
			sc->sc_pins[pin].pin_mapped = 1;
			map->pm_map[npins++] = pin;
		}
	map->pm_size = npins;

	return (0);
}

void
gpio_pin_unmap(void *gpio, struct gpio_pinmap *map)
{
	struct gpio_softc *sc = gpio;
	int pin, i;

	for (i = 0; i < map->pm_size; i++) {
		pin = map->pm_map[i];
		sc->sc_pins[pin].pin_mapped = 0;
	}
}

int
gpio_pin_read(void *gpio, struct gpio_pinmap *map, int pin)
{
	struct gpio_softc *sc = gpio;

	return (gpiobus_pin_read(sc->sc_gc, map->pm_map[pin]));
}

void
gpio_pin_write(void *gpio, struct gpio_pinmap *map, int pin, int value)
{
	struct gpio_softc *sc = gpio;

	return (gpiobus_pin_write(sc->sc_gc, map->pm_map[pin], value));
}

void
gpio_pin_ctl(void *gpio, struct gpio_pinmap *map, int pin, int flags)
{
	struct gpio_softc *sc = gpio;

	return (gpiobus_pin_ctl(sc->sc_gc, map->pm_map[pin], flags));
}

int
gpio_pin_caps(void *gpio, struct gpio_pinmap *map, int pin)
{
	struct gpio_softc *sc = gpio;

	return (sc->sc_pins[map->pm_map[pin]].pin_caps);
}

int
gpio_npins(u_int32_t mask)
{
	int npins, i;

	for (npins = 0, i = 0; i < 32; i++)
		if (mask & (1 << i))
			npins++;

	return (npins);
}

int
gpioopen(dev_t dev, int flag, int mode, struct proc *p)
{
	struct gpio_softc *sc;

	sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev));
	if (sc == NULL)
		return (ENXIO);

	if (sc->sc_opened)
		return (EBUSY);
	sc->sc_opened = 1;

	return (0);
}

int
gpioclose(dev_t dev, int flag, int mode, struct proc *p)
{
	struct gpio_softc *sc;

	sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev));
	sc->sc_opened = 0;

	return (0);
}

int
gpio_pinbyname(struct gpio_softc *sc, char *gp_name)
{
	struct gpio_name *nm;

	LIST_FOREACH(nm, &sc->sc_names, gp_next)
		if (!strcmp(nm->gp_name, gp_name))
			return (nm->gp_pin);
	return (-1);
}

int
gpioioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct gpio_softc *sc;
	gpio_chipset_tag_t gc;
	struct gpio_info *info;
	struct gpio_pin_op *op;
	struct gpio_attach *attach;
	struct gpio_attach_args ga;
	struct gpio_dev *gdev;
	struct gpio_name *nm;
	struct gpio_pin_set *set;
	struct device *dv;
	int pin, value, flags, npins, found;

	sc = (struct gpio_softc *)device_lookup(&gpio_cd, minor(dev));
	gc = sc->sc_gc;

	switch (cmd) {
	case GPIOINFO:
		info = (struct gpio_info *)data;
		if (securelevel < 1)
			info->gpio_npins = sc->sc_npins;
		else {
			for (pin = npins = 0; pin < sc->sc_npins; pin++)
				if (sc->sc_pins[pin].pin_flags & GPIO_PIN_SET)
					++npins;
			info->gpio_npins = npins;
		}
		break;
	case GPIOPINREAD:
		op = (struct gpio_pin_op *)data;

		if (op->gp_name[0] != '\0') {
			pin = gpio_pinbyname(sc, op->gp_name);
			if (pin == -1)
				return (EINVAL);
		} else
			pin = op->gp_pin;

		if (pin < 0 || pin >= sc->sc_npins)
			return (EINVAL);

		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
		    securelevel > 0)
			return (EPERM);

		/* return read value */
		op->gp_value = gpiobus_pin_read(gc, pin);
		break;
	case GPIOPINWRITE:
		if ((flag & FWRITE) == 0)
			return (EBADF);

		op = (struct gpio_pin_op *)data;

		if (op->gp_name[0] != '\0') {
			pin = gpio_pinbyname(sc, op->gp_name);
			if (pin == -1)
				return (EINVAL);
		} else
			pin = op->gp_pin;

		if (pin < 0 || pin >= sc->sc_npins)
			return (EINVAL);

		if (sc->sc_pins[pin].pin_mapped)
			return (EBUSY);

		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
		    securelevel > 0)
			return (EPERM);

		value = op->gp_value;
		if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH)
			return (EINVAL);

		gpiobus_pin_write(gc, pin, value);
		/* return old value */
		op->gp_value = sc->sc_pins[pin].pin_state;
		/* update current value */
		sc->sc_pins[pin].pin_state = value;
		break;
	case GPIOPINTOGGLE:
		if ((flag & FWRITE) == 0)
			return (EBADF);

		op = (struct gpio_pin_op *)data;

		if (op->gp_name[0] != '\0') {
			pin = gpio_pinbyname(sc, op->gp_name);
			if (pin == -1)
				return (EINVAL);
		} else
			pin = op->gp_pin;

		if (pin < 0 || pin >= sc->sc_npins)
			return (EINVAL);

		if (sc->sc_pins[pin].pin_mapped)
			return (EBUSY);

		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
		    securelevel > 0)
			return (EPERM);

		value = (sc->sc_pins[pin].pin_state == GPIO_PIN_LOW ?
		    GPIO_PIN_HIGH : GPIO_PIN_LOW);
		gpiobus_pin_write(gc, pin, value);
		/* return old value */
		op->gp_value = sc->sc_pins[pin].pin_state;
		/* update current value */
		sc->sc_pins[pin].pin_state = value;
		break;
	case GPIOATTACH:
		if (securelevel > 0)
			return (EPERM);

		attach = (struct gpio_attach *)data;
		bzero(&ga, sizeof(ga));
		ga.ga_gpio = sc;
		ga.ga_dvname = attach->ga_dvname;
		ga.ga_offset = attach->ga_offset;
		ga.ga_mask = attach->ga_mask;
		ga.ga_flags = attach->ga_flags;
		dv = config_found_sm((struct device *)sc, &ga, gpiobus_print,
		    gpio_submatch);
		if (dv != NULL) {
			gdev = malloc(sizeof(struct gpio_dev), M_DEVBUF,
			    M_WAITOK);
			gdev->sc_dev = dv;
			LIST_INSERT_HEAD(&sc->sc_devs, gdev, sc_next);
		}
		break;
	case GPIODETACH:
		if (securelevel > 0)
			return (EPERM);

		attach = (struct gpio_attach *)data;
		LIST_FOREACH(gdev, &sc->sc_devs, sc_next) {
			if (strcmp(gdev->sc_dev->dv_xname, attach->ga_dvname)
			    == 0) {
				if (config_detach(gdev->sc_dev, 0) == 0) {
					LIST_REMOVE(gdev, sc_next);
					free(gdev, M_DEVBUF, 0);
				}
				break;
			}
		}
		break;
	case GPIOPINSET:
		if (securelevel > 0)
			return (EPERM);

		set = (struct gpio_pin_set *)data;

		if (set->gp_name[0] != '\0') {
			pin = gpio_pinbyname(sc, set->gp_name);
			if (pin == -1)
				return (EINVAL);
		} else
			pin = set->gp_pin;
		if (pin < 0 || pin >= sc->sc_npins)
			return (EINVAL);
		flags = set->gp_flags;
		/* check that the controller supports all requested flags */
		if ((flags & sc->sc_pins[pin].pin_caps) != flags)
			return (ENODEV);
		flags = set->gp_flags | GPIO_PIN_SET;

		set->gp_caps = sc->sc_pins[pin].pin_caps;
		/* return old value */
		set->gp_flags = sc->sc_pins[pin].pin_flags;
		if (flags > 0) {
			gpiobus_pin_ctl(gc, pin, flags);
			/* update current value */
			sc->sc_pins[pin].pin_flags = flags;
		}

		/* rename pin or new pin? */
		if (set->gp_name2[0] != '\0') {
			found = 0;
			LIST_FOREACH(nm, &sc->sc_names, gp_next)
				if (nm->gp_pin == pin) {
					strlcpy(nm->gp_name, set->gp_name2,
					    sizeof(nm->gp_name));
					found = 1;
					break;
				}
			if (!found) {
				nm = malloc(sizeof(struct gpio_name),
				    M_DEVBUF, M_WAITOK);
				strlcpy(nm->gp_name, set->gp_name2,
				    sizeof(nm->gp_name));
				nm->gp_pin = set->gp_pin;
				LIST_INSERT_HEAD(&sc->sc_names, nm, gp_next);
			}
		}
		break;
	case GPIOPINUNSET:
		if (securelevel > 0)
			return (EPERM);

		set = (struct gpio_pin_set *)data;
		if (set->gp_name[0] != '\0') {
			pin = gpio_pinbyname(sc, set->gp_name);
			if (pin == -1)
				return (EINVAL);
		} else
			pin = set->gp_pin;

		if (pin < 0 || pin >= sc->sc_npins)
			return (EINVAL);
		if (sc->sc_pins[pin].pin_mapped)
			return (EBUSY);
		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET))
			return (EINVAL);

		LIST_FOREACH(nm, &sc->sc_names, gp_next) {
			if (nm->gp_pin == pin) {
				LIST_REMOVE(nm, gp_next);
				free(nm, M_DEVBUF, 0);
				break;
			}
		}
		sc->sc_pins[pin].pin_flags &= ~GPIO_PIN_SET;
		break;
	default:
		return (ENOTTY);
	}

	return (0);
}


code:
/*	$OpenBSD: gpiodcf.c,v 1.5 2015/06/07 20:11:52 claudio Exp $ */

/*
 * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/select.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/device.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <sys/sensors.h>
#include <sys/gpio.h>

#include <dev/gpio/gpiovar.h>

#ifdef GPIODCF_DEBUG
#define DPRINTFN(n, x)	do { if (gpiodcfdebug > (n)) printf x; } while (0)
int gpiodcfdebug = 0;
#else
#define DPRINTFN(n, x)
#endif
#define DPRINTF(x)	DPRINTFN(0, x)

#define DPERIOD1	((long) 5 * 60)		/* degrade OK -> WARN */
#define DPERIOD2	((long) 15 * 60)	/* degrade WARN -> CRIT */

/* max. skew of received time diff vs. measured time diff in percent. */
#define MAX_SKEW	5

#define GPIODCF_NPINS		1
#define	GPIODCF_PIN_DATA	0

struct gpiodcf_softc {
	struct device		sc_dev;		/* base device */
	void			*sc_gpio;
	struct gpio_pinmap	sc_map;
	int			__map[GPIODCF_NPINS];
	u_char			sc_dying;	/* disconnecting */
	int			sc_data;

	struct timeout		sc_to;

	struct timeout		sc_bv_to;	/* bit-value detect */
	struct timeout		sc_db_to;	/* debounce */
	struct timeout		sc_mg_to;	/* minute-gap detect */
	struct timeout		sc_sl_to;	/* signal-loss detect */
	struct timeout		sc_it_to;	/* invalidate time */

	int			sc_sync;	/* 1 during sync */
	u_int64_t		sc_mask;	/* 64 bit mask */
	u_int64_t		sc_tbits;	/* Time bits */
	int			sc_minute;
	int			sc_level;
	time_t			sc_last_mg;
	time_t			sc_current;	/* current time */
	time_t			sc_next;	/* time to become valid next */
	time_t			sc_last;
	int			sc_nrecv;	/* consecutive valid times */
	struct timeval		sc_last_tv;	/* uptime of last valid time */
	struct ksensor		sc_sensor;
#ifdef GPIODCF_DEBUG
	struct ksensor		sc_skew;	/* recv vs local skew */
#endif
	struct ksensordev	sc_sensordev;
};

/*
 * timeouts being used in hz:
 * t_bv		bit value detection (150ms)
 * t_sync	sync (950ms)
 * t_mg		minute gap detection (1500ms)
 * t_mgsync	resync after a minute gap (450ms)
 * t_sl		detect signal loss (3sec)
 * t_wait	wait (5sec)
 * t_warn	degrade sensor status to warning (5min)
 * t_crit	degrade sensor status to critical (15min)
 */
static int t_bv, t_sync, t_mg, t_sl, t_mgsync, t_wait, t_warn, t_crit;

void	gpiodcf_intr(void *);
void	gpiodcf_probe(void *);
void	gpiodcf_bv_probe(void *);
void	gpiodcf_mg_probe(void *);
void	gpiodcf_sl_probe(void *);
void	gpiodcf_invalidate(void *);

int gpiodcf_match(struct device *, void *, void *);
void gpiodcf_attach(struct device *, struct device *, void *);
int gpiodcf_detach(struct device *, int);
int gpiodcf_activate(struct device *, int);

int gpiodcf_signal(struct gpiodcf_softc *);

struct cfdriver gpiodcf_cd = {
	NULL, "gpiodcf", DV_DULL
};

const struct cfattach gpiodcf_ca = {
	sizeof(struct gpiodcf_softc),
	gpiodcf_match,
	gpiodcf_attach,
	gpiodcf_detach,
	gpiodcf_activate
};

int
gpiodcf_match(struct device *parent, void *match, void *aux)
{
	struct cfdata *cf = match;
	struct gpio_attach_args *ga = aux;

	if (ga->ga_offset == -1)
		return 0;

	return (strcmp(cf->cf_driver->cd_name, "gpiodcf") == 0);
}

void
gpiodcf_attach(struct device *parent, struct device *self, void *aux)
{
	struct gpiodcf_softc		*sc = (struct gpiodcf_softc *)self;
	struct gpio_attach_args		*ga = aux;
	struct timeval			 t;
	int				 caps;

	if (gpio_npins(ga->ga_mask) != GPIODCF_NPINS) {
		printf(": invalid pin mask\n");
		return;
	}
	sc->sc_gpio = ga->ga_gpio;
	sc->sc_map.pm_map = sc->__map;
	if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask,
	    &sc->sc_map)) {
		printf(": can't map pins\n");
		return;
	}

	caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA);
	if (!(caps & GPIO_PIN_INPUT)) {
		printf(": data pin is unable to receive input\n");
		goto fishy;
	}
	printf(": DATA[%d]", sc->sc_map.pm_map[GPIODCF_PIN_DATA]);
	sc->sc_data = GPIO_PIN_INPUT;
	gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA, sc->sc_data);
	printf("\n");

	strlcpy(sc->sc_sensor.desc, "DCF77", sizeof(sc->sc_sensor.desc));

	timeout_set(&sc->sc_to, gpiodcf_probe, sc);
	timeout_set(&sc->sc_bv_to, gpiodcf_bv_probe, sc);
	timeout_set(&sc->sc_mg_to, gpiodcf_mg_probe, sc);
	timeout_set(&sc->sc_sl_to, gpiodcf_sl_probe, sc);
	timeout_set(&sc->sc_it_to, gpiodcf_invalidate, sc);

	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
	    sizeof(sc->sc_sensordev.xname));

	sc->sc_sensor.type = SENSOR_TIMEDELTA;
	sc->sc_sensor.status = SENSOR_S_UNKNOWN;
	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);

#ifdef GPIODCF_DEBUG
	sc->sc_skew.type = SENSOR_TIMEDELTA;
	sc->sc_skew.status = SENSOR_S_UNKNOWN;
	strlcpy(sc->sc_skew.desc, "local clock skew",
	    sizeof(sc->sc_skew.desc));
	sensor_attach(&sc->sc_sensordev, &sc->sc_skew);
#endif
	sensordev_install(&sc->sc_sensordev);

	sc->sc_level = 0;
	sc->sc_minute = 0;
	sc->sc_last_mg = 0L;

	sc->sc_sync = 1;

	sc->sc_current = 0L;
	sc->sc_next = 0L;
	sc->sc_nrecv = 0;
	sc->sc_last = 0L;
	sc->sc_last_tv.tv_sec = 0L;

	/* convert timevals to hz */
	t.tv_sec = 0L;
	t.tv_usec = 150000L;
	t_bv = tvtohz(&t);

	t.tv_usec = 450000L;
	t_mgsync = tvtohz(&t);

	t.tv_usec = 950000L;
	t_sync = tvtohz(&t);

	t.tv_sec = 1L;
	t.tv_usec = 500000L;
	t_mg = tvtohz(&t);

	t.tv_sec = 3L;
	t.tv_usec = 0L;
	t_sl = tvtohz(&t);

	t.tv_sec = 5L;
	t_wait = tvtohz(&t);

	t.tv_sec = DPERIOD1;
	t_warn = tvtohz(&t);

	t.tv_sec = DPERIOD2;
	t_crit = tvtohz(&t);

	/* Give the receiver some slack to stabilize */
	timeout_add(&sc->sc_to, t_wait);

	/* Detect signal loss */
	timeout_add(&sc->sc_sl_to, t_wait + t_sl);

	DPRINTF(("synchronizing\n"));
	return;

fishy:
	DPRINTF(("gpiodcf_attach failed\n"));
	gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
	sc->sc_dying = 1;
}

int
gpiodcf_detach(struct device *self, int flags)
{
	struct gpiodcf_softc	*sc = (struct gpiodcf_softc *)self;

	sc->sc_dying = 1;

	timeout_del(&sc->sc_to);
	timeout_del(&sc->sc_bv_to);
	timeout_del(&sc->sc_mg_to);
	timeout_del(&sc->sc_sl_to);
	timeout_del(&sc->sc_it_to);

	/* Unregister the clock with the kernel */
	sensordev_deinstall(&sc->sc_sensordev);

	/* Finally unmap the GPIO pin */
	gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);

	return 0;
}

/*
 * return 1 during high-power-, 0 during low-power-emission
 * If bit 0 is set, the transmitter emits at full power.
 * During the low-power emission we decode a zero bit.
 */
int
gpiodcf_signal(struct gpiodcf_softc *sc)
{
	return (gpio_pin_read(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA) ==
	    GPIO_PIN_HIGH ? 1 : 0);
}

/* gpiodcf_probe runs in a process context. */
void
gpiodcf_probe(void *xsc)
{
	struct gpiodcf_softc	*sc = xsc;
	struct timespec		 now;
	int			 data;

	if (sc->sc_dying)
		return;

	data = gpiodcf_signal(sc);
	if (data == -1)
		return;

	if (data) {
		sc->sc_level = 1;
		timeout_add(&sc->sc_to, 1);
		return;
	}

	if (sc->sc_level == 0)
		return;

	/* the beginning of a second */
	sc->sc_level = 0;
	if (sc->sc_minute == 1) {
		if (sc->sc_sync) {
			DPRINTF(("start collecting bits\n"));
			sc->sc_sync = 0;
		} else {
			/* provide the timedelta */
			microtime(&sc->sc_sensor.tv);
			nanotime(&now);
			sc->sc_current = sc->sc_next;
			sc->sc_sensor.value = (int64_t)(now.tv_sec -
			    sc->sc_current) * 1000000000LL + now.tv_nsec;

			sc->sc_sensor.status = SENSOR_S_OK;

			/*
			 * if no valid time information is received
			 * during the next 5 minutes, the sensor state
			 * will be degraded to SENSOR_S_WARN
			 */
			timeout_add(&sc->sc_it_to, t_warn);
		}
		sc->sc_minute = 0;
	}

	timeout_add(&sc->sc_to, t_sync);	/* resync in 950 ms */

	/* no clock and bit detection during sync */
	if (!sc->sc_sync) {
		/* detect bit value */
		timeout_add(&sc->sc_bv_to, t_bv);
	}
	timeout_add(&sc->sc_mg_to, t_mg);	/* detect minute gap */
	timeout_add(&sc->sc_sl_to, t_sl);	/* detect signal loss */
}

/* detect the bit value */
void
gpiodcf_bv_probe(void *xsc)
{
	struct gpiodcf_softc	*sc = xsc;
	int			 data;

	if (sc->sc_dying)
		return;

	data = gpiodcf_signal(sc);
	if (data == -1) {
		DPRINTF(("bit detection failed\n"));
		return;
	}

	DPRINTFN(1, (data ? "0" : "1"));
	if (!(data))
		sc->sc_tbits |= sc->sc_mask;
	sc->sc_mask <<= 1;
}

/* detect the minute gap */
void
gpiodcf_mg_probe(void *xsc)
{
	struct gpiodcf_softc	*sc = xsc;
	struct clock_ymdhms	 ymdhm;
	struct timeval		 monotime;
	int			 tdiff_recv, tdiff_local;
	int			 skew;
	int			 minute_bits, hour_bits, day_bits;
	int			 month_bits, year_bits, wday;
	int			 p1, p2, p3;
	int			 p1_bit, p2_bit, p3_bit;
	int			 r_bit, a1_bit, a2_bit, z1_bit, z2_bit;
	int			 s_bit, m_bit;
	u_int32_t		 parity = 0x6996;

	if (sc->sc_sync) {
		sc->sc_minute = 1;
		goto cleanbits;
	}

	if (time_second - sc->sc_last_mg < 57) {
		DPRINTF(("\nunexpected gap, resync\n"));
		sc->sc_sync = sc->sc_minute = 1;
		goto cleanbits;
	}

	/* extract bits w/o parity */
	m_bit = sc->sc_tbits & 1;
	r_bit = sc->sc_tbits >> 15 & 1;
	a1_bit = sc->sc_tbits >> 16 & 1;
	z1_bit = sc->sc_tbits >> 17 & 1;
	z2_bit = sc->sc_tbits >> 18 & 1;
	a2_bit = sc->sc_tbits >> 19 & 1;
	s_bit = sc->sc_tbits >> 20 & 1;
	p1_bit = sc->sc_tbits >> 28 & 1;
	p2_bit = sc->sc_tbits >> 35 & 1;
	p3_bit = sc->sc_tbits >> 58 & 1;

	minute_bits = sc->sc_tbits >> 21 & 0x7f;
	hour_bits = sc->sc_tbits >> 29 & 0x3f;
	day_bits = sc->sc_tbits >> 36 & 0x3f;
	wday = (sc->sc_tbits >> 42) & 0x07;
	month_bits = sc->sc_tbits >> 45 & 0x1f;
	year_bits = sc->sc_tbits >> 50 & 0xff;

	/* validate time information */
	p1 = (parity >> (minute_bits & 0x0f) & 1) ^
	    (parity >> (minute_bits >> 4) & 1);

	p2 = (parity >> (hour_bits & 0x0f) & 1) ^
	    (parity >> (hour_bits >> 4) & 1);

	p3 = (parity >> (day_bits & 0x0f) & 1) ^
	    (parity >> (day_bits >> 4) & 1) ^
	    ((parity >> wday) & 1) ^ (parity >> (month_bits & 0x0f) & 1) ^
	    (parity >> (month_bits >> 4) & 1) ^
	    (parity >> (year_bits & 0x0f) & 1) ^
	    (parity >> (year_bits >> 4) & 1);

	if (m_bit == 0 && s_bit == 1 && p1 == p1_bit && p2 == p2_bit &&
	    p3 == p3_bit && (z1_bit ^ z2_bit)) {

		/* Decode time */
		if ((ymdhm.dt_year = 2000 + FROMBCD(year_bits)) > 2037) {
			DPRINTF(("year out of range, resync\n"));
			sc->sc_sync = 1;
			goto cleanbits;
		}
		ymdhm.dt_min = FROMBCD(minute_bits);
		ymdhm.dt_hour = FROMBCD(hour_bits);
		ymdhm.dt_day = FROMBCD(day_bits);
		ymdhm.dt_mon = FROMBCD(month_bits);
		ymdhm.dt_sec = 0;

		sc->sc_next = clock_ymdhms_to_secs(&ymdhm);
		getmicrouptime(&monotime);

		/* convert to coordinated universal time */
		sc->sc_next -= z1_bit ? 7200 : 3600;

		DPRINTF(("\n%02d.%02d.%04d %02d:%02d:00 %s",
		    ymdhm.dt_day, ymdhm.dt_mon, ymdhm.dt_year,
		    ymdhm.dt_hour, ymdhm.dt_min, z1_bit ? "CEST" : "CET"));
		DPRINTF((r_bit ? ", call bit" : ""));
		DPRINTF((a1_bit ? ", dst chg ann." : ""));
		DPRINTF((a2_bit ? ", leap sec ann." : ""));
		DPRINTF(("\n"));

		if (sc->sc_last) {
			tdiff_recv = sc->sc_next - sc->sc_last;
			tdiff_local = monotime.tv_sec - sc->sc_last_tv.tv_sec;
			skew = abs(tdiff_local - tdiff_recv);
#ifdef GPIODCF_DEBUG
			if (sc->sc_skew.status == SENSOR_S_UNKNOWN)
				sc->sc_skew.status = SENSOR_S_CRIT;
			sc->sc_skew.value = skew * 1000000000LL;
			getmicrotime(&sc->sc_skew.tv);
#endif
			DPRINTF(("local = %d, recv = %d, skew = %d\n",
			    tdiff_local, tdiff_recv, skew));

			if (skew && skew * 100LL / tdiff_local > MAX_SKEW) {
				DPRINTF(("skew out of tolerated range\n"));
				goto cleanbits;
			} else {
				if (sc->sc_nrecv < 2) {
					sc->sc_nrecv++;
					DPRINTF(("got frame %d\n",
					    sc->sc_nrecv));
				} else {
					DPRINTF(("data is valid\n"));
					sc->sc_minute = 1;
				}
			}
		} else {
			DPRINTF(("received the first frame\n"));
			sc->sc_nrecv = 1;
		}

		/* record the time received and when it was received */
		sc->sc_last = sc->sc_next;
		sc->sc_last_tv.tv_sec = monotime.tv_sec;
	} else {
		DPRINTF(("\nparity error, resync\n"));
		sc->sc_sync = sc->sc_minute = 1;
	}

cleanbits:
	timeout_add(&sc->sc_to, t_mgsync);	/* re-sync in 450 ms */
	sc->sc_last_mg = time_second;
	sc->sc_tbits = 0LL;
	sc->sc_mask = 1LL;
}

/* detect signal loss */
void
gpiodcf_sl_probe(void *xsc)
{
	struct gpiodcf_softc *sc = xsc;

	if (sc->sc_dying)
		return;

	DPRINTF(("no signal\n"));
	sc->sc_sync = 1;
	timeout_add(&sc->sc_to, t_wait);
	timeout_add(&sc->sc_sl_to, t_wait + t_sl);
}

/* invalidate timedelta (called in an interrupt context) */
void
gpiodcf_invalidate(void *xsc)
{
	struct gpiodcf_softc *sc = xsc;

	if (sc->sc_dying)
		return;

	if (sc->sc_sensor.status == SENSOR_S_OK) {
		sc->sc_sensor.status = SENSOR_S_WARN;
		/*
		 * further degrade in 15 minutes if we dont receive any new
		 * time information
		 */
		timeout_add(&sc->sc_it_to, t_crit);
	} else {
		sc->sc_sensor.status = SENSOR_S_CRIT;
		sc->sc_nrecv = 0;
	}
}

int
gpiodcf_activate(struct device *self, int act)
{
	struct gpiodcf_softc *sc = (struct gpiodcf_softc *)self;

	switch (act) {
	case DVACT_DEACTIVATE:
		sc->sc_dying = 1;
		break;
	}
	return 0;
}


code:
/*	$OpenBSD: gpioiic.c,v 1.10 2013/04/19 23:44:34 miod Exp $	*/

/*
 * Copyright (c) 2006 Alexander Yurchenko <grange@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * I2C bus bit-banging through GPIO pins.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/gpio.h>
#include <sys/rwlock.h>

#include <dev/gpio/gpiovar.h>

#include <dev/i2c/i2cvar.h>
#include <dev/i2c/i2c_bitbang.h>

#define GPIOIIC_PIN_SDA		0
#define GPIOIIC_PIN_SCL		1
#define GPIOIIC_NPINS		2

/* flags */
#define GPIOIIC_PIN_REVERSE	0x01

#define GPIOIIC_SDA		0x01
#define GPIOIIC_SCL		0x02

struct gpioiic_softc {
	struct device		sc_dev;

	void *			sc_gpio;
	struct gpio_pinmap	sc_map;
	int			__map[GPIOIIC_NPINS];

	struct i2c_controller	sc_i2c_tag;
	struct rwlock		sc_i2c_lock;

	int			sc_pin_sda;
	int			sc_pin_scl;

	int			sc_sda;
	int			sc_scl;
};

int		gpioiic_match(struct device *, void *, void *);
void		gpioiic_attach(struct device *, struct device *, void *);
int		gpioiic_detach(struct device *, int);

int		gpioiic_i2c_acquire_bus(void *, int);
void		gpioiic_i2c_release_bus(void *, int);
int		gpioiic_i2c_send_start(void *, int);
int		gpioiic_i2c_send_stop(void *, int);
int		gpioiic_i2c_initiate_xfer(void *, i2c_addr_t, int);
int		gpioiic_i2c_read_byte(void *, u_int8_t *, int);
int		gpioiic_i2c_write_byte(void *, u_int8_t, int);

void		gpioiic_bb_set_bits(void *, u_int32_t);
void		gpioiic_bb_set_dir(void *, u_int32_t);
u_int32_t	gpioiic_bb_read_bits(void *);

struct cfattach gpioiic_ca = {
	sizeof(struct gpioiic_softc),
	gpioiic_match,
	gpioiic_attach,
	gpioiic_detach
};

struct cfdriver gpioiic_cd = {
	NULL, "gpioiic", DV_DULL
};

static const struct i2c_bitbang_ops gpioiic_bbops = {
	gpioiic_bb_set_bits,
	gpioiic_bb_set_dir,
	gpioiic_bb_read_bits,
	{ GPIOIIC_SDA, GPIOIIC_SCL, GPIOIIC_SDA, 0 }
};

int
gpioiic_match(struct device *parent, void *match, void *aux)
{
	struct cfdata *cf = match;
	struct gpio_attach_args *ga = aux;

	if (ga->ga_offset == -1)
		return 0;

	return (strcmp(cf->cf_driver->cd_name, "gpioiic") == 0);
}

void
gpioiic_attach(struct device *parent, struct device *self, void *aux)
{
	struct gpioiic_softc *sc = (struct gpioiic_softc *)self;
	struct gpio_attach_args *ga = aux;
	struct i2cbus_attach_args iba;
	int caps;

	/* Check that we have enough pins */
	if (gpio_npins(ga->ga_mask) != GPIOIIC_NPINS) {
		printf(": invalid pin mask\n");
		return;
	}

	/* Map pins */
	sc->sc_gpio = ga->ga_gpio;
	sc->sc_map.pm_map = sc->__map;
	if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask,
	    &sc->sc_map)) {
		printf(": can't map pins\n");
		return;
	}

	if (ga->ga_flags & GPIOIIC_PIN_REVERSE) {
		sc->sc_pin_sda = GPIOIIC_PIN_SCL;
		sc->sc_pin_scl = GPIOIIC_PIN_SDA;
	} else {
		sc->sc_pin_sda = GPIOIIC_PIN_SDA;
		sc->sc_pin_scl = GPIOIIC_PIN_SCL;
	}

	/* Configure SDA pin */
	caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, sc->sc_pin_sda);
	if (!(caps & GPIO_PIN_OUTPUT)) {
		printf(": SDA pin is unable to drive output\n");
		goto fail;
	}
	if (!(caps & GPIO_PIN_INPUT)) {
		printf(": SDA pin is unable to read input\n");
		goto fail;
	}
	printf(": SDA[%d]", sc->sc_map.pm_map[sc->sc_pin_sda]);
	sc->sc_sda = GPIO_PIN_OUTPUT;
	if (caps & GPIO_PIN_OPENDRAIN) {
		printf(" open-drain");
		sc->sc_sda |= GPIO_PIN_OPENDRAIN;
	} else if ((caps & GPIO_PIN_PUSHPULL) && (caps & GPIO_PIN_TRISTATE)) {
		printf(" push-pull tri-state");
		sc->sc_sda |= GPIO_PIN_PUSHPULL;
	}
	if (caps & GPIO_PIN_PULLUP) {
		printf(" pull-up");
		sc->sc_sda |= GPIO_PIN_PULLUP;
	}
	gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, sc->sc_pin_sda, sc->sc_sda);

	/* Configure SCL pin */
	caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, sc->sc_pin_scl);
	if (!(caps & GPIO_PIN_OUTPUT)) {
		printf(": SCL pin is unable to drive output\n");
		goto fail;
	}
	printf(", SCL[%d]", sc->sc_map.pm_map[sc->sc_pin_scl]);
	sc->sc_scl = GPIO_PIN_OUTPUT;
	if (caps & GPIO_PIN_OPENDRAIN) {
		printf(" open-drain");
		sc->sc_scl |= GPIO_PIN_OPENDRAIN;
		if (caps & GPIO_PIN_PULLUP) {
			printf(" pull-up");
			sc->sc_scl |= GPIO_PIN_PULLUP;
		}
	} else if (caps & GPIO_PIN_PUSHPULL) {
		printf(" push-pull");
		sc->sc_scl |= GPIO_PIN_PUSHPULL;
	}
	gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, sc->sc_pin_scl, sc->sc_scl);

	printf("\n");

	/* Attach I2C bus */
	rw_init(&sc->sc_i2c_lock, "iiclk");
	sc->sc_i2c_tag.ic_cookie = sc;
	sc->sc_i2c_tag.ic_acquire_bus = gpioiic_i2c_acquire_bus;
	sc->sc_i2c_tag.ic_release_bus = gpioiic_i2c_release_bus;
	sc->sc_i2c_tag.ic_send_start = gpioiic_i2c_send_start;
	sc->sc_i2c_tag.ic_send_stop = gpioiic_i2c_send_stop;
	sc->sc_i2c_tag.ic_initiate_xfer = gpioiic_i2c_initiate_xfer;
	sc->sc_i2c_tag.ic_read_byte = gpioiic_i2c_read_byte;
	sc->sc_i2c_tag.ic_write_byte = gpioiic_i2c_write_byte;

	bzero(&iba, sizeof(iba));
	iba.iba_name = "iic";
	iba.iba_tag = &sc->sc_i2c_tag;
	config_found(self, &iba, iicbus_print);

	return;

fail:
	gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
}

int
gpioiic_detach(struct device *self, int flags)
{
	return (0);
}

int
gpioiic_i2c_acquire_bus(void *cookie, int flags)
{
	struct gpioiic_softc *sc = cookie;

	if (cold || (flags & I2C_F_POLL))
		return (0);

	return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
}

void
gpioiic_i2c_release_bus(void *cookie, int flags)
{
	struct gpioiic_softc *sc = cookie;

	if (cold || (flags & I2C_F_POLL))
		return;

	rw_exit(&sc->sc_i2c_lock);
}

int
gpioiic_i2c_send_start(void *cookie, int flags)
{
	return (i2c_bitbang_send_start(cookie, flags, &gpioiic_bbops));
}

int
gpioiic_i2c_send_stop(void *cookie, int flags)
{
	return (i2c_bitbang_send_stop(cookie, flags, &gpioiic_bbops));
}

int
gpioiic_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
{
	return (i2c_bitbang_initiate_xfer(cookie, addr, flags, &gpioiic_bbops));
}

int
gpioiic_i2c_read_byte(void *cookie, u_int8_t *bytep, int flags)
{
	return (i2c_bitbang_read_byte(cookie, bytep, flags, &gpioiic_bbops));
}

int
gpioiic_i2c_write_byte(void *cookie, u_int8_t byte, int flags)
{
	return (i2c_bitbang_write_byte(cookie, byte, flags, &gpioiic_bbops));
}

void
gpioiic_bb_set_bits(void *cookie, u_int32_t bits)
{
	struct gpioiic_softc *sc = cookie;

	gpio_pin_write(sc->sc_gpio, &sc->sc_map, sc->sc_pin_sda,
	    bits & GPIOIIC_SDA ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
	gpio_pin_write(sc->sc_gpio, &sc->sc_map, sc->sc_pin_scl,
	    bits & GPIOIIC_SCL ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
}

void
gpioiic_bb_set_dir(void *cookie, u_int32_t bits)
{
	struct gpioiic_softc *sc = cookie;
	int sda = sc->sc_sda;

	sda &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
	sda |= (bits & GPIOIIC_SDA ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT);
	if ((sda & GPIO_PIN_PUSHPULL) && !(bits & GPIOIIC_SDA))
		sda |= GPIO_PIN_TRISTATE;
	if (sc->sc_sda != sda) {
		sc->sc_sda = sda;
		gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, sc->sc_pin_sda,
		    sc->sc_sda);
	}
}

u_int32_t
gpioiic_bb_read_bits(void *cookie)
{
	struct gpioiic_softc *sc = cookie;
	u_int32_t bits = 0;

	if (gpio_pin_read(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SDA) ==
	    GPIO_PIN_HIGH)
		bits |= GPIOIIC_SDA;
	if (gpio_pin_read(sc->sc_gpio, &sc->sc_map, GPIOIIC_PIN_SCL) ==
	    GPIO_PIN_HIGH)
		bits |= GPIOIIC_SCL;

	return bits;
}


code:
/*	$OpenBSD: gpioow.c,v 1.5 2011/07/03 15:47:16 matthew Exp $	*/

/*
 * Copyright (c) 2006 Alexander Yurchenko <grange@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * 1-Wire bus bit-banging through GPIO pin.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/gpio.h>

#include <dev/gpio/gpiovar.h>

#include <dev/onewire/onewirevar.h>

#define GPIOOW_NPINS		1
#define GPIOOW_PIN_DATA		0

struct gpioow_softc {
	struct device		sc_dev;

	void *			sc_gpio;
	struct gpio_pinmap	sc_map;
	int			__map[GPIOOW_NPINS];

	struct onewire_bus	sc_ow_bus;
	struct device *		sc_ow_dev;

	int			sc_data;
	int			sc_dying;
};

int	gpioow_match(struct device *, void *, void *);
void	gpioow_attach(struct device *, struct device *, void *);
int	gpioow_detach(struct device *, int);
int	gpioow_activate(struct device *, int);

int	gpioow_ow_reset(void *);
int	gpioow_ow_bit(void *, int);

void	gpioow_bb_rx(void *);
void	gpioow_bb_tx(void *);
int	gpioow_bb_get(void *);
void	gpioow_bb_set(void *, int);

struct cfattach gpioow_ca = {
	sizeof(struct gpioow_softc),
	gpioow_match,
	gpioow_attach,
	gpioow_detach,
	gpioow_activate
};

struct cfdriver gpioow_cd = {
	NULL, "gpioow", DV_DULL
};

static const struct onewire_bbops gpioow_bbops = {
	gpioow_bb_rx,
	gpioow_bb_tx,
	gpioow_bb_get,
	gpioow_bb_set
};

int
gpioow_match(struct device *parent, void *match, void *aux)
{
	struct cfdata *cf = match;
	struct gpio_attach_args *ga = aux;

	if (ga->ga_offset == -1)
		return 0;

	return (strcmp(cf->cf_driver->cd_name, "gpioow") == 0);
}

void
gpioow_attach(struct device *parent, struct device *self, void *aux)
{
	struct gpioow_softc *sc = (struct gpioow_softc *)self;
	struct gpio_attach_args *ga = aux;
	struct onewirebus_attach_args oba;
	int caps;

	/* Check that we have enough pins */
	if (gpio_npins(ga->ga_mask) != GPIOOW_NPINS) {
		printf(": invalid pin mask\n");
		return;
	}

	/* Map pins */
	sc->sc_gpio = ga->ga_gpio;
	sc->sc_map.pm_map = sc->__map;
	if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask,
	    &sc->sc_map)) {
		printf(": can't map pins\n");
		return;
	}

	/* Configure data pin */
	caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, GPIOOW_PIN_DATA);
	if (!(caps & GPIO_PIN_OUTPUT)) {
		printf(": data pin is unable to drive output\n");
		goto fail;
	}
	if (!(caps & GPIO_PIN_INPUT)) {
		printf(": data pin is unable to read input\n");
		goto fail;
	}
	printf(": DATA[%d]", sc->sc_map.pm_map[GPIOOW_PIN_DATA]);
	sc->sc_data = GPIO_PIN_OUTPUT;
	if (caps & GPIO_PIN_OPENDRAIN) {
		printf(" open-drain");
		sc->sc_data |= GPIO_PIN_OPENDRAIN;
	} else if ((caps & GPIO_PIN_PUSHPULL) && (caps & GPIO_PIN_TRISTATE)) {
		printf(" push-pull tri-state");
		sc->sc_data |= GPIO_PIN_PUSHPULL;
	}
	if (caps & GPIO_PIN_PULLUP) {
		printf(" pull-up");
		sc->sc_data |= GPIO_PIN_PULLUP;
	}
	gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, GPIOOW_PIN_DATA, sc->sc_data);

	printf("\n");

	/* Attach 1-Wire bus */
	sc->sc_ow_bus.bus_cookie = sc;
	sc->sc_ow_bus.bus_reset = gpioow_ow_reset;
	sc->sc_ow_bus.bus_bit = gpioow_ow_bit;

	bzero(&oba, sizeof(oba));
	oba.oba_bus = &sc->sc_ow_bus;
	sc->sc_ow_dev = config_found(self, &oba, onewirebus_print);

	return;

fail:
	gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
}

int
gpioow_detach(struct device *self, int flags)
{
	struct gpioow_softc *sc = (struct gpioow_softc *)self;
	int rv = 0;

	gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);

	if (sc->sc_ow_dev != NULL)
		rv = config_detach(sc->sc_ow_dev, flags);

	return (rv);
}

int
gpioow_activate(struct device *self, int act)
{
	struct gpioow_softc *sc = (struct gpioow_softc *)self;
	int rv = 0;

	switch (act) {
	case DVACT_DEACTIVATE:
		sc->sc_dying = 1;
		if (sc->sc_ow_dev != NULL)
			rv = config_deactivate(sc->sc_ow_dev);
		break;
	}

	return (rv);
}

int
gpioow_ow_reset(void *arg)
{
	return (onewire_bb_reset(&gpioow_bbops, arg));
}

int
gpioow_ow_bit(void *arg, int value)
{
	return (onewire_bb_bit(&gpioow_bbops, arg, value));
}

void
gpioow_bb_rx(void *arg)
{
	struct gpioow_softc *sc = arg;
	int data = sc->sc_data;

	data &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
	data |= GPIO_PIN_INPUT;
	if (data & GPIO_PIN_PUSHPULL)
		data |= GPIO_PIN_TRISTATE;
	if (sc->sc_data != data) {
		sc->sc_data = data;
		gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, GPIOOW_PIN_DATA,
		    sc->sc_data);
	}
}

void
gpioow_bb_tx(void *arg)
{
	struct gpioow_softc *sc = arg;
	int data = sc->sc_data;

	data &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
	data |= GPIO_PIN_OUTPUT;
	if (sc->sc_data != data) {
		sc->sc_data = data;
		gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, GPIOOW_PIN_DATA,
		    sc->sc_data);
	}
}

int
gpioow_bb_get(void *arg)
{
	struct gpioow_softc *sc = arg;

	return (gpio_pin_read(sc->sc_gpio, &sc->sc_map, GPIOOW_PIN_DATA) ==
	    GPIO_PIN_HIGH ? 1 : 0);
}

void
gpioow_bb_set(void *arg, int value)
{
	struct gpioow_softc *sc = arg;

	gpio_pin_write(sc->sc_gpio, &sc->sc_map, GPIOOW_PIN_DATA,
	    value ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
}


code:
/*      $OpenBSD: gpiosim.c,v 1.1 2008/11/23 18:46:49 mbalmer Exp $	*/

/*
 * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* 32 bit wide GPIO simulator  */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/gpio.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/ioccom.h>

#include <dev/gpio/gpiovar.h>
#include <dev/biovar.h>

#define	GPIOSIM_NPINS	32

struct gpiosim_softc {
	struct device		sc_dev;
	u_int32_t		sc_state;
	struct gpio_chipset_tag	sc_gpio_gc;
	gpio_pin_t		sc_gpio_pins[GPIOSIM_NPINS];
};

struct gpiosim_op {
	void *cookie;
	u_int32_t mask;
	u_int32_t state;
};
#define GPIOSIMREAD	_IOWR('G', 0, struct gpiosim_op)
#define GPIOSIMWRITE	_IOW('G', 1, struct gpiosim_op)

int	gpiosim_match(struct device *, void *, void *);
void	gpiosim_attach(struct device *, struct device *, void *);
int	gpiosim_ioctl(struct device *, u_long cmd, caddr_t data);

int	gpiosim_pin_read(void *, int);
void	gpiosim_pin_write(void *, int, int);
void	gpiosim_pin_ctl(void *, int, int);

struct cfattach gpiosim_ca = {
	sizeof(struct gpiosim_softc), gpiosim_match, gpiosim_attach
};

struct cfdriver gpiosim_cd = {
	NULL, "gpiosim", DV_DULL
};

int
gpiosim_match(struct device *parent, void *match, void *aux)
{
	return 1;
}

void
gpiosim_attach(struct device *parent, struct device *self, void *aux)
{
	struct gpiosim_softc *sc = (void *)self;
	struct gpiobus_attach_args gba;
	int i;

	/* initialize pin array */
	for (i = 0; i < GPIOSIM_NPINS; i++) {
		sc->sc_gpio_pins[i].pin_num = i;
		sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
		    GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN |
		    GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN |
		    GPIO_PIN_INVIN | GPIO_PIN_INVOUT;

		/* read initial state */
		sc->sc_gpio_pins[i].pin_flags = GPIO_PIN_INPUT;
		sc->sc_state = 0;

		/* create controller tag */
		sc->sc_gpio_gc.gp_cookie = sc;
		sc->sc_gpio_gc.gp_pin_read = gpiosim_pin_read;
		sc->sc_gpio_gc.gp_pin_write = gpiosim_pin_write;
		sc->sc_gpio_gc.gp_pin_ctl = gpiosim_pin_ctl;

		gba.gba_name = "gpio";
		gba.gba_gc = &sc->sc_gpio_gc;
		gba.gba_pins = sc->sc_gpio_pins;
		gba.gba_npins = GPIOSIM_NPINS;
	}
	printf("\n");
	config_found(&sc->sc_dev, &gba, gpiobus_print);
	bio_register(&sc->sc_dev, gpiosim_ioctl);
}

int
gpiosim_ioctl(struct device *self, u_long cmd, caddr_t data)
{
	struct gpiosim_softc *sc = (void *)self;
	struct gpiosim_op *op = (void *)data;

	switch (cmd) {
		case GPIOSIMREAD:
			op->state = sc->sc_state;
			break;
		case GPIOSIMWRITE:
			sc->sc_state = (sc->sc_state & ~op->mask) |
			    (op->state & op->mask);
			break;
	}
	return 0;
}

int
gpiosim_pin_read(void *arg, int pin)
{
	struct gpiosim_softc *sc = (struct gpiosim_softc *)arg;

	if (sc->sc_state & (1 << pin))
		return GPIO_PIN_HIGH;
	else
		return GPIO_PIN_LOW;
}

void
gpiosim_pin_write(void *arg, int pin, int value)
{
	struct gpiosim_softc *sc = (struct gpiosim_softc *)arg;

	if (value == 0)
		sc->sc_state &= ~(1 << pin);
	else
		sc->sc_state |= (1 << pin);
}

void
gpiosim_pin_ctl(void *arg, int pin, int flags)
{
	struct gpiosim_softc *sc = (struct gpiosim_softc *)arg;

	sc->sc_gpio_pins[pin].pin_flags = flags;
}


code:
/*	$OpenBSD: gpiovar.h,v 1.6 2011/10/03 20:24:51 matthieu Exp $	*/

/*
 * Copyright (c) 2004, 2006 Alexander Yurchenko <grange@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _DEV_GPIO_GPIOVAR_H_
#define _DEV_GPIO_GPIOVAR_H_

/* GPIO controller description */
typedef struct gpio_chipset_tag {
	void	*gp_cookie;

	int	(*gp_pin_read)(void *, int);
	void	(*gp_pin_write)(void *, int, int);
	void	(*gp_pin_ctl)(void *, int, int);
} *gpio_chipset_tag_t;

/* GPIO pin description */
typedef struct gpio_pin {
	int	pin_num;		/* number */
	int	pin_caps;		/* capabilities */
	int	pin_flags;		/* current configuration */
	int	pin_state;		/* current state */
	int	pin_mapped;		/* is mapped */
} gpio_pin_t;

/* Attach GPIO framework to the controller */
struct gpiobus_attach_args {
	const char		*gba_name;	/* bus name */
	gpio_chipset_tag_t	gba_gc;		/* underlying controller */
	gpio_pin_t		*gba_pins;	/* pins array */
	int			gba_npins;	/* total number of pins */
};

int gpiobus_print(void *, const char *);

/* GPIO framework private methods */
#define gpiobus_pin_read(gc, pin) \
    ((gc)->gp_pin_read((gc)->gp_cookie, (pin)))
#define gpiobus_pin_write(gc, pin, value) \
    ((gc)->gp_pin_write((gc)->gp_cookie, (pin), (value)))
#define gpiobus_pin_ctl(gc, pin, flags) \
    ((gc)->gp_pin_ctl((gc)->gp_cookie, (pin), (flags)))

/* Attach devices connected to the GPIO pins */
struct gpio_attach_args {
	void *			ga_gpio;
	int			ga_offset;
	u_int32_t		ga_mask;
	char			*ga_dvname;
	u_int32_t		ga_flags;
};

/* GPIO pin map */
struct gpio_pinmap {
	int *	pm_map;			/* pin map */
	int	pm_size;		/* map size */
};

struct gpio_dev {
	struct device		*sc_dev;	/* the gpio device */
	LIST_ENTRY(gpio_dev)	 sc_next;
};

struct gpio_name {
	char			gp_name[GPIOPINMAXNAME];
	int			gp_pin;
	LIST_ENTRY(gpio_name)	gp_next;
};

int	gpio_pin_map(void *, int, u_int32_t, struct gpio_pinmap *);
void	gpio_pin_unmap(void *, struct gpio_pinmap *);
int	gpio_pin_read(void *, struct gpio_pinmap *, int);
void	gpio_pin_write(void *, struct gpio_pinmap *, int, int);
void	gpio_pin_ctl(void *, struct gpio_pinmap *, int, int);
int	gpio_pin_caps(void *, struct gpio_pinmap *, int);

int	gpio_npins(u_int32_t);

#endif	/* !_DEV_GPIO_GPIOVAR_H_ */


code: src/sys/dev/hil

path: sys/dev/hil/CVS/Repository


code:
#	$OpenBSD: Makefile,v 1.1 2003/02/11 19:39:30 miod Exp $

AWK=	awk
PROG=	devlist2h.awk
SOURCE=	hildevs

${SOURCE}_data.h: ${SOURCE} ${PROG}
	/bin/rm -f $@
	${AWK} -f ${PROG} ${SOURCE} > $@

path: sys/dev/hil/Makefile


code:
#! /usr/bin/awk -f
#	$OpenBSD: devlist2h.awk,v 1.4 2006/08/10 23:44:16 miod Exp $
#
# Copyright (c) 2003, Miodrag Vallat.
# 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.
#
# 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.
#
BEGIN {
	header = 0
}
NR == 1 {
	VERSION = $0
	gsub("\\$", "", VERSION)

	printf("/*\t\$OpenBSD\$\t*/\n\n")
	printf("/*\n")
	printf(" * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.\n")
	printf(" *\n")
	printf(" * generated from:\n")
	printf(" *\t%s\n", VERSION)
	printf(" */\n")

	next
}
$1 == "keyboard" || $1 == "mouse" || $1 == "idmodule" || $1 == "buttonbox" {

	if (header == 0) {
		printf("const struct hildevice hildevs[] = {\n")
		header = 1
	}

	printf("\t{ 0x%s, 0x%s, HIL_DEVICE_%s, \"",
	    $2, $3, toupper($1))

	# description, with optional ``#''-prefixed comments
	comment = 0
	i = 4
	f = i
	while (f <= NF) {
		if ($f == "#") {
			comment = 1
			printf ("\" },\t/*")
		} else {
			if (f > i)
				printf(" ")
			printf("%s", $f)
		}
		f++
	}
	if (comment)
		printf(" */\n");
	else
		printf("\" },\n")

	next
}
{
	if ($0 == "")
		blanklines++
	if (blanklines < 2)
		print $0
}
END {
	printf("\t{ -1, -1, -1, NULL }\n")
	printf("};\n")
}


code:
#	$OpenBSD: files.hil,v 1.6 2006/08/10 23:41:16 miod Exp $
#
# Configuration file for machine-independent HIL code.
#

device	hil {[code = -1]}
file	dev/hil/hil.c		hil

device	hilkbd: wskbddev
attach	hilkbd at hil
file	dev/hil/hilkbd.c	hilkbd			needs-flag
file	dev/hil/hilkbdmap.c	hilkbd

device	hilms: wsmousedev
attach	hilms at hil
file	dev/hil/hilms.c		hilms

device	hilid
attach	hilid at hil
file	dev/hil/hilid.c		hilid


code:
/*	$OpenBSD: hil.c,v 1.26 2015/05/15 13:32:08 jsg Exp $	*/
/*
 * Copyright (c) 2003, 2004, Miodrag Vallat.
 * 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.
 *
 * 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.
 *
 */

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * 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. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
 *
 * from: Utah $Hdr: hil.c 1.38 92/01/21$
 *
 *	@(#)hil.c	8.2 (Berkeley) 1/12/94
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/kthread.h>

#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/cpu.h>

#include <dev/hil/hilreg.h>
#include <dev/hil/hilvar.h>
#include <dev/hil/hildevs.h>
#include <dev/hil/hildevs_data.h>

#include "hilkbd.h"

/*
 * splhigh is extremely conservative but insures atomic operation,
 * splvm (clock only interrupts) seems to be good enough in practice.
 */
#define	splhil	splvm

struct cfdriver hil_cd = {
	NULL, "hil", DV_DULL
};

void	hilconfig(struct hil_softc *, u_int);
void	hilempty(struct hil_softc *);
int	hilsubmatch(struct device *, void *, void *);
void	hil_process_int(struct hil_softc *, u_int8_t, u_int8_t);
int	hil_process_poll(struct hil_softc *, u_int8_t, u_int8_t);
void	hil_thread(void *);
int	send_device_cmd(struct hil_softc *sc, u_int device, u_int cmd);
void	polloff(struct hil_softc *);
void	pollon(struct hil_softc *);

static int hilwait(struct hil_softc *);
static int hildatawait(struct hil_softc *);

#define	hil_process_pending(sc)	wakeup(&(sc)->sc_pending)

static __inline int
hilwait(struct hil_softc *sc)
{
	int cnt;

	for (cnt = 50000; cnt != 0; cnt--) {
		DELAY(1);
		if ((bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT) &
		    HIL_BUSY) == 0)
			break;
	}

	return (cnt);
}

static __inline int
hildatawait(struct hil_softc *sc)
{
	int cnt;

	for (cnt = 50000; cnt != 0; cnt--) {
		DELAY(1);
		if ((bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT) &
		    HIL_DATA_RDY) != 0)
			break;
	}

	return (cnt);
}

/*
 * Common HIL bus attachment
 */

void
hil_attach(struct hil_softc *sc, int *hil_is_console)
{
	printf("\n");

	/*
	 * Initialize loop information
	 */
	sc->sc_cmdending = 0;
	sc->sc_actdev = sc->sc_cmddev = 0;
	sc->sc_cmddone = 0;
	sc->sc_cmdbp = sc->sc_cmdbuf;
	sc->sc_pollbp = sc->sc_pollbuf;
	sc->sc_console = hil_is_console;
}

/*
 * HIL subdevice attachment
 */

int
hildevprint(void *aux, const char *pnp)
{
	struct hil_attach_args *ha = aux;

	if (pnp != NULL) {
		printf("\"%s\" at %s id %x",
		    ha->ha_descr, pnp, ha->ha_id);
	}
	printf(" code %d", ha->ha_code);
	if (pnp == NULL) {
		printf(": %s", ha->ha_descr);
	}

	return (UNCONF);
}

int
hilsubmatch(struct device *parent, void *vcf, void *aux)
{
	struct hil_attach_args *ha = aux;
	struct cfdata *cf = vcf;

	if (cf->cf_loc[0] != -1 &&
	    cf->cf_loc[0] != ha->ha_code)
		return (0);

	return ((*cf->cf_attach->ca_match)(parent, vcf, aux));
}

void
hil_attach_deferred(void *v)
{
	struct hil_softc *sc = v;
	int tries;
	u_int8_t db;

	sc->sc_status = HIL_STATUS_BUSY;

	/*
	 * Initialize the loop: reconfigure, don't report errors,
	 * put keyboard in cooked mode, and enable autopolling.
	 */
	db = LPC_RECONF | LPC_KBDCOOK | LPC_NOERROR | LPC_AUTOPOLL;
	send_hil_cmd(sc, HIL_WRITELPCTRL, &db, 1, NULL);

	/*
	 * Delay one second for reconfiguration and then read the
	 * data to clear the interrupt (if the loop reconfigured).
	 */
	DELAY(1000000);
	if (bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT) &
	    HIL_DATA_RDY) {
		db = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
		DELAY(1);
	}

	/*
	 * The HIL loop may have reconfigured.  If so we proceed on,
	 * if not we loop a few times until a successful reconfiguration
	 * is reported back to us. If the HIL loop is still lost after a
	 * few seconds, give up.
	 */
	for (tries = 10; tries != 0; tries--) {
		if (send_hil_cmd(sc, HIL_READLPSTAT, NULL, 0, &db) == 0) {
			if (db & (LPS_CONFFAIL | LPS_CONFGOOD))
				break;
		}

#ifdef HILDEBUG
		printf("%s: loop not ready, retrying...\n",
		    sc->sc_dev.dv_xname);
#endif

		DELAY(1000000);
        }

	if (tries == 0 || (db & LPS_CONFFAIL)) {
		printf("%s: no devices\n", sc->sc_dev.dv_xname);
		sc->sc_pending = 0;
		if (tries == 0)
			return;
	}

	/*
	 * Create asynchronous loop event handler thread.
	 */
	if (kthread_create(hil_thread, sc, &sc->sc_thread,
	    sc->sc_dev.dv_xname) != 0) {
		printf("%s: unable to create event thread\n",
		    sc->sc_dev.dv_xname);
		return;
	}

	/*
	 * Enable loop interrupts.
	 */
	send_hil_cmd(sc, HIL_INTON, NULL, 0, NULL);

	/*
	 * Reconfigure if necessary
	 */
	sc->sc_status = HIL_STATUS_READY;
	hil_process_pending(sc);
}

/*
 * Asynchronous event processing
 */

int
hil_intr(void *v)
{
	struct hil_softc *sc = v;
	u_int8_t c, stat;

	if (cold)
		return (0);

	stat = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT);

	/*
	 * This should never happen if the interrupt comes from the
	 * loop.
	 */
	if ((stat & HIL_DATA_RDY) == 0)
		return (0);	/* not for us */

	c = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
	    HILP_DATA);	/* clears interrupt */
	DELAY(1);

	hil_process_int(sc, stat, c);

	if (sc->sc_status != HIL_STATUS_BUSY)
		hil_process_pending(sc);

	return (1);
}

void
hil_process_int(struct hil_softc *sc, u_int8_t stat, u_int8_t c)
{
	struct hildev_softc *dev;

	switch ((stat >> HIL_SSHIFT) & HIL_SMASK) {
	case HIL_STATUS:
		if (c & HIL_ERROR) {
		  	sc->sc_cmddone = 1;
			switch (c) {
			case HIL_RECONFIG:
				sc->sc_pending = HIL_PENDING_RECONFIG;
				break;
			case HIL_UNPLUGGED:
				sc->sc_pending = HIL_PENDING_UNPLUGGED;
				break;
			}
			break;
		}
		if (c & HIL_COMMAND) {
		  	if (c & HIL_POLLDATA) {	/* End of data */
				dev = sc->sc_devices[sc->sc_actdev];
				if (dev != NULL && dev->sc_fn != NULL)
					dev->sc_fn(dev,
					    sc->sc_pollbp - sc->sc_pollbuf,
					    sc->sc_pollbuf);
			} else {		/* End of command */
			  	sc->sc_cmdending = 1;
			}
			sc->sc_actdev = 0;
		} else {
		  	if (c & HIL_POLLDATA) {	/* Start of polled data */
				sc->sc_actdev = (c & HIL_DEVMASK);
				sc->sc_pollbp = sc->sc_pollbuf;
			} else {		/* Start of command */
				if (sc->sc_cmddev == (c & HIL_DEVMASK)) {
					sc->sc_cmdbp = sc->sc_cmdbuf;
					sc->sc_actdev = 0;
				}
			}
		}
	        break;
	case HIL_DATA:
		if (sc->sc_actdev != 0)	/* Collecting poll data */
			*sc->sc_pollbp++ = c;
		else {
			if (sc->sc_cmddev != 0) {  /* Collecting cmd data */
				if (sc->sc_cmdending) {
					sc->sc_cmddone = 1;
					sc->sc_cmdending = 0;
				} else
					*sc->sc_cmdbp++ = c;
		        }
		}
		break;
	}
}

/*
 * Same as above, but in polled mode: return data as it gets seen, instead
 * of buffering it.
 */
int
hil_process_poll(struct hil_softc *sc, u_int8_t stat, u_int8_t c)
{
	u_int8_t db;

	switch ((stat >> HIL_SSHIFT) & HIL_SMASK) {
	case HIL_STATUS:
		if (c & HIL_ERROR) {
		  	sc->sc_cmddone = 1;
			switch (c) {
			case HIL_RECONFIG:
				/*
				 * Remember that a configuration event
				 * occurred; it will be processed upon
				 * leaving polled mode...
				 */
				sc->sc_pending = HIL_PENDING_RECONFIG;
				/*
				 * However, the keyboard will come back as
				 * cooked, and we rely on it being in raw
				 * mode. So, put it back in raw mode right
				 * now.
				 */
				db = 0;
				send_hil_cmd(sc, HIL_WRITEKBDSADR, &db,
				    1, NULL);
				break;
			case HIL_UNPLUGGED:
				/*
				 * Remember that an unplugged event
				 * occured; it will be processed upon
				 * leaving polled mode...
				 */
				sc->sc_pending = HIL_PENDING_UNPLUGGED;
				break;
			}
			break;
		}
		if (c & HIL_COMMAND) {
		  	if (!(c & HIL_POLLDATA)) {
				/* End of command */
			  	sc->sc_cmdending = 1;
			}
			sc->sc_actdev = 0;
		} else {
		  	if (c & HIL_POLLDATA) {
				/* Start of polled data */
				sc->sc_actdev = (c & HIL_DEVMASK);
				sc->sc_pollbp = sc->sc_pollbuf;
			} else {
				/* Start of command - should not happen */
				if (sc->sc_cmddev == (c & HIL_DEVMASK)) {
					sc->sc_cmdbp = sc->sc_cmdbuf;
					sc->sc_actdev = 0;
				}
			}
		}
	        break;
	case HIL_DATA:
		if (sc->sc_actdev != 0)	/* Collecting poll data */
			return 1;
		else {
			if (sc->sc_cmddev != 0) {  /* Discarding cmd data */
				if (sc->sc_cmdending) {
					sc->sc_cmddone = 1;
					sc->sc_cmdending = 0;
				}
		        }
		}
		break;
	}

	return 0;
}

void
hil_thread(void *arg)
{
	struct hil_softc *sc = arg;
	int s;

	for (;;) {
		s = splhil();
		if (sc->sc_pending == 0) {
			splx(s);
			(void)tsleep(&sc->sc_pending, PWAIT, "hil_event", 0);
			continue;
		}

		switch (sc->sc_pending) {
		case HIL_PENDING_RECONFIG:
			sc->sc_pending = 0;
			hilconfig(sc, sc->sc_maxdev);
			break;
		case HIL_PENDING_UNPLUGGED:
			sc->sc_pending = 0;
			hilempty(sc);
			break;
		}
		splx(s);
	}
}

/*
 * Called after the loop has reconfigured.  Here we need to:
 *	- determine how many devices are on the loop
 *	  (some may have been added or removed)
 *	- make sure all keyboards are in raw mode
 *
 * Note that our device state is now potentially invalid as
 * devices may no longer be where they were.  What we should
 * do here is either track where the devices went and move
 * state around accordingly...
 *
 * Note that it is necessary that we operate the loop with the keyboards
 * in raw mode: they won't cause the loop to generate an NMI if the
 * ``reset'' key combination is pressed, and we do not handle the hil
 * NMI interrupt...
 */
void
hilconfig(struct hil_softc *sc, u_int knowndevs)
{
	struct hil_attach_args ha;
	u_int8_t db;
	int id, s;

	s = splhil();

	/*
	 * Determine how many devices are on the loop.
	 */
	db = 0;
	send_hil_cmd(sc, HIL_READLPSTAT, NULL, 0, &db);
	sc->sc_maxdev = db & LPS_DEVMASK;
#ifdef HILDEBUG
	printf("%s: %d device(s)\n", sc->sc_dev.dv_xname, sc->sc_maxdev);
#endif

	/*
	 * Put all keyboards in raw mode now.
	 */
	db = 0;
	send_hil_cmd(sc, HIL_WRITEKBDSADR, &db, 1, NULL);

	/*
	 * If the loop grew, attach new devices.
	 */
	for (id = knowndevs + 1; id <= sc->sc_maxdev; id++) {
		int len;
		const struct hildevice *hd;

		if (send_device_cmd(sc, id, HIL_IDENTIFY) != 0) {
			printf("%s: no answer from device %d\n",
			    sc->sc_dev.dv_xname, id);
			continue;
		}

		len = sc->sc_cmdbp - sc->sc_cmdbuf;
		if (len == 0) {
#ifdef HILDEBUG
			printf("%s: no device at code %d\n",
			    sc->sc_dev.dv_xname, id);
#endif
			continue;
		}

		/* Identify and attach device */
		for (hd = hildevs; hd->minid >= 0; hd++)
			if (sc->sc_cmdbuf[0] >= hd->minid &&
			    sc->sc_cmdbuf[0] <= hd->maxid) {

			ha.ha_console = *sc->sc_console;
			ha.ha_code = id;
			ha.ha_type = hd->type;
			ha.ha_descr = hd->descr;
			ha.ha_infolen = len;
			bcopy(sc->sc_cmdbuf, ha.ha_info, len);

			sc->sc_devices[id] = (struct hildev_softc *)
			    config_found_sm(&sc->sc_dev, &ha, hildevprint,
			        hilsubmatch);

#if NHILKBD > 0
			/*
			 * If we just attached a keyboard as console,
			 * console choice is not indeterminate anymore.
			 */
			if (sc->sc_devices[id] != NULL &&
			    ha.ha_type == HIL_DEVICE_KEYBOARD &&
			    ha.ha_console != 0)
				*sc->sc_console = 1;
#endif
		}
	}

	/*
	 * Detach remaining devices, if the loop has shrunk.
	 */
	for (id = sc->sc_maxdev + 1; id < NHILD; id++) {
		if (sc->sc_devices[id] != NULL)
			config_detach((struct device *)sc->sc_devices[id],
			    DETACH_FORCE);
		sc->sc_devices[id] = NULL;
	}

	sc->sc_cmdbp = sc->sc_cmdbuf;

	splx(s);
}

/*
 * Called after the loop has been unplugged. We simply force detach of
 * all our children.
 */
void
hilempty(struct hil_softc *sc)
{
	u_int8_t db;
	int id, s;
	u_int oldmaxdev;

	s = splhil();

	/*
	 * Wait for the loop to be stable.
	 */
	for (;;) {
		if (send_hil_cmd(sc, HIL_READLPSTAT, NULL, 0, &db) == 0) {
			if (db & (LPS_CONFFAIL | LPS_CONFGOOD))
				break;
		} else {
			db = LPS_CONFFAIL;
			break;
		}
	}

	if (db & LPS_CONFFAIL) {
		sc->sc_maxdev = 0;
	} else {
		db = 0;
		send_hil_cmd(sc, HIL_READLPSTAT, NULL, 0, &db);
		oldmaxdev = sc->sc_maxdev;
		sc->sc_maxdev = db & LPS_DEVMASK;

		if (sc->sc_maxdev != 0) {
			/*
			 * The loop was not unplugged after all, but its
			 * configuration has changed.
			 */
			hilconfig(sc, oldmaxdev);
			splx(s);
			return;
		}
	}

	/*
	 * Now detach all hil devices.
	 */
	for (id = sc->sc_maxdev + 1; id < NHILD; id++) {
		if (sc->sc_devices[id] != NULL)
			config_detach((struct device *)sc->sc_devices[id],
			    DETACH_FORCE);
		sc->sc_devices[id] = NULL;
	}

	sc->sc_cmdbp = sc->sc_cmdbuf;

	splx(s);
}

/*
 * Low level routines which actually talk to the 8042 chip.
 */

/*
 * Send a command to the 8042 with zero or more bytes of data.
 * If rdata is non-null, wait for and return a byte of data.
 */
int
send_hil_cmd(struct hil_softc *sc, u_int cmd, u_int8_t *data, u_int dlen,
    u_int8_t *rdata)
{
	u_int8_t status;
	int s;

	s = splhil();

	if (hilwait(sc) == 0) {
#ifdef HILDEBUG
		printf("%s: no answer from the loop\n", sc->sc_dev.dv_xname);
#endif
		splx(s);
		return (EBUSY);
	}

	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, cmd);
	while (dlen--) {
	  	hilwait(sc);
		bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, *data++);
		DELAY(1);
	}
	if (rdata) {
		do {
			if (hildatawait(sc) == 0) {
#ifdef HILDEBUG
				printf("%s: no answer from the loop\n",
				    sc->sc_dev.dv_xname);
#endif
				break;
			}
			status = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
			    HILP_STAT);
			*rdata = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
			    HILP_DATA);
			DELAY(1);
		} while (((status >> HIL_SSHIFT) & HIL_SMASK) != HIL_68K);
	}
	splx(s);
	return (0);
}

/*
 * Send a command to a device on the loop.
 * Since only one command can be active on the loop at any time,
 * we must ensure that we are not interrupted during this process.
 * Hence we mask interrupts to prevent potential access from most
 * interrupt routines and turn off auto-polling to disable the
 * internally generated poll commands.
 * Needs to be called at splhil().
 */
int
send_device_cmd(struct hil_softc *sc, u_int device, u_int cmd)
{
	u_int8_t status, c;
	int rc = 0;

	polloff(sc);

	sc->sc_cmdbp = sc->sc_cmdbuf;
	sc->sc_cmddev = device;

	if (hilwait(sc) == 0) {
#ifdef HILDEBUG
		printf("%s: no answer from device %d\n",
		    sc->sc_dev.dv_xname, device);
#endif
		rc = EBUSY;
		goto out;
	}

	/*
	 * Transfer the command and device info to the chip
	 */
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_STARTCMD);
  	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, 8 + device);
  	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, cmd);
  	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, HIL_TIMEOUT);

	/*
	 * Trigger the command and wait for completion
	 */
	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_TRIGGER);
	sc->sc_cmddone = 0;
	do {
		if (hildatawait(sc) == 0) {
#ifdef HILDEBUG
			printf("%s: no answer from device %d\n",
			    sc->sc_dev.dv_xname, device);
#endif
			rc = EBUSY;
			break;
		}
		status = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT);
		c = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
		DELAY(1);
		hil_process_int(sc, status, c);
	} while (sc->sc_cmddone == 0);
out:
	sc->sc_cmddev = 0;

	pollon(sc);
	return (rc);
}

int
send_hildev_cmd(struct hildev_softc *dev, u_int cmd,
    u_int8_t *outbuf, u_int *outlen)
{
	struct hil_softc *sc = (struct hil_softc *)dev->sc_dev.dv_parent;
	int s, rc;

	s = splhil();

	if ((rc = send_device_cmd(sc, dev->sc_code, cmd)) == 0) {
		/*
		 * Return the command response in the buffer if necessary
	 	*/
		if (outbuf != NULL && outlen != NULL) {
			*outlen = min(*outlen, sc->sc_cmdbp - sc->sc_cmdbuf);
			bcopy(sc->sc_cmdbuf, outbuf, *outlen);
		}
	}

	splx(s);
	return (rc);
}

/*
 * Turn auto-polling off and on.
 */
void
polloff(struct hil_softc *sc)
{
	u_int8_t db;

	if (hilwait(sc) == 0)
		return;

	/*
	 * Turn off auto repeat
	 */
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_SETARR);
	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, 0);

	/*
	 * Turn off auto-polling
	 */
	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_READLPCTRL);
	hildatawait(sc);
	db = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
	db &= ~LPC_AUTOPOLL;
	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_WRITELPCTRL);
	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, db);

	/*
	 * Must wait until polling is really stopped
	 */
	do {
		hilwait(sc);
		bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_READBUSY);
		hildatawait(sc);
		db = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
	} while (db & BSY_LOOPBUSY);

	sc->sc_cmddone = 0;
	sc->sc_cmddev = 0;
}

void
pollon(struct hil_softc *sc)
{
	u_int8_t db;

	if (hilwait(sc) == 0)
		return;

	/*
	 * Turn on auto polling
	 */
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_READLPCTRL);
	hildatawait(sc);
	db = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
	db |= LPC_AUTOPOLL;
	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_WRITELPCTRL);
	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, db);

	/*
	 * Turn off auto repeat - we emulate this through wscons
	 */
	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_CMD, HIL_SETARR);
	hilwait(sc);
	bus_space_write_1(sc->sc_bst, sc->sc_bsh, HILP_DATA, 0);
	DELAY(1);
}

void
hil_set_poll(struct hil_softc *sc, int on)
{
	if (on) {
		pollon(sc);
	} else {
		hil_process_pending(sc);
		send_hil_cmd(sc, HIL_INTON, NULL, 0, NULL);
	}
}

int
hil_poll_data(struct hildev_softc *dev, u_int8_t *stat, u_int8_t *data)
{
	struct hil_softc *sc = (struct hil_softc *)dev->sc_dev.dv_parent;
	u_int8_t s, c;

	s = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_STAT);
	if ((s & HIL_DATA_RDY) == 0)
		return -1;

	c = bus_space_read_1(sc->sc_bst, sc->sc_bsh, HILP_DATA);
	DELAY(1);

	if (hil_process_poll(sc, s, c)) {
		/* Discard any data not for us */
		if (sc->sc_actdev == dev->sc_code) {
			*stat = s;
			*data = c;
			return 0;
		}
	}

	return -1;
}


code:
$OpenBSD: hildevs,v 1.3 2006/08/10 23:44:16 miod Exp $
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * 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.
 *
 * 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.
 *
 */

buttonbox	00	1f	Keypad
buttonbox	2f	2f	LPFK Button box
buttonbox	30	33	Button box	# 31-33 rumored not to exist
idmodule	34	34	ID module
buttonbox	35	3f	Button box
buttonbox	5c	5f	Barcode reader
mouse		60	60	Single knob
mouse		61	61	Nine knob	# can also be quadrature
mouse		62	67	Quadrature
mouse		68	6b	Mouse
mouse		6c	6f	Trackball
mouse		70	70	Knob box
mouse		71	71	Spaceball
mouse		88	8b	Touchpad
mouse		8c	8f	Touchscreen
mouse		90	97	Tablet
mouse		98	98	MMII 1812 Tablet
mouse		99	99	MMII 1201 Tablet
keyboard	a0	bf	93-key keyboard
keyboard	c0	df	109-key keyboard
keyboard	e0	ff	87-key keyboard

path: sys/dev/hil/hildevs


code:
/*	$OpenBSD: hildevs.h,v 1.4 2005/05/13 14:54:44 miod Exp $	*/
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * 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.
 *
 * 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.
 *
 */

/* Entries in hildevs_data.h for device probe */
struct	hildevice {
	int		minid;
	int		maxid;
	int		type;
	const char	*descr;
};

/* Arguments passed to attach routines */
struct hil_attach_args {
	int		ha_code;	/* hil code */
	int		ha_type;	/* hil device type */
	int		ha_console;	/* console set to hil */
	int		ha_infolen;	/* identify info length */
	u_int8_t	ha_info[HILBUFSIZE];	/* identify info bits */
#define	ha_id		ha_info[0]	/* hil probe id */

	const char	*ha_descr;	/* device description */
};

/* ha_type values */
#define	HIL_DEVICE_KEYBOARD	1
#define	HIL_DEVICE_IDMODULE	2
#define	HIL_DEVICE_MOUSE	3
#define	HIL_DEVICE_BUTTONBOX	4

/* Common softc part for hil devices */
struct hildev_softc {
	struct device	sc_dev;

	int		sc_code;	/* hil code */
	int		sc_type;	/* hil device type */

	int		sc_infolen;	/* identify info length */
	u_int8_t	sc_info[HILBUFSIZE];	/* identify info bits */

	void		(*sc_fn)(struct hildev_softc *, u_int, u_int8_t *);
};

#define	hd_code		sc_hildev.sc_code
#define	hd_fn		sc_hildev.sc_fn
#define	hd_info		sc_hildev.sc_info
#define	hd_infolen	sc_hildev.sc_infolen
#define	hd_parent	sc_hildev.sc_dev.dv_parent
#define	hd_type		sc_hildev.sc_type


code:
/*	$OpenBSD: hildevs_data.h,v 1.4 2006/08/10 23:48:06 miod Exp $	*/

/*
 * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.
 *
 * generated from:
 *	OpenBSD: hildevs,v 1.3 2006/08/10 23:44:16 miod Exp
 */
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * 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.
 *
 * 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.
 *
 */

const struct hildevice hildevs[] = {
	{ 0x00, 0x1f, HIL_DEVICE_BUTTONBOX, "Keypad" },
	{ 0x2f, 0x2f, HIL_DEVICE_BUTTONBOX, "LPFK Button box" },
	{ 0x30, 0x33, HIL_DEVICE_BUTTONBOX, "Button box" },	/* 31-33 rumored not to exist */
	{ 0x34, 0x34, HIL_DEVICE_IDMODULE, "ID module" },
	{ 0x35, 0x3f, HIL_DEVICE_BUTTONBOX, "Button box" },
	{ 0x5c, 0x5f, HIL_DEVICE_BUTTONBOX, "Barcode reader" },
	{ 0x60, 0x60, HIL_DEVICE_MOUSE, "Single knob" },
	{ 0x61, 0x61, HIL_DEVICE_MOUSE, "Nine knob" },	/* can also be quadrature */
	{ 0x62, 0x67, HIL_DEVICE_MOUSE, "Quadrature" },
	{ 0x68, 0x6b, HIL_DEVICE_MOUSE, "Mouse" },
	{ 0x6c, 0x6f, HIL_DEVICE_MOUSE, "Trackball" },
	{ 0x70, 0x70, HIL_DEVICE_MOUSE, "Knob box" },
	{ 0x71, 0x71, HIL_DEVICE_MOUSE, "Spaceball" },
	{ 0x88, 0x8b, HIL_DEVICE_MOUSE, "Touchpad" },
	{ 0x8c, 0x8f, HIL_DEVICE_MOUSE, "Touchscreen" },
	{ 0x90, 0x97, HIL_DEVICE_MOUSE, "Tablet" },
	{ 0x98, 0x98, HIL_DEVICE_MOUSE, "MMII 1812 Tablet" },
	{ 0x99, 0x99, HIL_DEVICE_MOUSE, "MMII 1201 Tablet" },
	{ 0xa0, 0xbf, HIL_DEVICE_KEYBOARD, "93-key keyboard" },
	{ 0xc0, 0xdf, HIL_DEVICE_KEYBOARD, "109-key keyboard" },
	{ 0xe0, 0xff, HIL_DEVICE_KEYBOARD, "87-key keyboard" },
	{ -1, -1, -1, NULL }
};


code:
/*	$OpenBSD: hilid.c,v 1.4 2005/01/09 23:49:36 miod Exp $	*/
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * 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.
 *
 * 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 <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/ioctl.h>

#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/cpu.h>

#include <dev/hil/hilreg.h>
#include <dev/hil/hilvar.h>
#include <dev/hil/hildevs.h>

struct hilid_softc {
	struct hildev_softc sc_hildev;

	u_int8_t	sc_id[16];
};

int	hilidprobe(struct device *, void *, void *);
void	hilidattach(struct device *, struct device *, void *);
int	hiliddetach(struct device *, int);

struct cfdriver hilid_cd = {
	NULL, "hilid", DV_DULL
};

struct cfattach hilid_ca = {
	sizeof(struct hilid_softc), hilidprobe, hilidattach, hiliddetach,
};

int
hilidprobe(struct device *parent, void *match, void *aux)
{
	struct hil_attach_args *ha = aux;

	if (ha->ha_type != HIL_DEVICE_IDMODULE)
		return (0);

	return (1);
}

void
hilidattach(struct device *parent, struct device *self, void *aux)
{
	struct hilid_softc *sc = (void *)self;
	struct hil_attach_args *ha = aux;
	u_int i, len;

	sc->hd_code = ha->ha_code;
	sc->hd_type = ha->ha_type;
	sc->hd_infolen = ha->ha_infolen;
	bcopy(ha->ha_info, sc->hd_info, ha->ha_infolen);
	sc->hd_fn = NULL;

	printf("\n");

	bzero(sc->sc_id, sizeof(sc->sc_id));
	len = sizeof(sc->sc_id);
	printf("%s: security code", self->dv_xname);

	if (send_hildev_cmd((struct hildev_softc *)sc,
	    HIL_SECURITY, sc->sc_id, &len) == 0) {
		for (i = 0; i < sizeof(sc->sc_id); i++)
			printf(" %02x", sc->sc_id[i]);
		printf("\n");
	} else
		printf(" unavailable\n");
}

int
hiliddetach(struct device *self, int flags)
{
	return (0);
}


code:
/*	$OpenBSD: hilkbd.c,v 1.16 2014/01/26 17:48:08 miod Exp $	*/
/*
 * Copyright (c) 2003, Miodrag Vallat.
 * 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.
 *
 * 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 <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/timeout.h>

#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/cpu.h>

#include <dev/hil/hilreg.h>
#include <dev/hil/hilvar.h>
#include <dev/hil/hildevs.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>
#ifdef WSDISPLAY_COMPAT_RAWKBD
#include <dev/wscons/wskbdraw.h>
#endif

#include <dev/hil/hilkbdmap.h>

struct hilkbd_softc {
	struct hildev_softc sc_hildev;

	int		sc_numleds;
	int		sc_ledstate;
	int		sc_enabled;
	int		sc_console;
	int		sc_lastarrow;

	struct device	*sc_wskbddev;

#ifdef WSDISPLAY_COMPAT_RAWKBD
	int		sc_rawkbd;
#endif
};

int	hilkbdprobe(struct device *, void *, void *);
void	hilkbdattach(struct device *, struct device *, void *);
int	hilkbddetach(struct device *, int);

struct cfdriver hilkbd_cd = {
	NULL, "hilkbd", DV_DULL
};

struct cfattach hilkbd_ca = {
	sizeof(struct hilkbd_softc), hilkbdprobe, hilkbdattach, hilkbddetach,
};

int	hilkbd_enable(void *, int);
void	hilkbd_set_leds(void *, int);
int	hilkbd_ioctl(void *, u_long, caddr_t, int, struct proc *);

const struct wskbd_accessops hilkbd_accessops = {
	hilkbd_enable,
	hilkbd_set_leds,
	hilkbd_ioctl,
};

void	hilkbd_cngetc(void *, u_int *, int *);
void	hilkbd_cnpollc(void *, int);
void	hilkbd_cnbell(void *, u_int, u_int, u_int);

const struct wskbd_consops hilkbd_consops = {
	hilkbd_cngetc,
	hilkbd_cnpollc,
	hilkbd_cnbell,
};

struct wskbd_mapdata hilkbd_keymapdata = {
	hilkbd_keydesctab,
#ifdef HILKBD_LAYOUT
	HILKBD_LAYOUT,
#else
	KB_US | KB_DEFAULT,
#endif
};

struct wskbd_mapdata hilkbd_keymapdata_ps2 = {
	hilkbd_keydesctab_ps2,
#ifdef HILKBD_LAYOUT
	HILKBD_LAYOUT,
#else
	KB_US | KB_DEFAULT,
#endif
};

void	hilkbd_bell(struct hil_softc *, u_int, u_int, u_int);
void	hilkbd_callback(struct hildev_softc *, u_int, u_int8_t *);
void	hilkbd_decode(struct hilkbd_softc *, u_int8_t, u_int *, int *, int);
int	hilkbd_is_console(int);

int	seen_hilkbd_console;

int
hilkbdprobe(struct device *parent, void *match, void *aux)
{
	struct hil_attach_args *ha = aux;

	if (ha->ha_type != HIL_DEVICE_KEYBOARD &&
	    ha->ha_type != HIL_DEVICE_BUTTONBOX)
		return (0);

	return (1);
}

void
hilkbdattach(struct device *parent, struct device *self, void *aux)
{
	struct hilkbd_softc *sc = (void *)self;
	struct hil_attach_args *ha = aux;
	struct wskbddev_attach_args a;
	u_int8_t layoutcode;
	int ps2;

	sc->hd_code = ha->ha_code;
	sc->hd_type = ha->ha_type;
	sc->hd_infolen = ha->ha_infolen;
	bcopy(ha->ha_info, sc->hd_info, ha->ha_infolen);
	sc->hd_fn = hilkbd_callback;

	if (ha->ha_type == HIL_DEVICE_KEYBOARD) {
		/*
		 * Determine the keyboard language configuration, but don't
		 * override a user-specified setting.
		 */
		layoutcode = ha->ha_id & (MAXHILKBDLAYOUT - 1);
#ifndef HILKBD_LAYOUT
		if (layoutcode < MAXHILKBDLAYOUT &&
		    hilkbd_layouts[layoutcode] != -1)
			hilkbd_keymapdata.layout =
			hilkbd_keymapdata_ps2.layout =
			    hilkbd_layouts[layoutcode];
#endif

		printf(", layout %x", layoutcode);
	}

	/*
	 * Interpret the identification bytes, if any
	 */
	if (ha->ha_infolen > 2 && (ha->ha_info[1] & HIL_IOB) != 0) {
		/* HILIOB_PROMPT is not always reported... */
		sc->sc_numleds = (ha->ha_info[2] & HILIOB_PMASK) >> 4;
		if (sc->sc_numleds != 0)
			printf(", %d leds", sc->sc_numleds);
	}

	printf("\n");

	/*
	 * Red lettered keyboards come in two flavours, the old one
	 * with only one control key, to the left of the escape key,
	 * and the modern one which has a PS/2 like layout, and leds.
	 *
	 * Unfortunately for us, they use the same device ID range.
	 * We'll differentiate them by looking at the leds property.
	 */
	ps2 = (sc->sc_numleds != 0);

	/* Do not consider button boxes as console devices. */
	if (ha->ha_type == HIL_DEVICE_BUTTONBOX)
		a.console = 0;
	else
		a.console = hilkbd_is_console(ha->ha_console);
	a.keymap = ps2 ? &hilkbd_keymapdata_ps2 : &hilkbd_keymapdata;
	a.accessops = &hilkbd_accessops;
	a.accesscookie = sc;

	if (a.console) {
		sc->sc_console = sc->sc_enabled = 1;
		wskbd_cnattach(&hilkbd_consops, sc, a.keymap);
	} else {
		sc->sc_console = sc->sc_enabled = 0;
	}

	sc->sc_wskbddev = config_found(self, &a, wskbddevprint);

	/*
	 * If this is an old keyboard with a numeric pad but no ``num lock''
	 * key, simulate it being pressed so that the keyboard runs in
	 * numeric mode.
	 */
	if (!ps2 && sc->sc_wskbddev != NULL) {
		wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_DOWN, 80);
		wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_UP, 80);
	}
}

int
hilkbddetach(struct device *self, int flags)
{
	struct hilkbd_softc *sc = (void *)self;

	/*
	 * Handle console keyboard for the best. It should have been set
	 * as the first device in the loop anyways.
	 */
	if (sc->sc_console) {
		wskbd_cndetach();
		seen_hilkbd_console = 0;
	}

	if (sc->sc_wskbddev != NULL)
		return config_detach(sc->sc_wskbddev, flags);

	return (0);
}

int
hilkbd_enable(void *v, int on)
{
	struct hilkbd_softc *sc = v;

	if (on) {
		if (sc->sc_enabled)
			return (EBUSY);
	} else {
		if (sc->sc_console)
			return (EBUSY);
	}

	sc->sc_enabled = on;

	return (0);
}

void
hilkbd_set_leds(void *v, int leds)
{
	struct hilkbd_softc *sc = v;
	int changemask;

	if (sc->sc_numleds == 0)
		return;

	changemask = leds ^ sc->sc_ledstate;
	if (changemask == 0)
		return;

	/* We do not handle more than 3 leds here */
	if (changemask & WSKBD_LED_SCROLL)
		send_hildev_cmd((struct hildev_softc *)sc,
		    (leds & WSKBD_LED_SCROLL) ? HIL_PROMPT1 : HIL_ACK1,
		    NULL, NULL);
	if (changemask & WSKBD_LED_NUM)
		send_hildev_cmd((struct hildev_softc *)sc,
		    (leds & WSKBD_LED_NUM) ? HIL_PROMPT2 : HIL_ACK2,
		    NULL, NULL);
	if (changemask & WSKBD_LED_CAPS)
		send_hildev_cmd((struct hildev_softc *)sc,
		    (leds & WSKBD_LED_CAPS) ? HIL_PROMPT3 : HIL_ACK3,
		    NULL, NULL);

	sc->sc_ledstate = leds;
}

int
hilkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct hilkbd_softc *sc = v;

	switch (cmd) {
	case WSKBDIO_GTYPE:
		*(int *)data = WSKBD_TYPE_HIL;
		return 0;
	case WSKBDIO_SETLEDS:
		hilkbd_set_leds(v, *(int *)data);
		return 0;
	case WSKBDIO_GETLEDS:
		*(int *)data = sc->sc_ledstate;
		return 0;
#ifdef WSDISPLAY_COMPAT_RAWKBD
	case WSKBDIO_SETMODE:
		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
		return 0;
#endif
	case WSKBDIO_COMPLEXBELL:
#define	d ((struct wskbd_bell_data *)data)
		hilkbd_bell((struct hil_softc *)sc->hd_parent,
		    d->pitch, d->period, d->volume);
#undef d
		return 0;
	}

	return -1;
}

void
hilkbd_cngetc(void *v, u_int *type, int *data)
{
	struct hilkbd_softc *sc = v;
	u_int8_t c, stat;

	for (;;) {
		while (hil_poll_data((struct hildev_softc *)sc, &stat, &c) != 0)
			;

		/*
		 * Disregard keyboard data packet header.
		 * Note that no key generates it, so we're safe.
		 */
		if (c != HIL_KBDBUTTON)
			break;
	}

	hilkbd_decode(sc, c, type, data, HIL_KBDBUTTON);
}

void
hilkbd_cnpollc(void *v, int on)
{
	struct hilkbd_softc *sc = v;

	hil_set_poll((struct hil_softc *)sc->hd_parent, on);
}

void
hilkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
{
	struct hilkbd_softc *sc = v;

	hilkbd_bell((struct hil_softc *)sc->hd_parent,
	    pitch, period, volume);
}

void
hilkbd_bell(struct hil_softc *sc, u_int pitch, u_int period, u_int volume)
{
	u_int8_t buf[2];

	/* XXX there could be at least a pitch -> HIL pitch conversion here */
#define	BELLDUR		80	/* tone duration in msec (10-2560) */
#define	BELLFREQ	8	/* tone frequency (0-63) */
	buf[0] = ar_format(period - 10);
	buf[1] = BELLFREQ;
	send_hil_cmd(sc, HIL_SETTONE, buf, 2, NULL);
}

void
hilkbd_callback(struct hildev_softc *dev, u_int buflen, u_int8_t *buf)
{
	struct hilkbd_softc *sc = (struct hilkbd_softc *)dev;
	u_int type;
	int kbdtype, key;
	int i, s;

	/*
	 * Ignore packet if we don't need it
	 */
	if (sc->sc_enabled == 0)
		return;

	if (buflen == 0)
		return;
	switch ((kbdtype = *buf & HIL_KBDDATA)) {
	case HIL_BUTTONBOX:
	case HIL_KBDBUTTON:
		break;
	default:
		return;
	}

#ifdef WSDISPLAY_COMPAT_RAWKBD
	if (sc->sc_rawkbd) {
		u_char cbuf[HILBUFSIZE * 2];
		int c, j = 0;

		for (i = 1, buf++; i < buflen; i++) {
			hilkbd_decode(sc, *buf++, &type, &key, kbdtype);
			c = hilkbd_raw[key];
			if (c == RAWKEY_Null)
				continue;
			/* fake extended scancode if necessary */
			if (c & 0x80)
				cbuf[j++] = 0xe0;
			cbuf[j] = c & 0x7f;
			if (type == WSCONS_EVENT_KEY_UP)
				cbuf[j] |= 0x80;
			j++;
		}

		s = spltty();
		wskbd_rawinput(sc->sc_wskbddev, cbuf, j);
		splx(s);
	} else
#endif
	{
		s = spltty();
		for (i = 1, buf++; i < buflen; i++) {
			hilkbd_decode(sc, *buf++, &type, &key, kbdtype);
			if (sc->sc_wskbddev != NULL)
				wskbd_input(sc->sc_wskbddev, type, key);
		}
		splx(s);
	}
}

void
hilkbd_decode(struct hilkbd_softc *sc, u_int8_t data, u_int *type, int *key,
    int kbdtype)
{
	if (kbdtype == HIL_BUTTONBOX) {
		if (data == 0x02)	/* repeat arrow */
			data = sc->sc_lastarrow;
		else if (data >= 0xf8)
			sc->sc_lastarrow = data;
	}

	*type = (data & 1) ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
	*key = data >> 1;
}

int
hilkbd_is_console(int hil_is_console)
{
	/* if not first hil keyboard, then not the console */
	if (seen_hilkbd_console)
		return (0);

	/* if PDC console does not match hil bus path, then not the console */
	if (hil_is_console == 0)
		return (0);

	seen_hilkbd_console = 1;
	return (1);
}