829 lines
19 KiB
C
829 lines
19 KiB
C
/* $NetBSD: sa1111_kbc.c,v 1.2 2003/07/15 00:24:50 lukem Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2002 Genetec Corporation. All rights reserved.
|
|
* Written by Hiroyuki Bessho for Genetec Corporation.
|
|
*
|
|
* 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 Genetec Corporation may not be used to endorse or
|
|
* promote products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY GENETEC 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 GENETEC 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, 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.
|
|
*
|
|
* Driver for keyboard controller in SA-1111 companion chip.
|
|
*
|
|
* PC keyboard driver (sys/dev/pckbc/pckbd.c) works only with 8042
|
|
* keyboard controller driver (sys/dev/ic/pckbc.c). This file
|
|
* provides same functions as those of 8042 driver.
|
|
*
|
|
* XXX: we need cleaner interface between the keyboard driver and
|
|
* keyboard controller drivers.
|
|
*/
|
|
/*
|
|
* Copyright (c) 1998
|
|
* Matthias Drochner. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed for the NetBSD Project
|
|
* by Matthias Drochner.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: sa1111_kbc.c,v 1.2 2003/07/15 00:24:50 lukem Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/types.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/device.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/lock.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <arm/sa11x0/sa1111_reg.h>
|
|
#include <arm/sa11x0/sa1111_var.h>
|
|
|
|
#include <dev/ic/pckbcvar.h> /* for prototypes */
|
|
|
|
#include "pckbd.h"
|
|
#include "rnd.h"
|
|
#include "locators.h"
|
|
|
|
/* descriptor for one device command */
|
|
struct pckbc_devcmd {
|
|
TAILQ_ENTRY(pckbc_devcmd) next;
|
|
int flags;
|
|
#define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
|
|
#define KBC_CMDFLAG_SLOW 2
|
|
u_char cmd[4];
|
|
int cmdlen, cmdidx, retries;
|
|
u_char response[4];
|
|
int status, responselen, responseidx;
|
|
};
|
|
|
|
struct sackbc_softc {
|
|
struct device dev;
|
|
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
|
|
void *ih_rx; /* receive interrupt */
|
|
int intr; /* interrupt number */
|
|
|
|
int polling; /* don't process data in interrupt handler */
|
|
int poll_stat; /* data read from inr handler if polling */
|
|
int poll_data; /* status read from intr handler if polling */
|
|
|
|
TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* active commands */
|
|
TAILQ_HEAD(, pckbc_devcmd) freequeue; /* free commands */
|
|
#define NCMD 5
|
|
struct pckbc_devcmd cmd[NCMD];
|
|
|
|
struct callout t_cleanup;
|
|
pckbc_inputfcn inputhandler;
|
|
void *inputarg;
|
|
const char *subname;
|
|
|
|
};
|
|
|
|
#define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
|
|
|
|
#define N_KBC_SLOTS 2
|
|
/*static struct sackbc_softc *sackbc_slot[N_KBC_SLOTS] = { NULL, NULL };*/
|
|
|
|
static int sackbc_match(struct device *, struct cfdata *, void *);
|
|
static void sackbc_attach(struct device *, struct device *, void *);
|
|
static int sackbc_cmdresponse( struct sackbc_softc *, int );
|
|
|
|
CFATTACH_DECL(sackbc, sizeof(struct sackbc_softc), sackbc_match,
|
|
sackbc_attach, NULL, NULL);
|
|
|
|
/* XXX should not be here */
|
|
#define KBC_DEVCMD_ACK 0xfa
|
|
#define KBC_DEVCMD_RESEND 0xfe
|
|
|
|
#define KBD_DELAY DELAY(8)
|
|
|
|
/*#define SACKBCDEBUG*/
|
|
|
|
#ifdef SACKBCDEBUG
|
|
#define DPRINTF(arg) printf arg
|
|
#else
|
|
#define DPRINTF(arg)
|
|
#endif
|
|
|
|
static void sackbc_poll_cmd1( struct sackbc_softc *, struct pckbc_devcmd * );
|
|
|
|
|
|
|
|
static int
|
|
sackbc_match(struct device *parent, struct cfdata *cf, void *aux)
|
|
{
|
|
struct sa1111_attach_args *aa = (struct sa1111_attach_args *)aux;
|
|
|
|
switch( aa->sa_addr ){
|
|
case SACC_KBD0: case SACC_KBD1:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
static int
|
|
sackbc_txint( void *cookie )
|
|
{
|
|
struct sackbc_softc *sc = cookie;
|
|
|
|
bus_space_read_4( sc->iot, sc->ioh, SACCKBD_STAT );
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
sackbc_rxint( void *cookie )
|
|
{
|
|
struct sackbc_softc *sc = cookie;
|
|
int stat, code=-1;
|
|
|
|
stat = bus_space_read_4( sc->iot, sc->ioh, SACCKBD_STAT );
|
|
DPRINTF(( "sackbc_rxint stat=%x\n", stat ));
|
|
if( stat & KBDSTAT_RXF ){
|
|
code = bus_space_read_4( sc->iot, sc->ioh, SACCKBD_DATA );
|
|
|
|
if( sc->polling ){
|
|
sc->poll_data = code;
|
|
sc->poll_stat = stat;
|
|
}
|
|
else if (CMD_IN_QUEUE(sc) && sackbc_cmdresponse(sc, code))
|
|
;
|
|
else if( sc->inputhandler ){
|
|
(* sc->inputhandler)( sc->inputarg, code );
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sackbcprint(void *aux, const char *pnp)
|
|
{
|
|
return (QUIET);
|
|
}
|
|
|
|
static void
|
|
sackbc_setup_intrhandler(struct sackbc_softc *sc)
|
|
{
|
|
if( !(sc->polling) && sc->ih_rx==NULL ){
|
|
sc->ih_rx = sacc_intr_establish(
|
|
(sacc_chipset_tag_t *)(sc->dev.dv_parent),
|
|
sc->intr+1, IST_EDGE_RAISE, IPL_TTY, sackbc_rxint, sc );
|
|
if( sc->ih_rx == NULL ){
|
|
printf( "%s: can't establish interrupt\n",
|
|
sc->dev.dv_xname );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
sackbc_disable_intrhandler( struct sackbc_softc *sc )
|
|
{
|
|
if( sc->polling && sc->ih_rx ){
|
|
sacc_intr_disestablish(
|
|
(sacc_chipset_tag_t *)(sc->dev.dv_parent),
|
|
sc->ih_rx );
|
|
sc->ih_rx = NULL;
|
|
}
|
|
}
|
|
|
|
static int
|
|
sackbc_submatch(struct device *parent, struct cfdata *cf, void *aux)
|
|
{
|
|
struct pckbc_attach_args *pa = aux;
|
|
|
|
DPRINTF(( "slot = %d ", cf->cf_loc[SACKBCCF_SLOT] ));
|
|
|
|
if( pa->pa_slot == PCKBCCF_SLOT_DEFAULT )
|
|
pa->pa_slot = cf->cf_loc[SACKBCCF_SLOT];
|
|
|
|
return config_match(parent, cf, aux);
|
|
}
|
|
|
|
static void
|
|
sackbc_attach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct sackbc_softc *sc = (struct sackbc_softc *)self;
|
|
struct sacc_softc *psc = (struct sacc_softc *)parent;
|
|
struct sa1111_attach_args *aa = (struct sa1111_attach_args *)aux;
|
|
uint32_t tmp, clock_bit;
|
|
int i, found, intr;
|
|
|
|
switch( aa->sa_addr ){
|
|
case SACC_KBD0: clock_bit = (1<<6); intr = 21; break;
|
|
case SACC_KBD1: clock_bit = (1<<5); intr = 18; break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if( aa->sa_size <= 0 )
|
|
aa->sa_size = SACCKBD_SIZE;
|
|
if( aa->sa_intr == SACCCF_INTR_DEFAULT )
|
|
aa->sa_intr = intr;
|
|
|
|
sc->iot = psc->sc_iot;
|
|
if( bus_space_subregion( psc->sc_iot, psc->sc_ioh,
|
|
aa->sa_addr, aa->sa_size, &sc->ioh ) ){
|
|
printf( ": can't map subregion\n" );
|
|
return;
|
|
}
|
|
|
|
/* enable clock for PS/2 kbd or mouse */
|
|
tmp = bus_space_read_4( psc->sc_iot, psc->sc_ioh, SACCSC_SKPCR );
|
|
bus_space_write_4( psc->sc_iot, psc->sc_ioh, SACCSC_SKPCR,
|
|
tmp | clock_bit );
|
|
|
|
sc->ih_rx = NULL;
|
|
sc->intr = aa->sa_intr;
|
|
sc->inputhandler = NULL;
|
|
sc->subname = sc->dev.dv_xname;
|
|
|
|
TAILQ_INIT(&sc->cmdqueue);
|
|
TAILQ_INIT(&sc->freequeue);
|
|
|
|
for (i = 0; i < NCMD; i++) {
|
|
TAILQ_INSERT_TAIL(&sc->freequeue, &(sc->cmd[i]), next);
|
|
}
|
|
sc->polling = 0;
|
|
|
|
tmp = bus_space_read_4( sc->iot, sc->ioh, SACCKBD_CR );
|
|
bus_space_write_4( sc->iot, sc->ioh, SACCKBD_CR, tmp | KBDCR_ENA );
|
|
|
|
/* XXX: this is necessary to get keyboard working. but I don't know why */
|
|
bus_space_write_4( sc->iot, sc->ioh, SACCKBD_CLKDIV, 2 );
|
|
|
|
tmp = bus_space_read_4( sc->iot, sc->ioh, SACCKBD_STAT );
|
|
if( (tmp & KBDSTAT_ENA) == 0 ){
|
|
printf("??? can't enable KBD controller\n");
|
|
return;
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
{
|
|
struct pckbc_attach_args pa;
|
|
|
|
pa.pa_tag = sc;
|
|
pa.pa_slot = PCKBCCF_SLOT_DEFAULT; /* Bogus */
|
|
|
|
found = (config_found_sm(self, &pa,
|
|
sackbcprint, sackbc_submatch) != NULL);
|
|
|
|
#if 0 && NRND > 0 /* XXX: not yet */
|
|
if (found && (t->t_slotdata[slot] != NULL))
|
|
rnd_attach_source(&t->t_slotdata[slot]->rnd_source,
|
|
sc->subname[slot], RND_TYPE_TTY, 0);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static inline int
|
|
sackbc_wait_output( struct sackbc_softc *sc )
|
|
{
|
|
u_int i, stat;
|
|
|
|
for (i = 100000; i; i--){
|
|
stat = bus_space_read_4(sc->iot, sc->ioh, SACCKBD_STAT);
|
|
delay(100);
|
|
if( stat & KBDSTAT_TXE)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
sackbc_poll_data1( struct sackbc_softc *sc )
|
|
{
|
|
int i, s, stat, c = -1;
|
|
|
|
s = spltty();
|
|
|
|
if (sc->polling){
|
|
stat = sc->poll_stat;
|
|
c = sc->poll_data;
|
|
sc->poll_data = -1;
|
|
sc->poll_stat = -1;
|
|
if( stat >= 0 &&
|
|
(stat & (KBDSTAT_RXF|KBDSTAT_STP)) == KBDSTAT_RXF ){
|
|
splx(s);
|
|
return c;
|
|
}
|
|
}
|
|
|
|
/* if 1 port read takes 1us (?), this polls for 100ms */
|
|
for (i = 100000; i; i--) {
|
|
stat = bus_space_read_4(sc->iot, sc->ioh, SACCKBD_STAT);
|
|
if( (stat & (KBDSTAT_RXF|KBDSTAT_STP)) == KBDSTAT_RXF ){
|
|
KBD_DELAY;
|
|
c = bus_space_read_4(sc->iot, sc->ioh, SACCKBD_DATA);
|
|
break;
|
|
}
|
|
}
|
|
|
|
splx(s);
|
|
return (c);
|
|
}
|
|
|
|
static int
|
|
sackbc_send_cmd( struct sackbc_softc *sc, int val )
|
|
{
|
|
if ( !sackbc_wait_output(sc) )
|
|
return (0);
|
|
bus_space_write_1( sc->iot, sc->ioh, SACCKBD_DATA, val );
|
|
return (1);
|
|
}
|
|
|
|
#define sackbc_send_devcmd sackbc_send_cmd
|
|
|
|
/*
|
|
* Clean up a command queue, throw away everything.
|
|
*/
|
|
static void
|
|
sackbc_cleanqueue( struct sackbc_softc *sc )
|
|
{
|
|
struct pckbc_devcmd *cmd;
|
|
#ifdef SACKBCDEBUG
|
|
int i;
|
|
#endif
|
|
|
|
while ((cmd = TAILQ_FIRST(&sc->cmdqueue))) {
|
|
TAILQ_REMOVE(&sc->cmdqueue, cmd, next);
|
|
#ifdef SACKBCDEBUG
|
|
printf("sackbc_cleanqueue: removing");
|
|
for (i = 0; i < cmd->cmdlen; i++)
|
|
printf(" %02x", cmd->cmd[i]);
|
|
printf("\n");
|
|
#endif
|
|
TAILQ_INSERT_TAIL(&sc->freequeue, cmd, next);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Timeout error handler: clean queues and data port.
|
|
* XXX could be less invasive.
|
|
*/
|
|
static void
|
|
sackbc_cleanup(void *self)
|
|
{
|
|
struct sackbc_softc *sc = self;
|
|
int s;
|
|
|
|
printf("sackbc: command timeout\n");
|
|
|
|
s = spltty();
|
|
|
|
sackbc_cleanqueue(sc);
|
|
|
|
while (bus_space_read_4(sc->iot, sc->ioh, SACCKBD_STAT) & KBDSTAT_RXF) {
|
|
KBD_DELAY;
|
|
(void) bus_space_read_4(sc->iot, sc->ioh, SACCKBD_DATA);
|
|
}
|
|
|
|
/* reset KBC? */
|
|
|
|
splx(s);
|
|
}
|
|
|
|
|
|
/*
|
|
* Pass command to device during normal operation.
|
|
* to be called at spltty()
|
|
*/
|
|
static void
|
|
sackbc_start( struct sackbc_softc *sc )
|
|
{
|
|
struct pckbc_devcmd *cmd = TAILQ_FIRST(&sc->cmdqueue);
|
|
|
|
if (sc->polling) {
|
|
while(cmd){
|
|
sackbc_poll_cmd1(sc, cmd);
|
|
if (cmd->status)
|
|
printf("sackbc_start: command error\n");
|
|
|
|
TAILQ_REMOVE(&sc->cmdqueue, cmd, next);
|
|
if (cmd->flags & KBC_CMDFLAG_SYNC)
|
|
wakeup(cmd);
|
|
else {
|
|
callout_stop(&sc->t_cleanup);
|
|
TAILQ_INSERT_TAIL(&sc->freequeue, cmd, next);
|
|
}
|
|
cmd = TAILQ_FIRST(&sc->cmdqueue);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!sackbc_send_devcmd(sc, cmd->cmd[cmd->cmdidx])) {
|
|
printf("sackbc_start: send error\n");
|
|
/* XXX what now? */
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle command responses coming in asynchonously,
|
|
* return nonzero if valid response.
|
|
* to be called at spltty()
|
|
*/
|
|
static int
|
|
sackbc_cmdresponse( struct sackbc_softc *sc, int data)
|
|
{
|
|
struct pckbc_devcmd *cmd = TAILQ_FIRST(&sc->cmdqueue);
|
|
#ifdef DIAGNOSTIC
|
|
if (!cmd)
|
|
panic("sackbc_cmdresponse: no active command");
|
|
#endif
|
|
if (cmd->cmdidx < cmd->cmdlen) {
|
|
if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
|
|
return (0);
|
|
|
|
if (data == KBC_DEVCMD_RESEND) {
|
|
if (cmd->retries++ < 5) {
|
|
/* try again last command */
|
|
goto restart;
|
|
} else {
|
|
printf("pckbc: cmd failed\n");
|
|
cmd->status = EIO;
|
|
/* dequeue */
|
|
}
|
|
} else {
|
|
if (++cmd->cmdidx < cmd->cmdlen)
|
|
goto restart;
|
|
if (cmd->responselen)
|
|
return (1);
|
|
/* else dequeue */
|
|
}
|
|
} else if (cmd->responseidx < cmd->responselen) {
|
|
cmd->response[cmd->responseidx++] = data;
|
|
if (cmd->responseidx < cmd->responselen)
|
|
return (1);
|
|
/* else dequeue */
|
|
} else
|
|
return (0);
|
|
|
|
/* dequeue: */
|
|
TAILQ_REMOVE(&sc->cmdqueue, cmd, next);
|
|
if (cmd->flags & KBC_CMDFLAG_SYNC)
|
|
wakeup(cmd);
|
|
else {
|
|
callout_stop(&sc->t_cleanup);
|
|
TAILQ_INSERT_TAIL(&sc->freequeue, cmd, next);
|
|
}
|
|
if (!CMD_IN_QUEUE(sc))
|
|
return (1);
|
|
restart:
|
|
sackbc_start(sc);
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Pass command to device, poll for ACK and data.
|
|
* to be called at spltty()
|
|
*/
|
|
static void
|
|
sackbc_poll_cmd1( struct sackbc_softc *sc, struct pckbc_devcmd *cmd )
|
|
{
|
|
int i, c = 0;
|
|
|
|
while (cmd->cmdidx < cmd->cmdlen) {
|
|
DPRINTF((" tx: %x ", cmd->cmd[cmd->cmdidx]));
|
|
if (!sackbc_send_devcmd(sc, cmd->cmd[cmd->cmdidx])) {
|
|
printf("sackbc_cmd: send error\n");
|
|
cmd->status = EIO;
|
|
return;
|
|
}
|
|
delay(1000);
|
|
for (i = 10; i; i--) { /* 1s ??? */
|
|
c = sackbc_poll_data1(sc);
|
|
if (c != -1){
|
|
DPRINTF((" rx: %x", c ));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (c == KBC_DEVCMD_ACK) {
|
|
cmd->cmdidx++;
|
|
continue;
|
|
}
|
|
if (c == KBC_DEVCMD_RESEND) {
|
|
DPRINTF(("sackbc_cmd: RESEND\n"));
|
|
|
|
if (cmd->retries++ < 5)
|
|
continue;
|
|
else {
|
|
DPRINTF(("sackbc: cmd failed\n"));
|
|
|
|
cmd->status = EIO;
|
|
return;
|
|
}
|
|
}
|
|
if (c == -1) {
|
|
DPRINTF(("pckbc_cmd: timeout\n"));
|
|
|
|
cmd->status = EIO;
|
|
return;
|
|
}
|
|
DPRINTF(("pckbc_cmd: lost 0x%x\n", c));
|
|
|
|
}
|
|
|
|
while (cmd->responseidx < cmd->responselen) {
|
|
if (cmd->flags & KBC_CMDFLAG_SLOW)
|
|
i = 100; /* 10s ??? */
|
|
else
|
|
i = 10; /* 1s ??? */
|
|
while (i--) {
|
|
c = sackbc_poll_data1(sc);
|
|
if (c != -1){
|
|
DPRINTF((" resp: %x", c));
|
|
break;
|
|
}
|
|
}
|
|
if (c == -1) {
|
|
DPRINTF(("pckbc_cmd: no response"));
|
|
|
|
cmd->status = ETIMEDOUT;
|
|
return;
|
|
} else
|
|
cmd->response[cmd->responseidx++] = c;
|
|
}
|
|
DPRINTF(("\n"));
|
|
}
|
|
|
|
|
|
/*
|
|
* Glue functions for pckbd on sackbc.
|
|
* These functions emulate those in dev/ic/pckbc.c.
|
|
*
|
|
*/
|
|
|
|
void
|
|
pckbc_set_inputhandler( pckbc_tag_t self, pckbc_slot_t slot,
|
|
pckbc_inputfcn func, void *arg, char *name)
|
|
{
|
|
struct sackbc_softc *sc = (struct sackbc_softc *) self;
|
|
|
|
if( sc == NULL )
|
|
return;
|
|
|
|
DPRINTF(( "set_inputhandler %p %p\n", func, arg ));
|
|
|
|
sc->inputhandler = func;
|
|
sc->inputarg = arg;
|
|
sc->subname = name;
|
|
|
|
sackbc_setup_intrhandler(sc);
|
|
}
|
|
|
|
|
|
/* for use in autoconfiguration */
|
|
int
|
|
pckbc_poll_cmd(pckbc_tag_t self, pckbc_slot_t slot,
|
|
u_char *cmd, int len, int responselen, u_char *respbuf, int slow)
|
|
{
|
|
struct pckbc_devcmd nc;
|
|
struct sackbc_softc *sc = (struct sackbc_softc *) self;
|
|
|
|
if( sc == NULL )
|
|
return EINVAL;
|
|
|
|
if ((len > 4) || (responselen > 4))
|
|
return EINVAL;
|
|
|
|
memset(&nc, 0, sizeof(nc));
|
|
memcpy(nc.cmd, cmd, len);
|
|
nc.cmdlen = len;
|
|
nc.responselen = responselen;
|
|
nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);
|
|
|
|
sackbc_poll_cmd1(sc, &nc);
|
|
|
|
if (nc.status == 0 && respbuf)
|
|
memcpy(respbuf, nc.response, responselen);
|
|
|
|
return (nc.status);
|
|
}
|
|
|
|
|
|
/*
|
|
* switch scancode translation on / off
|
|
* return nonzero on success
|
|
*/
|
|
int
|
|
pckbc_xt_translation(pckbc_tag_t self, pckbc_slot_t slot, int on)
|
|
{
|
|
/* KBD/Mouse controller doesn't have scancode translation */
|
|
return !on;
|
|
}
|
|
|
|
void
|
|
pckbc_slot_enable(pckbc_tag_t self, pckbc_slot_t slot, int on)
|
|
{
|
|
#if 0
|
|
struct sackbc_softc *sc = (struct sackbc_softc *) self;
|
|
int cmd;
|
|
|
|
cmd = on ? KBC_KBDENABLE : KBC_KBDDISABLE;
|
|
if ( !sackbc_send_cmd(sc, cmd ) )
|
|
printf("sackbc_slot_enable(%d) failed\n", on);
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
pckbc_flush(pckbc_tag_t self, pckbc_slot_t slot)
|
|
{
|
|
struct sackbc_softc *sc = (struct sackbc_softc *)self;
|
|
|
|
(void) sackbc_poll_data1(sc);
|
|
}
|
|
|
|
#if 0
|
|
int
|
|
sackbc_poll_data( struct sackbc_softc *sc )
|
|
{
|
|
struct pckbc_internal *t = self;
|
|
struct pckbc_slotdata *q = t->t_slotdata[slot];
|
|
int c;
|
|
|
|
c = pckbc_poll_data1(t, slot, t->t_haveaux);
|
|
if (c != -1 && q && CMD_IN_QUEUE(q)) {
|
|
/* we jumped into a running command - try to
|
|
deliver the response */
|
|
if (pckbc_cmdresponse(t, slot, c))
|
|
return (-1);
|
|
}
|
|
return (c);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
pckbc_set_poll(pckbc_tag_t self, pckbc_slot_t slot, int on)
|
|
{
|
|
struct sackbc_softc *sc = (struct sackbc_softc *)self;
|
|
int s;
|
|
|
|
s = spltty();
|
|
|
|
if( sc->polling != on ){
|
|
|
|
sc->polling = on;
|
|
|
|
if( on ){
|
|
sc->poll_data = sc->poll_stat = -1;
|
|
sackbc_disable_intrhandler(sc);
|
|
}
|
|
else {
|
|
/*
|
|
* If disabling polling on a device that's
|
|
* been configured, make sure there are no
|
|
* bytes left in the FIFO, holding up the
|
|
* interrupt line. Otherwise we won't get any
|
|
* further interrupts.
|
|
*/
|
|
sackbc_rxint(sc);
|
|
sackbc_setup_intrhandler(sc);
|
|
}
|
|
}
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* Put command into the device's command queue, return zero or errno.
|
|
*/
|
|
int
|
|
pckbc_enqueue_cmd( pckbc_tag_t self, pckbc_slot_t slot, u_char *cmd,
|
|
int len, int responselen, int sync, u_char *respbuf)
|
|
{
|
|
struct sackbc_softc *sc = (struct sackbc_softc *)self;
|
|
struct pckbc_devcmd *nc;
|
|
int s, isactive, res = 0;
|
|
|
|
if ( sc == NULL || (len > 4) || (responselen > 4) )
|
|
return (EINVAL);
|
|
|
|
s = spltty();
|
|
nc = TAILQ_FIRST(&sc->freequeue);
|
|
if (nc) {
|
|
TAILQ_REMOVE(&sc->freequeue, nc, next);
|
|
}
|
|
splx(s);
|
|
if (!nc)
|
|
return (ENOMEM);
|
|
|
|
memset(nc, 0, sizeof(*nc));
|
|
memcpy(nc->cmd, cmd, len);
|
|
nc->cmdlen = len;
|
|
nc->responselen = responselen;
|
|
nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);
|
|
|
|
s = spltty();
|
|
|
|
if (sc->polling && sync) {
|
|
/*
|
|
* XXX We should poll until the queue is empty.
|
|
* But we don't come here normally, so make
|
|
* it simple and throw away everything.
|
|
*/
|
|
sackbc_cleanqueue(sc);
|
|
}
|
|
|
|
isactive = CMD_IN_QUEUE(sc);
|
|
TAILQ_INSERT_TAIL(&sc->cmdqueue, nc, next);
|
|
if (!isactive)
|
|
sackbc_start(sc);
|
|
|
|
if (sc->polling)
|
|
res = (sync ? nc->status : 0);
|
|
else if (sync) {
|
|
if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) {
|
|
TAILQ_REMOVE(&sc->cmdqueue, nc, next);
|
|
sackbc_cleanup(sc);
|
|
} else
|
|
res = nc->status;
|
|
} else
|
|
callout_reset(&sc->t_cleanup, hz, sackbc_cleanup, sc);
|
|
|
|
if (sync) {
|
|
if (respbuf)
|
|
memcpy(respbuf, nc->response, responselen);
|
|
TAILQ_INSERT_TAIL(&sc->freequeue, nc, next);
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return (res);
|
|
}
|
|
|
|
int
|
|
pckbc_poll_data(pckbc_tag_t self, pckbc_slot_t slot)
|
|
{
|
|
struct sackbc_softc *sc = (struct sackbc_softc *)self;
|
|
int c;
|
|
|
|
c = sackbc_poll_data1(sc);
|
|
if (c != -1 && CMD_IN_QUEUE(sc)) {
|
|
/* we jumped into a running command - try to
|
|
deliver the response */
|
|
if (sackbc_cmdresponse(sc, c))
|
|
return -1;
|
|
}
|
|
return (c);
|
|
}
|