NetBSD-5.0.2/sys/dev/hpc/hpf1275a_tty.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: hpf1275a_tty.c,v 1.23 2008/04/06 20:28:36 cegger Exp $ */

/*
 * Copyright (c) 2004 Valeriy E. Ushakov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: hpf1275a_tty.c,v 1.23 2008/04/06 20:28:36 cegger Exp $");

#include "opt_wsdisplay_compat.h"

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/tty.h>
#include <sys/fcntl.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/kauth.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wskbdvar.h>
#include <dev/wscons/wsksymdef.h>
#include <dev/wscons/wsksymvar.h>

#include <dev/pckbport/wskbdmap_mfii.h>
#ifdef WSDISPLAY_COMPAT_RAWKBD
#include <dev/hpc/pckbd_encode.h>
#endif


extern struct cfdriver hpf1275a_cd;

struct hpf1275a_softc {
	struct device sc_dev;

	struct tty *sc_tp;		/* back reference to the tty */
	struct device *sc_wskbd;	/* wskbd child */
	int sc_enabled;
#ifdef WSDISPLAY_COMPAT_RAWKBD
	int sc_rawkbd;
#endif
};


/* pseudo-device initialization */
extern void	hpf1275aattach(int);

/* line discipline methods */
static int	hpf1275a_open(dev_t, struct tty *);
static int	hpf1275a_close(struct tty *, int);
static int	hpf1275a_input(int, struct tty *);

/* autoconf(9) methods */
static int	hpf1275a_match(struct device *, struct cfdata *, void *);
static void	hpf1275a_attach(struct device *, struct device *, void *);
static int	hpf1275a_detach(struct device *, int);

/* wskbd(4) accessops */
static int	hpf1275a_wskbd_enable(void *, int);
static void	hpf1275a_wskbd_set_leds(void *, int);
static int	hpf1275a_wskbd_ioctl(void *, u_long, void *, int,
				     struct lwp *);


/* 
 * It doesn't need to be exported, as only hpf1275aattach() uses it,
 * but there's no "official" way to make it static.
 */
CFATTACH_DECL(hpf1275a, sizeof(struct hpf1275a_softc),
    hpf1275a_match, hpf1275a_attach, hpf1275a_detach, NULL);


static struct linesw hpf1275a_disc = {
	.l_name = "hpf1275a",
	.l_open = hpf1275a_open,
	.l_close = hpf1275a_close,
	.l_read = ttyerrio,
	.l_write = ttyerrio,
	.l_ioctl = ttynullioctl,
	.l_rint = hpf1275a_input,
	.l_start = ttstart,
	.l_modem = nullmodem,
	.l_poll = ttpoll
};


static const struct wskbd_accessops hpf1275a_wskbd_accessops = {
	hpf1275a_wskbd_enable,
	hpf1275a_wskbd_set_leds,
	hpf1275a_wskbd_ioctl
};


static const struct wskbd_mapdata hpf1275a_wskbd_keymapdata = {
	pckbd_keydesctab, KB_US
};


