Index: stable/11/sys/arm/samsung/exynos/chrome_kb.c
===================================================================
--- stable/11/sys/arm/samsung/exynos/chrome_kb.c (revision 356019)
+++ stable/11/sys/arm/samsung/exynos/chrome_kb.c (revision 356020)
@@ -1,919 +1,918 @@
/*-
* Copyright (c) 2014 Ruslan Bukin
* 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 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 AUTHOR 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.
*/
/*
* Samsung Chromebook Keyboard
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
#include "gpio_if.h"
#include
#include
#include
#include
#define CKB_LOCK() mtx_lock(&Giant)
#define CKB_UNLOCK() mtx_unlock(&Giant)
#ifdef INVARIANTS
/*
* Assert that the lock is held in all contexts
* where the code can be executed.
*/
#define CKB_LOCK_ASSERT() mtx_assert(&Giant, MA_OWNED)
/*
* Assert that the lock is held in the contexts
* where it really has to be so.
*/
#define CKB_CTX_LOCK_ASSERT() \
do { \
if (!kdb_active && panicstr == NULL) \
mtx_assert(&Giant, MA_OWNED); \
} while (0)
#else
#define CKB_LOCK_ASSERT() (void)0
#define CKB_CTX_LOCK_ASSERT() (void)0
#endif
/*
* Define a stub keyboard driver in case one hasn't been
* compiled into the kernel
*/
#include
#include
#include
#define CKB_NFKEY 12
#define CKB_FLAG_COMPOSE 0x1
#define CKB_FLAG_POLLING 0x2
#define KBD_DRIVER_NAME "ckbd"
struct ckb_softc {
keyboard_t sc_kbd;
keymap_t sc_keymap;
accentmap_t sc_accmap;
fkeytab_t sc_fkeymap[CKB_NFKEY];
struct resource* sc_mem_res;
struct resource* sc_irq_res;
void* sc_intr_hl;
int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
int sc_state; /* shift/lock key state */
int sc_accents; /* accent key index (> 0) */
int sc_flags; /* flags */
struct callout sc_repeat_callout;
int sc_repeat_key;
int sc_repeating;
int flag;
int rows;
int cols;
int gpio;
device_t dev;
device_t gpio_dev;
struct thread *sc_poll_thread;
uint16_t *keymap;
uint8_t *scan_local;
uint8_t *scan;
};
/* prototypes */
static int ckb_set_typematic(keyboard_t *, int);
static uint32_t ckb_read_char(keyboard_t *, int);
static void ckb_clear_state(keyboard_t *);
static int ckb_ioctl(keyboard_t *, u_long, caddr_t);
static int ckb_enable(keyboard_t *);
static int ckb_disable(keyboard_t *);
static void
ckb_repeat(void *arg)
{
struct ckb_softc *sc;
sc = arg;
if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) {
if (sc->sc_repeat_key != -1) {
sc->sc_repeating = 1;
sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd,
KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg);
}
}
}
/* detect a keyboard, not used */
static int
ckb__probe(int unit, void *arg, int flags)
{
return (ENXIO);
}
/* reset and initialize the device, not used */
static int
ckb_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
return (ENXIO);
}
/* test the interface to the device, not used */
static int
ckb_test_if(keyboard_t *kbd)
{
return (0);
}
/* finish using this keyboard, not used */
static int
ckb_term(keyboard_t *kbd)
{
return (ENXIO);
}
/* keyboard interrupt routine, not used */
static int
ckb_intr(keyboard_t *kbd, void *arg)
{
return (0);
}
/* lock the access to the keyboard, not used */
static int
ckb_lock(keyboard_t *kbd, int lock)
{
return (1);
}
/* clear the internal state of the keyboard */
static void
ckb_clear_state(keyboard_t *kbd)
{
struct ckb_softc *sc;
sc = kbd->kb_data;
CKB_CTX_LOCK_ASSERT();
sc->sc_flags &= ~(CKB_FLAG_COMPOSE | CKB_FLAG_POLLING);
sc->sc_state &= LOCK_MASK; /* preserve locking key state */
sc->sc_accents = 0;
}
/* save the internal state, not used */
static int
ckb_get_state(keyboard_t *kbd, void *buf, size_t len)
{
return (len == 0) ? 1 : -1;
}
/* set the internal state, not used */
static int
ckb_set_state(keyboard_t *kbd, void *buf, size_t len)
{
return (EINVAL);
}
/* check if data is waiting */
static int
ckb_check(keyboard_t *kbd)
{
struct ckb_softc *sc;
int i;
sc = kbd->kb_data;
CKB_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (0);
if (sc->sc_flags & CKB_FLAG_POLLING) {
return (1);
}
for (i = 0; i < sc->cols; i++)
if (sc->scan_local[i] != sc->scan[i]) {
return (1);
}
if (sc->sc_repeating)
return (1);
return (0);
}
/* check if char is waiting */
static int
ckb_check_char_locked(keyboard_t *kbd)
{
CKB_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (0);
return (ckb_check(kbd));
}
static int
ckb_check_char(keyboard_t *kbd)
{
int result;
CKB_LOCK();
result = ckb_check_char_locked(kbd);
CKB_UNLOCK();
return (result);
}
/* read one byte from the keyboard if it's allowed */
/* Currently unused. */
static int
ckb_read(keyboard_t *kbd, int wait)
{
CKB_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (-1);
printf("Implement ME: %s\n", __func__);
return (0);
}
static uint16_t
keymap_read(struct ckb_softc *sc, int col, int row)
{
KASSERT(sc->keymap != NULL, ("keymap_read: no keymap"));
if (col >= 0 && col < sc->cols &&
row >= 0 && row < sc->rows) {
return sc->keymap[row * sc->cols + col];
}
return (0);
}
static int
keymap_write(struct ckb_softc *sc, int col, int row, uint16_t key)
{
KASSERT(sc->keymap != NULL, ("keymap_write: no keymap"));
if (col >= 0 && col < sc->cols &&
row >= 0 && row < sc->rows) {
sc->keymap[row * sc->cols + col] = key;
return (0);
}
return (-1);
}
/* read char from the keyboard */
static uint32_t
ckb_read_char_locked(keyboard_t *kbd, int wait)
{
struct ckb_softc *sc;
int i,j;
uint16_t key;
int oldbit;
int newbit;
int status;
sc = kbd->kb_data;
CKB_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (NOKEY);
if (sc->sc_repeating) {
sc->sc_repeating = 0;
callout_reset(&sc->sc_repeat_callout, hz / 10,
ckb_repeat, sc);
return (sc->sc_repeat_key);
}
if (sc->sc_flags & CKB_FLAG_POLLING) {
for (;;) {
GPIO_PIN_GET(sc->gpio_dev, sc->gpio, &status);
if (status == 0) {
if (ec_command(EC_CMD_MKBP_STATE, sc->scan,
sc->cols,
sc->scan, sc->cols)) {
return (NOKEY);
}
break;
}
if (!wait) {
return (NOKEY);
}
DELAY(1000);
}
}
for (i = 0; i < sc->cols; i++) {
for (j = 0; j < sc->rows; j++) {
oldbit = (sc->scan_local[i] & (1 << j));
newbit = (sc->scan[i] & (1 << j));
if (oldbit == newbit)
continue;
key = keymap_read(sc, i, j);
if (key == 0) {
continue;
}
if (newbit > 0) {
/* key pressed */
sc->scan_local[i] |= (1 << j);
/* setup repeating */
sc->sc_repeat_key = key;
callout_reset(&sc->sc_repeat_callout,
hz / 2, ckb_repeat, sc);
} else {
/* key released */
sc->scan_local[i] &= ~(1 << j);
/* release flag */
key |= 0x80;
/* unsetup repeating */
sc->sc_repeat_key = -1;
callout_stop(&sc->sc_repeat_callout);
}
return (key);
}
}
return (NOKEY);
}
/* Currently wait is always false. */
static uint32_t
ckb_read_char(keyboard_t *kbd, int wait)
{
uint32_t keycode;
CKB_LOCK();
keycode = ckb_read_char_locked(kbd, wait);
CKB_UNLOCK();
return (keycode);
}
/* some useful control functions */
static int
ckb_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
struct ckb_softc *sc;
int i;
sc = kbd->kb_data;
CKB_LOCK_ASSERT();
switch (cmd) {
case KDGKBMODE: /* get keyboard mode */
*(int *)arg = sc->sc_mode;
break;
case KDSKBMODE: /* set keyboard mode */
switch (*(int *)arg) {
case K_XLATE:
if (sc->sc_mode != K_XLATE) {
/* make lock key state and LED state match */
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (sc->sc_mode != *(int *)arg) {
if ((sc->sc_flags & CKB_FLAG_POLLING) == 0)
ckb_clear_state(kbd);
sc->sc_mode = *(int *)arg;
}
break;
default:
return (EINVAL);
}
break;
case KDGETLED: /* get keyboard LED */
*(int *)arg = KBD_LED_VAL(kbd);
break;
case KDSETLED: /* set keyboard LED */
/* NOTE: lock key state in "sc_state" won't be changed */
if (*(int *)arg & ~LOCK_MASK)
return (EINVAL);
i = *(int *)arg;
/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
if (sc->sc_mode == K_XLATE &&
kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
if (i & ALKED)
i |= CLKED;
else
i &= ~CLKED;
}
if (KBD_HAS_DEVICE(kbd)) {
/* Configure LED */
}
KBD_LED_VAL(kbd) = *(int *)arg;
break;
case KDGKBSTATE: /* get lock key state */
*(int *)arg = sc->sc_state & LOCK_MASK;
break;
case KDSKBSTATE: /* set lock key state */
if (*(int *)arg & ~LOCK_MASK) {
return (EINVAL);
}
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= *(int *)arg;
/* set LEDs and quit */
return (ckb_ioctl(kbd, KDSETLED, arg));
case KDSETREPEAT: /* set keyboard repeat rate (new
* interface) */
if (!KBD_HAS_DEVICE(kbd)) {
return (0);
}
if (((int *)arg)[1] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 200) /* fastest possible value */
kbd->kb_delay1 = 200;
else
kbd->kb_delay1 = ((int *)arg)[0];
kbd->kb_delay2 = ((int *)arg)[1];
return (0);
case KDSETRAD: /* set keyboard repeat rate (old
* interface) */
return (ckb_set_typematic(kbd, *(int *)arg));
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table
* (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table
* entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
sc->sc_accents = 0;
/* FALLTHROUGH */
default:
return (genkbd_commonioctl(kbd, cmd, arg));
}
return (0);
}
static int
ckb_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
int result;
/*
* XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any
* context where printf(9) can be called, which among other things
* includes interrupt filters and threads with any kinds of locks
* already held. For this reason it would be dangerous to acquire
* the Giant here unconditionally. On the other hand we have to
* have it to handle the ioctl.
* So we make our best effort to auto-detect whether we can grab
* the Giant or not. Blame syscons(4) for this.
*/
switch (cmd) {
case KDGKBSTATE:
case KDSKBSTATE:
case KDSETLED:
if (!mtx_owned(&Giant) && !SCHEDULER_STOPPED())
return (EDEADLK); /* best I could come up with */
/* FALLTHROUGH */
default:
CKB_LOCK();
result = ckb_ioctl_locked(kbd, cmd, arg);
CKB_UNLOCK();
return (result);
}
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
ckb_enable(keyboard_t *kbd)
{
CKB_LOCK();
KBD_ACTIVATE(kbd);
CKB_UNLOCK();
return (0);
}
/* disallow the access to the device */
static int
ckb_disable(keyboard_t *kbd)
{
CKB_LOCK();
KBD_DEACTIVATE(kbd);
CKB_UNLOCK();
return (0);
}
/* local functions */
static int
ckb_set_typematic(keyboard_t *kbd, int code)
{
static const int delays[] = {250, 500, 750, 1000};
static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504};
if (code & ~0x7f) {
return (EINVAL);
}
kbd->kb_delay1 = delays[(code >> 5) & 3];
kbd->kb_delay2 = rates[code & 0x1f];
return (0);
}
static int
ckb_poll(keyboard_t *kbd, int on)
{
struct ckb_softc *sc;
sc = kbd->kb_data;
CKB_LOCK();
if (on) {
sc->sc_flags |= CKB_FLAG_POLLING;
sc->sc_poll_thread = curthread;
} else {
sc->sc_flags &= ~CKB_FLAG_POLLING;
}
CKB_UNLOCK();
return (0);
}
/* local functions */
static int dummy_kbd_configure(int flags);
keyboard_switch_t ckbdsw = {
.probe = &ckb__probe,
.init = &ckb_init,
.term = &ckb_term,
.intr = &ckb_intr,
.test_if = &ckb_test_if,
.enable = &ckb_enable,
.disable = &ckb_disable,
.read = &ckb_read,
.check = &ckb_check,
.read_char = &ckb_read_char,
.check_char = &ckb_check_char,
.ioctl = &ckb_ioctl,
.lock = &ckb_lock,
.clear_state = &ckb_clear_state,
.get_state = &ckb_get_state,
.set_state = &ckb_set_state,
.poll = &ckb_poll,
};
static int
dummy_kbd_configure(int flags)
{
return (0);
}
KEYBOARD_DRIVER(ckbd, ckbdsw, dummy_kbd_configure);
/*
* Parses 'keymap' into sc->keymap.
* Requires sc->cols and sc->rows to be set.
*/
static int
parse_keymap(struct ckb_softc *sc, pcell_t *keymap, size_t len)
{
int i;
sc->keymap = malloc(sc->cols * sc->rows * sizeof(sc->keymap[0]),
M_DEVBUF, M_NOWAIT | M_ZERO);
if (sc->keymap == NULL) {
return (ENOMEM);
}
for (i = 0; i < len; i++) {
/*
* Return value is ignored, we just write whatever fits into
* specified number of rows and columns and silently ignore
* everything else.
* Keymap entries follow this format: 0xRRCCKKKK
* RR - row number, CC - column number, KKKK - key code
*/
keymap_write(sc, (keymap[i] >> 16) & 0xff,
(keymap[i] >> 24) & 0xff,
keymap[i] & 0xffff);
}
return (0);
}
/* Allocates a new array for keymap and returns it in 'keymap'. */
static int
read_keymap(phandle_t node, const char *prop, pcell_t **keymap, size_t *len)
{
if ((*len = OF_getproplen(node, prop)) <= 0) {
return (ENXIO);
}
if ((*keymap = malloc(*len, M_DEVBUF, M_NOWAIT)) == NULL) {
return (ENOMEM);
}
if (OF_getencprop(node, prop, *keymap, *len) != *len) {
return (ENXIO);
}
return (0);
}
static int
parse_dts(struct ckb_softc *sc)
{
phandle_t node;
pcell_t dts_value;
pcell_t *keymap;
int len, ret;
const char *keymap_prop = NULL;
if ((node = ofw_bus_get_node(sc->dev)) == -1)
return (ENXIO);
if ((len = OF_getproplen(node, "google,key-rows")) <= 0)
return (ENXIO);
OF_getencprop(node, "google,key-rows", &dts_value, len);
sc->rows = dts_value;
if ((len = OF_getproplen(node, "google,key-columns")) <= 0)
return (ENXIO);
OF_getencprop(node, "google,key-columns", &dts_value, len);
sc->cols = dts_value;
if ((len = OF_getproplen(node, "freebsd,intr-gpio")) <= 0)
return (ENXIO);
OF_getencprop(node, "freebsd,intr-gpio", &dts_value, len);
sc->gpio = dts_value;
if (OF_hasprop(node, "freebsd,keymap")) {
keymap_prop = "freebsd,keymap";
device_printf(sc->dev, "using FreeBSD-specific keymap from FDT\n");
} else if (OF_hasprop(node, "linux,keymap")) {
keymap_prop = "linux,keymap";
device_printf(sc->dev, "using Linux keymap from FDT\n");
} else {
device_printf(sc->dev, "using built-in keymap\n");
}
if (keymap_prop != NULL) {
if ((ret = read_keymap(node, keymap_prop, &keymap, &len))) {
device_printf(sc->dev,
"failed to read keymap from FDT: %d\n", ret);
return (ret);
}
ret = parse_keymap(sc, keymap, len);
free(keymap, M_DEVBUF);
if (ret) {
return (ret);
}
} else {
if ((ret = parse_keymap(sc, default_keymap, KEYMAP_LEN))) {
return (ret);
}
}
if ((sc->rows == 0) || (sc->cols == 0) || (sc->gpio == 0))
return (ENXIO);
return (0);
}
void
ckb_ec_intr(void *arg)
{
struct ckb_softc *sc;
sc = arg;
if (sc->sc_flags & CKB_FLAG_POLLING)
return;
ec_command(EC_CMD_MKBP_STATE, sc->scan, sc->cols,
sc->scan, sc->cols);
(sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
sc->sc_kbd.kb_callback.kc_arg);
};
static int
chrome_kb_attach(device_t dev)
{
struct ckb_softc *sc;
keyboard_t *kbd;
int error;
int rid;
int i;
sc = device_get_softc(dev);
sc->dev = dev;
sc->keymap = NULL;
if ((error = parse_dts(sc)) != 0)
return error;
sc->gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
if (sc->gpio_dev == NULL) {
device_printf(sc->dev, "Can't find gpio device.\n");
return (ENXIO);
}
#if 0
device_printf(sc->dev, "Keyboard matrix [%dx%d]\n",
sc->cols, sc->rows);
#endif
pad_setup_intr(sc->gpio, ckb_ec_intr, sc);
kbd = &sc->sc_kbd;
rid = 0;
sc->scan_local = malloc(sc->cols, M_DEVBUF, M_NOWAIT);
sc->scan = malloc(sc->cols, M_DEVBUF, M_NOWAIT);
for (i = 0; i < sc->cols; i++) {
sc->scan_local[i] = 0;
sc->scan[i] = 0;
}
kbd_init_struct(kbd, KBD_DRIVER_NAME, KB_OTHER,
device_get_unit(dev), 0, 0, 0);
kbd->kb_data = (void *)sc;
sc->sc_keymap = key_map;
sc->sc_accmap = accent_map;
for (i = 0; i < CKB_NFKEY; i++) {
sc->sc_fkeymap[i] = fkey_tab[i];
}
kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
sc->sc_fkeymap, CKB_NFKEY);
KBD_FOUND_DEVICE(kbd);
ckb_clear_state(kbd);
KBD_PROBE_DONE(kbd);
callout_init(&sc->sc_repeat_callout, 0);
KBD_INIT_DONE(kbd);
if (kbd_register(kbd) < 0) {
return (ENXIO);
}
KBD_CONFIG_DONE(kbd);
return (0);
}
static int
chrome_kb_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_is_compatible(dev, "google,cros-ec-keyb") ||
ofw_bus_is_compatible(dev, "google,mkbp-keyb")) {
device_set_desc(dev, "Chrome EC Keyboard");
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
}
static int
chrome_kb_detach(device_t dev)
{
struct ckb_softc *sc;
sc = device_get_softc(dev);
if (sc->keymap != NULL) {
free(sc->keymap, M_DEVBUF);
}
return 0;
}
static device_method_t chrome_kb_methods[] = {
DEVMETHOD(device_probe, chrome_kb_probe),
DEVMETHOD(device_attach, chrome_kb_attach),
DEVMETHOD(device_detach, chrome_kb_detach),
{ 0, 0 }
};
static driver_t chrome_kb_driver = {
"chrome_kb",
chrome_kb_methods,
sizeof(struct ckb_softc),
};
static devclass_t chrome_kb_devclass;
DRIVER_MODULE(chrome_kb, simplebus, chrome_kb_driver,
chrome_kb_devclass, 0, 0);
Index: stable/11/sys/arm/samsung/exynos/exynos_uart.c
===================================================================
--- stable/11/sys/arm/samsung/exynos/exynos_uart.c (revision 356019)
+++ stable/11/sys/arm/samsung/exynos/exynos_uart.c (revision 356020)
@@ -1,390 +1,389 @@
/*
* Copyright (c) 2003 Marcel Moolenaar
* Copyright (c) 2007-2009 Andrew Turner
* Copyright (c) 2013 Ruslan Bukin
* 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
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "uart_if.h"
#define DEF_CLK 100000000
static int sscomspeed(long, long);
static int exynos4210_uart_param(struct uart_bas *, int, int, int, int);
/*
* Low-level UART interface.
*/
static int exynos4210_probe(struct uart_bas *bas);
static void exynos4210_init(struct uart_bas *bas, int, int, int, int);
static void exynos4210_term(struct uart_bas *bas);
static void exynos4210_putc(struct uart_bas *bas, int);
static int exynos4210_rxready(struct uart_bas *bas);
static int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx);
extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
static int
sscomspeed(long speed, long frequency)
{
int x;
if (speed <= 0 || frequency <= 0)
return (-1);
x = (frequency / 16) / speed;
return (x-1);
}
static int
exynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits,
int stopbits, int parity)
{
int brd, ulcon;
ulcon = 0;
switch(databits) {
case 5:
ulcon |= ULCON_LENGTH_5;
break;
case 6:
ulcon |= ULCON_LENGTH_6;
break;
case 7:
ulcon |= ULCON_LENGTH_7;
break;
case 8:
ulcon |= ULCON_LENGTH_8;
break;
default:
return (EINVAL);
}
switch (parity) {
case UART_PARITY_NONE:
ulcon |= ULCON_PARITY_NONE;
break;
case UART_PARITY_ODD:
ulcon |= ULCON_PARITY_ODD;
break;
case UART_PARITY_EVEN:
ulcon |= ULCON_PARITY_EVEN;
break;
case UART_PARITY_MARK:
case UART_PARITY_SPACE:
default:
return (EINVAL);
}
if (stopbits == 2)
ulcon |= ULCON_STOP;
uart_setreg(bas, SSCOM_ULCON, ulcon);
brd = sscomspeed(baudrate, bas->rclk);
uart_setreg(bas, SSCOM_UBRDIV, brd);
return (0);
}
struct uart_ops uart_exynos4210_ops = {
.probe = exynos4210_probe,
.init = exynos4210_init,
.term = exynos4210_term,
.putc = exynos4210_putc,
.rxready = exynos4210_rxready,
.getc = exynos4210_getc,
};
static int
exynos4210_probe(struct uart_bas *bas)
{
return (0);
}
static void
exynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
int parity)
{
if (bas->rclk == 0)
bas->rclk = DEF_CLK;
KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk"));
uart_setreg(bas, SSCOM_UCON, 0);
uart_setreg(bas, SSCOM_UFCON,
UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 |
UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET |
UFCON_FIFO_ENABLE);
exynos4210_uart_param(bas, baudrate, databits, stopbits, parity);
/* Enable UART. */
uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT |
UCON_TOINT);
uart_setreg(bas, SSCOM_UMCON, UMCON_RTS);
}
static void
exynos4210_term(struct uart_bas *bas)
{
/* XXX */
}
static void
exynos4210_putc(struct uart_bas *bas, int c)
{
while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) &
UFSTAT_TXFULL) == UFSTAT_TXFULL)
continue;
uart_setreg(bas, SSCOM_UTXH, c);
}
static int
exynos4210_rxready(struct uart_bas *bas)
{
return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) ==
UTRSTAT_RXREADY);
}
static int
exynos4210_getc(struct uart_bas *bas, struct mtx *mtx)
{
int utrstat;
utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
while (!(utrstat & UTRSTAT_RXREADY)) {
utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT);
continue;
}
return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH));
}
static int exynos4210_bus_probe(struct uart_softc *sc);
static int exynos4210_bus_attach(struct uart_softc *sc);
static int exynos4210_bus_flush(struct uart_softc *, int);
static int exynos4210_bus_getsig(struct uart_softc *);
static int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t);
static int exynos4210_bus_ipend(struct uart_softc *);
static int exynos4210_bus_param(struct uart_softc *, int, int, int, int);
static int exynos4210_bus_receive(struct uart_softc *);
static int exynos4210_bus_setsig(struct uart_softc *, int);
static int exynos4210_bus_transmit(struct uart_softc *);
static kobj_method_t exynos4210_methods[] = {
KOBJMETHOD(uart_probe, exynos4210_bus_probe),
KOBJMETHOD(uart_attach, exynos4210_bus_attach),
KOBJMETHOD(uart_flush, exynos4210_bus_flush),
KOBJMETHOD(uart_getsig, exynos4210_bus_getsig),
KOBJMETHOD(uart_ioctl, exynos4210_bus_ioctl),
KOBJMETHOD(uart_ipend, exynos4210_bus_ipend),
KOBJMETHOD(uart_param, exynos4210_bus_param),
KOBJMETHOD(uart_receive, exynos4210_bus_receive),
KOBJMETHOD(uart_setsig, exynos4210_bus_setsig),
KOBJMETHOD(uart_transmit, exynos4210_bus_transmit),
{0, 0 }
};
int
exynos4210_bus_probe(struct uart_softc *sc)
{
sc->sc_txfifosz = 16;
sc->sc_rxfifosz = 16;
return (0);
}
static int
exynos4210_bus_attach(struct uart_softc *sc)
{
sc->sc_hwiflow = 0;
sc->sc_hwoflow = 0;
return (0);
}
static int
exynos4210_bus_transmit(struct uart_softc *sc)
{
int i;
int reg;
uart_lock(sc->sc_hwmtx);
for (i = 0; i < sc->sc_txdatasz; i++) {
exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]);
uart_barrier(&sc->sc_bas);
}
sc->sc_txbusy = 1;
uart_unlock(sc->sc_hwmtx);
/* unmask TX interrupt */
reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM);
reg &= ~(1 << 2);
bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg);
return (0);
}
static int
exynos4210_bus_setsig(struct uart_softc *sc, int sig)
{
return (0);
}
static int
exynos4210_bus_receive(struct uart_softc *sc)
{
struct uart_bas *bas;
bas = &sc->sc_bas;
while (bus_space_read_4(bas->bst, bas->bsh,
SSCOM_UFSTAT) & UFSTAT_RXCOUNT)
uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH));
return (0);
}
static int
exynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits,
int stopbits, int parity)
{
int error;
if (sc->sc_bas.rclk == 0)
sc->sc_bas.rclk = DEF_CLK;
KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk"));
uart_lock(sc->sc_hwmtx);
error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits,
parity);
uart_unlock(sc->sc_hwmtx);
return (error);
}
static int
exynos4210_bus_ipend(struct uart_softc *sc)
{
uint32_t ints;
uint32_t txempty, rxready;
int reg;
int ipend;
uart_lock(sc->sc_hwmtx);
ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP);
bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints);
txempty = (1 << 2);
rxready = (1 << 0);
ipend = 0;
if ((ints & txempty) > 0) {
if (sc->sc_txbusy != 0)
ipend |= SER_INT_TXIDLE;
/* mask TX interrupt */
reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh,
SSCOM_UINTM);
reg |= (1 << 2);
bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh,
SSCOM_UINTM, reg);
}
if ((ints & rxready) > 0) {
ipend |= SER_INT_RXREADY;
}
uart_unlock(sc->sc_hwmtx);
return (ipend);
}
static int
exynos4210_bus_flush(struct uart_softc *sc, int what)
{
return (0);
}
static int
exynos4210_bus_getsig(struct uart_softc *sc)
{
return (0);
}
static int
exynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
{
return (EINVAL);
}
static struct uart_class uart_exynos4210_class = {
"exynos4210 class",
exynos4210_methods,
1,
.uc_ops = &uart_exynos4210_ops,
.uc_range = 8,
.uc_rclk = 0,
.uc_rshift = 0
};
static struct ofw_compat_data compat_data[] = {
{"exynos", (uintptr_t)&uart_exynos4210_class},
{NULL, (uintptr_t)NULL},
};
UART_FDT_CLASS_AND_DEVICE(compat_data);
Index: stable/11/sys/arm/versatile/pl050.c
===================================================================
--- stable/11/sys/arm/versatile/pl050.c (revision 356019)
+++ stable/11/sys/arm/versatile/pl050.c (revision 356020)
@@ -1,744 +1,745 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2012 Oleksandr Tymoshenko
* All rights reserved.
*
* Based on dev/usb/input/ukbd.c
*
* 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 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 AUTHOR 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.
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
#define KMI_LOCK() mtx_lock(&Giant)
#define KMI_UNLOCK() mtx_unlock(&Giant)
#ifdef INVARIANTS
/*
* Assert that the lock is held in all contexts
* where the code can be executed.
*/
#define KMI_LOCK_ASSERT() mtx_assert(&Giant, MA_OWNED)
/*
* Assert that the lock is held in the contexts
* where it really has to be so.
*/
#define KMI_CTX_LOCK_ASSERT() \
do { \
if (!kdb_active && panicstr == NULL) \
mtx_assert(&Giant, MA_OWNED); \
} while (0)
#else
#define KMI_LOCK_ASSERT() (void)0
#define KMI_CTX_LOCK_ASSERT() (void)0
#endif
#define KMICR 0x00
#define KMICR_TYPE_NONPS2 (1 << 5)
#define KMICR_RXINTREN (1 << 4)
#define KMICR_TXINTREN (1 << 3)
#define KMICR_EN (1 << 2)
#define KMICR_FKMID (1 << 1)
#define KMICR_FKMIC (1 << 0)
#define KMISTAT 0x04
#define KMISTAT_TXEMPTY (1 << 6)
#define KMISTAT_TXBUSY (1 << 5)
#define KMISTAT_RXFULL (1 << 4)
#define KMISTAT_RXBUSY (1 << 3)
#define KMISTAT_RXPARITY (1 << 2)
#define KMISTAT_KMIC (1 << 1)
#define KMISTAT_KMID (1 << 0)
#define KMIDATA 0x08
#define KMICLKDIV 0x0C
#define KMIIR 0x10
#define KMIIR_TXINTR (1 << 1)
#define KMIIR_RXINTR (1 << 0)
#define KMI_DRIVER_NAME "kmi"
#define KMI_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
#define SET_SCANCODE_SET 0xf0
struct kmi_softc {
device_t sc_dev;
keyboard_t sc_kbd;
keymap_t sc_keymap;
accentmap_t sc_accmap;
fkeytab_t sc_fkeymap[KMI_NFKEY];
struct resource* sc_mem_res;
struct resource* sc_irq_res;
void* sc_intr_hl;
int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
int sc_state; /* shift/lock key state */
int sc_accents; /* accent key index (> 0) */
uint32_t sc_flags; /* flags */
#define KMI_FLAG_COMPOSE 0x00000001
#define KMI_FLAG_POLLING 0x00000002
struct thread *sc_poll_thread;
};
/* Read/Write macros for Timer used as timecounter */
#define pl050_kmi_read_4(sc, reg) \
bus_read_4((sc)->sc_mem_res, (reg))
#define pl050_kmi_write_4(sc, reg, val) \
bus_write_4((sc)->sc_mem_res, (reg), (val))
/* prototypes */
static void kmi_set_leds(struct kmi_softc *, uint8_t);
static int kmi_set_typematic(keyboard_t *, int);
static uint32_t kmi_read_char(keyboard_t *, int);
static void kmi_clear_state(keyboard_t *);
static int kmi_ioctl(keyboard_t *, u_long, caddr_t);
static int kmi_enable(keyboard_t *);
static int kmi_disable(keyboard_t *);
static int kmi_attached = 0;
/* early keyboard probe, not supported */
static int
kmi_configure(int flags)
{
return (0);
}
/* detect a keyboard, not used */
static int
kmi_probe(int unit, void *arg, int flags)
{
return (ENXIO);
}
/* reset and initialize the device, not used */
static int
kmi_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
return (ENXIO);
}
/* test the interface to the device, not used */
static int
kmi_test_if(keyboard_t *kbd)
{
return (0);
}
/* finish using this keyboard, not used */
static int
kmi_term(keyboard_t *kbd)
{
return (ENXIO);
}
/* keyboard interrupt routine, not used */
static int
kmi_intr(keyboard_t *kbd, void *arg)
{
return (0);
}
/* lock the access to the keyboard, not used */
static int
kmi_lock(keyboard_t *kbd, int lock)
{
return (1);
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
kmi_enable(keyboard_t *kbd)
{
KMI_LOCK();
KBD_ACTIVATE(kbd);
KMI_UNLOCK();
return (0);
}
/* disallow the access to the device */
static int
kmi_disable(keyboard_t *kbd)
{
KMI_LOCK();
KBD_DEACTIVATE(kbd);
KMI_UNLOCK();
return (0);
}
/* check if data is waiting */
static int
kmi_check(keyboard_t *kbd)
{
struct kmi_softc *sc = kbd->kb_data;
uint32_t reg;
KMI_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (0);
reg = pl050_kmi_read_4(sc, KMIIR);
return (reg & KMIIR_RXINTR);
}
/* check if char is waiting */
static int
kmi_check_char_locked(keyboard_t *kbd)
{
KMI_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (0);
return (kmi_check(kbd));
}
static int
kmi_check_char(keyboard_t *kbd)
{
int result;
KMI_LOCK();
result = kmi_check_char_locked(kbd);
KMI_UNLOCK();
return (result);
}
/* read one byte from the keyboard if it's allowed */
/* Currently unused. */
static int
kmi_read(keyboard_t *kbd, int wait)
{
KMI_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (-1);
++(kbd->kb_count);
printf("Implement ME: %s\n", __func__);
return (0);
}
/* read char from the keyboard */
static uint32_t
kmi_read_char_locked(keyboard_t *kbd, int wait)
{
struct kmi_softc *sc = kbd->kb_data;
uint32_t reg, data;
KMI_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (NOKEY);
reg = pl050_kmi_read_4(sc, KMIIR);
if (reg & KMIIR_RXINTR) {
data = pl050_kmi_read_4(sc, KMIDATA);
return (data);
}
++kbd->kb_count;
return (NOKEY);
}
/* Currently wait is always false. */
static uint32_t
kmi_read_char(keyboard_t *kbd, int wait)
{
uint32_t keycode;
KMI_LOCK();
keycode = kmi_read_char_locked(kbd, wait);
KMI_UNLOCK();
return (keycode);
}
/* some useful control functions */
static int
kmi_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
struct kmi_softc *sc = kbd->kb_data;
int i;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
int ival;
#endif
KMI_LOCK_ASSERT();
switch (cmd) {
case KDGKBMODE: /* get keyboard mode */
*(int *)arg = sc->sc_mode;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 7):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBMODE: /* set keyboard mode */
switch (*(int *)arg) {
case K_XLATE:
if (sc->sc_mode != K_XLATE) {
/* make lock key state and LED state match */
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (sc->sc_mode != *(int *)arg) {
if ((sc->sc_flags & KMI_FLAG_POLLING) == 0)
kmi_clear_state(kbd);
sc->sc_mode = *(int *)arg;
}
break;
default:
return (EINVAL);
}
break;
case KDGETLED: /* get keyboard LED */
*(int *)arg = KBD_LED_VAL(kbd);
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 66):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETLED: /* set keyboard LED */
/* NOTE: lock key state in "sc_state" won't be changed */
if (*(int *)arg & ~LOCK_MASK)
return (EINVAL);
i = *(int *)arg;
/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
if (sc->sc_mode == K_XLATE &&
kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
if (i & ALKED)
i |= CLKED;
else
i &= ~CLKED;
}
if (KBD_HAS_DEVICE(kbd))
kmi_set_leds(sc, i);
KBD_LED_VAL(kbd) = *(int *)arg;
break;
case KDGKBSTATE: /* get lock key state */
*(int *)arg = sc->sc_state & LOCK_MASK;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 20):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBSTATE: /* set lock key state */
if (*(int *)arg & ~LOCK_MASK) {
return (EINVAL);
}
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= *(int *)arg;
/* set LEDs and quit */
return (kmi_ioctl(kbd, KDSETLED, arg));
case KDSETREPEAT: /* set keyboard repeat rate (new
* interface) */
if (!KBD_HAS_DEVICE(kbd)) {
return (0);
}
if (((int *)arg)[1] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 200) /* fastest possible value */
kbd->kb_delay1 = 200;
else
kbd->kb_delay1 = ((int *)arg)[0];
kbd->kb_delay2 = ((int *)arg)[1];
return (0);
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 67):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETRAD: /* set keyboard repeat rate (old
* interface) */
return (kmi_set_typematic(kbd, *(int *)arg));
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table
* (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table
* entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
sc->sc_accents = 0;
/* FALLTHROUGH */
default:
return (genkbd_commonioctl(kbd, cmd, arg));
}
return (0);
}
static int
kmi_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
int result;
/*
* XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any
* context where printf(9) can be called, which among other things
* includes interrupt filters and threads with any kinds of locks
* already held. For this reason it would be dangerous to acquire
* the Giant here unconditionally. On the other hand we have to
* have it to handle the ioctl.
* So we make our best effort to auto-detect whether we can grab
* the Giant or not. Blame syscons(4) for this.
*/
switch (cmd) {
case KDGKBSTATE:
case KDSKBSTATE:
case KDSETLED:
if (!mtx_owned(&Giant) && !SCHEDULER_STOPPED())
return (EDEADLK); /* best I could come up with */
/* FALLTHROUGH */
default:
KMI_LOCK();
result = kmi_ioctl_locked(kbd, cmd, arg);
KMI_UNLOCK();
return (result);
}
}
/* clear the internal state of the keyboard */
static void
kmi_clear_state(keyboard_t *kbd)
{
struct kmi_softc *sc = kbd->kb_data;
KMI_CTX_LOCK_ASSERT();
sc->sc_flags &= ~(KMI_FLAG_COMPOSE | KMI_FLAG_POLLING);
sc->sc_state &= LOCK_MASK; /* preserve locking key state */
sc->sc_accents = 0;
}
/* save the internal state, not used */
static int
kmi_get_state(keyboard_t *kbd, void *buf, size_t len)
{
return (len == 0) ? 1 : -1;
}
/* set the internal state, not used */
static int
kmi_set_state(keyboard_t *kbd, void *buf, size_t len)
{
return (EINVAL);
}
static int
kmi_poll(keyboard_t *kbd, int on)
{
struct kmi_softc *sc = kbd->kb_data;
KMI_LOCK();
if (on) {
sc->sc_flags |= KMI_FLAG_POLLING;
sc->sc_poll_thread = curthread;
} else {
sc->sc_flags &= ~KMI_FLAG_POLLING;
}
KMI_UNLOCK();
return (0);
}
/* local functions */
static void
kmi_set_leds(struct kmi_softc *sc, uint8_t leds)
{
KMI_LOCK_ASSERT();
/* start transfer, if not already started */
printf("Implement me: %s\n", __func__);
}
static int
kmi_set_typematic(keyboard_t *kbd, int code)
{
static const int delays[] = {250, 500, 750, 1000};
static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504};
if (code & ~0x7f) {
return (EINVAL);
}
kbd->kb_delay1 = delays[(code >> 5) & 3];
kbd->kb_delay2 = rates[code & 0x1f];
return (0);
}
static keyboard_switch_t kmisw = {
.probe = &kmi_probe,
.init = &kmi_init,
.term = &kmi_term,
.intr = &kmi_intr,
.test_if = &kmi_test_if,
.enable = &kmi_enable,
.disable = &kmi_disable,
.read = &kmi_read,
.check = &kmi_check,
.read_char = &kmi_read_char,
.check_char = &kmi_check_char,
.ioctl = &kmi_ioctl,
.lock = &kmi_lock,
.clear_state = &kmi_clear_state,
.get_state = &kmi_get_state,
.set_state = &kmi_set_state,
.get_fkeystr = &genkbd_get_fkeystr,
.poll = &kmi_poll,
.diag = &genkbd_diag,
};
KEYBOARD_DRIVER(kmi, kmisw, kmi_configure);
static void
pl050_kmi_intr(void *arg)
{
struct kmi_softc *sc = arg;
uint32_t c;
KMI_CTX_LOCK_ASSERT();
if ((sc->sc_flags & KMI_FLAG_POLLING) != 0)
return;
if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
KBD_IS_BUSY(&sc->sc_kbd)) {
/* let the callback function process the input */
(sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
sc->sc_kbd.kb_callback.kc_arg);
} else {
/* read and discard the input, no one is waiting for it */
do {
c = kmi_read_char_locked(&sc->sc_kbd, 0);
} while (c != NOKEY);
}
}
static int
pl050_kmi_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
/*
* PL050 is plain PS2 port that pushes bytes to/from computer
* VersatilePB has two such ports and QEMU simulates keyboard
* connected to port #0 and mouse connected to port #1. This
* information can't be obtained from device tree so we just
* hardcode this knowledge here. We attach keyboard driver to
* port #0 and ignore port #1
*/
if (kmi_attached)
return (ENXIO);
if (ofw_bus_is_compatible(dev, "arm,pl050")) {
device_set_desc(dev, "PL050 Keyboard/Mouse Interface");
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
}
static int
pl050_kmi_attach(device_t dev)
{
struct kmi_softc *sc = device_get_softc(dev);
keyboard_t *kbd;
int rid;
int i;
uint32_t ack;
sc->sc_dev = dev;
kbd = &sc->sc_kbd;
rid = 0;
sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
if (sc->sc_mem_res == NULL) {
device_printf(dev, "could not allocate memory resource\n");
return (ENXIO);
}
/* Request the IRQ resources */
sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
if (sc->sc_irq_res == NULL) {
device_printf(dev, "Error: could not allocate irq resources\n");
return (ENXIO);
}
/* Setup and enable the timer */
if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_CLK,
NULL, pl050_kmi_intr, sc,
&sc->sc_intr_hl) != 0) {
bus_release_resource(dev, SYS_RES_IRQ, rid,
sc->sc_irq_res);
device_printf(dev, "Unable to setup the clock irq handler.\n");
return (ENXIO);
}
/* TODO: clock & divisor */
pl050_kmi_write_4(sc, KMICR, KMICR_EN);
pl050_kmi_write_4(sc, KMIDATA, SET_SCANCODE_SET);
/* read out ACK */
ack = pl050_kmi_read_4(sc, KMIDATA);
/* Set Scan Code set 1 (XT) */
pl050_kmi_write_4(sc, KMIDATA, 1);
/* read out ACK */
ack = pl050_kmi_read_4(sc, KMIDATA);
pl050_kmi_write_4(sc, KMICR, KMICR_EN | KMICR_RXINTREN);
kbd_init_struct(kbd, KMI_DRIVER_NAME, KB_OTHER,
device_get_unit(dev), 0, 0, 0);
kbd->kb_data = (void *)sc;
sc->sc_keymap = key_map;
sc->sc_accmap = accent_map;
for (i = 0; i < KMI_NFKEY; i++) {
sc->sc_fkeymap[i] = fkey_tab[i];
}
kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
sc->sc_fkeymap, KMI_NFKEY);
KBD_FOUND_DEVICE(kbd);
kmi_clear_state(kbd);
KBD_PROBE_DONE(kbd);
KBD_INIT_DONE(kbd);
if (kbd_register(kbd) < 0) {
goto detach;
}
KBD_CONFIG_DONE(kbd);
#ifdef KBD_INSTALL_CDEV
if (kbd_attach(kbd)) {
goto detach;
}
#endif
if (bootverbose) {
kbdd_diag(kbd, bootverbose);
}
kmi_attached = 1;
return (0);
detach:
return (ENXIO);
}
static device_method_t pl050_kmi_methods[] = {
DEVMETHOD(device_probe, pl050_kmi_probe),
DEVMETHOD(device_attach, pl050_kmi_attach),
{ 0, 0 }
};
static driver_t pl050_kmi_driver = {
"kmi",
pl050_kmi_methods,
sizeof(struct kmi_softc),
};
static devclass_t pl050_kmi_devclass;
DRIVER_MODULE(pl050_kmi, simplebus, pl050_kmi_driver, pl050_kmi_devclass, 0, 0);
Index: stable/11/sys/arm/xilinx/uart_dev_cdnc.c
===================================================================
--- stable/11/sys/arm/xilinx/uart_dev_cdnc.c (revision 356019)
+++ stable/11/sys/arm/xilinx/uart_dev_cdnc.c (revision 356020)
@@ -1,714 +1,713 @@
/*-
* Copyright (c) 2005 M. Warner Losh
* Copyright (c) 2005 Olivier Houchard
* Copyright (c) 2012 Thomas Skibo
* 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 AUTHOR 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 AUTHOR 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.
*/
/* A driver for the Cadence AMBA UART as used by the Xilinx Zynq-7000.
*
* Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
* (v1.4) November 16, 2012. Xilinx doc UG585. UART is covered in Ch. 19
* and register definitions are in appendix B.33.
*/
#include
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
#include
#include "uart_if.h"
#define UART_FIFO_SIZE 64
#define RD4(bas, reg) \
bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg)))
#define WR4(bas, reg, value) \
bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg)), \
(value))
/* Register definitions for Cadence UART Controller.
*/
#define CDNC_UART_CTRL_REG 0x00 /* Control Register. */
#define CDNC_UART_CTRL_REG_STOPBRK (1<<8)
#define CDNC_UART_CTRL_REG_STARTBRK (1<<7)
#define CDNC_UART_CTRL_REG_TORST (1<<6)
#define CDNC_UART_CTRL_REG_TX_DIS (1<<5)
#define CDNC_UART_CTRL_REG_TX_EN (1<<4)
#define CDNC_UART_CTRL_REG_RX_DIS (1<<3)
#define CDNC_UART_CTRL_REG_RX_EN (1<<2)
#define CDNC_UART_CTRL_REG_TXRST (1<<1)
#define CDNC_UART_CTRL_REG_RXRST (1<<0)
#define CDNC_UART_MODE_REG 0x04 /* Mode Register. */
#define CDNC_UART_MODE_REG_CHMOD_R_LOOP (3<<8) /* [9:8] - channel mode */
#define CDNC_UART_MODE_REG_CHMOD_L_LOOP (2<<8)
#define CDNC_UART_MODE_REG_CHMOD_AUTECHO (1<<8)
#define CDNC_UART_MODE_REG_STOP2 (2<<6) /* [7:6] - stop bits */
#define CDNC_UART_MODE_REG_PAR_NONE (4<<3) /* [5:3] - parity type */
#define CDNC_UART_MODE_REG_PAR_MARK (3<<3)
#define CDNC_UART_MODE_REG_PAR_SPACE (2<<3)
#define CDNC_UART_MODE_REG_PAR_ODD (1<<3)
#define CDNC_UART_MODE_REG_PAR_EVEN (0<<3)
#define CDNC_UART_MODE_REG_6BIT (3<<1) /* [2:1] - character len */
#define CDNC_UART_MODE_REG_7BIT (2<<1)
#define CDNC_UART_MODE_REG_8BIT (0<<1)
#define CDNC_UART_MODE_REG_CLKSEL (1<<0)
#define CDNC_UART_IEN_REG 0x08 /* Interrupt registers. */
#define CDNC_UART_IDIS_REG 0x0C
#define CDNC_UART_IMASK_REG 0x10
#define CDNC_UART_ISTAT_REG 0x14
#define CDNC_UART_INT_TXOVR (1<<12)
#define CDNC_UART_INT_TXNRLYFUL (1<<11) /* tx "nearly" full */
#define CDNC_UART_INT_TXTRIG (1<<10)
#define CDNC_UART_INT_DMSI (1<<9) /* delta modem status */
#define CDNC_UART_INT_RXTMOUT (1<<8)
#define CDNC_UART_INT_PARITY (1<<7)
#define CDNC_UART_INT_FRAMING (1<<6)
#define CDNC_UART_INT_RXOVR (1<<5)
#define CDNC_UART_INT_TXFULL (1<<4)
#define CDNC_UART_INT_TXEMPTY (1<<3)
#define CDNC_UART_INT_RXFULL (1<<2)
#define CDNC_UART_INT_RXEMPTY (1<<1)
#define CDNC_UART_INT_RXTRIG (1<<0)
#define CDNC_UART_INT_ALL 0x1FFF
#define CDNC_UART_BAUDGEN_REG 0x18
#define CDNC_UART_RX_TIMEO_REG 0x1C
#define CDNC_UART_RX_WATER_REG 0x20
#define CDNC_UART_MODEM_CTRL_REG 0x24
#define CDNC_UART_MODEM_CTRL_REG_FCM (1<<5) /* automatic flow control */
#define CDNC_UART_MODEM_CTRL_REG_RTS (1<<1)
#define CDNC_UART_MODEM_CTRL_REG_DTR (1<<0)
#define CDNC_UART_MODEM_STAT_REG 0x28
#define CDNC_UART_MODEM_STAT_REG_FCMS (1<<8) /* flow control mode (rw) */
#define CDNC_UART_MODEM_STAT_REG_DCD (1<<7)
#define CDNC_UART_MODEM_STAT_REG_RI (1<<6)
#define CDNC_UART_MODEM_STAT_REG_DSR (1<<5)
#define CDNC_UART_MODEM_STAT_REG_CTS (1<<4)
#define CDNC_UART_MODEM_STAT_REG_DDCD (1<<3) /* change in DCD (w1tc) */
#define CDNC_UART_MODEM_STAT_REG_TERI (1<<2) /* trail edge ring (w1tc) */
#define CDNC_UART_MODEM_STAT_REG_DDSR (1<<1) /* change in DSR (w1tc) */
#define CDNC_UART_MODEM_STAT_REG_DCTS (1<<0) /* change in CTS (w1tc) */
#define CDNC_UART_CHAN_STAT_REG 0x2C /* Channel status register. */
#define CDNC_UART_CHAN_STAT_REG_TXNRLYFUL (1<<14) /* tx "nearly" full */
#define CDNC_UART_CHAN_STAT_REG_TXTRIG (1<<13)
#define CDNC_UART_CHAN_STAT_REG_FDELT (1<<12)
#define CDNC_UART_CHAN_STAT_REG_TXACTIVE (1<<11)
#define CDNC_UART_CHAN_STAT_REG_RXACTIVE (1<<10)
#define CDNC_UART_CHAN_STAT_REG_TXFULL (1<<4)
#define CDNC_UART_CHAN_STAT_REG_TXEMPTY (1<<3)
#define CDNC_UART_CHAN_STAT_REG_RXEMPTY (1<<1)
#define CDNC_UART_CHAN_STAT_REG_RXTRIG (1<<0)
#define CDNC_UART_FIFO 0x30 /* Data FIFO (tx and rx) */
#define CDNC_UART_BAUDDIV_REG 0x34
#define CDNC_UART_FLOWDEL_REG 0x38
#define CDNC_UART_TX_WATER_REG 0x44
/*
* Low-level UART interface.
*/
static int cdnc_uart_probe(struct uart_bas *bas);
static void cdnc_uart_init(struct uart_bas *bas, int, int, int, int);
static void cdnc_uart_term(struct uart_bas *bas);
static void cdnc_uart_putc(struct uart_bas *bas, int);
static int cdnc_uart_rxready(struct uart_bas *bas);
static int cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx);
extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs;
static struct uart_ops cdnc_uart_ops = {
.probe = cdnc_uart_probe,
.init = cdnc_uart_init,
.term = cdnc_uart_term,
.putc = cdnc_uart_putc,
.rxready = cdnc_uart_rxready,
.getc = cdnc_uart_getc,
};
#define SIGCHG(c, i, s, d) \
if (c) { \
i |= (i & s) ? s : s | d; \
} else { \
i = (i & s) ? (i & ~s) | d : i; \
}
static int
cdnc_uart_probe(struct uart_bas *bas)
{
return (0);
}
static int
cdnc_uart_set_baud(struct uart_bas *bas, int baudrate)
{
uint32_t baudgen, bauddiv;
uint32_t best_bauddiv, best_baudgen, best_error;
uint32_t baud_out, err;
best_bauddiv = 0;
best_baudgen = 0;
best_error = ~0;
/* Try all possible bauddiv values and pick best match. */
for (bauddiv = 4; bauddiv <= 255; bauddiv++) {
baudgen = (bas->rclk + (baudrate * (bauddiv + 1)) / 2) /
(baudrate * (bauddiv + 1));
if (baudgen < 1 || baudgen > 0xffff)
continue;
baud_out = bas->rclk / (baudgen * (bauddiv + 1));
err = baud_out > baudrate ?
baud_out - baudrate : baudrate - baud_out;
if (err < best_error) {
best_error = err;
best_bauddiv = bauddiv;
best_baudgen = baudgen;
}
}
if (best_bauddiv > 0) {
WR4(bas, CDNC_UART_BAUDDIV_REG, best_bauddiv);
WR4(bas, CDNC_UART_BAUDGEN_REG, best_baudgen);
return (0);
} else
return (-1); /* out of range */
}
static int
cdnc_uart_set_params(struct uart_bas *bas, int baudrate, int databits,
int stopbits, int parity)
{
uint32_t mode_reg_value = 0;
switch (databits) {
case 6:
mode_reg_value |= CDNC_UART_MODE_REG_6BIT;
break;
case 7:
mode_reg_value |= CDNC_UART_MODE_REG_7BIT;
break;
case 8:
default:
mode_reg_value |= CDNC_UART_MODE_REG_8BIT;
break;
}
if (stopbits == 2)
mode_reg_value |= CDNC_UART_MODE_REG_STOP2;
switch (parity) {
case UART_PARITY_MARK:
mode_reg_value |= CDNC_UART_MODE_REG_PAR_MARK;
break;
case UART_PARITY_SPACE:
mode_reg_value |= CDNC_UART_MODE_REG_PAR_SPACE;
break;
case UART_PARITY_ODD:
mode_reg_value |= CDNC_UART_MODE_REG_PAR_ODD;
break;
case UART_PARITY_EVEN:
mode_reg_value |= CDNC_UART_MODE_REG_PAR_EVEN;
break;
case UART_PARITY_NONE:
default:
mode_reg_value |= CDNC_UART_MODE_REG_PAR_NONE;
break;
}
WR4(bas, CDNC_UART_MODE_REG, mode_reg_value);
if (baudrate > 0 && cdnc_uart_set_baud(bas, baudrate) < 0)
return (EINVAL);
return(0);
}
static void
cdnc_uart_hw_init(struct uart_bas *bas)
{
/* Reset RX and TX. */
WR4(bas, CDNC_UART_CTRL_REG,
CDNC_UART_CTRL_REG_RXRST | CDNC_UART_CTRL_REG_TXRST);
/* Interrupts all off. */
WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_ALL);
WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_ALL);
/* Clear delta bits. */
WR4(bas, CDNC_UART_MODEM_STAT_REG,
CDNC_UART_MODEM_STAT_REG_DDCD | CDNC_UART_MODEM_STAT_REG_TERI |
CDNC_UART_MODEM_STAT_REG_DDSR | CDNC_UART_MODEM_STAT_REG_DCTS);
/* RX FIFO water level, stale timeout */
WR4(bas, CDNC_UART_RX_WATER_REG, UART_FIFO_SIZE/2);
WR4(bas, CDNC_UART_RX_TIMEO_REG, 10);
/* TX FIFO water level (not used.) */
WR4(bas, CDNC_UART_TX_WATER_REG, UART_FIFO_SIZE/2);
/* Bring RX and TX online. */
WR4(bas, CDNC_UART_CTRL_REG,
CDNC_UART_CTRL_REG_RX_EN | CDNC_UART_CTRL_REG_TX_EN |
CDNC_UART_CTRL_REG_TORST | CDNC_UART_CTRL_REG_STOPBRK);
/* Set DTR and RTS. */
WR4(bas, CDNC_UART_MODEM_CTRL_REG, CDNC_UART_MODEM_CTRL_REG_DTR |
CDNC_UART_MODEM_CTRL_REG_RTS);
}
/*
* Initialize this device for use as a console.
*/
static void
cdnc_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
int parity)
{
/* Initialize hardware. */
cdnc_uart_hw_init(bas);
/* Set baudrate, parameters. */
(void)cdnc_uart_set_params(bas, baudrate, databits, stopbits, parity);
}
/*
* Free resources now that we're no longer the console. This appears to
* be never called, and I'm unsure quite what to do if I am called.
*/
static void
cdnc_uart_term(struct uart_bas *bas)
{
/* XXX */
}
/*
* Put a character of console output (so we do it here polling rather than
* interrutp driven).
*/
static void
cdnc_uart_putc(struct uart_bas *bas, int c)
{
/* Wait for room. */
while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) &
CDNC_UART_CHAN_STAT_REG_TXFULL) != 0)
;
WR4(bas, CDNC_UART_FIFO, c);
while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) &
CDNC_UART_CHAN_STAT_REG_TXEMPTY) == 0)
;
}
/*
* Check for a character available.
*/
static int
cdnc_uart_rxready(struct uart_bas *bas)
{
return ((RD4(bas, CDNC_UART_CHAN_STAT_REG) &
CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0);
}
/*
* Block waiting for a character.
*/
static int
cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx)
{
int c;
uart_lock(mtx);
while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) &
CDNC_UART_CHAN_STAT_REG_RXEMPTY) != 0) {
uart_unlock(mtx);
DELAY(4);
uart_lock(mtx);
}
c = RD4(bas, CDNC_UART_FIFO);
uart_unlock(mtx);
c &= 0xff;
return (c);
}
/*****************************************************************************/
/*
* High-level UART interface.
*/
static int cdnc_uart_bus_probe(struct uart_softc *sc);
static int cdnc_uart_bus_attach(struct uart_softc *sc);
static int cdnc_uart_bus_flush(struct uart_softc *, int);
static int cdnc_uart_bus_getsig(struct uart_softc *);
static int cdnc_uart_bus_ioctl(struct uart_softc *, int, intptr_t);
static int cdnc_uart_bus_ipend(struct uart_softc *);
static int cdnc_uart_bus_param(struct uart_softc *, int, int, int, int);
static int cdnc_uart_bus_receive(struct uart_softc *);
static int cdnc_uart_bus_setsig(struct uart_softc *, int);
static int cdnc_uart_bus_transmit(struct uart_softc *);
static void cdnc_uart_bus_grab(struct uart_softc *);
static void cdnc_uart_bus_ungrab(struct uart_softc *);
static kobj_method_t cdnc_uart_bus_methods[] = {
KOBJMETHOD(uart_probe, cdnc_uart_bus_probe),
KOBJMETHOD(uart_attach, cdnc_uart_bus_attach),
KOBJMETHOD(uart_flush, cdnc_uart_bus_flush),
KOBJMETHOD(uart_getsig, cdnc_uart_bus_getsig),
KOBJMETHOD(uart_ioctl, cdnc_uart_bus_ioctl),
KOBJMETHOD(uart_ipend, cdnc_uart_bus_ipend),
KOBJMETHOD(uart_param, cdnc_uart_bus_param),
KOBJMETHOD(uart_receive, cdnc_uart_bus_receive),
KOBJMETHOD(uart_setsig, cdnc_uart_bus_setsig),
KOBJMETHOD(uart_transmit, cdnc_uart_bus_transmit),
KOBJMETHOD(uart_grab, cdnc_uart_bus_grab),
KOBJMETHOD(uart_ungrab, cdnc_uart_bus_ungrab),
KOBJMETHOD_END
};
int
cdnc_uart_bus_probe(struct uart_softc *sc)
{
sc->sc_txfifosz = UART_FIFO_SIZE;
sc->sc_rxfifosz = UART_FIFO_SIZE;
sc->sc_hwiflow = 0;
sc->sc_hwoflow = 0;
device_set_desc(sc->sc_dev, "Cadence UART");
return (0);
}
static int
cdnc_uart_bus_attach(struct uart_softc *sc)
{
struct uart_bas *bas = &sc->sc_bas;
struct uart_devinfo *di;
if (sc->sc_sysdev != NULL) {
di = sc->sc_sysdev;
(void)cdnc_uart_set_params(bas, di->baudrate, di->databits,
di->stopbits, di->parity);
} else
cdnc_uart_hw_init(bas);
(void)cdnc_uart_bus_getsig(sc);
/* Enable interrupts. */
WR4(bas, CDNC_UART_IEN_REG,
CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT |
CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
CDNC_UART_INT_DMSI);
return (0);
}
static int
cdnc_uart_bus_transmit(struct uart_softc *sc)
{
int i;
struct uart_bas *bas = &sc->sc_bas;
uart_lock(sc->sc_hwmtx);
/* Clear sticky TXEMPTY status bit. */
WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_TXEMPTY);
for (i = 0; i < sc->sc_txdatasz; i++)
WR4(bas, CDNC_UART_FIFO, sc->sc_txbuf[i]);
/* Enable TX empty interrupt. */
WR4(bas, CDNC_UART_IEN_REG, CDNC_UART_INT_TXEMPTY);
sc->sc_txbusy = 1;
uart_unlock(sc->sc_hwmtx);
return (0);
}
static int
cdnc_uart_bus_setsig(struct uart_softc *sc, int sig)
{
struct uart_bas *bas = &sc->sc_bas;
uint32_t new, old, modem_ctrl;
do {
old = sc->sc_hwsig;
new = old;
if (sig & SER_DDTR) {
SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
}
if (sig & SER_DRTS) {
SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
}
} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
uart_lock(sc->sc_hwmtx);
modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG) &
~(CDNC_UART_MODEM_CTRL_REG_DTR | CDNC_UART_MODEM_CTRL_REG_RTS);
if ((new & SER_DTR) != 0)
modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_DTR;
if ((new & SER_RTS) != 0)
modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS;
WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl);
uart_unlock(sc->sc_hwmtx);
return (0);
}
static int
cdnc_uart_bus_receive(struct uart_softc *sc)
{
struct uart_bas *bas = &sc->sc_bas;
uint32_t status;
int c, c_status = 0;
uart_lock(sc->sc_hwmtx);
/* Check for parity or framing errors and clear the status bits. */
status = RD4(bas, CDNC_UART_ISTAT_REG);
if ((status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)) != 0) {
WR4(bas, CDNC_UART_ISTAT_REG,
status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY));
if ((status & CDNC_UART_INT_PARITY) != 0)
c_status |= UART_STAT_PARERR;
if ((status & CDNC_UART_INT_FRAMING) != 0)
c_status |= UART_STAT_FRAMERR;
}
while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) &
CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0) {
c = RD4(bas, CDNC_UART_FIFO) & 0xff;
#ifdef KDB
/* Detect break and drop into debugger. */
if (c == 0 && (c_status & UART_STAT_FRAMERR) != 0 &&
sc->sc_sysdev != NULL &&
sc->sc_sysdev->type == UART_DEV_CONSOLE) {
kdb_break();
WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_FRAMING);
}
#endif
uart_rx_put(sc, c | c_status);
}
uart_unlock(sc->sc_hwmtx);
return (0);
}
static int
cdnc_uart_bus_param(struct uart_softc *sc, int baudrate, int databits,
int stopbits, int parity)
{
return (cdnc_uart_set_params(&sc->sc_bas, baudrate,
databits, stopbits, parity));
}
static int
cdnc_uart_bus_ipend(struct uart_softc *sc)
{
int ipend = 0;
struct uart_bas *bas = &sc->sc_bas;
uint32_t istatus;
uart_lock(sc->sc_hwmtx);
istatus = RD4(bas, CDNC_UART_ISTAT_REG);
/* Clear interrupt bits. */
WR4(bas, CDNC_UART_ISTAT_REG, istatus &
(CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT |
CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
CDNC_UART_INT_TXEMPTY | CDNC_UART_INT_DMSI));
/* Receive data. */
if ((istatus & (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT)) != 0)
ipend |= SER_INT_RXREADY;
/* Transmit fifo empty. */
if (sc->sc_txbusy && (istatus & CDNC_UART_INT_TXEMPTY) != 0) {
/* disable txempty interrupt. */
WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_TXEMPTY);
ipend |= SER_INT_TXIDLE;
}
/* TX Overflow. */
if ((istatus & CDNC_UART_INT_TXOVR) != 0)
ipend |= SER_INT_OVERRUN;
/* RX Overflow. */
if ((istatus & CDNC_UART_INT_RXOVR) != 0)
ipend |= SER_INT_OVERRUN;
/* Modem signal change. */
if ((istatus & CDNC_UART_INT_DMSI) != 0) {
WR4(bas, CDNC_UART_MODEM_STAT_REG,
CDNC_UART_MODEM_STAT_REG_DDCD |
CDNC_UART_MODEM_STAT_REG_TERI |
CDNC_UART_MODEM_STAT_REG_DDSR |
CDNC_UART_MODEM_STAT_REG_DCTS);
ipend |= SER_INT_SIGCHG;
}
uart_unlock(sc->sc_hwmtx);
return (ipend);
}
static int
cdnc_uart_bus_flush(struct uart_softc *sc, int what)
{
return (0);
}
static int
cdnc_uart_bus_getsig(struct uart_softc *sc)
{
struct uart_bas *bas = &sc->sc_bas;
uint32_t new, old, sig;
uint8_t modem_status;
do {
old = sc->sc_hwsig;
sig = old;
uart_lock(sc->sc_hwmtx);
modem_status = RD4(bas, CDNC_UART_MODEM_STAT_REG);
uart_unlock(sc->sc_hwmtx);
SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DSR,
sig, SER_DSR, SER_DDSR);
SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_CTS,
sig, SER_CTS, SER_DCTS);
SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DCD,
sig, SER_DCD, SER_DDCD);
SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_RI,
sig, SER_RI, SER_DRI);
new = sig & ~SER_MASK_DELTA;
} while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
return (sig);
}
static int
cdnc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
{
struct uart_bas *bas = &sc->sc_bas;
uint32_t uart_ctrl, modem_ctrl;
int error = 0;
uart_lock(sc->sc_hwmtx);
switch (request) {
case UART_IOCTL_BREAK:
uart_ctrl = RD4(bas, CDNC_UART_CTRL_REG);
if (data) {
uart_ctrl |= CDNC_UART_CTRL_REG_STARTBRK;
uart_ctrl &= ~CDNC_UART_CTRL_REG_STOPBRK;
} else {
uart_ctrl |= CDNC_UART_CTRL_REG_STOPBRK;
uart_ctrl &= ~CDNC_UART_CTRL_REG_STARTBRK;
}
WR4(bas, CDNC_UART_CTRL_REG, uart_ctrl);
break;
case UART_IOCTL_IFLOW:
modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG);
if (data)
modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS;
else
modem_ctrl &= ~CDNC_UART_MODEM_CTRL_REG_RTS;
WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl);
break;
default:
error = EINVAL;
break;
}
uart_unlock(sc->sc_hwmtx);
return (error);
}
static void
cdnc_uart_bus_grab(struct uart_softc *sc)
{
/* Enable interrupts. */
WR4(&sc->sc_bas, CDNC_UART_IEN_REG,
CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
CDNC_UART_INT_DMSI);
}
static void
cdnc_uart_bus_ungrab(struct uart_softc *sc)
{
/* Enable interrupts. */
WR4(&sc->sc_bas, CDNC_UART_IEN_REG,
CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT |
CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
CDNC_UART_INT_DMSI);
}
static struct uart_class uart_cdnc_class = {
"cdnc_uart",
cdnc_uart_bus_methods,
sizeof(struct uart_softc),
.uc_ops = &cdnc_uart_ops,
.uc_range = 8
};
static struct ofw_compat_data compat_data[] = {
{"cadence,uart", (uintptr_t)&uart_cdnc_class},
{NULL, (uintptr_t)NULL},
};
UART_FDT_CLASS_AND_DEVICE(compat_data);
Index: stable/11/sys/dev/gpio/gpiokeys.c
===================================================================
--- stable/11/sys/dev/gpio/gpiokeys.c (revision 356019)
+++ stable/11/sys/dev/gpio/gpiokeys.c (revision 356020)
@@ -1,1015 +1,1014 @@
/*-
* Copyright (c) 2015-2016 Oleksandr Tymoshenko
* 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 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 AUTHOR 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.
*/
#include
__FBSDID("$FreeBSD$");
#include "opt_platform.h"
#include "opt_kbd.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
#include
#include
#include
#include
#define KBD_DRIVER_NAME "gpiokeys"
#define GPIOKEYS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
#define GPIOKEYS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
#define GPIOKEYS_LOCK_INIT(_sc) \
mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
"gpiokeys", MTX_DEF)
#define GPIOKEYS_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx);
#define GPIOKEYS_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
#define GPIOKEY_LOCK(_key) mtx_lock(&(_key)->mtx)
#define GPIOKEY_UNLOCK(_key) mtx_unlock(&(_key)->mtx)
#define GPIOKEY_LOCK_INIT(_key) \
mtx_init(&(_key)->mtx, "gpiokey", "gpiokey", MTX_DEF)
#define GPIOKEY_LOCK_DESTROY(_key) mtx_destroy(&(_key)->mtx);
#define KEY_PRESS 0
#define KEY_RELEASE 0x80
#define SCAN_PRESS 0
#define SCAN_RELEASE 0x80
#define SCAN_CHAR(c) ((c) & 0x7f)
#define GPIOKEYS_GLOBAL_NMOD 8 /* units */
#define GPIOKEYS_GLOBAL_NKEYCODE 6 /* units */
#define GPIOKEYS_GLOBAL_IN_BUF_SIZE (2*(GPIOKEYS_GLOBAL_NMOD + (2*GPIOKEYS_GLOBAL_NKEYCODE))) /* bytes */
#define GPIOKEYS_GLOBAL_IN_BUF_FULL (GPIOKEYS_GLOBAL_IN_BUF_SIZE / 2) /* bytes */
#define GPIOKEYS_GLOBAL_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
#define GPIOKEYS_GLOBAL_BUFFER_SIZE 64 /* bytes */
#define AUTOREPEAT_DELAY 250
#define AUTOREPEAT_REPEAT 34
struct gpiokeys_softc;
struct gpiokey
{
struct gpiokeys_softc *parent_sc;
gpio_pin_t pin;
int irq_rid;
struct resource *irq_res;
void *intr_hl;
struct mtx mtx;
uint32_t keycode;
int autorepeat;
struct callout debounce_callout;
struct callout repeat_callout;
int repeat_delay;
int repeat;
int debounce_interval;
};
struct gpiokeys_softc
{
device_t sc_dev;
struct mtx sc_mtx;
struct gpiokey *sc_keys;
int sc_total_keys;
keyboard_t sc_kbd;
keymap_t sc_keymap;
accentmap_t sc_accmap;
fkeytab_t sc_fkeymap[GPIOKEYS_GLOBAL_NFKEY];
uint32_t sc_input[GPIOKEYS_GLOBAL_IN_BUF_SIZE]; /* input buffer */
uint32_t sc_time_ms;
#define GPIOKEYS_GLOBAL_FLAG_POLLING 0x00000002
uint32_t sc_flags; /* flags */
int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
int sc_state; /* shift/lock key state */
int sc_accents; /* accent key index (> 0) */
int sc_kbd_size;
uint16_t sc_inputs;
uint16_t sc_inputhead;
uint16_t sc_inputtail;
uint8_t sc_kbd_id;
};
/* gpio-keys device */
static int gpiokeys_probe(device_t);
static int gpiokeys_attach(device_t);
static int gpiokeys_detach(device_t);
/* kbd methods prototypes */
static int gpiokeys_set_typematic(keyboard_t *, int);
static uint32_t gpiokeys_read_char(keyboard_t *, int);
static void gpiokeys_clear_state(keyboard_t *);
static int gpiokeys_ioctl(keyboard_t *, u_long, caddr_t);
static int gpiokeys_enable(keyboard_t *);
static int gpiokeys_disable(keyboard_t *);
static void gpiokeys_event_keyinput(struct gpiokeys_softc *);
static void
gpiokeys_put_key(struct gpiokeys_softc *sc, uint32_t key)
{
GPIOKEYS_ASSERT_LOCKED(sc);
if (sc->sc_inputs < GPIOKEYS_GLOBAL_IN_BUF_SIZE) {
sc->sc_input[sc->sc_inputtail] = key;
++(sc->sc_inputs);
++(sc->sc_inputtail);
if (sc->sc_inputtail >= GPIOKEYS_GLOBAL_IN_BUF_SIZE) {
sc->sc_inputtail = 0;
}
} else {
device_printf(sc->sc_dev, "input buffer is full\n");
}
}
static void
gpiokeys_key_event(struct gpiokeys_softc *sc, uint16_t keycode, int pressed)
{
uint32_t key;
key = keycode & SCAN_KEYCODE_MASK;
if (!pressed)
key |= KEY_RELEASE;
GPIOKEYS_LOCK(sc);
if (keycode & SCAN_PREFIX_E0)
gpiokeys_put_key(sc, 0xe0);
else if (keycode & SCAN_PREFIX_E1)
gpiokeys_put_key(sc, 0xe1);
gpiokeys_put_key(sc, key);
GPIOKEYS_UNLOCK(sc);
gpiokeys_event_keyinput(sc);
}
static void
gpiokey_autorepeat(void *arg)
{
struct gpiokey *key;
key = arg;
if (key->keycode == GPIOKEY_NONE)
return;
gpiokeys_key_event(key->parent_sc, key->keycode, 1);
callout_reset(&key->repeat_callout, key->repeat,
gpiokey_autorepeat, key);
}
static void
gpiokey_debounced_intr(void *arg)
{
struct gpiokey *key;
bool active;
key = arg;
if (key->keycode == GPIOKEY_NONE)
return;
gpio_pin_is_active(key->pin, &active);
if (active) {
gpiokeys_key_event(key->parent_sc, key->keycode, 1);
if (key->autorepeat) {
callout_reset(&key->repeat_callout, key->repeat_delay,
gpiokey_autorepeat, key);
}
}
else {
if (key->autorepeat &&
callout_pending(&key->repeat_callout))
callout_stop(&key->repeat_callout);
gpiokeys_key_event(key->parent_sc, key->keycode, 0);
}
}
static void
gpiokey_intr(void *arg)
{
struct gpiokey *key;
int debounce_ticks;
key = arg;
GPIOKEY_LOCK(key);
debounce_ticks = (hz * key->debounce_interval) / 1000;
if (debounce_ticks == 0)
debounce_ticks = 1;
if (!callout_pending(&key->debounce_callout))
callout_reset(&key->debounce_callout, debounce_ticks,
gpiokey_debounced_intr, key);
GPIOKEY_UNLOCK(key);
}
static void
gpiokeys_attach_key(struct gpiokeys_softc *sc, phandle_t node,
struct gpiokey *key)
{
pcell_t prop;
char *name;
uint32_t code;
int err;
const char *key_name;
GPIOKEY_LOCK_INIT(key);
key->parent_sc = sc;
callout_init_mtx(&key->debounce_callout, &key->mtx, 0);
callout_init_mtx(&key->repeat_callout, &key->mtx, 0);
name = NULL;
if (OF_getprop_alloc(node, "label", 1, (void **)&name) == -1)
OF_getprop_alloc(node, "name", 1, (void **)&name);
if (name != NULL)
key_name = name;
else
key_name = "unknown";
key->autorepeat = OF_hasprop(node, "autorepeat");
key->repeat_delay = (hz * AUTOREPEAT_DELAY) / 1000;
if (key->repeat_delay == 0)
key->repeat_delay = 1;
key->repeat = (hz * AUTOREPEAT_REPEAT) / 1000;
if (key->repeat == 0)
key->repeat = 1;
if ((OF_getprop(node, "debounce-interval", &prop, sizeof(prop))) > 0)
key->debounce_interval = fdt32_to_cpu(prop);
else
key->debounce_interval = 5;
if ((OF_getprop(node, "freebsd,code", &prop, sizeof(prop))) > 0)
key->keycode = fdt32_to_cpu(prop);
else if ((OF_getprop(node, "linux,code", &prop, sizeof(prop))) > 0) {
code = fdt32_to_cpu(prop);
key->keycode = gpiokey_map_linux_code(code);
if (key->keycode == GPIOKEY_NONE)
device_printf(sc->sc_dev, "<%s> failed to map linux,code value 0x%x\n",
key_name, code);
}
else
device_printf(sc->sc_dev, "<%s> no linux,code or freebsd,code property\n",
key_name);
err = gpio_pin_get_by_ofw_idx(sc->sc_dev, node, 0, &key->pin);
if (err) {
device_printf(sc->sc_dev, "<%s> failed to map pin\n", key_name);
if (name)
OF_prop_free(name);
return;
}
key->irq_res = gpio_alloc_intr_resource(sc->sc_dev, &key->irq_rid,
RF_ACTIVE, key->pin, GPIO_INTR_EDGE_BOTH);
if (!key->irq_res) {
device_printf(sc->sc_dev, "<%s> cannot allocate interrupt\n", key_name);
gpio_pin_release(key->pin);
key->pin = NULL;
if (name)
OF_prop_free(name);
return;
}
if (bus_setup_intr(sc->sc_dev, key->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
NULL, gpiokey_intr, key,
&key->intr_hl) != 0) {
device_printf(sc->sc_dev, "<%s> unable to setup the irq handler\n", key_name);
bus_release_resource(sc->sc_dev, SYS_RES_IRQ, key->irq_rid,
key->irq_res);
gpio_pin_release(key->pin);
key->pin = NULL;
key->irq_res = NULL;
if (name)
OF_prop_free(name);
return;
}
if (bootverbose)
device_printf(sc->sc_dev, "<%s> code=%08x, autorepeat=%d, "\
"repeat=%d, repeat_delay=%d\n", key_name, key->keycode,
key->autorepeat, key->repeat, key->repeat_delay);
if (name)
OF_prop_free(name);
}
static void
gpiokeys_detach_key(struct gpiokeys_softc *sc, struct gpiokey *key)
{
GPIOKEY_LOCK(key);
if (key->intr_hl)
bus_teardown_intr(sc->sc_dev, key->irq_res, key->intr_hl);
if (key->irq_res)
bus_release_resource(sc->sc_dev, SYS_RES_IRQ,
key->irq_rid, key->irq_res);
if (callout_pending(&key->repeat_callout))
callout_drain(&key->repeat_callout);
if (callout_pending(&key->debounce_callout))
callout_drain(&key->debounce_callout);
if (key->pin)
gpio_pin_release(key->pin);
GPIOKEY_UNLOCK(key);
GPIOKEY_LOCK_DESTROY(key);
}
static int
gpiokeys_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "gpio-keys"))
return (ENXIO);
device_set_desc(dev, "GPIO keyboard");
return (0);
}
static int
gpiokeys_attach(device_t dev)
{
int unit;
struct gpiokeys_softc *sc;
keyboard_t *kbd;
phandle_t keys, child;
int total_keys;
if ((keys = ofw_bus_get_node(dev)) == -1)
return (ENXIO);
sc = device_get_softc(dev);
sc->sc_dev = dev;
kbd = &sc->sc_kbd;
GPIOKEYS_LOCK_INIT(sc);
unit = device_get_unit(dev);
kbd_init_struct(kbd, KBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
kbd->kb_data = (void *)sc;
sc->sc_mode = K_XLATE;
sc->sc_keymap = key_map;
sc->sc_accmap = accent_map;
kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
sc->sc_fkeymap, GPIOKEYS_GLOBAL_NFKEY);
KBD_FOUND_DEVICE(kbd);
gpiokeys_clear_state(kbd);
KBD_PROBE_DONE(kbd);
KBD_INIT_DONE(kbd);
if (kbd_register(kbd) < 0) {
goto detach;
}
KBD_CONFIG_DONE(kbd);
gpiokeys_enable(kbd);
#ifdef KBD_INSTALL_CDEV
if (kbd_attach(kbd)) {
goto detach;
}
#endif
if (bootverbose) {
kbdd_diag(kbd, 1);
}
total_keys = 0;
/* Traverse the 'gpio-keys' node and count keys */
for (child = OF_child(keys); child != 0; child = OF_peer(child)) {
if (!OF_hasprop(child, "gpios"))
continue;
total_keys++;
}
if (total_keys) {
sc->sc_keys = malloc(sizeof(struct gpiokey) * total_keys,
M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_total_keys = 0;
/* Traverse the 'gpio-keys' node and count keys */
for (child = OF_child(keys); child != 0; child = OF_peer(child)) {
if (!OF_hasprop(child, "gpios"))
continue;
gpiokeys_attach_key(sc, child ,&sc->sc_keys[sc->sc_total_keys]);
sc->sc_total_keys++;
}
}
return (0);
detach:
gpiokeys_detach(dev);
return (ENXIO);
}
static int
gpiokeys_detach(device_t dev)
{
struct gpiokeys_softc *sc;
keyboard_t *kbd;
int i;
sc = device_get_softc(dev);
for (i = 0; i < sc->sc_total_keys; i++)
gpiokeys_detach_key(sc, &sc->sc_keys[i]);
kbd = kbd_get_keyboard(kbd_find_keyboard(KBD_DRIVER_NAME,
device_get_unit(dev)));
#ifdef KBD_INSTALL_CDEV
kbd_detach(kbd);
#endif
kbd_unregister(kbd);
GPIOKEYS_LOCK_DESTROY(sc);
if (sc->sc_keys)
free(sc->sc_keys, M_DEVBUF);
return (0);
}
/* early keyboard probe, not supported */
static int
gpiokeys_configure(int flags)
{
return (0);
}
/* detect a keyboard, not used */
static int
gpiokeys__probe(int unit, void *arg, int flags)
{
return (ENXIO);
}
/* reset and initialize the device, not used */
static int
gpiokeys_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
return (ENXIO);
}
/* test the interface to the device, not used */
static int
gpiokeys_test_if(keyboard_t *kbd)
{
return (0);
}
/* finish using this keyboard, not used */
static int
gpiokeys_term(keyboard_t *kbd)
{
return (ENXIO);
}
/* keyboard interrupt routine, not used */
static int
gpiokeys_intr(keyboard_t *kbd, void *arg)
{
return (0);
}
/* lock the access to the keyboard, not used */
static int
gpiokeys_lock(keyboard_t *kbd, int lock)
{
return (1);
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
gpiokeys_enable(keyboard_t *kbd)
{
struct gpiokeys_softc *sc;
sc = kbd->kb_data;
GPIOKEYS_LOCK(sc);
KBD_ACTIVATE(kbd);
GPIOKEYS_UNLOCK(sc);
return (0);
}
/* disallow the access to the device */
static int
gpiokeys_disable(keyboard_t *kbd)
{
struct gpiokeys_softc *sc;
sc = kbd->kb_data;
GPIOKEYS_LOCK(sc);
KBD_DEACTIVATE(kbd);
GPIOKEYS_UNLOCK(sc);
return (0);
}
static void
gpiokeys_do_poll(struct gpiokeys_softc *sc, uint8_t wait)
{
KASSERT((sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) != 0,
("gpiokeys_do_poll called when not polling\n"));
GPIOKEYS_ASSERT_LOCKED(sc);
if (!kdb_active && !SCHEDULER_STOPPED()) {
while (sc->sc_inputs == 0) {
kern_yield(PRI_UNCHANGED);
if (!wait)
break;
}
return;
}
while ((sc->sc_inputs == 0) && wait) {
printf("POLL!\n");
}
}
/* check if data is waiting */
static int
gpiokeys_check(keyboard_t *kbd)
{
struct gpiokeys_softc *sc = kbd->kb_data;
GPIOKEYS_ASSERT_LOCKED(sc);
if (!KBD_IS_ACTIVE(kbd))
return (0);
if (sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING)
gpiokeys_do_poll(sc, 0);
if (sc->sc_inputs > 0) {
return (1);
}
return (0);
}
/* check if char is waiting */
static int
gpiokeys_check_char_locked(keyboard_t *kbd)
{
if (!KBD_IS_ACTIVE(kbd))
return (0);
return (gpiokeys_check(kbd));
}
static int
gpiokeys_check_char(keyboard_t *kbd)
{
int result;
struct gpiokeys_softc *sc = kbd->kb_data;
GPIOKEYS_LOCK(sc);
result = gpiokeys_check_char_locked(kbd);
GPIOKEYS_UNLOCK(sc);
return (result);
}
static int32_t
gpiokeys_get_key(struct gpiokeys_softc *sc, uint8_t wait)
{
int32_t c;
KASSERT((!kdb_active && !SCHEDULER_STOPPED())
|| (sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) != 0,
("not polling in kdb or panic\n"));
GPIOKEYS_ASSERT_LOCKED(sc);
if (sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING)
gpiokeys_do_poll(sc, wait);
if (sc->sc_inputs == 0) {
c = -1;
} else {
c = sc->sc_input[sc->sc_inputhead];
--(sc->sc_inputs);
++(sc->sc_inputhead);
if (sc->sc_inputhead >= GPIOKEYS_GLOBAL_IN_BUF_SIZE) {
sc->sc_inputhead = 0;
}
}
return (c);
}
/* read one byte from the keyboard if it's allowed */
static int
gpiokeys_read(keyboard_t *kbd, int wait)
{
struct gpiokeys_softc *sc = kbd->kb_data;
int32_t keycode;
if (!KBD_IS_ACTIVE(kbd))
return (-1);
/* XXX */
keycode = gpiokeys_get_key(sc, (wait == FALSE) ? 0 : 1);
if (!KBD_IS_ACTIVE(kbd) || (keycode == -1))
return (-1);
++(kbd->kb_count);
return (keycode);
}
/* read char from the keyboard */
static uint32_t
gpiokeys_read_char_locked(keyboard_t *kbd, int wait)
{
struct gpiokeys_softc *sc = kbd->kb_data;
uint32_t action;
uint32_t keycode;
if (!KBD_IS_ACTIVE(kbd))
return (NOKEY);
next_code:
/* see if there is something in the keyboard port */
/* XXX */
keycode = gpiokeys_get_key(sc, (wait == FALSE) ? 0 : 1);
++kbd->kb_count;
/* return the byte as is for the K_RAW mode */
if (sc->sc_mode == K_RAW) {
return (keycode);
}
/* return the key code in the K_CODE mode */
/* XXX: keycode |= SCAN_RELEASE; */
if (sc->sc_mode == K_CODE) {
return (keycode);
}
/* keycode to key action */
action = genkbd_keyaction(kbd, SCAN_CHAR(keycode),
(keycode & SCAN_RELEASE),
&sc->sc_state, &sc->sc_accents);
if (action == NOKEY) {
goto next_code;
}
return (action);
}
/* Currently wait is always false. */
static uint32_t
gpiokeys_read_char(keyboard_t *kbd, int wait)
{
uint32_t keycode;
struct gpiokeys_softc *sc = kbd->kb_data;
GPIOKEYS_LOCK(sc);
keycode = gpiokeys_read_char_locked(kbd, wait);
GPIOKEYS_UNLOCK(sc);
return (keycode);
}
/* some useful control functions */
static int
gpiokeys_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
struct gpiokeys_softc *sc = kbd->kb_data;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
int ival;
#endif
switch (cmd) {
case KDGKBMODE: /* get keyboard mode */
*(int *)arg = sc->sc_mode;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 7):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBMODE: /* set keyboard mode */
switch (*(int *)arg) {
case K_XLATE:
if (sc->sc_mode != K_XLATE) {
/* make lock key state and LED state match */
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (sc->sc_mode != *(int *)arg) {
if ((sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) == 0)
gpiokeys_clear_state(kbd);
sc->sc_mode = *(int *)arg;
}
break;
default:
return (EINVAL);
}
break;
case KDGETLED: /* get keyboard LED */
*(int *)arg = KBD_LED_VAL(kbd);
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 66):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETLED: /* set keyboard LED */
KBD_LED_VAL(kbd) = *(int *)arg;
break;
case KDGKBSTATE: /* get lock key state */
*(int *)arg = sc->sc_state & LOCK_MASK;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 20):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBSTATE: /* set lock key state */
if (*(int *)arg & ~LOCK_MASK) {
return (EINVAL);
}
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= *(int *)arg;
return (0);
case KDSETREPEAT: /* set keyboard repeat rate (new
* interface) */
if (!KBD_HAS_DEVICE(kbd)) {
return (0);
}
if (((int *)arg)[1] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 200) /* fastest possible value */
kbd->kb_delay1 = 200;
else
kbd->kb_delay1 = ((int *)arg)[0];
kbd->kb_delay2 = ((int *)arg)[1];
return (0);
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 67):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETRAD: /* set keyboard repeat rate (old
* interface) */
return (gpiokeys_set_typematic(kbd, *(int *)arg));
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table
* (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table
* entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
sc->sc_accents = 0;
/* FALLTHROUGH */
default:
return (genkbd_commonioctl(kbd, cmd, arg));
}
return (0);
}
static int
gpiokeys_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
int result;
struct gpiokeys_softc *sc;
sc = kbd->kb_data;
/*
* XXX Check if someone is calling us from a critical section:
*/
if (curthread->td_critnest != 0)
return (EDEADLK);
GPIOKEYS_LOCK(sc);
result = gpiokeys_ioctl_locked(kbd, cmd, arg);
GPIOKEYS_UNLOCK(sc);
return (result);
}
/* clear the internal state of the keyboard */
static void
gpiokeys_clear_state(keyboard_t *kbd)
{
struct gpiokeys_softc *sc = kbd->kb_data;
sc->sc_flags &= ~(GPIOKEYS_GLOBAL_FLAG_POLLING);
sc->sc_state &= LOCK_MASK; /* preserve locking key state */
sc->sc_accents = 0;
}
/* get the internal state, not used */
static int
gpiokeys_get_state(keyboard_t *kbd, void *buf, size_t len)
{
return (len == 0) ? 1 : -1;
}
/* set the internal state, not used */
static int
gpiokeys_set_state(keyboard_t *kbd, void *buf, size_t len)
{
return (EINVAL);
}
static int
gpiokeys_poll(keyboard_t *kbd, int on)
{
struct gpiokeys_softc *sc = kbd->kb_data;
GPIOKEYS_LOCK(sc);
if (on)
sc->sc_flags |= GPIOKEYS_GLOBAL_FLAG_POLLING;
else
sc->sc_flags &= ~GPIOKEYS_GLOBAL_FLAG_POLLING;
GPIOKEYS_UNLOCK(sc);
return (0);
}
static int
gpiokeys_set_typematic(keyboard_t *kbd, int code)
{
static const int delays[] = {250, 500, 750, 1000};
static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504};
if (code & ~0x7f) {
return (EINVAL);
}
kbd->kb_delay1 = delays[(code >> 5) & 3];
kbd->kb_delay2 = rates[code & 0x1f];
return (0);
}
static void
gpiokeys_event_keyinput(struct gpiokeys_softc *sc)
{
int c;
if ((sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) != 0)
return;
if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
KBD_IS_BUSY(&sc->sc_kbd)) {
/* let the callback function process the input */
(sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
sc->sc_kbd.kb_callback.kc_arg);
} else {
/* read and discard the input, no one is waiting for it */
do {
c = gpiokeys_read_char(&sc->sc_kbd, 0);
} while (c != NOKEY);
}
}
static keyboard_switch_t gpiokeyssw = {
.probe = &gpiokeys__probe,
.init = &gpiokeys_init,
.term = &gpiokeys_term,
.intr = &gpiokeys_intr,
.test_if = &gpiokeys_test_if,
.enable = &gpiokeys_enable,
.disable = &gpiokeys_disable,
.read = &gpiokeys_read,
.check = &gpiokeys_check,
.read_char = &gpiokeys_read_char,
.check_char = &gpiokeys_check_char,
.ioctl = &gpiokeys_ioctl,
.lock = &gpiokeys_lock,
.clear_state = &gpiokeys_clear_state,
.get_state = &gpiokeys_get_state,
.set_state = &gpiokeys_set_state,
.poll = &gpiokeys_poll,
};
KEYBOARD_DRIVER(gpiokeys, gpiokeyssw, gpiokeys_configure);
static int
gpiokeys_driver_load(module_t mod, int what, void *arg)
{
switch (what) {
case MOD_LOAD:
kbd_add_driver(&gpiokeys_kbd_driver);
break;
case MOD_UNLOAD:
kbd_delete_driver(&gpiokeys_kbd_driver);
break;
}
return (0);
}
static devclass_t gpiokeys_devclass;
static device_method_t gpiokeys_methods[] = {
DEVMETHOD(device_probe, gpiokeys_probe),
DEVMETHOD(device_attach, gpiokeys_attach),
DEVMETHOD(device_detach, gpiokeys_detach),
DEVMETHOD_END
};
static driver_t gpiokeys_driver = {
"gpiokeys",
gpiokeys_methods,
sizeof(struct gpiokeys_softc),
};
DRIVER_MODULE(gpiokeys, simplebus, gpiokeys_driver, gpiokeys_devclass, gpiokeys_driver_load, 0);
MODULE_VERSION(gpiokeys, 1);
Index: stable/11/sys/dev/quicc/quicc_bfe_fdt.c
===================================================================
--- stable/11/sys/dev/quicc/quicc_bfe_fdt.c (revision 356019)
+++ stable/11/sys/dev/quicc/quicc_bfe_fdt.c (revision 356020)
@@ -1,91 +1,90 @@
/*-
* Copyright (c) 2006 Juniper Networks.
* 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
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
static int quicc_fdt_probe(device_t dev);
static device_method_t quicc_fdt_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, quicc_fdt_probe),
DEVMETHOD(device_attach, quicc_bfe_attach),
DEVMETHOD(device_detach, quicc_bfe_detach),
DEVMETHOD(bus_alloc_resource, quicc_bus_alloc_resource),
DEVMETHOD(bus_release_resource, quicc_bus_release_resource),
DEVMETHOD(bus_get_resource, quicc_bus_get_resource),
DEVMETHOD(bus_read_ivar, quicc_bus_read_ivar),
DEVMETHOD(bus_setup_intr, quicc_bus_setup_intr),
DEVMETHOD(bus_teardown_intr, quicc_bus_teardown_intr),
DEVMETHOD_END
};
static driver_t quicc_fdt_driver = {
quicc_driver_name,
quicc_fdt_methods,
sizeof(struct quicc_softc),
};
static int
quicc_fdt_probe(device_t dev)
{
phandle_t par;
pcell_t clock;
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "fsl,cpm2"))
return (ENXIO);
par = OF_parent(ofw_bus_get_node(dev));
if (OF_getprop(par, "bus-frequency", &clock, sizeof(clock)) <= 0)
clock = 0;
return (quicc_bfe_probe(dev, (uintptr_t)clock));
}
DRIVER_MODULE(quicc, simplebus, quicc_fdt_driver, quicc_devclass, 0, 0);
Index: stable/11/sys/dev/rp/rp_isa.c
===================================================================
--- stable/11/sys/dev/rp/rp_isa.c (revision 356019)
+++ stable/11/sys/dev/rp/rp_isa.c (revision 356020)
@@ -1,505 +1,506 @@
/*-
* Copyright (c) Comtrol Corporation
* All rights reserved.
*
* ISA-specific part separated from:
* sys/i386/isa/rp.c,v 1.33 1999/09/28 11:45:27 phk Exp
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted prodived that the follwoing conditions
* are met.
* 1. Redistributions of source code must retain the above copyright
* notive, this list of conditions and the following disclainer.
* 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 prodided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Comtrol Corporation.
* 4. The name of Comtrol Corporation may not be used to endorse or
* promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``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 COMTROL CORPORATION 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, LIFE 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
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
-#include
#include
#include
+#include
#include
+#include
#include
#include
#include
#include
#define ROCKET_C
#include
#include
#include
/* ISA-specific part of CONTROLLER_t */
struct ISACONTROLLER_T {
int MBaseIO; /* rid of the Mudbac controller for this controller */
int MReg0IO; /* offset0 of the Mudbac controller for this controller */
int MReg1IO; /* offset1 of the Mudbac controller for this controller */
int MReg2IO; /* offset2 of the Mudbac controller for this controller */
int MReg3IO; /* offset3 of the Mudbac controller for this controller */
Byte_t MReg2;
Byte_t MReg3;
};
typedef struct ISACONTROLLER_T ISACONTROLLER_t;
#define ISACTL(ctlp) ((ISACONTROLLER_t *)((ctlp)->bus_ctlp))
/***************************************************************************
Function: sControllerEOI
Purpose: Strobe the MUDBAC's End Of Interrupt bit.
Call: sControllerEOI(MudbacCtlP,CtlP)
CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
CONTROLLER_T *CtlP; Ptr to controller structure
*/
#define sControllerEOI(MudbacCtlP,CtlP) \
rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg2IO,ISACTL(CtlP)->MReg2 | INT_STROB)
/***************************************************************************
Function: sDisAiop
Purpose: Disable I/O access to an AIOP
Call: sDisAiop(MudbacCtlP,CtlP)
CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
CONTROLLER_T *CtlP; Ptr to controller structure
int AiopNum; Number of AIOP on controller
*/
#define sDisAiop(MudbacCtlP,CtlP,AIOPNUM) \
{ \
ISACTL(CtlP)->MReg3 &= rp_sBitMapClrTbl[AIOPNUM]; \
rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg3IO,ISACTL(CtlP)->MReg3); \
}
/***************************************************************************
Function: sEnAiop
Purpose: Enable I/O access to an AIOP
Call: sEnAiop(MudbacCtlP,CtlP)
CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
CONTROLLER_T *CtlP; Ptr to controller structure
int AiopNum; Number of AIOP on controller
*/
#define sEnAiop(MudbacCtlP,CtlP,AIOPNUM) \
{ \
ISACTL(CtlP)->MReg3 |= rp_sBitMapSetTbl[AIOPNUM]; \
rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg3IO,ISACTL(CtlP)->MReg3); \
}
/***************************************************************************
Function: sGetControllerIntStatus
Purpose: Get the controller interrupt status
Call: sGetControllerIntStatus(MudbacCtlP,CtlP)
CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
CONTROLLER_T *CtlP; Ptr to controller structure
Return: Byte_t: The controller interrupt status in the lower 4
bits. Bits 0 through 3 represent AIOP's 0
through 3 respectively. If a bit is set that
AIOP is interrupting. Bits 4 through 7 will
always be cleared.
*/
#define sGetControllerIntStatus(MudbacCtlP,CtlP) \
(rp_readio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg1IO) & 0x0f)
static devclass_t rp_devclass;
static CONTROLLER_t *rp_controller;
static int rp_nisadevs;
static int rp_probe(device_t dev);
static int rp_attach(device_t dev);
static void rp_isareleaseresource(CONTROLLER_t *ctlp);
static int sInitController(CONTROLLER_T *CtlP,
CONTROLLER_T *MudbacCtlP,
int AiopNum,
int IRQNum,
Byte_t Frequency,
int PeriodicOnly);
static rp_aiop2rid_t rp_isa_aiop2rid;
static rp_aiop2off_t rp_isa_aiop2off;
static rp_ctlmask_t rp_isa_ctlmask;
static int
rp_probe(device_t dev)
{
int unit;
CONTROLLER_t *controller;
int num_aiops;
CONTROLLER_t *ctlp;
int retval;
/*
* We have no PnP RocketPort cards.
* (At least according to LINT)
*/
if (isa_get_logicalid(dev) != 0)
return (ENXIO);
/* We need IO port resource to configure an ISA device. */
if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 0)
return (ENXIO);
unit = device_get_unit(dev);
if (unit >= 4) {
device_printf(dev, "rpprobe: unit number %d invalid.\n", unit);
return (ENXIO);
}
device_printf(dev, "probing for RocketPort(ISA) unit %d.\n", unit);
ctlp = device_get_softc(dev);
bzero(ctlp, sizeof(*ctlp));
ctlp->dev = dev;
ctlp->aiop2rid = rp_isa_aiop2rid;
ctlp->aiop2off = rp_isa_aiop2off;
ctlp->ctlmask = rp_isa_ctlmask;
/* The IO ports of AIOPs for an ISA controller are discrete. */
ctlp->io_num = 1;
ctlp->io_rid = malloc(sizeof(*(ctlp->io_rid)) * MAX_AIOPS_PER_BOARD, M_DEVBUF, M_NOWAIT | M_ZERO);
ctlp->io = malloc(sizeof(*(ctlp->io)) * MAX_AIOPS_PER_BOARD, M_DEVBUF, M_NOWAIT | M_ZERO);
if (ctlp->io_rid == NULL || ctlp->io == NULL) {
device_printf(dev, "rp_attach: Out of memory.\n");
retval = ENOMEM;
goto nogo;
}
ctlp->bus_ctlp = malloc(sizeof(ISACONTROLLER_t) * 1, M_DEVBUF, M_NOWAIT | M_ZERO);
if (ctlp->bus_ctlp == NULL) {
device_printf(dev, "rp_attach: Out of memory.\n");
retval = ENOMEM;
goto nogo;
}
ctlp->io_rid[0] = 0;
if (rp_controller != NULL) {
controller = rp_controller;
ctlp->io[0] = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &ctlp->io_rid[0], 0x40, RF_ACTIVE);
} else {
controller = rp_controller = ctlp;
ctlp->io[0] = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &ctlp->io_rid[0], 0x44, RF_ACTIVE);
}
if (ctlp->io[0] == NULL) {
device_printf(dev, "rp_attach: Resource not available.\n");
retval = ENXIO;
goto nogo;
}
num_aiops = sInitController(ctlp,
controller,
MAX_AIOPS_PER_BOARD, 0,
FREQ_DIS, 0);
if (num_aiops <= 0) {
device_printf(dev, "board%d init failed.\n", unit);
retval = ENXIO;
goto nogo;
}
if (rp_controller == NULL)
rp_controller = controller;
rp_nisadevs++;
device_set_desc(dev, "RocketPort ISA");
return (0);
nogo:
rp_isareleaseresource(ctlp);
return (retval);
}
static int
rp_attach(device_t dev)
{
int unit;
int num_ports, num_aiops;
int aiop;
CONTROLLER_t *ctlp;
int retval;
unit = device_get_unit(dev);
ctlp = device_get_softc(dev);
#ifdef notdef
num_aiops = sInitController(ctlp,
rp_controller,
MAX_AIOPS_PER_BOARD, 0,
FREQ_DIS, 0);
#else
num_aiops = ctlp->NumAiop;
#endif /* notdef */
num_ports = 0;
for(aiop=0; aiop < num_aiops; aiop++) {
sResetAiopByNum(ctlp, aiop);
sEnAiop(rp_controller, ctlp, aiop);
num_ports += sGetAiopNumChan(ctlp, aiop);
}
retval = rp_attachcommon(ctlp, num_aiops, num_ports);
if (retval != 0)
goto nogo;
return (0);
nogo:
rp_isareleaseresource(ctlp);
return (retval);
}
static void
rp_isareleaseresource(CONTROLLER_t *ctlp)
{
int i;
rp_releaseresource(ctlp);
if (ctlp == rp_controller)
rp_controller = NULL;
if (ctlp->io != NULL) {
for (i = 0 ; i < MAX_AIOPS_PER_BOARD ; i++)
if (ctlp->io[i] != NULL)
bus_release_resource(ctlp->dev, SYS_RES_IOPORT, ctlp->io_rid[i], ctlp->io[i]);
free(ctlp->io, M_DEVBUF);
}
if (ctlp->io_rid != NULL)
free(ctlp->io_rid, M_DEVBUF);
if (rp_controller != NULL && rp_controller->io[ISACTL(ctlp)->MBaseIO] != NULL) {
bus_release_resource(rp_controller->dev, SYS_RES_IOPORT, rp_controller->io_rid[ISACTL(ctlp)->MBaseIO], rp_controller->io[ISACTL(ctlp)->MBaseIO]);
rp_controller->io[ISACTL(ctlp)->MBaseIO] = NULL;
rp_controller->io_rid[ISACTL(ctlp)->MBaseIO] = 0;
}
if (ctlp->bus_ctlp != NULL)
free(ctlp->bus_ctlp, M_DEVBUF);
}
/***************************************************************************
Function: sInitController
Purpose: Initialization of controller global registers and controller
structure.
Call: sInitController(CtlP,MudbacCtlP,AiopNum,
IRQNum,Frequency,PeriodicOnly)
CONTROLLER_T *CtlP; Ptr to controller structure
CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure
int AiopNum; Number of Aiops
int IRQNum; Interrupt Request number. Can be any of the following:
0: Disable global interrupts
3: IRQ 3
4: IRQ 4
5: IRQ 5
9: IRQ 9
10: IRQ 10
11: IRQ 11
12: IRQ 12
15: IRQ 15
Byte_t Frequency: A flag identifying the frequency
of the periodic interrupt, can be any one of the following:
FREQ_DIS - periodic interrupt disabled
FREQ_137HZ - 137 Hertz
FREQ_69HZ - 69 Hertz
FREQ_34HZ - 34 Hertz
FREQ_17HZ - 17 Hertz
FREQ_9HZ - 9 Hertz
FREQ_4HZ - 4 Hertz
If IRQNum is set to 0 the Frequency parameter is
overidden, it is forced to a value of FREQ_DIS.
int PeriodicOnly: TRUE if all interrupts except the periodic
interrupt are to be blocked.
FALSE is both the periodic interrupt and
other channel interrupts are allowed.
If IRQNum is set to 0 the PeriodicOnly parameter is
overidden, it is forced to a value of FALSE.
Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller
initialization failed.
Comments:
If periodic interrupts are to be disabled but AIOP interrupts
are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE.
If interrupts are to be completely disabled set IRQNum to 0.
Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an
invalid combination.
This function performs initialization of global interrupt modes,
but it does not actually enable global interrupts. To enable
and disable global interrupts use functions sEnGlobalInt() and
sDisGlobalInt(). Enabling of global interrupts is normally not
done until all other initializations are complete.
Even if interrupts are globally enabled, they must also be
individually enabled for each channel that is to generate
interrupts.
Warnings: No range checking on any of the parameters is done.
No context switches are allowed while executing this function.
After this function all AIOPs on the controller are disabled,
they can be enabled with sEnAiop().
*/
static int
sInitController( CONTROLLER_T *CtlP,
CONTROLLER_T *MudbacCtlP,
int AiopNum,
int IRQNum,
Byte_t Frequency,
int PeriodicOnly)
{
int i;
int ctl_base, aiop_base, aiop_size;
CtlP->CtlID = CTLID_0001; /* controller release 1 */
ISACTL(CtlP)->MBaseIO = rp_nisadevs;
if (MudbacCtlP->io[ISACTL(CtlP)->MBaseIO] != NULL) {
ISACTL(CtlP)->MReg0IO = 0x40 + 0;
ISACTL(CtlP)->MReg1IO = 0x40 + 1;
ISACTL(CtlP)->MReg2IO = 0x40 + 2;
ISACTL(CtlP)->MReg3IO = 0x40 + 3;
} else {
MudbacCtlP->io_rid[ISACTL(CtlP)->MBaseIO] = ISACTL(CtlP)->MBaseIO;
ctl_base = rman_get_start(MudbacCtlP->io[0]) + 0x40 + 0x400 * rp_nisadevs;
MudbacCtlP->io[ISACTL(CtlP)->MBaseIO] = bus_alloc_resource(MudbacCtlP->dev, SYS_RES_IOPORT, &CtlP->io_rid[ISACTL(CtlP)->MBaseIO], ctl_base, ctl_base + 3, 4, RF_ACTIVE);
ISACTL(CtlP)->MReg0IO = 0;
ISACTL(CtlP)->MReg1IO = 1;
ISACTL(CtlP)->MReg2IO = 2;
ISACTL(CtlP)->MReg3IO = 3;
}
#if 1
ISACTL(CtlP)->MReg2 = 0; /* interrupt disable */
ISACTL(CtlP)->MReg3 = 0; /* no periodic interrupts */
#else
if(sIRQMap[IRQNum] == 0) /* interrupts globally disabled */
{
ISACTL(CtlP)->MReg2 = 0; /* interrupt disable */
ISACTL(CtlP)->MReg3 = 0; /* no periodic interrupts */
}
else
{
ISACTL(CtlP)->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */
ISACTL(CtlP)->MReg3 = Frequency; /* set frequency */
if(PeriodicOnly) /* periodic interrupt only */
{
ISACTL(CtlP)->MReg3 |= PERIODIC_ONLY;
}
}
#endif
rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg2IO,ISACTL(CtlP)->MReg2);
rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg3IO,ISACTL(CtlP)->MReg3);
sControllerEOI(MudbacCtlP,CtlP); /* clear EOI if warm init */
/* Init AIOPs */
CtlP->NumAiop = 0;
for(i=0; i < AiopNum; i++)
{
if (CtlP->io[i] == NULL) {
CtlP->io_rid[i] = i;
aiop_base = rman_get_start(CtlP->io[0]) + 0x400 * i;
if (rp_nisadevs == 0)
aiop_size = 0x44;
else
aiop_size = 0x40;
CtlP->io[i] = bus_alloc_resource(CtlP->dev, SYS_RES_IOPORT, &CtlP->io_rid[i], aiop_base, aiop_base + aiop_size - 1, aiop_size, RF_ACTIVE);
} else
aiop_base = rman_get_start(CtlP->io[i]);
rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,
ISACTL(CtlP)->MReg2IO,
ISACTL(CtlP)->MReg2 | (i & 0x03)); /* AIOP index */
rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,
ISACTL(CtlP)->MReg0IO,
(Byte_t)(aiop_base >> 6)); /* set up AIOP I/O in MUDBAC */
sEnAiop(MudbacCtlP,CtlP,i); /* enable the AIOP */
CtlP->AiopID[i] = sReadAiopID(CtlP, i); /* read AIOP ID */
if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */
{
sDisAiop(MudbacCtlP,CtlP,i); /* disable AIOP */
bus_release_resource(CtlP->dev, SYS_RES_IOPORT, CtlP->io_rid[i], CtlP->io[i]);
CtlP->io[i] = NULL;
break; /* done looking for AIOPs */
}
CtlP->AiopNumChan[i] = sReadAiopNumChan(CtlP, i); /* num channels in AIOP */
rp_writeaiop2(CtlP,i,_INDX_ADDR,_CLK_PRE); /* clock prescaler */
rp_writeaiop1(CtlP,i,_INDX_DATA,CLOCK_PRESC);
CtlP->NumAiop++; /* bump count of AIOPs */
sDisAiop(MudbacCtlP,CtlP,i); /* disable AIOP */
}
if(CtlP->NumAiop == 0)
return(-1);
else
return(CtlP->NumAiop);
}
/*
* ARGSUSED
* Maps (aiop, offset) to rid.
*/
static int
rp_isa_aiop2rid(int aiop, int offset)
{
/* rid equals to aiop for an ISA controller. */
return aiop;
}
/*
* ARGSUSED
* Maps (aiop, offset) to the offset of resource.
*/
static int
rp_isa_aiop2off(int aiop, int offset)
{
/* Each aiop has its own resource. */
return offset;
}
/* Read the int status for an ISA controller. */
static unsigned char
rp_isa_ctlmask(CONTROLLER_t *ctlp)
{
return sGetControllerIntStatus(rp_controller,ctlp);
}
static device_method_t rp_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, rp_probe),
DEVMETHOD(device_attach, rp_attach),
{ 0, 0 }
};
static driver_t rp_driver = {
"rp",
rp_methods,
sizeof(CONTROLLER_t),
};
/*
* rp can be attached to an isa bus.
*/
DRIVER_MODULE(rp, isa, rp_driver, rp_devclass, 0, 0);
Index: stable/11/sys/dev/rp/rp_pci.c
===================================================================
--- stable/11/sys/dev/rp/rp_pci.c (revision 356019)
+++ stable/11/sys/dev/rp/rp_pci.c (revision 356020)
@@ -1,365 +1,366 @@
/*-
* Copyright (c) Comtrol Corporation
* All rights reserved.
*
* PCI-specific part separated from:
* sys/i386/isa/rp.c,v 1.33 1999/09/28 11:45:27 phk Exp
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted prodived that the follwoing conditions
* are met.
* 1. Redistributions of source code must retain the above copyright
* notive, this list of conditions and the following disclainer.
* 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 prodided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Comtrol Corporation.
* 4. The name of Comtrol Corporation may not be used to endorse or
* promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``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 COMTROL CORPORATION 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, LIFE 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
__FBSDID("$FreeBSD$");
#include
#include
#include
#include
-#include
#include
#include
+#include
#include
+#include
#include
#include
#include
#include
#define ROCKET_C
#include
#include
#include
#include
/* PCI IDs */
#define RP_VENDOR_ID 0x11FE
#define RP_DEVICE_ID_32I 0x0001
#define RP_DEVICE_ID_8I 0x0002
#define RP_DEVICE_ID_16I 0x0003
#define RP_DEVICE_ID_4Q 0x0004
#define RP_DEVICE_ID_8O 0x0005
#define RP_DEVICE_ID_8J 0x0006
#define RP_DEVICE_ID_4J 0x0007
#define RP_DEVICE_ID_6M 0x000C
#define RP_DEVICE_ID_4M 0x000D
#define RP_DEVICE_ID_UPCI_32 0x0801
#define RP_DEVICE_ID_UPCI_16 0x0803
#define RP_DEVICE_ID_UPCI_8O 0x0805
/**************************************************************************
MUDBAC remapped for PCI
**************************************************************************/
#define _CFG_INT_PCI 0x40
#define _PCI_INT_FUNC 0x3A
#define PCI_STROB 0x2000
#define INTR_EN_PCI 0x0010
/***************************************************************************
Function: sPCIControllerEOI
Purpose: Strobe the MUDBAC's End Of Interrupt bit.
Call: sPCIControllerEOI(CtlP)
CONTROLLER_T *CtlP; Ptr to controller structure
*/
#define sPCIControllerEOI(CtlP) rp_writeio2(CtlP, 0, _PCI_INT_FUNC, PCI_STROB)
/***************************************************************************
Function: sPCIGetControllerIntStatus
Purpose: Get the controller interrupt status
Call: sPCIGetControllerIntStatus(CtlP)
CONTROLLER_T *CtlP; Ptr to controller structure
Return: Byte_t: The controller interrupt status in the lower 4
bits. Bits 0 through 3 represent AIOP's 0
through 3 respectively. If a bit is set that
AIOP is interrupting. Bits 4 through 7 will
always be cleared.
*/
#define sPCIGetControllerIntStatus(CTLP) ((rp_readio2(CTLP, 0, _PCI_INT_FUNC) >> 8) & 0x1f)
static devclass_t rp_devclass;
static int rp_pciprobe(device_t dev);
static int rp_pciattach(device_t dev);
#ifdef notdef
static int rp_pcidetach(device_t dev);
static int rp_pcishutdown(device_t dev);
#endif /* notdef */
static void rp_pcireleaseresource(CONTROLLER_t *ctlp);
static int sPCIInitController( CONTROLLER_t *CtlP,
int AiopNum,
int IRQNum,
Byte_t Frequency,
int PeriodicOnly,
int VendorDevice);
static rp_aiop2rid_t rp_pci_aiop2rid;
static rp_aiop2off_t rp_pci_aiop2off;
static rp_ctlmask_t rp_pci_ctlmask;
/*
* The following functions are the pci-specific part
* of rp driver.
*/
static int
rp_pciprobe(device_t dev)
{
char *s;
s = NULL;
if (pci_get_vendor(dev) == RP_VENDOR_ID)
s = "RocketPort PCI";
if (s != NULL) {
device_set_desc(dev, s);
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
}
static int
rp_pciattach(device_t dev)
{
int num_ports, num_aiops;
int aiop;
CONTROLLER_t *ctlp;
int unit;
int retval;
ctlp = device_get_softc(dev);
bzero(ctlp, sizeof(*ctlp));
ctlp->dev = dev;
unit = device_get_unit(dev);
ctlp->aiop2rid = rp_pci_aiop2rid;
ctlp->aiop2off = rp_pci_aiop2off;
ctlp->ctlmask = rp_pci_ctlmask;
/* The IO ports of AIOPs for a PCI controller are continuous. */
ctlp->io_num = 1;
ctlp->io_rid = malloc(sizeof(*(ctlp->io_rid)) * ctlp->io_num, M_DEVBUF, M_NOWAIT | M_ZERO);
ctlp->io = malloc(sizeof(*(ctlp->io)) * ctlp->io_num, M_DEVBUF, M_NOWAIT | M_ZERO);
if (ctlp->io_rid == NULL || ctlp->io == NULL) {
device_printf(dev, "rp_pciattach: Out of memory.\n");
retval = ENOMEM;
goto nogo;
}
ctlp->bus_ctlp = NULL;
switch (pci_get_device(dev)) {
case RP_DEVICE_ID_UPCI_16:
case RP_DEVICE_ID_UPCI_32:
case RP_DEVICE_ID_UPCI_8O:
ctlp->io_rid[0] = PCIR_BAR(2);
break;
default:
ctlp->io_rid[0] = PCIR_BAR(0);
break;
}
ctlp->io[0] = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
&ctlp->io_rid[0], RF_ACTIVE);
if(ctlp->io[0] == NULL) {
device_printf(dev, "ioaddr mapping failed for RocketPort(PCI).\n");
retval = ENXIO;
goto nogo;
}
num_aiops = sPCIInitController(ctlp,
MAX_AIOPS_PER_BOARD, 0,
FREQ_DIS, 0, pci_get_device(dev));
num_ports = 0;
for(aiop=0; aiop < num_aiops; aiop++) {
sResetAiopByNum(ctlp, aiop);
num_ports += sGetAiopNumChan(ctlp, aiop);
}
retval = rp_attachcommon(ctlp, num_aiops, num_ports);
if (retval != 0)
goto nogo;
return (0);
nogo:
rp_pcireleaseresource(ctlp);
return (retval);
}
static int
rp_pcidetach(device_t dev)
{
CONTROLLER_t *ctlp;
ctlp = device_get_softc(dev);
rp_pcireleaseresource(ctlp);
return (0);
}
static int
rp_pcishutdown(device_t dev)
{
CONTROLLER_t *ctlp;
ctlp = device_get_softc(dev);
rp_pcireleaseresource(ctlp);
return (0);
}
static void
rp_pcireleaseresource(CONTROLLER_t *ctlp)
{
rp_releaseresource(ctlp);
if (ctlp->io != NULL) {
if (ctlp->io[0] != NULL)
bus_release_resource(ctlp->dev, SYS_RES_IOPORT, ctlp->io_rid[0], ctlp->io[0]);
free(ctlp->io, M_DEVBUF);
ctlp->io = NULL;
}
if (ctlp->io_rid != NULL) {
free(ctlp->io_rid, M_DEVBUF);
ctlp->io = NULL;
}
}
static int
sPCIInitController( CONTROLLER_t *CtlP,
int AiopNum,
int IRQNum,
Byte_t Frequency,
int PeriodicOnly,
int VendorDevice)
{
int i;
CtlP->CtlID = CTLID_0001; /* controller release 1 */
sPCIControllerEOI(CtlP);
/* Init AIOPs */
CtlP->NumAiop = 0;
for(i=0; i < AiopNum; i++)
{
/*device_printf(CtlP->dev, "aiop %d.\n", i);*/
CtlP->AiopID[i] = sReadAiopID(CtlP, i); /* read AIOP ID */
/*device_printf(CtlP->dev, "ID = %d.\n", CtlP->AiopID[i]);*/
if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */
{
break; /* done looking for AIOPs */
}
switch( VendorDevice ) {
case RP_DEVICE_ID_4Q:
case RP_DEVICE_ID_4J:
case RP_DEVICE_ID_4M:
CtlP->AiopNumChan[i] = 4;
break;
case RP_DEVICE_ID_6M:
CtlP->AiopNumChan[i] = 6;
break;
case RP_DEVICE_ID_8O:
case RP_DEVICE_ID_8J:
case RP_DEVICE_ID_8I:
case RP_DEVICE_ID_16I:
case RP_DEVICE_ID_32I:
CtlP->AiopNumChan[i] = 8;
break;
default:
#ifdef notdef
CtlP->AiopNumChan[i] = 8;
#else
CtlP->AiopNumChan[i] = sReadAiopNumChan(CtlP, i);
#endif /* notdef */
break;
}
/*device_printf(CtlP->dev, "%d channels.\n", CtlP->AiopNumChan[i]);*/
rp_writeaiop2(CtlP, i, _INDX_ADDR,_CLK_PRE); /* clock prescaler */
/*device_printf(CtlP->dev, "configuring clock prescaler.\n");*/
rp_writeaiop1(CtlP, i, _INDX_DATA,CLOCK_PRESC);
/*device_printf(CtlP->dev, "configured clock prescaler.\n");*/
CtlP->NumAiop++; /* bump count of AIOPs */
}
if(CtlP->NumAiop == 0)
return(-1);
else
return(CtlP->NumAiop);
}
/*
* ARGSUSED
* Maps (aiop, offset) to rid.
*/
static int
rp_pci_aiop2rid(int aiop, int offset)
{
/* Always return zero for a PCI controller. */
return 0;
}
/*
* ARGSUSED
* Maps (aiop, offset) to the offset of resource.
*/
static int
rp_pci_aiop2off(int aiop, int offset)
{
/* Each AIOP reserves 0x40 bytes. */
return aiop * 0x40 + offset;
}
/* Read the int status for a PCI controller. */
static unsigned char
rp_pci_ctlmask(CONTROLLER_t *ctlp)
{
return sPCIGetControllerIntStatus(ctlp);
}
static device_method_t rp_pcimethods[] = {
/* Device interface */
DEVMETHOD(device_probe, rp_pciprobe),
DEVMETHOD(device_attach, rp_pciattach),
DEVMETHOD(device_detach, rp_pcidetach),
DEVMETHOD(device_shutdown, rp_pcishutdown),
{ 0, 0 }
};
static driver_t rp_pcidriver = {
"rp",
rp_pcimethods,
sizeof(CONTROLLER_t),
};
/*
* rp can be attached to a pci bus.
*/
DRIVER_MODULE(rp, pci, rp_pcidriver, rp_devclass, 0, 0);
Index: stable/11/sys/dev/usb/input/uep.c
===================================================================
--- stable/11/sys/dev/usb/input/uep.c (revision 356019)
+++ stable/11/sys/dev/usb/input/uep.c (revision 356020)
@@ -1,444 +1,443 @@
/*-
* Copyright 2010, Gleb Smirnoff
* 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 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 AUTHOR 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.
*
* $FreeBSD$
*/
/*
* http://www.eeti.com.tw/pdf/Software%20Programming%20Guide_v2.0.pdf
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "usbdevs.h"
#include
#include
-#include
#define USB_DEBUG_VAR uep_debug
#include
#ifdef USB_DEBUG
static int uep_debug = 0;
static SYSCTL_NODE(_hw_usb, OID_AUTO, uep, CTLFLAG_RW, 0, "USB uep");
SYSCTL_INT(_hw_usb_uep, OID_AUTO, debug, CTLFLAG_RWTUN,
&uep_debug, 0, "Debug level");
#endif
#define UEP_MAX_X 2047
#define UEP_MAX_Y 2047
#define UEP_DOWN 0x01
#define UEP_PACKET_LEN_MAX 16
#define UEP_PACKET_LEN_REPORT 5
#define UEP_PACKET_LEN_REPORT2 6
#define UEP_PACKET_DIAG 0x0a
#define UEP_PACKET_REPORT_MASK 0xe0
#define UEP_PACKET_REPORT 0x80
#define UEP_PACKET_REPORT_PRESSURE 0xc0
#define UEP_PACKET_REPORT_PLAYER 0xa0
#define UEP_PACKET_LEN_MASK
#define UEP_FIFO_BUF_SIZE 8 /* bytes */
#define UEP_FIFO_QUEUE_MAXLEN 50 /* units */
enum {
UEP_INTR_DT,
UEP_N_TRANSFER,
};
struct uep_softc {
struct mtx mtx;
struct usb_xfer *xfer[UEP_N_TRANSFER];
struct usb_fifo_sc fifo;
u_int pollrate;
u_int state;
#define UEP_ENABLED 0x01
/* Reassembling buffer. */
u_char buf[UEP_PACKET_LEN_MAX];
uint8_t buf_len;
};
static usb_callback_t uep_intr_callback;
static device_probe_t uep_probe;
static device_attach_t uep_attach;
static device_detach_t uep_detach;
static usb_fifo_cmd_t uep_start_read;
static usb_fifo_cmd_t uep_stop_read;
static usb_fifo_open_t uep_open;
static usb_fifo_close_t uep_close;
static void uep_put_queue(struct uep_softc *, u_char *);
static struct usb_fifo_methods uep_fifo_methods = {
.f_open = &uep_open,
.f_close = &uep_close,
.f_start_read = &uep_start_read,
.f_stop_read = &uep_stop_read,
.basename[0] = "uep",
};
static int
get_pkt_len(u_char *buf)
{
if (buf[0] == UEP_PACKET_DIAG) {
int len;
len = buf[1] + 2;
if (len > UEP_PACKET_LEN_MAX) {
DPRINTF("bad packet len %u\n", len);
return (UEP_PACKET_LEN_MAX);
}
return (len);
}
switch (buf[0] & UEP_PACKET_REPORT_MASK) {
case UEP_PACKET_REPORT:
return (UEP_PACKET_LEN_REPORT);
case UEP_PACKET_REPORT_PRESSURE:
case UEP_PACKET_REPORT_PLAYER:
case UEP_PACKET_REPORT_PRESSURE | UEP_PACKET_REPORT_PLAYER:
return (UEP_PACKET_LEN_REPORT2);
default:
DPRINTF("bad packet len 0\n");
return (0);
}
}
static void
uep_process_pkt(struct uep_softc *sc, u_char *buf)
{
int32_t x, y;
if ((buf[0] & 0xFE) != 0x80) {
DPRINTF("bad input packet format 0x%.2x\n", buf[0]);
return;
}
/*
* Packet format is 5 bytes:
*
* 1000000T
* 0000AAAA
* 0AAAAAAA
* 0000BBBB
* 0BBBBBBB
*
* T: 1=touched 0=not touched
* A: bits of axis A position, MSB to LSB
* B: bits of axis B position, MSB to LSB
*
* For the unit I have, which is CTF1020-S from CarTFT.com,
* A = X and B = Y. But in NetBSD uep(4) it is other way round :)
*
* The controller sends a stream of T=1 events while the
* panel is touched, followed by a single T=0 event.
*
*/
x = (buf[1] << 7) | buf[2];
y = (buf[3] << 7) | buf[4];
DPRINTFN(2, "x %u y %u\n", x, y);
uep_put_queue(sc, buf);
}
static void
uep_intr_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct uep_softc *sc = usbd_xfer_softc(xfer);
int len;
usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
{
struct usb_page_cache *pc;
u_char buf[17], *p;
int pkt_len;
if (len > (int)sizeof(buf)) {
DPRINTF("bad input length %d\n", len);
goto tr_setup;
}
pc = usbd_xfer_get_frame(xfer, 0);
usbd_copy_out(pc, 0, buf, len);
/*
* The below code mimics Linux a lot. I don't know
* why NetBSD reads complete packets, but we need
* to reassamble 'em like Linux does (tries?).
*/
if (sc->buf_len > 0) {
int res;
if (sc->buf_len == 1)
sc->buf[1] = buf[0];
if ((pkt_len = get_pkt_len(sc->buf)) == 0)
goto tr_setup;
res = pkt_len - sc->buf_len;
memcpy(sc->buf + sc->buf_len, buf, res);
uep_process_pkt(sc, sc->buf);
sc->buf_len = 0;
p = buf + res;
len -= res;
} else
p = buf;
if (len == 1) {
sc->buf[0] = buf[0];
sc->buf_len = 1;
goto tr_setup;
}
while (len > 0) {
if ((pkt_len = get_pkt_len(p)) == 0)
goto tr_setup;
/* full packet: process */
if (pkt_len <= len) {
uep_process_pkt(sc, p);
} else {
/* incomplete packet: save in buffer */
memcpy(sc->buf, p, len);
sc->buf_len = len;
}
p += pkt_len;
len -= pkt_len;
}
}
case USB_ST_SETUP:
tr_setup:
/* check if we can put more data into the FIFO */
if (usb_fifo_put_bytes_max(sc->fifo.fp[USB_FIFO_RX]) != 0) {
usbd_xfer_set_frame_len(xfer, 0,
usbd_xfer_max_len(xfer));
usbd_transfer_submit(xfer);
}
break;
default:
if (error != USB_ERR_CANCELLED) {
/* try clear stall first */
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
break;
}
}
static const struct usb_config uep_config[UEP_N_TRANSFER] = {
[UEP_INTR_DT] = {
.type = UE_INTERRUPT,
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_IN,
.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
.bufsize = 0, /* use wMaxPacketSize */
.callback = &uep_intr_callback,
},
};
static const STRUCT_USB_HOST_ID uep_devs[] = {
{USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL, 0)},
{USB_VPI(USB_VENDOR_EGALAX, USB_PRODUCT_EGALAX_TPANEL2, 0)},
{USB_VPI(USB_VENDOR_EGALAX2, USB_PRODUCT_EGALAX2_TPANEL, 0)},
};
static int
uep_probe(device_t dev)
{
struct usb_attach_arg *uaa = device_get_ivars(dev);
if (uaa->usb_mode != USB_MODE_HOST)
return (ENXIO);
if (uaa->info.bConfigIndex != 0)
return (ENXIO);
if (uaa->info.bIfaceIndex != 0)
return (ENXIO);
return (usbd_lookup_id_by_uaa(uep_devs, sizeof(uep_devs), uaa));
}
static int
uep_attach(device_t dev)
{
struct usb_attach_arg *uaa = device_get_ivars(dev);
struct uep_softc *sc = device_get_softc(dev);
int error;
device_set_usb_desc(dev);
mtx_init(&sc->mtx, "uep lock", NULL, MTX_DEF);
error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
sc->xfer, uep_config, UEP_N_TRANSFER, sc, &sc->mtx);
if (error) {
DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error));
goto detach;
}
error = usb_fifo_attach(uaa->device, sc, &sc->mtx, &uep_fifo_methods,
&sc->fifo, device_get_unit(dev), -1, uaa->info.bIfaceIndex,
UID_ROOT, GID_OPERATOR, 0644);
if (error) {
DPRINTF("usb_fifo_attach error=%s\n", usbd_errstr(error));
goto detach;
}
sc->buf_len = 0;
return (0);
detach:
uep_detach(dev);
return (ENOMEM); /* XXX */
}
static int
uep_detach(device_t dev)
{
struct uep_softc *sc = device_get_softc(dev);
usb_fifo_detach(&sc->fifo);
usbd_transfer_unsetup(sc->xfer, UEP_N_TRANSFER);
mtx_destroy(&sc->mtx);
return (0);
}
static void
uep_start_read(struct usb_fifo *fifo)
{
struct uep_softc *sc = usb_fifo_softc(fifo);
u_int rate;
if ((rate = sc->pollrate) > 1000)
rate = 1000;
if (rate > 0 && sc->xfer[UEP_INTR_DT] != NULL) {
usbd_transfer_stop(sc->xfer[UEP_INTR_DT]);
usbd_xfer_set_interval(sc->xfer[UEP_INTR_DT], 1000 / rate);
sc->pollrate = 0;
}
usbd_transfer_start(sc->xfer[UEP_INTR_DT]);
}
static void
uep_stop_read(struct usb_fifo *fifo)
{
struct uep_softc *sc = usb_fifo_softc(fifo);
usbd_transfer_stop(sc->xfer[UEP_INTR_DT]);
}
static void
uep_put_queue(struct uep_softc *sc, u_char *buf)
{
usb_fifo_put_data_linear(sc->fifo.fp[USB_FIFO_RX], buf,
UEP_PACKET_LEN_REPORT, 1);
}
static int
uep_open(struct usb_fifo *fifo, int fflags)
{
if (fflags & FREAD) {
struct uep_softc *sc = usb_fifo_softc(fifo);
if (sc->state & UEP_ENABLED)
return (EBUSY);
if (usb_fifo_alloc_buffer(fifo, UEP_FIFO_BUF_SIZE,
UEP_FIFO_QUEUE_MAXLEN))
return (ENOMEM);
sc->state |= UEP_ENABLED;
}
return (0);
}
static void
uep_close(struct usb_fifo *fifo, int fflags)
{
if (fflags & FREAD) {
struct uep_softc *sc = usb_fifo_softc(fifo);
sc->state &= ~(UEP_ENABLED);
usb_fifo_free_buffer(fifo);
}
}
static devclass_t uep_devclass;
static device_method_t uep_methods[] = {
DEVMETHOD(device_probe, uep_probe),
DEVMETHOD(device_attach, uep_attach),
DEVMETHOD(device_detach, uep_detach),
{ 0, 0 },
};
static driver_t uep_driver = {
.name = "uep",
.methods = uep_methods,
.size = sizeof(struct uep_softc),
};
DRIVER_MODULE(uep, uhub, uep_driver, uep_devclass, NULL, NULL);
MODULE_DEPEND(uep, usb, 1, 1, 1);
MODULE_VERSION(uep, 1);
USB_PNP_HOST_INFO(uep_devs);
Index: stable/11/sys/dev/usb/input/ukbd.c
===================================================================
--- stable/11/sys/dev/usb/input/ukbd.c (revision 356019)
+++ stable/11/sys/dev/usb/input/ukbd.c (revision 356020)
@@ -1,2302 +1,2301 @@
#include
__FBSDID("$FreeBSD$");
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Lennart Augustsson (lennart@augustsson.net) at
* Carlstedt Research & Technology.
*
* 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*
*/
/*
* HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
*/
#include "opt_compat.h"
#include "opt_kbd.h"
#include "opt_ukbd.h"
#include "opt_evdev.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define USB_DEBUG_VAR ukbd_debug
#include
#include
#ifdef EVDEV_SUPPORT
#include
#include
#endif
#include
#include
-#include
#include
#include
/* the initial key map, accent map and fkey strings */
#if defined(UKBD_DFLT_KEYMAP) && !defined(KLD_MODULE)
#define KBD_DFLT_KEYMAP
#include "ukbdmap.h"
#endif
/* the following file must be included after "ukbdmap.h" */
#include
#ifdef USB_DEBUG
static int ukbd_debug = 0;
static int ukbd_no_leds = 0;
static int ukbd_pollrate = 0;
static SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB keyboard");
SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, debug, CTLFLAG_RWTUN,
&ukbd_debug, 0, "Debug level");
SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, no_leds, CTLFLAG_RWTUN,
&ukbd_no_leds, 0, "Disables setting of keyboard leds");
SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, pollrate, CTLFLAG_RWTUN,
&ukbd_pollrate, 0, "Force this polling rate, 1-1000Hz");
#endif
#define UKBD_EMULATE_ATSCANCODE 1
#define UKBD_DRIVER_NAME "ukbd"
#define UKBD_NMOD 8 /* units */
#define UKBD_NKEYCODE 6 /* units */
#define UKBD_IN_BUF_SIZE (2*(UKBD_NMOD + (2*UKBD_NKEYCODE))) /* bytes */
#define UKBD_IN_BUF_FULL ((UKBD_IN_BUF_SIZE / 2) - 1) /* bytes */
#define UKBD_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
#define UKBD_BUFFER_SIZE 64 /* bytes */
struct ukbd_data {
uint16_t modifiers;
#define MOD_CONTROL_L 0x01
#define MOD_CONTROL_R 0x10
#define MOD_SHIFT_L 0x02
#define MOD_SHIFT_R 0x20
#define MOD_ALT_L 0x04
#define MOD_ALT_R 0x40
#define MOD_WIN_L 0x08
#define MOD_WIN_R 0x80
/* internal */
#define MOD_EJECT 0x0100
#define MOD_FN 0x0200
uint8_t keycode[UKBD_NKEYCODE];
};
enum {
UKBD_INTR_DT_0,
UKBD_INTR_DT_1,
UKBD_CTRL_LED,
UKBD_N_TRANSFER,
};
struct ukbd_softc {
keyboard_t sc_kbd;
keymap_t sc_keymap;
accentmap_t sc_accmap;
fkeytab_t sc_fkeymap[UKBD_NFKEY];
struct hid_location sc_loc_apple_eject;
struct hid_location sc_loc_apple_fn;
struct hid_location sc_loc_ctrl_l;
struct hid_location sc_loc_ctrl_r;
struct hid_location sc_loc_shift_l;
struct hid_location sc_loc_shift_r;
struct hid_location sc_loc_alt_l;
struct hid_location sc_loc_alt_r;
struct hid_location sc_loc_win_l;
struct hid_location sc_loc_win_r;
struct hid_location sc_loc_events;
struct hid_location sc_loc_numlock;
struct hid_location sc_loc_capslock;
struct hid_location sc_loc_scrolllock;
struct usb_callout sc_callout;
struct ukbd_data sc_ndata;
struct ukbd_data sc_odata;
struct thread *sc_poll_thread;
struct usb_device *sc_udev;
struct usb_interface *sc_iface;
struct usb_xfer *sc_xfer[UKBD_N_TRANSFER];
#ifdef EVDEV_SUPPORT
struct evdev_dev *sc_evdev;
#endif
uint32_t sc_ntime[UKBD_NKEYCODE];
uint32_t sc_otime[UKBD_NKEYCODE];
uint32_t sc_input[UKBD_IN_BUF_SIZE]; /* input buffer */
uint32_t sc_time_ms;
uint32_t sc_composed_char; /* composed char code, if non-zero */
#ifdef UKBD_EMULATE_ATSCANCODE
uint32_t sc_buffered_char[2];
#endif
uint32_t sc_flags; /* flags */
#define UKBD_FLAG_COMPOSE 0x00000001
#define UKBD_FLAG_POLLING 0x00000002
#define UKBD_FLAG_SET_LEDS 0x00000004
#define UKBD_FLAG_ATTACHED 0x00000010
#define UKBD_FLAG_GONE 0x00000020
#define UKBD_FLAG_HID_MASK 0x003fffc0
#define UKBD_FLAG_APPLE_EJECT 0x00000040
#define UKBD_FLAG_APPLE_FN 0x00000080
#define UKBD_FLAG_APPLE_SWAP 0x00000100
#define UKBD_FLAG_TIMER_RUNNING 0x00000200
#define UKBD_FLAG_CTRL_L 0x00000400
#define UKBD_FLAG_CTRL_R 0x00000800
#define UKBD_FLAG_SHIFT_L 0x00001000
#define UKBD_FLAG_SHIFT_R 0x00002000
#define UKBD_FLAG_ALT_L 0x00004000
#define UKBD_FLAG_ALT_R 0x00008000
#define UKBD_FLAG_WIN_L 0x00010000
#define UKBD_FLAG_WIN_R 0x00020000
#define UKBD_FLAG_EVENTS 0x00040000
#define UKBD_FLAG_NUMLOCK 0x00080000
#define UKBD_FLAG_CAPSLOCK 0x00100000
#define UKBD_FLAG_SCROLLLOCK 0x00200000
int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
int sc_state; /* shift/lock key state */
int sc_accents; /* accent key index (> 0) */
int sc_polling; /* polling recursion count */
int sc_led_size;
int sc_kbd_size;
uint16_t sc_inputs;
uint16_t sc_inputhead;
uint16_t sc_inputtail;
uint16_t sc_modifiers;
uint8_t sc_leds; /* store for async led requests */
uint8_t sc_iface_index;
uint8_t sc_iface_no;
uint8_t sc_id_apple_eject;
uint8_t sc_id_apple_fn;
uint8_t sc_id_ctrl_l;
uint8_t sc_id_ctrl_r;
uint8_t sc_id_shift_l;
uint8_t sc_id_shift_r;
uint8_t sc_id_alt_l;
uint8_t sc_id_alt_r;
uint8_t sc_id_win_l;
uint8_t sc_id_win_r;
uint8_t sc_id_event;
uint8_t sc_id_numlock;
uint8_t sc_id_capslock;
uint8_t sc_id_scrolllock;
uint8_t sc_id_events;
uint8_t sc_kbd_id;
uint8_t sc_buffer[UKBD_BUFFER_SIZE];
};
#define KEY_ERROR 0x01
#define KEY_PRESS 0
#define KEY_RELEASE 0x400
#define KEY_INDEX(c) ((c) & 0xFF)
#define SCAN_PRESS 0
#define SCAN_RELEASE 0x80
#define SCAN_PREFIX_E0 0x100
#define SCAN_PREFIX_E1 0x200
#define SCAN_PREFIX_CTL 0x400
#define SCAN_PREFIX_SHIFT 0x800
#define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | \
SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT)
#define SCAN_CHAR(c) ((c) & 0x7f)
#define UKBD_LOCK() mtx_lock(&Giant)
#define UKBD_UNLOCK() mtx_unlock(&Giant)
#ifdef INVARIANTS
/*
* Assert that the lock is held in all contexts
* where the code can be executed.
*/
#define UKBD_LOCK_ASSERT() mtx_assert(&Giant, MA_OWNED)
/*
* Assert that the lock is held in the contexts
* where it really has to be so.
*/
#define UKBD_CTX_LOCK_ASSERT() \
do { \
if (!kdb_active && panicstr == NULL) \
mtx_assert(&Giant, MA_OWNED); \
} while (0)
#else
#define UKBD_LOCK_ASSERT() (void)0
#define UKBD_CTX_LOCK_ASSERT() (void)0
#endif
struct ukbd_mods {
uint32_t mask, key;
};
static const struct ukbd_mods ukbd_mods[UKBD_NMOD] = {
{MOD_CONTROL_L, 0xe0},
{MOD_CONTROL_R, 0xe4},
{MOD_SHIFT_L, 0xe1},
{MOD_SHIFT_R, 0xe5},
{MOD_ALT_L, 0xe2},
{MOD_ALT_R, 0xe6},
{MOD_WIN_L, 0xe3},
{MOD_WIN_R, 0xe7},
};
#define NN 0 /* no translation */
/*
* Translate USB keycodes to AT keyboard scancodes.
*/
/*
* FIXME: Mac USB keyboard generates:
* 0x53: keypad NumLock/Clear
* 0x66: Power
* 0x67: keypad =
* 0x68: F13
* 0x69: F14
* 0x6a: F15
*
* USB Apple Keyboard JIS generates:
* 0x90: Kana
* 0x91: Eisu
*/
static const uint8_t ukbd_trtab[256] = {
0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */
18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */
50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */
22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */
4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */
28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */
27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */
53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */
65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */
104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */
97, 100, 95, 69, 91, 55, 74, 78,/* 50 - 57 */
89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */
72, 73, 82, 83, 86, 107, 122, NN, /* 60 - 67 */
NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */
NN, NN, NN, NN, 115, 108, 111, 113, /* 70 - 77 */
109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */
121, 120, NN, NN, NN, NN, NN, 123, /* 80 - 87 */
124, 125, 126, 127, 128, NN, NN, NN, /* 88 - 8F */
129, 130, NN, NN, NN, NN, NN, NN, /* 90 - 97 */
NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */
NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */
NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */
NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */
NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */
29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */
NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */
NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */
};
static const uint8_t ukbd_boot_desc[] = {
0x05, 0x01, 0x09, 0x06, 0xa1,
0x01, 0x05, 0x07, 0x19, 0xe0,
0x29, 0xe7, 0x15, 0x00, 0x25,
0x01, 0x75, 0x01, 0x95, 0x08,
0x81, 0x02, 0x95, 0x01, 0x75,
0x08, 0x81, 0x01, 0x95, 0x03,
0x75, 0x01, 0x05, 0x08, 0x19,
0x01, 0x29, 0x03, 0x91, 0x02,
0x95, 0x05, 0x75, 0x01, 0x91,
0x01, 0x95, 0x06, 0x75, 0x08,
0x15, 0x00, 0x26, 0xff, 0x00,
0x05, 0x07, 0x19, 0x00, 0x2a,
0xff, 0x00, 0x81, 0x00, 0xc0
};
/* prototypes */
static void ukbd_timeout(void *);
static void ukbd_set_leds(struct ukbd_softc *, uint8_t);
static int ukbd_set_typematic(keyboard_t *, int);
#ifdef UKBD_EMULATE_ATSCANCODE
static int ukbd_key2scan(struct ukbd_softc *, int, int, int);
#endif
static uint32_t ukbd_read_char(keyboard_t *, int);
static void ukbd_clear_state(keyboard_t *);
static int ukbd_ioctl(keyboard_t *, u_long, caddr_t);
static int ukbd_enable(keyboard_t *);
static int ukbd_disable(keyboard_t *);
static void ukbd_interrupt(struct ukbd_softc *);
static void ukbd_event_keyinput(struct ukbd_softc *);
static device_probe_t ukbd_probe;
static device_attach_t ukbd_attach;
static device_detach_t ukbd_detach;
static device_resume_t ukbd_resume;
#ifdef EVDEV_SUPPORT
static const struct evdev_methods ukbd_evdev_methods = {
.ev_event = evdev_ev_kbd_event,
};
#endif
static uint8_t
ukbd_any_key_pressed(struct ukbd_softc *sc)
{
uint8_t i;
uint8_t j;
for (j = i = 0; i < UKBD_NKEYCODE; i++)
j |= sc->sc_odata.keycode[i];
return (j ? 1 : 0);
}
static void
ukbd_start_timer(struct ukbd_softc *sc)
{
sc->sc_flags |= UKBD_FLAG_TIMER_RUNNING;
usb_callout_reset(&sc->sc_callout, hz / 40, &ukbd_timeout, sc);
}
static void
ukbd_put_key(struct ukbd_softc *sc, uint32_t key)
{
UKBD_CTX_LOCK_ASSERT();
DPRINTF("0x%02x (%d) %s\n", key, key,
(key & KEY_RELEASE) ? "released" : "pressed");
#ifdef EVDEV_SUPPORT
if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD && sc->sc_evdev != NULL) {
evdev_push_event(sc->sc_evdev, EV_KEY,
evdev_hid2key(KEY_INDEX(key)), !(key & KEY_RELEASE));
evdev_sync(sc->sc_evdev);
}
#endif
if (sc->sc_inputs < UKBD_IN_BUF_SIZE) {
sc->sc_input[sc->sc_inputtail] = key;
++(sc->sc_inputs);
++(sc->sc_inputtail);
if (sc->sc_inputtail >= UKBD_IN_BUF_SIZE) {
sc->sc_inputtail = 0;
}
} else {
DPRINTF("input buffer is full\n");
}
}
static void
ukbd_do_poll(struct ukbd_softc *sc, uint8_t wait)
{
UKBD_CTX_LOCK_ASSERT();
KASSERT((sc->sc_flags & UKBD_FLAG_POLLING) != 0,
("ukbd_do_poll called when not polling\n"));
DPRINTFN(2, "polling\n");
if (!kdb_active && !SCHEDULER_STOPPED()) {
/*
* In this context the kernel is polling for input,
* but the USB subsystem works in normal interrupt-driven
* mode, so we just wait on the USB threads to do the job.
* Note that we currently hold the Giant, but it's also used
* as the transfer mtx, so we must release it while waiting.
*/
while (sc->sc_inputs == 0) {
/*
* Give USB threads a chance to run. Note that
* kern_yield performs DROP_GIANT + PICKUP_GIANT.
*/
kern_yield(PRI_UNCHANGED);
if (!wait)
break;
}
return;
}
while (sc->sc_inputs == 0) {
usbd_transfer_poll(sc->sc_xfer, UKBD_N_TRANSFER);
/* Delay-optimised support for repetition of keys */
if (ukbd_any_key_pressed(sc)) {
/* a key is pressed - need timekeeping */
DELAY(1000);
/* 1 millisecond has passed */
sc->sc_time_ms += 1;
}
ukbd_interrupt(sc);
if (!wait)
break;
}
}
static int32_t
ukbd_get_key(struct ukbd_softc *sc, uint8_t wait)
{
int32_t c;
UKBD_CTX_LOCK_ASSERT();
KASSERT((!kdb_active && !SCHEDULER_STOPPED())
|| (sc->sc_flags & UKBD_FLAG_POLLING) != 0,
("not polling in kdb or panic\n"));
if (sc->sc_inputs == 0 &&
(sc->sc_flags & UKBD_FLAG_GONE) == 0) {
/* start transfer, if not already started */
usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_0]);
usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_1]);
}
if (sc->sc_flags & UKBD_FLAG_POLLING)
ukbd_do_poll(sc, wait);
if (sc->sc_inputs == 0) {
c = -1;
} else {
c = sc->sc_input[sc->sc_inputhead];
--(sc->sc_inputs);
++(sc->sc_inputhead);
if (sc->sc_inputhead >= UKBD_IN_BUF_SIZE) {
sc->sc_inputhead = 0;
}
}
return (c);
}
static void
ukbd_interrupt(struct ukbd_softc *sc)
{
uint32_t n_mod;
uint32_t o_mod;
uint32_t now = sc->sc_time_ms;
uint32_t dtime;
uint8_t key;
uint8_t i;
uint8_t j;
UKBD_CTX_LOCK_ASSERT();
if (sc->sc_ndata.keycode[0] == KEY_ERROR)
return;
n_mod = sc->sc_ndata.modifiers;
o_mod = sc->sc_odata.modifiers;
if (n_mod != o_mod) {
for (i = 0; i < UKBD_NMOD; i++) {
if ((n_mod & ukbd_mods[i].mask) !=
(o_mod & ukbd_mods[i].mask)) {
ukbd_put_key(sc, ukbd_mods[i].key |
((n_mod & ukbd_mods[i].mask) ?
KEY_PRESS : KEY_RELEASE));
}
}
}
/* Check for released keys. */
for (i = 0; i < UKBD_NKEYCODE; i++) {
key = sc->sc_odata.keycode[i];
if (key == 0) {
continue;
}
for (j = 0; j < UKBD_NKEYCODE; j++) {
if (sc->sc_ndata.keycode[j] == 0) {
continue;
}
if (key == sc->sc_ndata.keycode[j]) {
goto rfound;
}
}
ukbd_put_key(sc, key | KEY_RELEASE);
rfound: ;
}
/* Check for pressed keys. */
for (i = 0; i < UKBD_NKEYCODE; i++) {
key = sc->sc_ndata.keycode[i];
if (key == 0) {
continue;
}
sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay1;
for (j = 0; j < UKBD_NKEYCODE; j++) {
if (sc->sc_odata.keycode[j] == 0) {
continue;
}
if (key == sc->sc_odata.keycode[j]) {
/* key is still pressed */
sc->sc_ntime[i] = sc->sc_otime[j];
dtime = (sc->sc_otime[j] - now);
if (!(dtime & 0x80000000)) {
/* time has not elapsed */
goto pfound;
}
sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay2;
break;
}
}
ukbd_put_key(sc, key | KEY_PRESS);
/*
* If any other key is presently down, force its repeat to be
* well in the future (100s). This makes the last key to be
* pressed do the autorepeat.
*/
for (j = 0; j != UKBD_NKEYCODE; j++) {
if (j != i)
sc->sc_ntime[j] = now + (100 * 1000);
}
pfound: ;
}
sc->sc_odata = sc->sc_ndata;
memcpy(sc->sc_otime, sc->sc_ntime, sizeof(sc->sc_otime));
ukbd_event_keyinput(sc);
}
static void
ukbd_event_keyinput(struct ukbd_softc *sc)
{
int c;
UKBD_CTX_LOCK_ASSERT();
if ((sc->sc_flags & UKBD_FLAG_POLLING) != 0)
return;
if (sc->sc_inputs == 0)
return;
if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
KBD_IS_BUSY(&sc->sc_kbd)) {
/* let the callback function process the input */
(sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
sc->sc_kbd.kb_callback.kc_arg);
} else {
/* read and discard the input, no one is waiting for it */
do {
c = ukbd_read_char(&sc->sc_kbd, 0);
} while (c != NOKEY);
}
}
static void
ukbd_timeout(void *arg)
{
struct ukbd_softc *sc = arg;
UKBD_LOCK_ASSERT();
sc->sc_time_ms += 25; /* milliseconds */
ukbd_interrupt(sc);
/* Make sure any leftover key events gets read out */
ukbd_event_keyinput(sc);
if (ukbd_any_key_pressed(sc) || (sc->sc_inputs != 0)) {
ukbd_start_timer(sc);
} else {
sc->sc_flags &= ~UKBD_FLAG_TIMER_RUNNING;
}
}
static uint8_t
ukbd_apple_fn(uint8_t keycode) {
switch (keycode) {
case 0x28: return 0x49; /* RETURN -> INSERT */
case 0x2a: return 0x4c; /* BACKSPACE -> DEL */
case 0x50: return 0x4a; /* LEFT ARROW -> HOME */
case 0x4f: return 0x4d; /* RIGHT ARROW -> END */
case 0x52: return 0x4b; /* UP ARROW -> PGUP */
case 0x51: return 0x4e; /* DOWN ARROW -> PGDN */
default: return keycode;
}
}
static uint8_t
ukbd_apple_swap(uint8_t keycode) {
switch (keycode) {
case 0x35: return 0x64;
case 0x64: return 0x35;
default: return keycode;
}
}
static void
ukbd_intr_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct ukbd_softc *sc = usbd_xfer_softc(xfer);
struct usb_page_cache *pc;
uint8_t i;
uint8_t offset;
uint8_t id;
int len;
UKBD_LOCK_ASSERT();
usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
pc = usbd_xfer_get_frame(xfer, 0);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
DPRINTF("actlen=%d bytes\n", len);
if (len == 0) {
DPRINTF("zero length data\n");
goto tr_setup;
}
if (sc->sc_kbd_id != 0) {
/* check and remove HID ID byte */
usbd_copy_out(pc, 0, &id, 1);
offset = 1;
len--;
if (len == 0) {
DPRINTF("zero length data\n");
goto tr_setup;
}
} else {
offset = 0;
id = 0;
}
if (len > UKBD_BUFFER_SIZE)
len = UKBD_BUFFER_SIZE;
/* get data */
usbd_copy_out(pc, offset, sc->sc_buffer, len);
/* clear temporary storage */
memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
/* scan through HID data */
if ((sc->sc_flags & UKBD_FLAG_APPLE_EJECT) &&
(id == sc->sc_id_apple_eject)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_apple_eject))
sc->sc_modifiers |= MOD_EJECT;
else
sc->sc_modifiers &= ~MOD_EJECT;
}
if ((sc->sc_flags & UKBD_FLAG_APPLE_FN) &&
(id == sc->sc_id_apple_fn)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_apple_fn))
sc->sc_modifiers |= MOD_FN;
else
sc->sc_modifiers &= ~MOD_FN;
}
if ((sc->sc_flags & UKBD_FLAG_CTRL_L) &&
(id == sc->sc_id_ctrl_l)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_ctrl_l))
sc-> sc_modifiers |= MOD_CONTROL_L;
else
sc-> sc_modifiers &= ~MOD_CONTROL_L;
}
if ((sc->sc_flags & UKBD_FLAG_CTRL_R) &&
(id == sc->sc_id_ctrl_r)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_ctrl_r))
sc->sc_modifiers |= MOD_CONTROL_R;
else
sc->sc_modifiers &= ~MOD_CONTROL_R;
}
if ((sc->sc_flags & UKBD_FLAG_SHIFT_L) &&
(id == sc->sc_id_shift_l)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_shift_l))
sc->sc_modifiers |= MOD_SHIFT_L;
else
sc->sc_modifiers &= ~MOD_SHIFT_L;
}
if ((sc->sc_flags & UKBD_FLAG_SHIFT_R) &&
(id == sc->sc_id_shift_r)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_shift_r))
sc->sc_modifiers |= MOD_SHIFT_R;
else
sc->sc_modifiers &= ~MOD_SHIFT_R;
}
if ((sc->sc_flags & UKBD_FLAG_ALT_L) &&
(id == sc->sc_id_alt_l)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_alt_l))
sc->sc_modifiers |= MOD_ALT_L;
else
sc->sc_modifiers &= ~MOD_ALT_L;
}
if ((sc->sc_flags & UKBD_FLAG_ALT_R) &&
(id == sc->sc_id_alt_r)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_alt_r))
sc->sc_modifiers |= MOD_ALT_R;
else
sc->sc_modifiers &= ~MOD_ALT_R;
}
if ((sc->sc_flags & UKBD_FLAG_WIN_L) &&
(id == sc->sc_id_win_l)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_win_l))
sc->sc_modifiers |= MOD_WIN_L;
else
sc->sc_modifiers &= ~MOD_WIN_L;
}
if ((sc->sc_flags & UKBD_FLAG_WIN_R) &&
(id == sc->sc_id_win_r)) {
if (hid_get_data(sc->sc_buffer, len, &sc->sc_loc_win_r))
sc->sc_modifiers |= MOD_WIN_R;
else
sc->sc_modifiers &= ~MOD_WIN_R;
}
sc->sc_ndata.modifiers = sc->sc_modifiers;
if ((sc->sc_flags & UKBD_FLAG_EVENTS) &&
(id == sc->sc_id_events)) {
i = sc->sc_loc_events.count;
if (i > UKBD_NKEYCODE)
i = UKBD_NKEYCODE;
if (i > len)
i = len;
while (i--) {
sc->sc_ndata.keycode[i] =
hid_get_data(sc->sc_buffer + i, len - i,
&sc->sc_loc_events);
}
}
#ifdef USB_DEBUG
DPRINTF("modifiers = 0x%04x\n", (int)sc->sc_modifiers);
for (i = 0; i < UKBD_NKEYCODE; i++) {
if (sc->sc_ndata.keycode[i]) {
DPRINTF("[%d] = 0x%02x\n",
(int)i, (int)sc->sc_ndata.keycode[i]);
}
}
#endif
if (sc->sc_modifiers & MOD_FN) {
for (i = 0; i < UKBD_NKEYCODE; i++) {
sc->sc_ndata.keycode[i] =
ukbd_apple_fn(sc->sc_ndata.keycode[i]);
}
}
if (sc->sc_flags & UKBD_FLAG_APPLE_SWAP) {
for (i = 0; i < UKBD_NKEYCODE; i++) {
sc->sc_ndata.keycode[i] =
ukbd_apple_swap(sc->sc_ndata.keycode[i]);
}
}
ukbd_interrupt(sc);
if (!(sc->sc_flags & UKBD_FLAG_TIMER_RUNNING)) {
if (ukbd_any_key_pressed(sc)) {
ukbd_start_timer(sc);
}
}
case USB_ST_SETUP:
tr_setup:
if (sc->sc_inputs < UKBD_IN_BUF_FULL) {
usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
usbd_transfer_submit(xfer);
} else {
DPRINTF("input queue is full!\n");
}
break;
default: /* Error */
DPRINTF("error=%s\n", usbd_errstr(error));
if (error != USB_ERR_CANCELLED) {
/* try to clear stall first */
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
break;
}
}
static void
ukbd_set_leds_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct ukbd_softc *sc = usbd_xfer_softc(xfer);
struct usb_device_request req;
struct usb_page_cache *pc;
uint8_t id;
uint8_t any;
int len;
UKBD_LOCK_ASSERT();
#ifdef USB_DEBUG
if (ukbd_no_leds)
return;
#endif
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
case USB_ST_SETUP:
if (!(sc->sc_flags & UKBD_FLAG_SET_LEDS))
break;
sc->sc_flags &= ~UKBD_FLAG_SET_LEDS;
req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
req.bRequest = UR_SET_REPORT;
USETW2(req.wValue, UHID_OUTPUT_REPORT, 0);
req.wIndex[0] = sc->sc_iface_no;
req.wIndex[1] = 0;
req.wLength[1] = 0;
memset(sc->sc_buffer, 0, UKBD_BUFFER_SIZE);
id = 0;
any = 0;
/* Assumption: All led bits must be in the same ID. */
if (sc->sc_flags & UKBD_FLAG_NUMLOCK) {
if (sc->sc_leds & NLKED) {
hid_put_data_unsigned(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
&sc->sc_loc_numlock, 1);
}
id = sc->sc_id_numlock;
any = 1;
}
if (sc->sc_flags & UKBD_FLAG_SCROLLLOCK) {
if (sc->sc_leds & SLKED) {
hid_put_data_unsigned(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
&sc->sc_loc_scrolllock, 1);
}
id = sc->sc_id_scrolllock;
any = 1;
}
if (sc->sc_flags & UKBD_FLAG_CAPSLOCK) {
if (sc->sc_leds & CLKED) {
hid_put_data_unsigned(sc->sc_buffer + 1, UKBD_BUFFER_SIZE - 1,
&sc->sc_loc_capslock, 1);
}
id = sc->sc_id_capslock;
any = 1;
}
/* if no leds, nothing to do */
if (!any)
break;
#ifdef EVDEV_SUPPORT
if (sc->sc_evdev != NULL)
evdev_push_leds(sc->sc_evdev, sc->sc_leds);
#endif
/* range check output report length */
len = sc->sc_led_size;
if (len > (UKBD_BUFFER_SIZE - 1))
len = (UKBD_BUFFER_SIZE - 1);
/* check if we need to prefix an ID byte */
sc->sc_buffer[0] = id;
pc = usbd_xfer_get_frame(xfer, 1);
if (id != 0) {
len++;
usbd_copy_in(pc, 0, sc->sc_buffer, len);
} else {
usbd_copy_in(pc, 0, sc->sc_buffer + 1, len);
}
req.wLength[0] = len;
usbd_xfer_set_frame_len(xfer, 1, len);
DPRINTF("len=%d, id=%d\n", len, id);
/* setup control request last */
pc = usbd_xfer_get_frame(xfer, 0);
usbd_copy_in(pc, 0, &req, sizeof(req));
usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
/* start data transfer */
usbd_xfer_set_frames(xfer, 2);
usbd_transfer_submit(xfer);
break;
default: /* Error */
DPRINTFN(1, "error=%s\n", usbd_errstr(error));
break;
}
}
static const struct usb_config ukbd_config[UKBD_N_TRANSFER] = {
[UKBD_INTR_DT_0] = {
.type = UE_INTERRUPT,
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_IN,
.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
.bufsize = 0, /* use wMaxPacketSize */
.callback = &ukbd_intr_callback,
},
[UKBD_INTR_DT_1] = {
.type = UE_INTERRUPT,
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_IN,
.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
.bufsize = 0, /* use wMaxPacketSize */
.callback = &ukbd_intr_callback,
},
[UKBD_CTRL_LED] = {
.type = UE_CONTROL,
.endpoint = 0x00, /* Control pipe */
.direction = UE_DIR_ANY,
.bufsize = sizeof(struct usb_device_request) + UKBD_BUFFER_SIZE,
.callback = &ukbd_set_leds_callback,
.timeout = 1000, /* 1 second */
},
};
/* A match on these entries will load ukbd */
static const STRUCT_USB_HOST_ID __used ukbd_devs[] = {
{USB_IFACE_CLASS(UICLASS_HID),
USB_IFACE_SUBCLASS(UISUBCLASS_BOOT),
USB_IFACE_PROTOCOL(UIPROTO_BOOT_KEYBOARD),},
};
static int
ukbd_probe(device_t dev)
{
keyboard_switch_t *sw = kbd_get_switch(UKBD_DRIVER_NAME);
struct usb_attach_arg *uaa = device_get_ivars(dev);
void *d_ptr;
int error;
uint16_t d_len;
UKBD_LOCK_ASSERT();
DPRINTFN(11, "\n");
if (sw == NULL) {
return (ENXIO);
}
if (uaa->usb_mode != USB_MODE_HOST) {
return (ENXIO);
}
if (uaa->info.bInterfaceClass != UICLASS_HID)
return (ENXIO);
if (usb_test_quirk(uaa, UQ_KBD_IGNORE))
return (ENXIO);
if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
(uaa->info.bInterfaceProtocol == UIPROTO_BOOT_KEYBOARD))
return (BUS_PROBE_DEFAULT);
error = usbd_req_get_hid_desc(uaa->device, NULL,
&d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
if (error)
return (ENXIO);
if (hid_is_keyboard(d_ptr, d_len)) {
if (hid_is_mouse(d_ptr, d_len)) {
/*
* NOTE: We currently don't support USB mouse
* and USB keyboard on the same USB endpoint.
* Let "ums" driver win.
*/
error = ENXIO;
} else {
error = BUS_PROBE_DEFAULT;
}
} else {
error = ENXIO;
}
free(d_ptr, M_TEMP);
return (error);
}
static void
ukbd_parse_hid(struct ukbd_softc *sc, const uint8_t *ptr, uint32_t len)
{
uint32_t flags;
/* reset detected bits */
sc->sc_flags &= ~UKBD_FLAG_HID_MASK;
/* check if there is an ID byte */
sc->sc_kbd_size = hid_report_size(ptr, len,
hid_input, &sc->sc_kbd_id);
/* investigate if this is an Apple Keyboard */
if (hid_locate(ptr, len,
HID_USAGE2(HUP_CONSUMER, HUG_APPLE_EJECT),
hid_input, 0, &sc->sc_loc_apple_eject, &flags,
&sc->sc_id_apple_eject)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_APPLE_EJECT |
UKBD_FLAG_APPLE_SWAP;
DPRINTFN(1, "Found Apple eject-key\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(0xFFFF, 0x0003),
hid_input, 0, &sc->sc_loc_apple_fn, &flags,
&sc->sc_id_apple_fn)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_APPLE_FN;
DPRINTFN(1, "Found Apple FN-key\n");
}
/* figure out some keys */
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE0),
hid_input, 0, &sc->sc_loc_ctrl_l, &flags,
&sc->sc_id_ctrl_l)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_CTRL_L;
DPRINTFN(1, "Found left control\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE4),
hid_input, 0, &sc->sc_loc_ctrl_r, &flags,
&sc->sc_id_ctrl_r)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_CTRL_R;
DPRINTFN(1, "Found right control\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE1),
hid_input, 0, &sc->sc_loc_shift_l, &flags,
&sc->sc_id_shift_l)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_SHIFT_L;
DPRINTFN(1, "Found left shift\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE5),
hid_input, 0, &sc->sc_loc_shift_r, &flags,
&sc->sc_id_shift_r)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_SHIFT_R;
DPRINTFN(1, "Found right shift\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE2),
hid_input, 0, &sc->sc_loc_alt_l, &flags,
&sc->sc_id_alt_l)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_ALT_L;
DPRINTFN(1, "Found left alt\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE6),
hid_input, 0, &sc->sc_loc_alt_r, &flags,
&sc->sc_id_alt_r)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_ALT_R;
DPRINTFN(1, "Found right alt\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE3),
hid_input, 0, &sc->sc_loc_win_l, &flags,
&sc->sc_id_win_l)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_WIN_L;
DPRINTFN(1, "Found left GUI\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0xE7),
hid_input, 0, &sc->sc_loc_win_r, &flags,
&sc->sc_id_win_r)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_WIN_R;
DPRINTFN(1, "Found right GUI\n");
}
/* figure out event buffer */
if (hid_locate(ptr, len,
HID_USAGE2(HUP_KEYBOARD, 0x00),
hid_input, 0, &sc->sc_loc_events, &flags,
&sc->sc_id_events)) {
if (flags & HIO_VARIABLE) {
DPRINTFN(1, "Ignoring keyboard event control\n");
} else {
sc->sc_flags |= UKBD_FLAG_EVENTS;
DPRINTFN(1, "Found keyboard event array\n");
}
}
/* figure out leds on keyboard */
sc->sc_led_size = hid_report_size(ptr, len,
hid_output, NULL);
if (hid_locate(ptr, len,
HID_USAGE2(HUP_LEDS, 0x01),
hid_output, 0, &sc->sc_loc_numlock, &flags,
&sc->sc_id_numlock)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_NUMLOCK;
DPRINTFN(1, "Found keyboard numlock\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_LEDS, 0x02),
hid_output, 0, &sc->sc_loc_capslock, &flags,
&sc->sc_id_capslock)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_CAPSLOCK;
DPRINTFN(1, "Found keyboard capslock\n");
}
if (hid_locate(ptr, len,
HID_USAGE2(HUP_LEDS, 0x03),
hid_output, 0, &sc->sc_loc_scrolllock, &flags,
&sc->sc_id_scrolllock)) {
if (flags & HIO_VARIABLE)
sc->sc_flags |= UKBD_FLAG_SCROLLLOCK;
DPRINTFN(1, "Found keyboard scrolllock\n");
}
}
static int
ukbd_attach(device_t dev)
{
struct ukbd_softc *sc = device_get_softc(dev);
struct usb_attach_arg *uaa = device_get_ivars(dev);
int unit = device_get_unit(dev);
keyboard_t *kbd = &sc->sc_kbd;
void *hid_ptr = NULL;
usb_error_t err;
uint16_t n;
uint16_t hid_len;
#ifdef EVDEV_SUPPORT
struct evdev_dev *evdev;
int i;
#endif
#ifdef USB_DEBUG
int rate;
#endif
UKBD_LOCK_ASSERT();
kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
kbd->kb_data = (void *)sc;
device_set_usb_desc(dev);
sc->sc_udev = uaa->device;
sc->sc_iface = uaa->iface;
sc->sc_iface_index = uaa->info.bIfaceIndex;
sc->sc_iface_no = uaa->info.bIfaceNum;
sc->sc_mode = K_XLATE;
usb_callout_init_mtx(&sc->sc_callout, &Giant, 0);
#ifdef UKBD_NO_POLLING
err = usbd_transfer_setup(uaa->device,
&uaa->info.bIfaceIndex, sc->sc_xfer, ukbd_config,
UKBD_N_TRANSFER, sc, &Giant);
#else
/*
* Setup the UKBD USB transfers one by one, so they are memory
* independent which allows for handling panics triggered by
* the keyboard driver itself, typically via CTRL+ALT+ESC
* sequences. Or if the USB keyboard driver was processing a
* key at the moment of panic.
*/
for (n = 0; n != UKBD_N_TRANSFER; n++) {
err = usbd_transfer_setup(uaa->device,
&uaa->info.bIfaceIndex, sc->sc_xfer + n, ukbd_config + n,
1, sc, &Giant);
if (err)
break;
}
#endif
if (err) {
DPRINTF("error=%s\n", usbd_errstr(err));
goto detach;
}
/* setup default keyboard maps */
sc->sc_keymap = key_map;
sc->sc_accmap = accent_map;
for (n = 0; n < UKBD_NFKEY; n++) {
sc->sc_fkeymap[n] = fkey_tab[n];
}
kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
sc->sc_fkeymap, UKBD_NFKEY);
KBD_FOUND_DEVICE(kbd);
ukbd_clear_state(kbd);
/*
* FIXME: set the initial value for lock keys in "sc_state"
* according to the BIOS data?
*/
KBD_PROBE_DONE(kbd);
/* get HID descriptor */
err = usbd_req_get_hid_desc(uaa->device, NULL, &hid_ptr,
&hid_len, M_TEMP, uaa->info.bIfaceIndex);
if (err == 0) {
DPRINTF("Parsing HID descriptor of %d bytes\n",
(int)hid_len);
ukbd_parse_hid(sc, hid_ptr, hid_len);
free(hid_ptr, M_TEMP);
}
/* check if we should use the boot protocol */
if (usb_test_quirk(uaa, UQ_KBD_BOOTPROTO) ||
(err != 0) || (!(sc->sc_flags & UKBD_FLAG_EVENTS))) {
DPRINTF("Forcing boot protocol\n");
err = usbd_req_set_protocol(sc->sc_udev, NULL,
sc->sc_iface_index, 0);
if (err != 0) {
DPRINTF("Set protocol error=%s (ignored)\n",
usbd_errstr(err));
}
ukbd_parse_hid(sc, ukbd_boot_desc, sizeof(ukbd_boot_desc));
}
/* ignore if SETIDLE fails, hence it is not crucial */
usbd_req_set_idle(sc->sc_udev, NULL, sc->sc_iface_index, 0, 0);
ukbd_ioctl(kbd, KDSETLED, (caddr_t)&sc->sc_state);
KBD_INIT_DONE(kbd);
if (kbd_register(kbd) < 0) {
goto detach;
}
KBD_CONFIG_DONE(kbd);
ukbd_enable(kbd);
#ifdef KBD_INSTALL_CDEV
if (kbd_attach(kbd)) {
goto detach;
}
#endif
#ifdef EVDEV_SUPPORT
evdev = evdev_alloc();
evdev_set_name(evdev, device_get_desc(dev));
evdev_set_phys(evdev, device_get_nameunit(dev));
evdev_set_id(evdev, BUS_USB, uaa->info.idVendor,
uaa->info.idProduct, 0);
evdev_set_serial(evdev, usb_get_serial(uaa->device));
evdev_set_methods(evdev, kbd, &ukbd_evdev_methods);
evdev_support_event(evdev, EV_SYN);
evdev_support_event(evdev, EV_KEY);
if (sc->sc_flags & (UKBD_FLAG_NUMLOCK | UKBD_FLAG_CAPSLOCK |
UKBD_FLAG_SCROLLLOCK))
evdev_support_event(evdev, EV_LED);
evdev_support_event(evdev, EV_REP);
for (i = 0x00; i <= 0xFF; i++)
evdev_support_key(evdev, evdev_hid2key(i));
if (sc->sc_flags & UKBD_FLAG_NUMLOCK)
evdev_support_led(evdev, LED_NUML);
if (sc->sc_flags & UKBD_FLAG_CAPSLOCK)
evdev_support_led(evdev, LED_CAPSL);
if (sc->sc_flags & UKBD_FLAG_SCROLLLOCK)
evdev_support_led(evdev, LED_SCROLLL);
if (evdev_register(evdev))
evdev_free(evdev);
else
sc->sc_evdev = evdev;
#endif
sc->sc_flags |= UKBD_FLAG_ATTACHED;
if (bootverbose) {
kbdd_diag(kbd, bootverbose);
}
#ifdef USB_DEBUG
/* check for polling rate override */
rate = ukbd_pollrate;
if (rate > 0) {
if (rate > 1000)
rate = 1;
else
rate = 1000 / rate;
/* set new polling interval in ms */
usbd_xfer_set_interval(sc->sc_xfer[UKBD_INTR_DT_0], rate);
usbd_xfer_set_interval(sc->sc_xfer[UKBD_INTR_DT_1], rate);
}
#endif
/* start the keyboard */
usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_0]);
usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT_1]);
return (0); /* success */
detach:
ukbd_detach(dev);
return (ENXIO); /* error */
}
static int
ukbd_detach(device_t dev)
{
struct ukbd_softc *sc = device_get_softc(dev);
int error;
UKBD_LOCK_ASSERT();
DPRINTF("\n");
sc->sc_flags |= UKBD_FLAG_GONE;
usb_callout_stop(&sc->sc_callout);
/* kill any stuck keys */
if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
/* stop receiving events from the USB keyboard */
usbd_transfer_stop(sc->sc_xfer[UKBD_INTR_DT_0]);
usbd_transfer_stop(sc->sc_xfer[UKBD_INTR_DT_1]);
/* release all leftover keys, if any */
memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
/* process releasing of all keys */
ukbd_interrupt(sc);
}
ukbd_disable(&sc->sc_kbd);
#ifdef KBD_INSTALL_CDEV
if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
error = kbd_detach(&sc->sc_kbd);
if (error) {
/* usb attach cannot return an error */
device_printf(dev, "WARNING: kbd_detach() "
"returned non-zero! (ignored)\n");
}
}
#endif
#ifdef EVDEV_SUPPORT
evdev_free(sc->sc_evdev);
#endif
if (KBD_IS_CONFIGURED(&sc->sc_kbd)) {
error = kbd_unregister(&sc->sc_kbd);
if (error) {
/* usb attach cannot return an error */
device_printf(dev, "WARNING: kbd_unregister() "
"returned non-zero! (ignored)\n");
}
}
sc->sc_kbd.kb_flags = 0;
usbd_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER);
usb_callout_drain(&sc->sc_callout);
DPRINTF("%s: disconnected\n",
device_get_nameunit(dev));
return (0);
}
static int
ukbd_resume(device_t dev)
{
struct ukbd_softc *sc = device_get_softc(dev);
UKBD_LOCK_ASSERT();
ukbd_clear_state(&sc->sc_kbd);
return (0);
}
/* early keyboard probe, not supported */
static int
ukbd_configure(int flags)
{
return (0);
}
/* detect a keyboard, not used */
static int
ukbd__probe(int unit, void *arg, int flags)
{
return (ENXIO);
}
/* reset and initialize the device, not used */
static int
ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
{
return (ENXIO);
}
/* test the interface to the device, not used */
static int
ukbd_test_if(keyboard_t *kbd)
{
return (0);
}
/* finish using this keyboard, not used */
static int
ukbd_term(keyboard_t *kbd)
{
return (ENXIO);
}
/* keyboard interrupt routine, not used */
static int
ukbd_intr(keyboard_t *kbd, void *arg)
{
return (0);
}
/* lock the access to the keyboard, not used */
static int
ukbd_lock(keyboard_t *kbd, int lock)
{
return (1);
}
/*
* Enable the access to the device; until this function is called,
* the client cannot read from the keyboard.
*/
static int
ukbd_enable(keyboard_t *kbd)
{
UKBD_LOCK();
KBD_ACTIVATE(kbd);
UKBD_UNLOCK();
return (0);
}
/* disallow the access to the device */
static int
ukbd_disable(keyboard_t *kbd)
{
UKBD_LOCK();
KBD_DEACTIVATE(kbd);
UKBD_UNLOCK();
return (0);
}
/* check if data is waiting */
/* Currently unused. */
static int
ukbd_check(keyboard_t *kbd)
{
struct ukbd_softc *sc = kbd->kb_data;
UKBD_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (0);
if (sc->sc_flags & UKBD_FLAG_POLLING)
ukbd_do_poll(sc, 0);
#ifdef UKBD_EMULATE_ATSCANCODE
if (sc->sc_buffered_char[0]) {
return (1);
}
#endif
if (sc->sc_inputs > 0) {
return (1);
}
return (0);
}
/* check if char is waiting */
static int
ukbd_check_char_locked(keyboard_t *kbd)
{
struct ukbd_softc *sc = kbd->kb_data;
UKBD_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (0);
if ((sc->sc_composed_char > 0) &&
(!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
return (1);
}
return (ukbd_check(kbd));
}
static int
ukbd_check_char(keyboard_t *kbd)
{
int result;
UKBD_LOCK();
result = ukbd_check_char_locked(kbd);
UKBD_UNLOCK();
return (result);
}
/* read one byte from the keyboard if it's allowed */
/* Currently unused. */
static int
ukbd_read(keyboard_t *kbd, int wait)
{
struct ukbd_softc *sc = kbd->kb_data;
int32_t usbcode;
#ifdef UKBD_EMULATE_ATSCANCODE
uint32_t keycode;
uint32_t scancode;
#endif
UKBD_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (-1);
#ifdef UKBD_EMULATE_ATSCANCODE
if (sc->sc_buffered_char[0]) {
scancode = sc->sc_buffered_char[0];
if (scancode & SCAN_PREFIX) {
sc->sc_buffered_char[0] &= ~SCAN_PREFIX;
return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
}
sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
sc->sc_buffered_char[1] = 0;
return (scancode);
}
#endif /* UKBD_EMULATE_ATSCANCODE */
/* XXX */
usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1))
return (-1);
++(kbd->kb_count);
#ifdef UKBD_EMULATE_ATSCANCODE
keycode = ukbd_trtab[KEY_INDEX(usbcode)];
if (keycode == NN) {
return -1;
}
return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers,
(usbcode & KEY_RELEASE)));
#else /* !UKBD_EMULATE_ATSCANCODE */
return (usbcode);
#endif /* UKBD_EMULATE_ATSCANCODE */
}
/* read char from the keyboard */
static uint32_t
ukbd_read_char_locked(keyboard_t *kbd, int wait)
{
struct ukbd_softc *sc = kbd->kb_data;
uint32_t action;
uint32_t keycode;
int32_t usbcode;
#ifdef UKBD_EMULATE_ATSCANCODE
uint32_t scancode;
#endif
UKBD_CTX_LOCK_ASSERT();
if (!KBD_IS_ACTIVE(kbd))
return (NOKEY);
next_code:
/* do we have a composed char to return ? */
if ((sc->sc_composed_char > 0) &&
(!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
action = sc->sc_composed_char;
sc->sc_composed_char = 0;
if (action > 0xFF) {
goto errkey;
}
goto done;
}
#ifdef UKBD_EMULATE_ATSCANCODE
/* do we have a pending raw scan code? */
if (sc->sc_mode == K_RAW) {
scancode = sc->sc_buffered_char[0];
if (scancode) {
if (scancode & SCAN_PREFIX) {
sc->sc_buffered_char[0] = (scancode & ~SCAN_PREFIX);
return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
}
sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
sc->sc_buffered_char[1] = 0;
return (scancode);
}
}
#endif /* UKBD_EMULATE_ATSCANCODE */
/* see if there is something in the keyboard port */
/* XXX */
usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
if (usbcode == -1) {
return (NOKEY);
}
++kbd->kb_count;
#ifdef UKBD_EMULATE_ATSCANCODE
/* USB key index -> key code -> AT scan code */
keycode = ukbd_trtab[KEY_INDEX(usbcode)];
if (keycode == NN) {
return (NOKEY);
}
/* return an AT scan code for the K_RAW mode */
if (sc->sc_mode == K_RAW) {
return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers,
(usbcode & KEY_RELEASE)));
}
#else /* !UKBD_EMULATE_ATSCANCODE */
/* return the byte as is for the K_RAW mode */
if (sc->sc_mode == K_RAW) {
return (usbcode);
}
/* USB key index -> key code */
keycode = ukbd_trtab[KEY_INDEX(usbcode)];
if (keycode == NN) {
return (NOKEY);
}
#endif /* UKBD_EMULATE_ATSCANCODE */
switch (keycode) {
case 0x38: /* left alt (compose key) */
if (usbcode & KEY_RELEASE) {
if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
if (sc->sc_composed_char > 0xFF) {
sc->sc_composed_char = 0;
}
}
} else {
if (!(sc->sc_flags & UKBD_FLAG_COMPOSE)) {
sc->sc_flags |= UKBD_FLAG_COMPOSE;
sc->sc_composed_char = 0;
}
}
break;
/* XXX: I don't like these... */
case 0x5c: /* print screen */
if (sc->sc_flags & ALTS) {
keycode = 0x54; /* sysrq */
}
break;
case 0x68: /* pause/break */
if (sc->sc_flags & CTLS) {
keycode = 0x6c; /* break */
}
break;
}
/* return the key code in the K_CODE mode */
if (usbcode & KEY_RELEASE) {
keycode |= SCAN_RELEASE;
}
if (sc->sc_mode == K_CODE) {
return (keycode);
}
/* compose a character code */
if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
switch (keycode) {
/* key pressed, process it */
case 0x47:
case 0x48:
case 0x49: /* keypad 7,8,9 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += keycode - 0x40;
goto check_composed;
case 0x4B:
case 0x4C:
case 0x4D: /* keypad 4,5,6 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += keycode - 0x47;
goto check_composed;
case 0x4F:
case 0x50:
case 0x51: /* keypad 1,2,3 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += keycode - 0x4E;
goto check_composed;
case 0x52: /* keypad 0 */
sc->sc_composed_char *= 10;
goto check_composed;
/* key released, no interest here */
case SCAN_RELEASE | 0x47:
case SCAN_RELEASE | 0x48:
case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */
case SCAN_RELEASE | 0x4B:
case SCAN_RELEASE | 0x4C:
case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */
case SCAN_RELEASE | 0x4F:
case SCAN_RELEASE | 0x50:
case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */
case SCAN_RELEASE | 0x52: /* keypad 0 */
goto next_code;
case 0x38: /* left alt key */
break;
default:
if (sc->sc_composed_char > 0) {
sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
sc->sc_composed_char = 0;
goto errkey;
}
break;
}
}
/* keycode to key action */
action = genkbd_keyaction(kbd, SCAN_CHAR(keycode),
(keycode & SCAN_RELEASE),
&sc->sc_state, &sc->sc_accents);
if (action == NOKEY) {
goto next_code;
}
done:
return (action);
check_composed:
if (sc->sc_composed_char <= 0xFF) {
goto next_code;
}
errkey:
return (ERRKEY);
}
/* Currently wait is always false. */
static uint32_t
ukbd_read_char(keyboard_t *kbd, int wait)
{
uint32_t keycode;
UKBD_LOCK();
keycode = ukbd_read_char_locked(kbd, wait);
UKBD_UNLOCK();
return (keycode);
}
/* some useful control functions */
static int
ukbd_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
struct ukbd_softc *sc = kbd->kb_data;
int i;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
int ival;
#endif
UKBD_LOCK_ASSERT();
switch (cmd) {
case KDGKBMODE: /* get keyboard mode */
*(int *)arg = sc->sc_mode;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 7):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBMODE: /* set keyboard mode */
switch (*(int *)arg) {
case K_XLATE:
if (sc->sc_mode != K_XLATE) {
/* make lock key state and LED state match */
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= KBD_LED_VAL(kbd);
}
/* FALLTHROUGH */
case K_RAW:
case K_CODE:
if (sc->sc_mode != *(int *)arg) {
if ((sc->sc_flags & UKBD_FLAG_POLLING) == 0)
ukbd_clear_state(kbd);
sc->sc_mode = *(int *)arg;
}
break;
default:
return (EINVAL);
}
break;
case KDGETLED: /* get keyboard LED */
*(int *)arg = KBD_LED_VAL(kbd);
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 66):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETLED: /* set keyboard LED */
/* NOTE: lock key state in "sc_state" won't be changed */
if (*(int *)arg & ~LOCK_MASK)
return (EINVAL);
i = *(int *)arg;
/* replace CAPS LED with ALTGR LED for ALTGR keyboards */
if (sc->sc_mode == K_XLATE &&
kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
if (i & ALKED)
i |= CLKED;
else
i &= ~CLKED;
}
if (KBD_HAS_DEVICE(kbd))
ukbd_set_leds(sc, i);
KBD_LED_VAL(kbd) = *(int *)arg;
break;
case KDGKBSTATE: /* get lock key state */
*(int *)arg = sc->sc_state & LOCK_MASK;
break;
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 20):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSKBSTATE: /* set lock key state */
if (*(int *)arg & ~LOCK_MASK) {
return (EINVAL);
}
sc->sc_state &= ~LOCK_MASK;
sc->sc_state |= *(int *)arg;
/* set LEDs and quit */
return (ukbd_ioctl(kbd, KDSETLED, arg));
case KDSETREPEAT: /* set keyboard repeat rate (new
* interface) */
if (!KBD_HAS_DEVICE(kbd)) {
return (0);
}
if (((int *)arg)[1] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 0) {
return (EINVAL);
}
if (((int *)arg)[0] < 200) /* fastest possible value */
kbd->kb_delay1 = 200;
else
kbd->kb_delay1 = ((int *)arg)[0];
kbd->kb_delay2 = ((int *)arg)[1];
#ifdef EVDEV_SUPPORT
if (sc->sc_evdev != NULL)
evdev_push_repeats(sc->sc_evdev, kbd);
#endif
return (0);
#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
case _IO('K', 67):
ival = IOCPARM_IVAL(arg);
arg = (caddr_t)&ival;
/* FALLTHROUGH */
#endif
case KDSETRAD: /* set keyboard repeat rate (old
* interface) */
return (ukbd_set_typematic(kbd, *(int *)arg));
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table
* (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table
* entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
sc->sc_accents = 0;
/* FALLTHROUGH */
default:
return (genkbd_commonioctl(kbd, cmd, arg));
}
return (0);
}
static int
ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
{
int result;
/*
* XXX Check if someone is calling us from a critical section:
*/
if (curthread->td_critnest != 0)
return (EDEADLK);
/*
* XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any
* context where printf(9) can be called, which among other things
* includes interrupt filters and threads with any kinds of locks
* already held. For this reason it would be dangerous to acquire
* the Giant here unconditionally. On the other hand we have to
* have it to handle the ioctl.
* So we make our best effort to auto-detect whether we can grab
* the Giant or not. Blame syscons(4) for this.
*/
switch (cmd) {
case KDGKBSTATE:
case KDSKBSTATE:
case KDSETLED:
if (!mtx_owned(&Giant) && !SCHEDULER_STOPPED())
return (EDEADLK); /* best I could come up with */
/* FALLTHROUGH */
default:
UKBD_LOCK();
result = ukbd_ioctl_locked(kbd, cmd, arg);
UKBD_UNLOCK();
return (result);
}
}
/* clear the internal state of the keyboard */
static void
ukbd_clear_state(keyboard_t *kbd)
{
struct ukbd_softc *sc = kbd->kb_data;
UKBD_CTX_LOCK_ASSERT();
sc->sc_flags &= ~(UKBD_FLAG_COMPOSE | UKBD_FLAG_POLLING);
sc->sc_state &= LOCK_MASK; /* preserve locking key state */
sc->sc_accents = 0;
sc->sc_composed_char = 0;
#ifdef UKBD_EMULATE_ATSCANCODE
sc->sc_buffered_char[0] = 0;
sc->sc_buffered_char[1] = 0;
#endif
memset(&sc->sc_ndata, 0, sizeof(sc->sc_ndata));
memset(&sc->sc_odata, 0, sizeof(sc->sc_odata));
memset(&sc->sc_ntime, 0, sizeof(sc->sc_ntime));
memset(&sc->sc_otime, 0, sizeof(sc->sc_otime));
}
/* save the internal state, not used */
static int
ukbd_get_state(keyboard_t *kbd, void *buf, size_t len)
{
return (len == 0) ? 1 : -1;
}
/* set the internal state, not used */
static int
ukbd_set_state(keyboard_t *kbd, void *buf, size_t len)
{
return (EINVAL);
}
static int
ukbd_poll(keyboard_t *kbd, int on)
{
struct ukbd_softc *sc = kbd->kb_data;
UKBD_LOCK();
/*
* Keep a reference count on polling to allow recursive
* cngrab() during a panic for example.
*/
if (on)
sc->sc_polling++;
else if (sc->sc_polling > 0)
sc->sc_polling--;
if (sc->sc_polling != 0) {
sc->sc_flags |= UKBD_FLAG_POLLING;
sc->sc_poll_thread = curthread;
} else {
sc->sc_flags &= ~UKBD_FLAG_POLLING;
ukbd_start_timer(sc); /* start timer */
}
UKBD_UNLOCK();
return (0);
}
/* local functions */
static void
ukbd_set_leds(struct ukbd_softc *sc, uint8_t leds)
{
UKBD_LOCK_ASSERT();
DPRINTF("leds=0x%02x\n", leds);
sc->sc_leds = leds;
sc->sc_flags |= UKBD_FLAG_SET_LEDS;
/* start transfer, if not already started */
usbd_transfer_start(sc->sc_xfer[UKBD_CTRL_LED]);
}
static int
ukbd_set_typematic(keyboard_t *kbd, int code)
{
#ifdef EVDEV_SUPPORT
struct ukbd_softc *sc = kbd->kb_data;
#endif
static const int delays[] = {250, 500, 750, 1000};
static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
68, 76, 84, 92, 100, 110, 118, 126,
136, 152, 168, 184, 200, 220, 236, 252,
272, 304, 336, 368, 400, 440, 472, 504};
if (code & ~0x7f) {
return (EINVAL);
}
kbd->kb_delay1 = delays[(code >> 5) & 3];
kbd->kb_delay2 = rates[code & 0x1f];
#ifdef EVDEV_SUPPORT
if (sc->sc_evdev != NULL)
evdev_push_repeats(sc->sc_evdev, kbd);
#endif
return (0);
}
#ifdef UKBD_EMULATE_ATSCANCODE
static int
ukbd_key2scan(struct ukbd_softc *sc, int code, int shift, int up)
{
static const int scan[] = {
/* 89 */
0x11c, /* Enter */
/* 90-99 */
0x11d, /* Ctrl-R */
0x135, /* Divide */
0x137 | SCAN_PREFIX_SHIFT, /* PrintScreen */
0x138, /* Alt-R */
0x147, /* Home */
0x148, /* Up */
0x149, /* PageUp */
0x14b, /* Left */
0x14d, /* Right */
0x14f, /* End */
/* 100-109 */
0x150, /* Down */
0x151, /* PageDown */
0x152, /* Insert */
0x153, /* Delete */
0x146, /* XXX Pause/Break */
0x15b, /* Win_L(Super_L) */
0x15c, /* Win_R(Super_R) */
0x15d, /* Application(Menu) */
/* SUN TYPE 6 USB KEYBOARD */
0x168, /* Sun Type 6 Help */
0x15e, /* Sun Type 6 Stop */
/* 110 - 119 */
0x15f, /* Sun Type 6 Again */
0x160, /* Sun Type 6 Props */
0x161, /* Sun Type 6 Undo */
0x162, /* Sun Type 6 Front */
0x163, /* Sun Type 6 Copy */
0x164, /* Sun Type 6 Open */
0x165, /* Sun Type 6 Paste */
0x166, /* Sun Type 6 Find */
0x167, /* Sun Type 6 Cut */
0x125, /* Sun Type 6 Mute */
/* 120 - 130 */
0x11f, /* Sun Type 6 VolumeDown */
0x11e, /* Sun Type 6 VolumeUp */
0x120, /* Sun Type 6 PowerDown */
/* Japanese 106/109 keyboard */
0x73, /* Keyboard Intl' 1 (backslash / underscore) */
0x70, /* Keyboard Intl' 2 (Katakana / Hiragana) */
0x7d, /* Keyboard Intl' 3 (Yen sign) (Not using in jp106/109) */
0x79, /* Keyboard Intl' 4 (Henkan) */
0x7b, /* Keyboard Intl' 5 (Muhenkan) */
0x5c, /* Keyboard Intl' 6 (Keypad ,) (For PC-9821 layout) */
0x71, /* Apple Keyboard JIS (Kana) */
0x72, /* Apple Keyboard JIS (Eisu) */
};
if ((code >= 89) && (code < (int)(89 + nitems(scan)))) {
code = scan[code - 89];
}
/* Pause/Break */
if ((code == 104) && (!(shift & (MOD_CONTROL_L | MOD_CONTROL_R)))) {
code = (0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL);
}
if (shift & (MOD_SHIFT_L | MOD_SHIFT_R)) {
code &= ~SCAN_PREFIX_SHIFT;
}
code |= (up ? SCAN_RELEASE : SCAN_PRESS);
if (code & SCAN_PREFIX) {
if (code & SCAN_PREFIX_CTL) {
/* Ctrl */
sc->sc_buffered_char[0] = (0x1d | (code & SCAN_RELEASE));
sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX);
} else if (code & SCAN_PREFIX_SHIFT) {
/* Shift */
sc->sc_buffered_char[0] = (0x2a | (code & SCAN_RELEASE));
sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX_SHIFT);
} else {
sc->sc_buffered_char[0] = (code & ~SCAN_PREFIX);
sc->sc_buffered_char[1] = 0;
}
return ((code & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
}
return (code);
}
#endif /* UKBD_EMULATE_ATSCANCODE */
static keyboard_switch_t ukbdsw = {
.probe = &ukbd__probe,
.init = &ukbd_init,
.term = &ukbd_term,
.intr = &ukbd_intr,
.test_if = &ukbd_test_if,
.enable = &ukbd_enable,
.disable = &ukbd_disable,
.read = &ukbd_read,
.check = &ukbd_check,
.read_char = &ukbd_read_char,
.check_char = &ukbd_check_char,
.ioctl = &ukbd_ioctl,
.lock = &ukbd_lock,
.clear_state = &ukbd_clear_state,
.get_state = &ukbd_get_state,
.set_state = &ukbd_set_state,
.poll = &ukbd_poll,
};
KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure);
static int
ukbd_driver_load(module_t mod, int what, void *arg)
{
switch (what) {
case MOD_LOAD:
kbd_add_driver(&ukbd_kbd_driver);
break;
case MOD_UNLOAD:
kbd_delete_driver(&ukbd_kbd_driver);
break;
}
return (0);
}
static devclass_t ukbd_devclass;
static device_method_t ukbd_methods[] = {
DEVMETHOD(device_probe, ukbd_probe),
DEVMETHOD(device_attach, ukbd_attach),
DEVMETHOD(device_detach, ukbd_detach),
DEVMETHOD(device_resume, ukbd_resume),
DEVMETHOD_END
};
static driver_t ukbd_driver = {
.name = "ukbd",
.methods = ukbd_methods,
.size = sizeof(struct ukbd_softc),
};
DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0);
MODULE_DEPEND(ukbd, usb, 1, 1, 1);
#ifdef EVDEV_SUPPORT
MODULE_DEPEND(ukbd, evdev, 1, 1, 1);
#endif
MODULE_VERSION(ukbd, 1);
USB_PNP_HOST_INFO(ukbd_devs);
Index: stable/11/sys/dev/usb/input/ums.c
===================================================================
--- stable/11/sys/dev/usb/input/ums.c (revision 356019)
+++ stable/11/sys/dev/usb/input/ums.c (revision 356020)
@@ -1,1229 +1,1228 @@
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Lennart Augustsson (lennart@augustsson.net) at
* Carlstedt Research & Technology.
*
* 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
#include
__FBSDID("$FreeBSD$");
/*
* HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
*/
#include "opt_evdev.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "usbdevs.h"
#define USB_DEBUG_VAR ums_debug
#include
#include
#ifdef EVDEV_SUPPORT
#include
#include
#endif
#include
#include
-#include
#include
#ifdef USB_DEBUG
static int ums_debug = 0;
static SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RWTUN,
&ums_debug, 0, "Debug level");
#endif
#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
#define MOUSE_FLAGS (HIO_RELATIVE)
#define UMS_BUF_SIZE 8 /* bytes */
#define UMS_IFQ_MAXLEN 50 /* units */
#define UMS_BUTTON_MAX 31 /* exclusive, must be less than 32 */
#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
#define UMS_INFO_MAX 2 /* maximum number of HID sets */
enum {
UMS_INTR_DT,
UMS_N_TRANSFER,
};
struct ums_info {
struct hid_location sc_loc_w;
struct hid_location sc_loc_x;
struct hid_location sc_loc_y;
struct hid_location sc_loc_z;
struct hid_location sc_loc_t;
struct hid_location sc_loc_btn[UMS_BUTTON_MAX];
uint32_t sc_flags;
#define UMS_FLAG_X_AXIS 0x0001
#define UMS_FLAG_Y_AXIS 0x0002
#define UMS_FLAG_Z_AXIS 0x0004
#define UMS_FLAG_T_AXIS 0x0008
#define UMS_FLAG_SBU 0x0010 /* spurious button up events */
#define UMS_FLAG_REVZ 0x0020 /* Z-axis is reversed */
#define UMS_FLAG_W_AXIS 0x0040
uint8_t sc_iid_w;
uint8_t sc_iid_x;
uint8_t sc_iid_y;
uint8_t sc_iid_z;
uint8_t sc_iid_t;
uint8_t sc_iid_btn[UMS_BUTTON_MAX];
uint8_t sc_buttons;
};
struct ums_softc {
struct usb_fifo_sc sc_fifo;
struct mtx sc_mtx;
struct usb_callout sc_callout;
struct ums_info sc_info[UMS_INFO_MAX];
mousehw_t sc_hw;
mousemode_t sc_mode;
mousestatus_t sc_status;
struct usb_xfer *sc_xfer[UMS_N_TRANSFER];
int sc_pollrate;
int sc_fflags;
#ifdef EVDEV_SUPPORT
int sc_evflags;
#define UMS_EVDEV_OPENED 1
#endif
uint8_t sc_buttons;
uint8_t sc_iid;
uint8_t sc_temp[64];
#ifdef EVDEV_SUPPORT
struct evdev_dev *sc_evdev;
#endif
};
static void ums_put_queue_timeout(void *__sc);
static usb_callback_t ums_intr_callback;
static device_probe_t ums_probe;
static device_attach_t ums_attach;
static device_detach_t ums_detach;
static usb_fifo_cmd_t ums_fifo_start_read;
static usb_fifo_cmd_t ums_fifo_stop_read;
static usb_fifo_open_t ums_fifo_open;
static usb_fifo_close_t ums_fifo_close;
static usb_fifo_ioctl_t ums_fifo_ioctl;
#ifdef EVDEV_SUPPORT
static evdev_open_t ums_ev_open;
static evdev_close_t ums_ev_close;
static void ums_evdev_push(struct ums_softc *, int32_t, int32_t,
int32_t, int32_t, int32_t);
#endif
static void ums_start_rx(struct ums_softc *);
static void ums_stop_rx(struct ums_softc *);
static void ums_put_queue(struct ums_softc *, int32_t, int32_t,
int32_t, int32_t, int32_t);
static int ums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS);
static struct usb_fifo_methods ums_fifo_methods = {
.f_open = &ums_fifo_open,
.f_close = &ums_fifo_close,
.f_ioctl = &ums_fifo_ioctl,
.f_start_read = &ums_fifo_start_read,
.f_stop_read = &ums_fifo_stop_read,
.basename[0] = "ums",
};
#ifdef EVDEV_SUPPORT
static const struct evdev_methods ums_evdev_methods = {
.ev_open = &ums_ev_open,
.ev_close = &ums_ev_close,
};
#endif
static void
ums_put_queue_timeout(void *__sc)
{
struct ums_softc *sc = __sc;
mtx_assert(&sc->sc_mtx, MA_OWNED);
ums_put_queue(sc, 0, 0, 0, 0, 0);
#ifdef EVDEV_SUPPORT
ums_evdev_push(sc, 0, 0, 0, 0, 0);
#endif
}
static void
ums_intr_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct ums_softc *sc = usbd_xfer_softc(xfer);
struct ums_info *info = &sc->sc_info[0];
struct usb_page_cache *pc;
uint8_t *buf = sc->sc_temp;
int32_t buttons = 0;
int32_t buttons_found = 0;
#ifdef EVDEV_SUPPORT
int32_t buttons_reported = 0;
#endif
int32_t dw = 0;
int32_t dx = 0;
int32_t dy = 0;
int32_t dz = 0;
int32_t dt = 0;
uint8_t i;
uint8_t id;
int len;
usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
DPRINTFN(6, "sc=%p actlen=%d\n", sc, len);
if (len > (int)sizeof(sc->sc_temp)) {
DPRINTFN(6, "truncating large packet to %zu bytes\n",
sizeof(sc->sc_temp));
len = sizeof(sc->sc_temp);
}
if (len == 0)
goto tr_setup;
pc = usbd_xfer_get_frame(xfer, 0);
usbd_copy_out(pc, 0, buf, len);
DPRINTFN(6, "data = %02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
(len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0,
(len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0,
(len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0,
(len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0);
if (sc->sc_iid) {
id = *buf;
len--;
buf++;
} else {
id = 0;
if (sc->sc_info[0].sc_flags & UMS_FLAG_SBU) {
if ((*buf == 0x14) || (*buf == 0x15)) {
goto tr_setup;
}
}
}
repeat:
if ((info->sc_flags & UMS_FLAG_W_AXIS) &&
(id == info->sc_iid_w))
dw += hid_get_data(buf, len, &info->sc_loc_w);
if ((info->sc_flags & UMS_FLAG_X_AXIS) &&
(id == info->sc_iid_x))
dx += hid_get_data(buf, len, &info->sc_loc_x);
if ((info->sc_flags & UMS_FLAG_Y_AXIS) &&
(id == info->sc_iid_y))
dy -= hid_get_data(buf, len, &info->sc_loc_y);
if ((info->sc_flags & UMS_FLAG_Z_AXIS) &&
(id == info->sc_iid_z)) {
int32_t temp;
temp = hid_get_data(buf, len, &info->sc_loc_z);
if (info->sc_flags & UMS_FLAG_REVZ)
temp = -temp;
dz -= temp;
}
if ((info->sc_flags & UMS_FLAG_T_AXIS) &&
(id == info->sc_iid_t)) {
dt += hid_get_data(buf, len, &info->sc_loc_t);
/* T-axis is translated into button presses */
buttons_found |= (1UL << 5) | (1UL << 6);
}
for (i = 0; i < info->sc_buttons; i++) {
uint32_t mask;
mask = 1UL << UMS_BUT(i);
/* check for correct button ID */
if (id != info->sc_iid_btn[i])
continue;
/* check for button pressed */
if (hid_get_data(buf, len, &info->sc_loc_btn[i]))
buttons |= mask;
/* register button mask */
buttons_found |= mask;
}
if (++info != &sc->sc_info[UMS_INFO_MAX])
goto repeat;
#ifdef EVDEV_SUPPORT
buttons_reported = buttons;
#endif
/* keep old button value(s) for non-detected buttons */
buttons |= sc->sc_status.button & ~buttons_found;
if (dx || dy || dz || dt || dw ||
(buttons != sc->sc_status.button)) {
DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n",
dx, dy, dz, dt, dw, buttons);
/* translate T-axis into button presses until further */
if (dt > 0) {
ums_put_queue(sc, 0, 0, 0, 0, buttons);
buttons |= 1UL << 6;
} else if (dt < 0) {
ums_put_queue(sc, 0, 0, 0, 0, buttons);
buttons |= 1UL << 5;
}
sc->sc_status.button = buttons;
sc->sc_status.dx += dx;
sc->sc_status.dy += dy;
sc->sc_status.dz += dz;
/*
* sc->sc_status.dt += dt;
* no way to export this yet
*/
/*
* The Qtronix keyboard has a built in PS/2
* port for a mouse. The firmware once in a
* while posts a spurious button up
* event. This event we ignore by doing a
* timeout for 50 msecs. If we receive
* dx=dy=dz=buttons=0 before we add the event
* to the queue. In any other case we delete
* the timeout event.
*/
if ((sc->sc_info[0].sc_flags & UMS_FLAG_SBU) &&
(dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) &&
(dw == 0) && (buttons == 0)) {
usb_callout_reset(&sc->sc_callout, hz / 20,
&ums_put_queue_timeout, sc);
} else {
usb_callout_stop(&sc->sc_callout);
ums_put_queue(sc, dx, dy, dz, dt, buttons);
#ifdef EVDEV_SUPPORT
ums_evdev_push(sc, dx, dy, dz, dt,
buttons_reported);
#endif
}
}
case USB_ST_SETUP:
tr_setup:
/* check if we can put more data into the FIFO */
if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) == 0) {
#ifdef EVDEV_SUPPORT
if (sc->sc_evflags == 0)
break;
#else
break;
#endif
}
usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
usbd_transfer_submit(xfer);
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
/* try clear stall first */
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
break;
}
}
static const struct usb_config ums_config[UMS_N_TRANSFER] = {
[UMS_INTR_DT] = {
.type = UE_INTERRUPT,
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_IN,
.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
.bufsize = 0, /* use wMaxPacketSize */
.callback = &ums_intr_callback,
},
};
/* A match on these entries will load ums */
static const STRUCT_USB_HOST_ID __used ums_devs[] = {
{USB_IFACE_CLASS(UICLASS_HID),
USB_IFACE_SUBCLASS(UISUBCLASS_BOOT),
USB_IFACE_PROTOCOL(UIPROTO_MOUSE),},
};
static int
ums_probe(device_t dev)
{
struct usb_attach_arg *uaa = device_get_ivars(dev);
void *d_ptr;
int error;
uint16_t d_len;
DPRINTFN(11, "\n");
if (uaa->usb_mode != USB_MODE_HOST)
return (ENXIO);
if (uaa->info.bInterfaceClass != UICLASS_HID)
return (ENXIO);
if (usb_test_quirk(uaa, UQ_UMS_IGNORE))
return (ENXIO);
if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
(uaa->info.bInterfaceProtocol == UIPROTO_MOUSE))
return (BUS_PROBE_DEFAULT);
error = usbd_req_get_hid_desc(uaa->device, NULL,
&d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
if (error)
return (ENXIO);
if (hid_is_mouse(d_ptr, d_len))
error = BUS_PROBE_DEFAULT;
else
error = ENXIO;
free(d_ptr, M_TEMP);
return (error);
}
static void
ums_hid_parse(struct ums_softc *sc, device_t dev, const uint8_t *buf,
uint16_t len, uint8_t index)
{
struct ums_info *info = &sc->sc_info[index];
uint32_t flags;
uint8_t i;
uint8_t j;
if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
hid_input, index, &info->sc_loc_x, &flags, &info->sc_iid_x)) {
if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
info->sc_flags |= UMS_FLAG_X_AXIS;
}
}
if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
hid_input, index, &info->sc_loc_y, &flags, &info->sc_iid_y)) {
if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
info->sc_flags |= UMS_FLAG_Y_AXIS;
}
}
/* Try the wheel first as the Z activator since it's tradition. */
if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
HUG_WHEEL), hid_input, index, &info->sc_loc_z, &flags,
&info->sc_iid_z) ||
hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
HUG_TWHEEL), hid_input, index, &info->sc_loc_z, &flags,
&info->sc_iid_z)) {
if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
info->sc_flags |= UMS_FLAG_Z_AXIS;
}
/*
* We might have both a wheel and Z direction, if so put
* put the Z on the W coordinate.
*/
if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
HUG_Z), hid_input, index, &info->sc_loc_w, &flags,
&info->sc_iid_w)) {
if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
info->sc_flags |= UMS_FLAG_W_AXIS;
}
}
} else if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
HUG_Z), hid_input, index, &info->sc_loc_z, &flags,
&info->sc_iid_z)) {
if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
info->sc_flags |= UMS_FLAG_Z_AXIS;
}
}
/*
* The Microsoft Wireless Intellimouse 2.0 reports it's wheel
* using 0x0048, which is HUG_TWHEEL, and seems to expect you
* to know that the byte after the wheel is the tilt axis.
* There are no other HID axis descriptors other than X,Y and
* TWHEEL
*/
if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
HUG_TWHEEL), hid_input, index, &info->sc_loc_t,
&flags, &info->sc_iid_t)) {
info->sc_loc_t.pos += 8;
if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
info->sc_flags |= UMS_FLAG_T_AXIS;
}
} else if (hid_locate(buf, len, HID_USAGE2(HUP_CONSUMER,
HUC_AC_PAN), hid_input, index, &info->sc_loc_t,
&flags, &info->sc_iid_t)) {
if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS)
info->sc_flags |= UMS_FLAG_T_AXIS;
}
/* figure out the number of buttons */
for (i = 0; i < UMS_BUTTON_MAX; i++) {
if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)),
hid_input, index, &info->sc_loc_btn[i], NULL,
&info->sc_iid_btn[i])) {
break;
}
}
/* detect other buttons */
for (j = 0; (i < UMS_BUTTON_MAX) && (j < 2); i++, j++) {
if (!hid_locate(buf, len, HID_USAGE2(HUP_MICROSOFT, (j + 1)),
hid_input, index, &info->sc_loc_btn[i], NULL,
&info->sc_iid_btn[i])) {
break;
}
}
info->sc_buttons = i;
if (i > sc->sc_buttons)
sc->sc_buttons = i;
if (info->sc_flags == 0)
return;
/* announce information about the mouse */
device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n",
(info->sc_buttons),
(info->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "",
(info->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "",
(info->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "",
(info->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "",
(info->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "",
info->sc_iid_x);
}
static int
ums_attach(device_t dev)
{
struct usb_attach_arg *uaa = device_get_ivars(dev);
struct ums_softc *sc = device_get_softc(dev);
struct ums_info *info;
void *d_ptr = NULL;
int isize;
int err;
uint16_t d_len;
uint8_t i;
#ifdef USB_DEBUG
uint8_t j;
#endif
DPRINTFN(11, "sc=%p\n", sc);
device_set_usb_desc(dev);
mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE);
usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
/*
* Force the report (non-boot) protocol.
*
* Mice without boot protocol support may choose not to implement
* Set_Protocol at all; Ignore any error.
*/
err = usbd_req_set_protocol(uaa->device, NULL,
uaa->info.bIfaceIndex, 1);
err = usbd_transfer_setup(uaa->device,
&uaa->info.bIfaceIndex, sc->sc_xfer, ums_config,
UMS_N_TRANSFER, sc, &sc->sc_mtx);
if (err) {
DPRINTF("error=%s\n", usbd_errstr(err));
goto detach;
}
/* Get HID descriptor */
err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr,
&d_len, M_TEMP, uaa->info.bIfaceIndex);
if (err) {
device_printf(dev, "error reading report description\n");
goto detach;
}
isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid);
/*
* The Microsoft Wireless Notebook Optical Mouse seems to be in worse
* shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
* all of its other button positions are all off. It also reports that
* it has two additional buttons and a tilt wheel.
*/
if (usb_test_quirk(uaa, UQ_MS_BAD_CLASS)) {
sc->sc_iid = 0;
info = &sc->sc_info[0];
info->sc_flags = (UMS_FLAG_X_AXIS |
UMS_FLAG_Y_AXIS |
UMS_FLAG_Z_AXIS |
UMS_FLAG_SBU);
info->sc_buttons = 3;
isize = 5;
/* 1st byte of descriptor report contains garbage */
info->sc_loc_x.pos = 16;
info->sc_loc_x.size = 8;
info->sc_loc_y.pos = 24;
info->sc_loc_y.size = 8;
info->sc_loc_z.pos = 32;
info->sc_loc_z.size = 8;
info->sc_loc_btn[0].pos = 8;
info->sc_loc_btn[0].size = 1;
info->sc_loc_btn[1].pos = 9;
info->sc_loc_btn[1].size = 1;
info->sc_loc_btn[2].pos = 10;
info->sc_loc_btn[2].size = 1;
/* Announce device */
device_printf(dev, "3 buttons and [XYZ] "
"coordinates ID=0\n");
} else {
/* Search the HID descriptor and announce device */
for (i = 0; i < UMS_INFO_MAX; i++) {
ums_hid_parse(sc, dev, d_ptr, d_len, i);
}
}
if (usb_test_quirk(uaa, UQ_MS_REVZ)) {
info = &sc->sc_info[0];
/* Some wheels need the Z axis reversed. */
info->sc_flags |= UMS_FLAG_REVZ;
}
if (isize > (int)usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT])) {
DPRINTF("WARNING: report size, %d bytes, is larger "
"than interrupt size, %d bytes!\n", isize,
usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT]));
}
free(d_ptr, M_TEMP);
d_ptr = NULL;
#ifdef USB_DEBUG
for (j = 0; j < UMS_INFO_MAX; j++) {
info = &sc->sc_info[j];
DPRINTF("sc=%p, index=%d\n", sc, j);
DPRINTF("X\t%d/%d id=%d\n", info->sc_loc_x.pos,
info->sc_loc_x.size, info->sc_iid_x);
DPRINTF("Y\t%d/%d id=%d\n", info->sc_loc_y.pos,
info->sc_loc_y.size, info->sc_iid_y);
DPRINTF("Z\t%d/%d id=%d\n", info->sc_loc_z.pos,
info->sc_loc_z.size, info->sc_iid_z);
DPRINTF("T\t%d/%d id=%d\n", info->sc_loc_t.pos,
info->sc_loc_t.size, info->sc_iid_t);
DPRINTF("W\t%d/%d id=%d\n", info->sc_loc_w.pos,
info->sc_loc_w.size, info->sc_iid_w);
for (i = 0; i < info->sc_buttons; i++) {
DPRINTF("B%d\t%d/%d id=%d\n",
i + 1, info->sc_loc_btn[i].pos,
info->sc_loc_btn[i].size, info->sc_iid_btn[i]);
}
}
DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid);
#endif
err = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx,
&ums_fifo_methods, &sc->sc_fifo,
device_get_unit(dev), -1, uaa->info.bIfaceIndex,
UID_ROOT, GID_OPERATOR, 0644);
if (err)
goto detach;
#ifdef EVDEV_SUPPORT
sc->sc_evdev = evdev_alloc();
evdev_set_name(sc->sc_evdev, device_get_desc(dev));
evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev));
evdev_set_id(sc->sc_evdev, BUS_USB, uaa->info.idVendor,
uaa->info.idProduct, 0);
evdev_set_serial(sc->sc_evdev, usb_get_serial(uaa->device));
evdev_set_methods(sc->sc_evdev, sc, &ums_evdev_methods);
evdev_support_prop(sc->sc_evdev, INPUT_PROP_POINTER);
evdev_support_event(sc->sc_evdev, EV_SYN);
evdev_support_event(sc->sc_evdev, EV_REL);
evdev_support_event(sc->sc_evdev, EV_KEY);
info = &sc->sc_info[0];
if (info->sc_flags & UMS_FLAG_X_AXIS)
evdev_support_rel(sc->sc_evdev, REL_X);
if (info->sc_flags & UMS_FLAG_Y_AXIS)
evdev_support_rel(sc->sc_evdev, REL_Y);
if (info->sc_flags & UMS_FLAG_Z_AXIS)
evdev_support_rel(sc->sc_evdev, REL_WHEEL);
if (info->sc_flags & UMS_FLAG_T_AXIS)
evdev_support_rel(sc->sc_evdev, REL_HWHEEL);
for (i = 0; i < info->sc_buttons; i++)
evdev_support_key(sc->sc_evdev, BTN_MOUSE + i);
err = evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx);
if (err)
goto detach;
#endif
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
OID_AUTO, "parseinfo", CTLTYPE_STRING|CTLFLAG_RD,
sc, 0, ums_sysctl_handler_parseinfo,
"", "Dump of parsed HID report descriptor");
return (0);
detach:
if (d_ptr) {
free(d_ptr, M_TEMP);
}
ums_detach(dev);
return (ENOMEM);
}
static int
ums_detach(device_t self)
{
struct ums_softc *sc = device_get_softc(self);
DPRINTF("sc=%p\n", sc);
usb_fifo_detach(&sc->sc_fifo);
#ifdef EVDEV_SUPPORT
evdev_free(sc->sc_evdev);
#endif
usbd_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER);
usb_callout_drain(&sc->sc_callout);
mtx_destroy(&sc->sc_mtx);
return (0);
}
static void
ums_reset(struct ums_softc *sc)
{
/* reset all USB mouse parameters */
if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
else
sc->sc_hw.buttons = sc->sc_buttons;
sc->sc_hw.iftype = MOUSE_IF_USB;
sc->sc_hw.type = MOUSE_MOUSE;
sc->sc_hw.model = MOUSE_MODEL_GENERIC;
sc->sc_hw.hwid = 0;
sc->sc_mode.protocol = MOUSE_PROTO_MSC;
sc->sc_mode.rate = -1;
sc->sc_mode.resolution = MOUSE_RES_UNKNOWN;
sc->sc_mode.accelfactor = 0;
sc->sc_mode.level = 0;
sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
/* reset status */
sc->sc_status.flags = 0;
sc->sc_status.button = 0;
sc->sc_status.obutton = 0;
sc->sc_status.dx = 0;
sc->sc_status.dy = 0;
sc->sc_status.dz = 0;
/* sc->sc_status.dt = 0; */
}
static void
ums_start_rx(struct ums_softc *sc)
{
int rate;
/* Check if we should override the default polling interval */
rate = sc->sc_pollrate;
/* Range check rate */
if (rate > 1000)
rate = 1000;
/* Check for set rate */
if ((rate > 0) && (sc->sc_xfer[UMS_INTR_DT] != NULL)) {
DPRINTF("Setting pollrate = %d\n", rate);
/* Stop current transfer, if any */
usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
/* Set new interval */
usbd_xfer_set_interval(sc->sc_xfer[UMS_INTR_DT], 1000 / rate);
/* Only set pollrate once */
sc->sc_pollrate = 0;
}
usbd_transfer_start(sc->sc_xfer[UMS_INTR_DT]);
}
static void
ums_stop_rx(struct ums_softc *sc)
{
usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
usb_callout_stop(&sc->sc_callout);
}
static void
ums_fifo_start_read(struct usb_fifo *fifo)
{
struct ums_softc *sc = usb_fifo_softc(fifo);
ums_start_rx(sc);
}
static void
ums_fifo_stop_read(struct usb_fifo *fifo)
{
struct ums_softc *sc = usb_fifo_softc(fifo);
ums_stop_rx(sc);
}
#if ((MOUSE_SYS_PACKETSIZE != 8) || \
(MOUSE_MSC_PACKETSIZE != 5))
#error "Software assumptions are not met. Please update code."
#endif
static void
ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy,
int32_t dz, int32_t dt, int32_t buttons)
{
uint8_t buf[8];
if (1) {
if (dx > 254)
dx = 254;
if (dx < -256)
dx = -256;
if (dy > 254)
dy = 254;
if (dy < -256)
dy = -256;
if (dz > 126)
dz = 126;
if (dz < -128)
dz = -128;
if (dt > 126)
dt = 126;
if (dt < -128)
dt = -128;
buf[0] = sc->sc_mode.syncmask[1];
buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS;
buf[1] = dx >> 1;
buf[2] = dy >> 1;
buf[3] = dx - (dx >> 1);
buf[4] = dy - (dy >> 1);
if (sc->sc_mode.level == 1) {
buf[5] = dz >> 1;
buf[6] = dz - (dz >> 1);
buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS);
}
usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
sc->sc_mode.packetsize, 1);
} else {
DPRINTF("Buffer full, discarded packet\n");
}
}
#ifdef EVDEV_SUPPORT
static void
ums_evdev_push(struct ums_softc *sc, int32_t dx, int32_t dy,
int32_t dz, int32_t dt, int32_t buttons)
{
if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) {
/* Push evdev event */
evdev_push_rel(sc->sc_evdev, REL_X, dx);
evdev_push_rel(sc->sc_evdev, REL_Y, -dy);
evdev_push_rel(sc->sc_evdev, REL_WHEEL, -dz);
evdev_push_rel(sc->sc_evdev, REL_HWHEEL, dt);
evdev_push_mouse_btn(sc->sc_evdev,
(buttons & ~MOUSE_STDBUTTONS) |
(buttons & (1 << 2) ? MOUSE_BUTTON1DOWN : 0) |
(buttons & (1 << 1) ? MOUSE_BUTTON2DOWN : 0) |
(buttons & (1 << 0) ? MOUSE_BUTTON3DOWN : 0));
evdev_sync(sc->sc_evdev);
}
}
#endif
static void
ums_reset_buf(struct ums_softc *sc)
{
/* reset read queue, must be called locked */
usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
}
#ifdef EVDEV_SUPPORT
static int
ums_ev_open(struct evdev_dev *evdev, void *ev_softc)
{
struct ums_softc *sc = (struct ums_softc *)ev_softc;
mtx_assert(&sc->sc_mtx, MA_OWNED);
sc->sc_evflags = UMS_EVDEV_OPENED;
if (sc->sc_fflags == 0) {
ums_reset(sc);
ums_start_rx(sc);
}
return (0);
}
static void
ums_ev_close(struct evdev_dev *evdev, void *ev_softc)
{
struct ums_softc *sc = (struct ums_softc *)ev_softc;
mtx_assert(&sc->sc_mtx, MA_OWNED);
sc->sc_evflags = 0;
if (sc->sc_fflags == 0)
ums_stop_rx(sc);
}
#endif
static int
ums_fifo_open(struct usb_fifo *fifo, int fflags)
{
struct ums_softc *sc = usb_fifo_softc(fifo);
DPRINTFN(2, "\n");
/* check for duplicate open, should not happen */
if (sc->sc_fflags & fflags)
return (EBUSY);
/* check for first open */
#ifdef EVDEV_SUPPORT
if (sc->sc_fflags == 0 && sc->sc_evflags == 0)
ums_reset(sc);
#else
if (sc->sc_fflags == 0)
ums_reset(sc);
#endif
if (fflags & FREAD) {
/* allocate RX buffer */
if (usb_fifo_alloc_buffer(fifo,
UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) {
return (ENOMEM);
}
}
sc->sc_fflags |= fflags & (FREAD | FWRITE);
return (0);
}
static void
ums_fifo_close(struct usb_fifo *fifo, int fflags)
{
struct ums_softc *sc = usb_fifo_softc(fifo);
DPRINTFN(2, "\n");
if (fflags & FREAD)
usb_fifo_free_buffer(fifo);
sc->sc_fflags &= ~(fflags & (FREAD | FWRITE));
}
static int
ums_fifo_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
{
struct ums_softc *sc = usb_fifo_softc(fifo);
mousemode_t mode;
int error = 0;
DPRINTFN(2, "\n");
mtx_lock(&sc->sc_mtx);
switch (cmd) {
case MOUSE_GETHWINFO:
*(mousehw_t *)addr = sc->sc_hw;
break;
case MOUSE_GETMODE:
*(mousemode_t *)addr = sc->sc_mode;
break;
case MOUSE_SETMODE:
mode = *(mousemode_t *)addr;
if (mode.level == -1) {
/* don't change the current setting */
} else if ((mode.level < 0) || (mode.level > 1)) {
error = EINVAL;
break;
} else {
sc->sc_mode.level = mode.level;
}
/* store polling rate */
sc->sc_pollrate = mode.rate;
if (sc->sc_mode.level == 0) {
if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
else
sc->sc_hw.buttons = sc->sc_buttons;
sc->sc_mode.protocol = MOUSE_PROTO_MSC;
sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
} else if (sc->sc_mode.level == 1) {
if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
else
sc->sc_hw.buttons = sc->sc_buttons;
sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
}
ums_reset_buf(sc);
break;
case MOUSE_GETLEVEL:
*(int *)addr = sc->sc_mode.level;
break;
case MOUSE_SETLEVEL:
if (*(int *)addr < 0 || *(int *)addr > 1) {
error = EINVAL;
break;
}
sc->sc_mode.level = *(int *)addr;
if (sc->sc_mode.level == 0) {
if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
else
sc->sc_hw.buttons = sc->sc_buttons;
sc->sc_mode.protocol = MOUSE_PROTO_MSC;
sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
} else if (sc->sc_mode.level == 1) {
if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
else
sc->sc_hw.buttons = sc->sc_buttons;
sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
}
ums_reset_buf(sc);
break;
case MOUSE_GETSTATUS:{
mousestatus_t *status = (mousestatus_t *)addr;
*status = sc->sc_status;
sc->sc_status.obutton = sc->sc_status.button;
sc->sc_status.button = 0;
sc->sc_status.dx = 0;
sc->sc_status.dy = 0;
sc->sc_status.dz = 0;
/* sc->sc_status.dt = 0; */
if (status->dx || status->dy || status->dz /* || status->dt */ ) {
status->flags |= MOUSE_POSCHANGED;
}
if (status->button != status->obutton) {
status->flags |= MOUSE_BUTTONSCHANGED;
}
break;
}
default:
error = ENOTTY;
break;
}
mtx_unlock(&sc->sc_mtx);
return (error);
}
static int
ums_sysctl_handler_parseinfo(SYSCTL_HANDLER_ARGS)
{
struct ums_softc *sc = arg1;
struct ums_info *info;
struct sbuf *sb;
int i, j, err, had_output;
sb = sbuf_new_auto();
for (i = 0, had_output = 0; i < UMS_INFO_MAX; i++) {
info = &sc->sc_info[i];
/* Don't emit empty info */
if ((info->sc_flags &
(UMS_FLAG_X_AXIS | UMS_FLAG_Y_AXIS | UMS_FLAG_Z_AXIS |
UMS_FLAG_T_AXIS | UMS_FLAG_W_AXIS)) == 0 &&
info->sc_buttons == 0)
continue;
if (had_output)
sbuf_printf(sb, "\n");
had_output = 1;
sbuf_printf(sb, "i%d:", i + 1);
if (info->sc_flags & UMS_FLAG_X_AXIS)
sbuf_printf(sb, " X:r%d, p%d, s%d;",
(int)info->sc_iid_x,
(int)info->sc_loc_x.pos,
(int)info->sc_loc_x.size);
if (info->sc_flags & UMS_FLAG_Y_AXIS)
sbuf_printf(sb, " Y:r%d, p%d, s%d;",
(int)info->sc_iid_y,
(int)info->sc_loc_y.pos,
(int)info->sc_loc_y.size);
if (info->sc_flags & UMS_FLAG_Z_AXIS)
sbuf_printf(sb, " Z:r%d, p%d, s%d;",
(int)info->sc_iid_z,
(int)info->sc_loc_z.pos,
(int)info->sc_loc_z.size);
if (info->sc_flags & UMS_FLAG_T_AXIS)
sbuf_printf(sb, " T:r%d, p%d, s%d;",
(int)info->sc_iid_t,
(int)info->sc_loc_t.pos,
(int)info->sc_loc_t.size);
if (info->sc_flags & UMS_FLAG_W_AXIS)
sbuf_printf(sb, " W:r%d, p%d, s%d;",
(int)info->sc_iid_w,
(int)info->sc_loc_w.pos,
(int)info->sc_loc_w.size);
for (j = 0; j < info->sc_buttons; j++) {
sbuf_printf(sb, " B%d:r%d, p%d, s%d;", j + 1,
(int)info->sc_iid_btn[j],
(int)info->sc_loc_btn[j].pos,
(int)info->sc_loc_btn[j].size);
}
}
sbuf_finish(sb);
err = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);
sbuf_delete(sb);
return (err);
}
static devclass_t ums_devclass;
static device_method_t ums_methods[] = {
DEVMETHOD(device_probe, ums_probe),
DEVMETHOD(device_attach, ums_attach),
DEVMETHOD(device_detach, ums_detach),
DEVMETHOD_END
};
static driver_t ums_driver = {
.name = "ums",
.methods = ums_methods,
.size = sizeof(struct ums_softc),
};
DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, NULL, 0);
MODULE_DEPEND(ums, usb, 1, 1, 1);
#ifdef EVDEV_SUPPORT
MODULE_DEPEND(ums, evdev, 1, 1, 1);
#endif
MODULE_VERSION(ums, 1);
USB_PNP_HOST_INFO(ums_devs);
Index: stable/11/sys/kern/tty.c
===================================================================
--- stable/11/sys/kern/tty.c (revision 356019)
+++ stable/11/sys/kern/tty.c (revision 356020)
@@ -1,2347 +1,2348 @@
/*-
* Copyright (c) 2008 Ed Schouten