/* F1275A scancodes -> XT scancodes so that we can use pckbd_keydesctab. */
static const uint8_t hpf1275a_to_xtscan[128] = {
	[0x04] = 30,		/* a */
	[0x05] = 48,		/* b */
	[0x06] = 46,		/* c */
	[0x07] = 32,		/* d */
	[0x08] = 18,		/* e */
	[0x09] = 33,		/* f */
	[0x0a] = 34,		/* g */
	[0x0b] = 35,		/* h */
	[0x0c] = 23,		/* i */
	[0x0d] = 36,		/* j */
	[0x0e] = 37,		/* k */
	[0x0f] = 38,		/* l */
	[0x10] = 50,		/* m */
	[0x11] = 49,		/* n */
	[0x12] = 24,		/* o */
	[0x13] = 25,		/* p */
	[0x14] = 16,		/* q */
	[0x15] = 19,		/* r */
	[0x16] = 31,		/* s */
	[0x17] = 20,		/* t */
	[0x18] = 22,		/* u */
	[0x19] = 47,		/* v */
	[0x1a] = 17,		/* w */
	[0x1b] = 45,		/* x */
	[0x1c] = 21,		/* y */
	[0x1d] = 44,		/* z */

	[0x1e] = 2,		/* 1 */
	[0x1f] = 3,		/* 2 */
	[0x20] = 4,		/* 3 */
	[0x21] = 5,		/* 4 */
	[0x22] = 6,		/* 5 */
	[0x23] = 7,		/* 6 */
	[0x24] = 8,		/* 7 */
	[0x25] = 9,		/* 8 */
	[0x26] = 10,		/* 9 */
	[0x27] = 11,		/* 0 */

	[0x28] = 28,		/* Enter */

	[0x29] = 1,		/* ESC */
	[0x2a] = 14,		/* Backspace */
	[0x2b] = 15,		/* Tab */
	[0x2c] = 57,		/* Space */

	[0x2d] = 12,		/* - */
	[0x2e] = 13,		/* = */
	[0x2f] = 26,		/* [ */
	[0x30] = 27,		/* ] */
	[0x31] = 43,		/* \ */

	[0x33] = 39,		/* ; */
	[0x34] = 40,		/* ' */
	[0x35] = 41,		/* ` */
	[0x36] = 51,		/* , */
	[0x37] = 52,		/* . */
	[0x38] = 53,		/* / */

	[0x3a] = 59,		/* F1 */
	[0x3b] = 60,		/* F2 */
	[0x3c] = 61,		/* F3 */
	[0x3d] = 62,		/* F4 */
	[0x3e] = 63,		/* F5 */
	[0x3f] = 64,		/* F6 */
	[0x40] = 65,		/* F7 */
	[0x41] = 66,		/* F8 */

	[0x42] = 68,		/* "OK" -> F10 */
	[0x43] = 87,		/* "Cancel" -> F11 */

	[0x4c] = 211,		/* Del */

	[0x4f] = 205,		/* Right */
	[0x50] = 203,		/* Left  */
	[0x51] = 208,		/* Down  */
	[0x52] = 200,		/* Up    */

	[0x53] = 67,		/* "task switch" -> F9 */

	[0x65] = 221,		/* windows */
	[0x66] = 88,		/* "keyboard" -> F12 */

	[0x74] = 42,		/* Shift (left) */
	[0x75] = 54,		/* Shift (right) */
	[0x76] = 56,		/* Alt (left) */
	[0x77] = 184,		/* Fn -> AltGr == Mode Switch */
	[0x78] = 29,		/* Control (left) */
};


/*
 * Pseudo-device initialization routine called from main().
 */
void
hpf1275aattach(int n)
{
	int error;

	error = ttyldisc_attach(&hpf1275a_disc);
	if (error) {
		printf("%s: unable to register line discipline, error = %d\n",
		       hpf1275a_cd.cd_name, error);
		return;
	}

	error = config_cfattach_attach(hpf1275a_cd.cd_name, &hpf1275a_ca);
	if (error) {
		printf("%s: unable to register cfattach, error = %d\n",
		       hpf1275a_cd.cd_name, error);
		config_cfdriver_detach(&hpf1275a_cd);
		(void) ttyldisc_detach(&hpf1275a_disc);
	}
}


/*
 * Autoconf match routine.
 *
 * XXX: unused: config_attach_pseudo(9) does not call ca_match.
 */
static int
hpf1275a_match(struct device *self,
	       struct cfdata *cfdata, void *arg)
{

	/* pseudo-device; always present */
	return (1);
}


/*
 * Autoconf attach routine.  Called by config_attach_pseudo(9) when we
 * open the line discipline.
 */
static void
hpf1275a_attach(struct device *parent,
		struct device *self, void *aux)
{
	struct hpf1275a_softc *sc = device_private(self);
	struct wskbddev_attach_args wska;

	wska.console = 0;
	wska.keymap = &hpf1275a_wskbd_keymapdata;
	wska.accessops = &hpf1275a_wskbd_accessops;
	wska.accesscookie = sc;

	sc->sc_enabled = 0;
#ifdef WSDISPLAY_COMPAT_RAWKBD
	sc->sc_rawkbd = 0;
#endif
	sc->sc_wskbd = config_found(self, &wska, wskbddevprint);
}


/*
 * Autoconf detach routine.  Called when we close the line discipline.
 */
static int
hpf1275a_detach(struct device *self, int flags)
{
	struct hpf1275a_softc *sc = device_private(self);
	int error;

	if (sc->sc_wskbd == NULL)
		return (0);

	error = config_detach(sc->sc_wskbd, 0);

	return (error);
}


/*
 * Line discipline open routine.
 */
static int
hpf1275a_open(dev_t dev, struct tty *tp)
{
	static struct cfdata hpf1275a_cfdata = {
		.cf_name = "hpf1275a",
		.cf_atname = "hpf1275a",
		.cf_unit = 0,
		.cf_fstate = FSTATE_STAR,
	};
	struct lwp *l = curlwp;		/* XXX */
	struct hpf1275a_softc *sc;
	int error, s;

	if ((error = kauth_authorize_device_tty(l->l_cred,
			KAUTH_DEVICE_TTY_OPEN, tp)))
		return (error);

	s = spltty();

	if (tp->t_linesw == &hpf1275a_disc) {
		splx(s);
		return 0;
	}

	sc = (struct hpf1275a_softc *)config_attach_pseudo(&hpf1275a_cfdata);
	if (sc == NULL) {
		splx(s);
		return (EIO);
	}

	tp->t_sc = sc;
	sc->sc_tp = tp;

	splx(s);
	return (0);
}


/*
 * Line discipline close routine.
 */
static int
hpf1275a_close(struct tty *tp, int flag)
{
	struct hpf1275a_softc *sc = tp->t_sc;
	int s;

	s = spltty();
	mutex_spin_enter(&tty_lock);
	ttyflush(tp, FREAD | FWRITE);
	mutex_spin_exit(&tty_lock);	 /* XXX */
	ttyldisc_release(tp->t_linesw);
	tp->t_linesw = ttyldisc_default();
	if (sc != NULL) {
		tp->t_sc = NULL;
		if (sc->sc_tp == tp)
			config_detach(&sc->sc_dev, 0);
	}
	splx(s);
	return (0);
}


/*
 * Feed input from the keyboard to wskbd(4).
 */
static int
hpf1275a_input(int c, struct tty *tp)
{
	struct hpf1275a_softc *sc = tp->t_sc;
	int code;
	u_int type;
	int xtscan;

	if (!sc->sc_enabled)
		return (0);

	if (c & TTY_ERRORMASK)
		return (0);	/* TODO? */

	code = c & TTY_CHARMASK;
	if (code & 0x80) {
		type = WSCONS_EVENT_KEY_UP;
		code &= ~0x80;
	} else
		type = WSCONS_EVENT_KEY_DOWN;

	xtscan = hpf1275a_to_xtscan[code];
	if (xtscan == 0) {
		aprint_error_dev(&sc->sc_dev, "unknown code 0x%x\n", code);
		return (0);
	}

	KASSERT(sc->sc_wskbd != NULL);

#ifdef WSDISPLAY_COMPAT_RAWKBD
	if (sc->sc_rawkbd) {
		u_char data[16];
		int n;

		n = pckbd_encode(type, xtscan, data);
		wskbd_rawinput(sc->sc_wskbd, data, n);
	} else
#endif
		wskbd_input(sc->sc_wskbd, type, xtscan);

	return (0);
}


static int
hpf1275a_wskbd_enable(void *self, int on)
{
	struct hpf1275a_softc *sc = self;

	sc->sc_enabled = on;
	return (0);
}


/* ARGSUSED */
static void
hpf1275a_wskbd_set_leds(void *self, int leds)
{

	/* this keyboard has no leds; nothing to do */
	return;
}


static int
hpf1275a_wskbd_ioctl(void *self, u_long cmd, void *data, int flag,
		     struct lwp *l)
{
#ifdef WSDISPLAY_COMPAT_RAWKBD
	struct hpf1275a_softc *sc = self;
#endif

	switch (cmd) {
	case WSKBDIO_GTYPE:
		*(int *)data = WSKBD_TYPE_HPC_KBD; /* may be use new type? */
		return (0);

	case WSKBDIO_GETLEDS:
		*(int *)data = 0; /* this keyboard has no leds */
		return (0);

#ifdef WSDISPLAY_COMPAT_RAWKBD
	case WSKBDIO_SETMODE:
		sc->sc_rawkbd = (*(int *)data == WSKBD_RAW);
		return (0);
#endif

	default:
		return (EPASSTHROUGH);
	}
}