<commit_before>/*-
 * Copyright (c) 1992, 1993 <NAME>.
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY ``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 I 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.
 *
 * $Id: psm.c,v 1.31 1996/11/28 17:18:56 phk Exp $
 */

/*
 *  Ported to 386bsd Oct 17, 1992
 *  <NAME>, Computer Science, University of Cape Town, South Africa
 *  Please send bug reports to <EMAIL>
 *
 *  Thanks are also due to <NAME>, <EMAIL> -
 *  although I was only partially successful in getting the alpha release
 *  of his "driver for the Logitech and ATI Inport Bus mice for use with
 *  386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless
 *  found his code to be an invaluable reference when porting this driver
 *  to 386bsd.
 *
 *  Further modifications for latest 386BSD+patchkit and port to NetBSD,
 *  <NAME> <<EMAIL>ple.apana.org.au> - 8 June 1993
 *
 *  Cloned from the Microsoft Bus Mouse driver, also by <NAME>, by
 *  <NAME> - 12 June 1993
 *
 *  Modified for PS/2 mouse by <NAME> <<EMAIL>>
 *  - 13 June 1993
 *
 *  Modified for PS/2 AUX mouse by <NAME> <<EMAIL>>
 *  - 24 October 1993
 *
 *  Hardware access routines and probe logic rewritten by
 *  <NAME> <<EMAIL>>
 *  - 3 October 1996.
 *  - 14 October 1996.
 *  - 22 October 1996.
 *  - 12 November 1996. IOCTLs and rearranging `psmread', `psmioctl'...
 *  - 14 November 1996. Uses `kbdio.c'.
 *  - 30 November 1996. More fixes.
 */

#include "psm.h"

#if NPSM > 0

#include <limits.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/syslog.h>
#include <sys/malloc.h>
#ifdef DEVFS
#include <sys/devfsext.h>
#endif

#include <i386/include/mouse.h>
#include <i386/include/clock.h>

#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/kbdio.h>

/*
 * driver specific options: the following options may be set by
 * `options' statements in the kernel configuration file.
 */

/* debugging */
#ifndef PSM_DEBUG
#define PSM_DEBUG	0	/* logging: 0: none, 1: brief, 2: verbose */
#endif

/* #define PSM_CHECKSYNC	   if defined, check the header data byte */

/* features */
#ifndef PSM_ACCEL
#define PSM_ACCEL	0	/* must be one or greater; acceleration will be
				 * disabled if zero */
#endif

/* #define PSM_EMULATION	   enables protocol emulation */

/* end of driver specific options */

/* default values */
#define PSMD_DEFAULT_RESOLUTION	200	/* resolution: 200 ppi */
#define PSMD_DEFAULT_RATE	100	/* report rate: 100 Hz */

/* some macros */
#define PSM_UNIT(dev)		(minor(dev) >> 1)
#define PSM_NBLOCKIO(dev)	(minor(dev) & 1)
#define PSM_MKMINOR(unit,block)	(((unit) << 1) | ((block) ? 0:1))

#ifndef max
#define max(x,y)	((x) > (y) ? (x) : (y))
#endif
#ifndef min
#define min(x,y)	((x) < (y) ? (x) : (y))
#endif

/* ring buffer */
#define PSM_BUFSIZE	256

typedef struct ringbuf {
    int count;
    int head;
    int tail;
    mousestatus_t buf[PSM_BUFSIZE];
} ringbuf_t;

/* driver control block */
typedef int (*packetfunc_t) __P((unsigned char *, int *, int, mousestatus_t *));

static struct psm_softc {    /* Driver status information */
    struct selinfo rsel;	/* Process selecting for Input */
    unsigned char state;	/* Mouse driver state */
    int     addr;		/* I/O port address */
    int     command_byte;	/* controller command byte */
    mousehw_t hw;		/* hardware information */
    mousemode_t mode;		/* operation mode */
    ringbuf_t queue;		/* mouse status queue */
    packetfunc_t mkpacket;	/* func. to turn queued data into output format */
    char    ipacket[MOUSE_PS2_PACKETSIZE];	/* interim input buffer */
    unsigned char opacket[PSM_BUFSIZE];		/* output buffer */
    int     inputbytes;		/* # of bytes in the input buffer */
    int     outputbytes;	/* # of bytes in the output buffer */
    int     outputhead;		/* points the head of the output buffer */
    int     button;		/* the latest button state */
#ifdef DEVFS
    void   *devfs_token;
    void   *n_devfs_token;
#endif
} *psm_softc[NPSM];

/* driver state flags (state) */
#define PSM_VALID	0x80
#define PSM_OPEN	1	/* Device is open */
#define PSM_ASLP	2	/* Waiting for mouse data */

/* function prototypes */
static int psmprobe __P((struct isa_device *));
static int psmattach __P((struct isa_device *));
static int mkms __P((unsigned char *, int *, int, mousestatus_t *));
static int mkman __P((unsigned char *, int *, int, mousestatus_t *));
static int mkmsc __P((unsigned char *, int *, int, mousestatus_t *));
static int mkmm __P((unsigned char *, int *, int, mousestatus_t *));
static int mkps2 __P((unsigned char *, int *, int, mousestatus_t *));

static d_open_t psmopen;
static d_close_t psmclose;
static d_read_t psmread;
static d_ioctl_t psmioctl;
static d_select_t psmselect;

/* device driver declarateion */
struct isa_driver psmdriver = { psmprobe, psmattach, "psm", FALSE };
#define CDEV_MAJOR        21

static struct  cdevsw psm_cdevsw = {
	psmopen,	psmclose,	psmread,	nowrite,	/* 21 */
	psmioctl,	nostop,		nullreset,	nodevtotty,
	psmselect,	nommap,		NULL,		"psm",	NULL,	-1
};

/* debug message level */
static int verbose = PSM_DEBUG;

/* device I/O routines */
static int
enable_aux_dev(int port)
{
    int res;

    res = send_aux_command(port, PSMC_ENABLE_DEV);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res);

    return (res == PSM_ACK);
}

static int
disable_aux_dev(int port)
{
    int res;

    res = send_aux_command(port, PSMC_DISABLE_DEV);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res);

    return (res == PSM_ACK);
}

static int
get_mouse_status(int port, int *status)
{
    int res;

    empty_both_buffers(port, 2);
    res = send_aux_command(port, PSMC_SEND_DEV_STATUS);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: SEND_AUX_STATUS return code:%04x\n", res);
    if (res != PSM_ACK)
        return FALSE;

    status[0] = read_aux_data(port);
    status[1] = read_aux_data(port);
    status[2] = read_aux_data(port);

    return TRUE;
}

static int
get_aux_id(int port)
{
    int res;
    int id;

    empty_both_buffers(port, 2);
    res = send_aux_command(port, PSMC_SEND_DEV_ID);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res);
    if (res != PSM_ACK)
	return (-1);

        /* 10ms delay */
        DELAY(10000);

    id = read_aux_data(port);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: device ID: %04x\n", id);

    return id;
}

static int
set_mouse_sampling_rate(int port, int rate)
{
    int res;

    res = send_aux_command_and_data(port, PSMC_SET_SAMPLING_RATE, rate);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res);

    return ((res == PSM_ACK) ? rate : -1);
}

static int
set_mouse_scaling(int port)
{
    int res;

    res = send_aux_command(port, PSMC_SET_SCALING11);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: SET_SCALING11 return code:%04x\n", res);

    return (res == PSM_ACK);
}

static struct {
        int resolution;
        int code;
} rescode[] = {
        { 25, PSMD_RESOLUTION_25 },
        { 50, PSMD_RESOLUTION_50 },
        { 100, PSMD_RESOLUTION_100 },
        { 200, PSMD_RESOLUTION_200 },
        { 400, PSMD_RESOLUTION_400 },        /* ?? */
        { 800, PSMD_RESOLUTION_800 },        /* ?? */
        { INT_MAX, PSMD_MAX_RESOLUTION },
    { -1 },
};

static int
set_mouse_resolution(int port, int res)
{
    int ret;
    int i;

    if (res <= 0)
        return (-1);
    for (i = 0; rescode[i].resolution > 0; ++i)
        if (rescode[i].resolution >= res)
            break;

    for (; i >= 0; --i) {
        ret = send_aux_command_and_data(port,
            PSMC_SET_RESOLUTION, rescode[i].code);
        if (verbose >= 2)
            log(LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n",
                rescode[i].code, ret);
        if (ret == PSM_ACK)
            return rescode[i].resolution;
    }

    return (-1);
}

/*
 * NOTE: once `set_mouse_mode()' is called, the mouse device must be
 * re-enabled by calling `enable_aux_dev()'
 */
static int
set_mouse_mode(int port)
{
    int res;

    res = send_aux_command(port, PSMC_SET_STREAM_MODE);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res);

    return (res == PSM_ACK);
}

static int
get_mouse_buttons(int port)
{
    int c = 2;		/* assume two buttons by default */
    int res;
    int status[3];

    /*
     * NOTE: a special sequence to obtain Logitech-Mouse-specific
     * information: set resolution to 25 ppi, set scaling to 1:1, set
     * scaling to 1:1, set scaling to 1:1. Then the second byte of the
     * mouse status bytes is the number of available buttons.
     */
    if (set_mouse_resolution(port, 25) != 25)
        return c;
    if (set_mouse_scaling(port) && set_mouse_scaling(port)
        && set_mouse_scaling(port) && get_mouse_status(port, status)) {
        if (verbose) {
            log(LOG_DEBUG, "psm: status %02x %02x %02x (get_mouse_buttons)\n",
                status[0], status[1], status[2]);
        }
        if (status[1] == 3)
            return 3;
    }
    return c;
}

/*
 * FIXME:XXX
 * someday, I will get the list of valid pointing devices and
 * their IDs...
 */
static int
is_a_mouse(int id)
{
    static int valid_ids[] = {
        PSM_MOUSE_ID,		/* mouse */
        PSM_BALLPOINT_ID,	/* ballpoint device */
        -1			/* end of table */
    };
#if 0
     int i;

     for(i = 0; valid_ids[i] >= 0; ++i)
         if (valid_ids[i] == id)
             return TRUE;
     return FALSE;
#else
    return TRUE;
#endif
}

static void
recover_from_error(int port)
{
    /* discard anything left in the output buffer */
    empty_both_buffers(port, 10);

#if 0
    /*
     * NOTE: KBDC_RESET_KBD may not restore the communication between the
     * keyboard and the controller.
     */
    reset_kbd(port);
#else
    /*
     * NOTE: somehow diagnostic and keyboard port test commands bring the
     * keyboard back.
     */
    if (!test_controller(port)) 
        log(LOG_ERR, "psm: keyboard controller failed.\n");
    /* if there isn't a keyboard in the system, the following error is OK */
    if (test_kbd_port(port) != 0) {
	if (verbose)
	    log(LOG_ERR, "psm: keyboard port failed.\n");
    }
#endif
}

static int
restore_controller(int port, int command_byte)
{
    if (!set_controller_command_byte(port, command_byte, 0)) {
	log(LOG_ERR, "psm: failed to restore the keyboard controller "
		     "command byte.\n");
	return FALSE;
    } else {
	return TRUE;
    }
}

/* 
 * Re-initialize the aux port and device. The aux port must be enabled
 * and its interrupt must be disabled before calling this routine. 
 * The aux device will be disabled before returning.
 */
static int
reinitialize(dev_t dev, mousemode_t *mode)
{
    int port = psm_softc[PSM_UNIT(dev)]->addr;
    int stat[3];
    int i;

    switch((i = test_aux_port(port))) {
    case 0:	/* no error */
    	break;
    case -1: 	/* time out */
    default: 	/* error */
    	recover_from_error(port);
    	log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n",
    	    PSM_UNIT(dev),i);
    	return FALSE;
    }

    /* 
     * NOTE: some controllers appears to hang the `keyboard' when
     * the aux port doesn't exist and `PSMC_RESET_DEV' is issued. 
     */
    if (!reset_aux_dev(port)) {
        recover_from_error(port);
        log(LOG_ERR, "psm%d: failed to reset the aux device.\n",
    	    PSM_UNIT(dev));
        return FALSE;
    }

    /* 
     * both the aux port and the aux device is functioning, see
     * if the device can be enabled. 
     */
    if (!enable_aux_dev(port) || !disable_aux_dev(port)) {
        log(LOG_ERR, "psm%d: failed to enable the aux device.\n",
    	    PSM_UNIT(dev));
        return FALSE;
    }
    empty_both_buffers(port, 10);

    /* set mouse parameters */
    if (mode != (mousemode_t *)NULL) {
	if (mode->rate > 0)
            mode->rate = set_mouse_sampling_rate(port, mode->rate);
	if (mode->resolution > 0)
            mode->resolution = set_mouse_resolution(port, mode->resolution);
        set_mouse_scaling(port);
        set_mouse_mode(port);	
    }

    /* just check the status of the mouse */
    if (verbose) {
        get_mouse_status(port, stat);
        log(LOG_DEBUG, "psm%d: status %02x %02x %02x (reinitialized)\n",
            PSM_UNIT(dev), stat[0], stat[1], stat[2]);
    }

    return TRUE;
}

/* psm driver entry points */

#define endprobe(v)	{   if (bootverbose) \
				--verbose;   \
			    return (v);	     \
			}

static int
psmprobe(struct isa_device *dvp)
{
    int unit = dvp->id_unit;
    int ioport = dvp->id_iobase;
    struct psm_softc *sc;
    int stat[3];
    int setparams;
    int i;

    /* validate unit number */
    if (unit >= NPSM)
        return (0);

    sc =  malloc(sizeof *sc, M_DEVBUF, M_NOWAIT);

    bzero(sc, sizeof *sc);

    sc->addr = ioport;
    if (bootverbose)
        ++verbose;

    /*
     * FIXME:XXX
     * the keyboard interrupt should be disabled while probing a
     * mouse?
     */

    /*
     * NOTE: two bits in the command byte controls the operation of the
     * aux port (mouse port): the aux port disable bit (bit 5) and the aux
     * port interrupt (IRQ 12) enable bit (bit 2). When this probe routine
     * is called, there are following possibilities about the presence of
     * the aux port and the PS/2 mouse.
     * 
     * Case 1: aux port disabled (bit 5:1), aux int. disabled (bit 2:0) The
     * aux port most certainly exists. A device may or may not be
     * connected to the port. No driver is probably installed yet.
     * 
     * Case 2: aux port enabled (bit 5:0), aux int. disabled (bit 2:0) Three
     * possibile situations here:
     * 
     * Case 2a: The aux port does not exist, therefore, is not explicitly
     * disabled. Case 2b: The aux port exists. A device and a driver may
     * exist, using the device in the polling(remote) mode. Case 2c: The
     * aux port exists. A device may exist, but someone who knows nothing
     * about the aux port has set the command byte this way (this is the
     * case with `syscons').
     * 
     * Case 3: aux port disabled (bit 5:1), aux int. enabled (bit 2:1) The
     * aux port exists, but someone is controlloing the device and
     * temporalily disabled the port.
     * 
     * Case 4: aux port enabled (bit 5:0), aux int. enabled (bit 2:1) The aux
     * port exists, a device is attached to the port, and someone is
     * controlling the device. Some BIOS set the bits this way after boot.
     * 
     * All in all, it is no use examing the bits for detecting the presence
     * of the port and the mouse device.
     */

    /* save the current command byte; it will be used later */
    if (!write_controller_command(ioport,KBDC_GET_COMMAND_BYTE)) {
        /* CONTROLLER ERROR */
        printf("psm%d: failed to get the current command byte value.\n",
            unit);
 	free(sc, M_DEVBUF);
        endprobe(0);
    }
    sc->command_byte = read_controller_data(ioport);
    if (verbose) {
        printf("psm%d: current command byte:%04x\n",
            unit, sc->command_byte);
    }
    if (sc->command_byte == -1) {
        /* CONTROLLER ERROR */
        printf("psm%d: unable to get the current command byte value.\n",
            unit);
	free(sc, M_DEVBUF);
        endprobe(0);
    }

    /*
     * disable the keyboard port while probing the aux port, which must be
     * enabled during this routine
     */
    if (!set_controller_command_byte(ioport,
  	    sc->command_byte & ~(KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS),
        KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
            | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
        /* 
	 * this is CONTROLLER ERROR; I don't know how to recover 
         * from this error... 
	 */
        restore_controller(ioport, sc->command_byte);
        printf("psm%d: unable to set the command byte.\n", unit);
 	free(sc, M_DEVBUF);
        endprobe(0);
    }

    /*
     * NOTE: `test_aux_port()' is designed to return with zero if the aux
     * port exists and is functioning. However, some controllers appears
     * to respond with zero even when the aux port doesn't exist. (It may
     * be that this is only the case when the controller DOES have the aux
     * port but the port is not wired on the motherboard.) The keyboard
     * controllers without the port, such as the original AT, are
     * supporsed to return with an error code or simply time out. In any
     * case, we have to continue probing the port even when the controller
     * passes this test.
     */
    switch ((i = test_aux_port(ioport))) {
    case 0:        /* no error */
        break;
    case -1:        /* time out */
    default:        /* error */
        recover_from_error(ioport);
        restore_controller(ioport, sc->command_byte);
        if (verbose)
            printf("psm%d: the aux port is not functioning (%d).\n",
                unit, i);
	free(sc, M_DEVBUF);
        endprobe(0);
    }

    /*
     * NOTE: some controllers appears to hang the `keyboard' when the aux
     * port doesn't exist and `PSMC_RESET_DEV' is issued.
     */
    if (!reset_aux_dev(ioport)) {
        recover_from_error(ioport);
        restore_controller(ioport, sc->command_byte);
        if (verbose)
            printf("psm%d: failed to reset the aux device.\n", unit);
	free(sc, M_DEVBUF);
        endprobe(0);
    }
    /*
     * both the aux port and the aux device is functioning, see if the
     * device can be enabled. NOTE: when enabled, the device will start
     * sending data; we shall immediately disable the device once we know
     * the device can be enabled.
     */
    if (!enable_aux_dev(ioport) || !disable_aux_dev(ioport)) {
	/* MOUSE ERROR */
        restore_controller(ioport, sc->command_byte);
        if (verbose)
            printf("psm%d: failed to enable the aux device.\n", unit);
	free(sc, M_DEVBUF);
        endprobe(0);
    }
    empty_both_buffers(ioport, 10);	/* remove stray data if any */

    /* save the default values after reset */
    if (get_mouse_status(ioport, stat)) {
	if (verbose)
            printf("psm%d: status after reset %02x %02x %02x\n",
                unit, stat[0], stat[1], stat[2]);
	sc->mode.rate = stat[2];
	sc->mode.resolution = PSMD_DEFAULT_RESOLUTION;
	for (i = 0; rescode[i].resolution > 0; ++i)
	    if (rescode[i].code == stat[1])
		sc->mode.resolution = rescode[i].resolution;
	setparams = TRUE;
    } else {
	sc->mode.rate = -1;
	sc->mode.resolution = -1;
	setparams = FALSE;
    }

    /* hardware information */
    sc->hw.iftype = MOUSE_IF_PS2;

    /* verify the device is a mouse */
    sc->hw.hwid = get_aux_id(ioport);
    if (!is_a_mouse(sc->hw.hwid)) {
        restore_controller(ioport, sc->command_byte);
        if (verbose)
            printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid);
	free(sc, M_DEVBUF);
        endprobe(0);
    }
    switch (sc->hw.hwid) {
    case PSM_BALLPOINT_ID:
        sc->hw.type = MOUSE_TRACKBALL;
        break;
    case PSM_MOUSE_ID:
        sc->hw.type = MOUSE_MOUSE;
        break;
    default:
        sc->hw.type = MOUSE_UNKNOWN;
        break;
    }

    /* # of buttons */
    sc->hw.buttons = (setparams) ? get_mouse_buttons(ioport) : 2;

    /* set mouse parameters */
    /* FIXME:XXX should we set them in `psmattach()' rather than here? */
    if (setparams) {
        if (sc->mode.rate > 0)
            sc->mode.rate = set_mouse_sampling_rate(ioport, sc->mode.rate);
        if (sc->mode.resolution > 0)
    sc->mode.resolution =
                set_mouse_resolution(ioport, sc->mode.resolution);
    }
    /* FIXME:XXX I don't know if these parameters are reasonable */
    set_mouse_scaling(ioport);    /* 1:1 scaling */
    set_mouse_mode(ioport);    /* stream mode */

    /* just check the status of the mouse */
    if (verbose) {
        get_mouse_status(ioport, stat);
        log(LOG_DEBUG, "psm%d: status %02x %02x %02x\n",
            unit, stat[0], stat[1], stat[2]);
    }

    /* disable the aux port for now... */

    /*
     * WARNING: we save the controller command byte and use it later
     * during `psmopen()' and `psmclose()'. This will be OK, so long as
     * the keyboard/console device driver won't change the command byte in
     * the course of its operation (this is the case with `syscons'). If
     * not,...
     */
    if (!set_controller_command_byte(ioport, 
	    sc->command_byte & ~KBD_AUX_CONTROL_BITS,
            KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
        /* 
	 * this is CONTROLLER ERROR; I don't know the proper way to 
         * recover from this error... 
	 */
        restore_controller(ioport, sc->command_byte);
        printf("psm%d: unable to set the command byte.\n", unit);
 	free(sc, M_DEVBUF);
        endprobe(0);
    }
    sc->command_byte &= ~KBD_AUX_CONTROL_BITS;

    /* done */
    psm_softc[unit] = sc;
    return (IO_PSMSIZE);
}

static int
psmattach(struct isa_device *dvp)
{
    int unit = dvp->id_unit;
    struct psm_softc *sc = psm_softc[unit];

    /* initial operation mode */
    sc->mode.accelfactor = PSM_ACCEL;
    sc->mode.protocol = MOUSE_PROTO_PS2;
    sc->mkpacket = mkps2;

    /* Setup initial state */
    sc->state = PSM_VALID;

    /* Done */
#ifdef    DEVFS
    sc->devfs_token =
        devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, TRUE),
        DV_CHR, 0, 0, 0666, "psm%d", unit);
    sc->n_devfs_token =
        devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, FALSE),
        DV_CHR, 0, 0, 0666, "npsm%d", unit);
#endif

    printf("psm%d: device ID %d, %d buttons?\n",
        unit, sc->hw.hwid, sc->hw.buttons);

    if (bootverbose)
        --verbose;

    return (1);
}

static int
psmopen(dev_t dev, int flag, int fmt, struct proc *p)
{
    int unit = PSM_UNIT(dev);
    int ioport;
    struct psm_softc *sc;
    int stat[3];

    /* Validate unit number */
    if (unit >= NPSM)
        return (ENXIO);

    /* Get device data */
    sc = psm_softc[unit];
    if ((sc->state & PSM_VALID) == 0)
	/* the device is no longer valid/functioning */
        return (ENXIO);
    ioport = sc->addr;

    /* Disallow multiple opens */
    if (sc->state & PSM_OPEN)
        return (EBUSY);
<commit_msg>Fix for PR/2333 - psm0 panics if no mouse attached:<commit_after>/*-
 * Copyright (c) 1992, 1993 <NAME>.
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY ``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 I 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.
 *
 * $Id: psm.c,v 1.32 1996/12/01 19:05:50 sos Exp $
 */

/*
 *  Ported to 386bsd Oct 17, 1992
 *  <NAME>, Computer Science, University of Cape Town, South Africa
 *  Please send bug reports to <EMAIL>
 *
 *  Thanks are also due to <NAME>, <EMAIL> -
 *  although I was only partially successful in getting the alpha release
 *  of his "driver for the Logitech and ATI Inport Bus mice for use with
 *  386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless
 *  found his code to be an invaluable reference when porting this driver
 *  to 386bsd.
 *
 *  Further modifications for latest 386BSD+patchkit and port to NetBSD,
 *  <NAME> <<EMAIL>> - 8 June 1993
 *
 *  Cloned from the Microsoft Bus Mouse driver, also by <NAME>, by
 *  <NAME> - 12 June 1993
 *
 *  Modified for PS/2 mouse by <NAME> <<EMAIL>>
 *  - 13 June 1993
 *
 *  Modified for PS/2 AUX mouse by <NAME> <<EMAIL>>
 *  - 24 October 1993
 *
 *  Hardware access routines and probe logic rewritten by
 *  <NAME> <<EMAIL>>
 *  - 3 October 1996.
 *  - 14 October 1996.
 *  - 22 October 1996.
 *  - 12 November 1996. IOCTLs and rearranging `psmread', `psmioctl'...
 *  - 14 November 1996. Uses `kbdio.c'.
 *  - 30 November 1996. More fixes.
 */

#include "psm.h"

#if NPSM > 0

#include <limits.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/syslog.h>
#include <sys/malloc.h>
#ifdef DEVFS
#include <sys/devfsext.h>
#endif

#include <i386/include/mouse.h>
#include <i386/include/clock.h>

#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/kbdio.h>

/*
 * driver specific options: the following options may be set by
 * `options' statements in the kernel configuration file.
 */

/* debugging */
#ifndef PSM_DEBUG
#define PSM_DEBUG	0	/* logging: 0: none, 1: brief, 2: verbose */
#endif

/* #define PSM_CHECKSYNC	   if defined, check the header data byte */

/* features */
#ifndef PSM_ACCEL
#define PSM_ACCEL	0	/* must be one or greater; acceleration will be
				 * disabled if zero */
#endif

/* #define PSM_EMULATION	   enables protocol emulation */

/* end of driver specific options */

/* default values */
#define PSMD_DEFAULT_RESOLUTION	200	/* resolution: 200 ppi */
#define PSMD_DEFAULT_RATE	100	/* report rate: 100 Hz */

/* some macros */
#define PSM_UNIT(dev)		(minor(dev) >> 1)
#define PSM_NBLOCKIO(dev)	(minor(dev) & 1)
#define PSM_MKMINOR(unit,block)	(((unit) << 1) | ((block) ? 0:1))

#ifndef max
#define max(x,y)	((x) > (y) ? (x) : (y))
#endif
#ifndef min
#define min(x,y)	((x) < (y) ? (x) : (y))
#endif

/* ring buffer */
#define PSM_BUFSIZE	256

typedef struct ringbuf {
    int count;
    int head;
    int tail;
    mousestatus_t buf[PSM_BUFSIZE];
} ringbuf_t;

/* driver control block */
typedef int (*packetfunc_t) __P((unsigned char *, int *, int, mousestatus_t *));

static struct psm_softc {    /* Driver status information */
    struct selinfo rsel;	/* Process selecting for Input */
    unsigned char state;	/* Mouse driver state */
    int     addr;		/* I/O port address */
    int     command_byte;	/* controller command byte */
    mousehw_t hw;		/* hardware information */
    mousemode_t mode;		/* operation mode */
    ringbuf_t queue;		/* mouse status queue */
    packetfunc_t mkpacket;	/* func. to turn queued data into output format */
    char    ipacket[MOUSE_PS2_PACKETSIZE];	/* interim input buffer */
    unsigned char opacket[PSM_BUFSIZE];		/* output buffer */
    int     inputbytes;		/* # of bytes in the input buffer */
    int     outputbytes;	/* # of bytes in the output buffer */
    int     outputhead;		/* points the head of the output buffer */
    int     button;		/* the latest button state */
#ifdef DEVFS
    void   *devfs_token;
    void   *n_devfs_token;
#endif
} *psm_softc[NPSM];

/* driver state flags (state) */
#define PSM_VALID	0x80
#define PSM_OPEN	1	/* Device is open */
#define PSM_ASLP	2	/* Waiting for mouse data */

/* function prototypes */
static int psmprobe __P((struct isa_device *));
static int psmattach __P((struct isa_device *));
static int mkms __P((unsigned char *, int *, int, mousestatus_t *));
static int mkman __P((unsigned char *, int *, int, mousestatus_t *));
static int mkmsc __P((unsigned char *, int *, int, mousestatus_t *));
static int mkmm __P((unsigned char *, int *, int, mousestatus_t *));
static int mkps2 __P((unsigned char *, int *, int, mousestatus_t *));

static d_open_t psmopen;
static d_close_t psmclose;
static d_read_t psmread;
static d_ioctl_t psmioctl;
static d_select_t psmselect;

/* device driver declarateion */
struct isa_driver psmdriver = { psmprobe, psmattach, "psm", FALSE };
#define CDEV_MAJOR        21

static struct  cdevsw psm_cdevsw = {
	psmopen,	psmclose,	psmread,	nowrite,	/* 21 */
	psmioctl,	nostop,		nullreset,	nodevtotty,
	psmselect,	nommap,		NULL,		"psm",	NULL,	-1
};

/* debug message level */
static int verbose = PSM_DEBUG;

/* device I/O routines */
static int
enable_aux_dev(int port)
{
    int res;

    res = send_aux_command(port, PSMC_ENABLE_DEV);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res);

    return (res == PSM_ACK);
}

static int
disable_aux_dev(int port)
{
    int res;

    res = send_aux_command(port, PSMC_DISABLE_DEV);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res);

    return (res == PSM_ACK);
}

static int
get_mouse_status(int port, int *status)
{
    int res;

    empty_both_buffers(port, 2);
    res = send_aux_command(port, PSMC_SEND_DEV_STATUS);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: SEND_AUX_STATUS return code:%04x\n", res);
    if (res != PSM_ACK)
        return FALSE;

    status[0] = read_aux_data(port);
    status[1] = read_aux_data(port);
    status[2] = read_aux_data(port);

    return TRUE;
}

static int
get_aux_id(int port)
{
    int res;
    int id;

    empty_both_buffers(port, 2);
    res = send_aux_command(port, PSMC_SEND_DEV_ID);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res);
    if (res != PSM_ACK)
	return (-1);

        /* 10ms delay */
        DELAY(10000);

    id = read_aux_data(port);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: device ID: %04x\n", id);

    return id;
}

static int
set_mouse_sampling_rate(int port, int rate)
{
    int res;

    res = send_aux_command_and_data(port, PSMC_SET_SAMPLING_RATE, rate);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res);

    return ((res == PSM_ACK) ? rate : -1);
}

static int
set_mouse_scaling(int port)
{
    int res;

    res = send_aux_command(port, PSMC_SET_SCALING11);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: SET_SCALING11 return code:%04x\n", res);

    return (res == PSM_ACK);
}

static struct {
        int resolution;
        int code;
} rescode[] = {
        { 25, PSMD_RESOLUTION_25 },
        { 50, PSMD_RESOLUTION_50 },
        { 100, PSMD_RESOLUTION_100 },
        { 200, PSMD_RESOLUTION_200 },
        { 400, PSMD_RESOLUTION_400 },        /* ?? */
        { 800, PSMD_RESOLUTION_800 },        /* ?? */
        { INT_MAX, PSMD_MAX_RESOLUTION },
    { -1 },
};

static int
set_mouse_resolution(int port, int res)
{
    int ret;
    int i;

    if (res <= 0)
        return (-1);
    for (i = 0; rescode[i].resolution > 0; ++i)
        if (rescode[i].resolution >= res)
            break;

    for (; i >= 0; --i) {
        ret = send_aux_command_and_data(port,
            PSMC_SET_RESOLUTION, rescode[i].code);
        if (verbose >= 2)
            log(LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n",
                rescode[i].code, ret);
        if (ret == PSM_ACK)
            return rescode[i].resolution;
    }

    return (-1);
}

/*
 * NOTE: once `set_mouse_mode()' is called, the mouse device must be
 * re-enabled by calling `enable_aux_dev()'
 */
static int
set_mouse_mode(int port)
{
    int res;

    res = send_aux_command(port, PSMC_SET_STREAM_MODE);
    if (verbose >= 2)
        log(LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res);

    return (res == PSM_ACK);
}

static int
get_mouse_buttons(int port)
{
    int c = 2;		/* assume two buttons by default */
    int res;
    int status[3];

    /*
     * NOTE: a special sequence to obtain Logitech-Mouse-specific
     * information: set resolution to 25 ppi, set scaling to 1:1, set
     * scaling to 1:1, set scaling to 1:1. Then the second byte of the
     * mouse status bytes is the number of available buttons.
     */
    if (set_mouse_resolution(port, 25) != 25)
        return c;
    if (set_mouse_scaling(port) && set_mouse_scaling(port)
        && set_mouse_scaling(port) && get_mouse_status(port, status)) {
        if (verbose) {
            log(LOG_DEBUG, "psm: status %02x %02x %02x (get_mouse_buttons)\n",
                status[0], status[1], status[2]);
        }
        if (status[1] == 3)
            return 3;
    }
    return c;
}

/*
 * FIXME:XXX
 * someday, I will get the list of valid pointing devices and
 * their IDs...
 */
static int
is_a_mouse(int id)
{
    static int valid_ids[] = {
        PSM_MOUSE_ID,		/* mouse */
        PSM_BALLPOINT_ID,	/* ballpoint device */
        -1			/* end of table */
    };
#if 0
     int i;

     for(i = 0; valid_ids[i] >= 0; ++i)
         if (valid_ids[i] == id)
             return TRUE;
     return FALSE;
#else
    return TRUE;
#endif
}

static void
recover_from_error(int port)
{
    /* discard anything left in the output buffer */
    empty_both_buffers(port, 10);

#if 0
    /*
     * NOTE: KBDC_RESET_KBD may not restore the communication between the
     * keyboard and the controller.
     */
    reset_kbd(port);
#else
    /*
     * NOTE: somehow diagnostic and keyboard port test commands bring the
     * keyboard back.
     */
    if (!test_controller(port)) 
        log(LOG_ERR, "psm: keyboard controller failed.\n");
    /* if there isn't a keyboard in the system, the following error is OK */
    if (test_kbd_port(port) != 0) {
	if (verbose)
	    log(LOG_ERR, "psm: keyboard port failed.\n");
    }
#endif
}

static int
restore_controller(int port, int command_byte)
{
    if (!set_controller_command_byte(port, command_byte, 0)) {
	log(LOG_ERR, "psm: failed to restore the keyboard controller "
		     "command byte.\n");
	return FALSE;
    } else {
	return TRUE;
    }
}

/* 
 * Re-initialize the aux port and device. The aux port must be enabled
 * and its interrupt must be disabled before calling this routine. 
 * The aux device will be disabled before returning.
 */
static int
reinitialize(dev_t dev, mousemode_t *mode)
{
    int port = psm_softc[PSM_UNIT(dev)]->addr;
    int stat[3];
    int i;

    switch((i = test_aux_port(port))) {
    case 0:	/* no error */
    	break;
    case -1: 	/* time out */
    default: 	/* error */
    	recover_from_error(port);
    	log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n",
    	    PSM_UNIT(dev),i);
    	return FALSE;
    }

    /* 
     * NOTE: some controllers appears to hang the `keyboard' when
     * the aux port doesn't exist and `PSMC_RESET_DEV' is issued. 
     */
    if (!reset_aux_dev(port)) {
        recover_from_error(port);
        log(LOG_ERR, "psm%d: failed to reset the aux device.\n",
    	    PSM_UNIT(dev));
        return FALSE;
    }

    /* 
     * both the aux port and the aux device is functioning, see
     * if the device can be enabled. 
     */
    if (!enable_aux_dev(port) || !disable_aux_dev(port)) {
        log(LOG_ERR, "psm%d: failed to enable the aux device.\n",
    	    PSM_UNIT(dev));
        return FALSE;
    }
    empty_both_buffers(port, 10);

    /* set mouse parameters */
    if (mode != (mousemode_t *)NULL) {
	if (mode->rate > 0)
            mode->rate = set_mouse_sampling_rate(port, mode->rate);
	if (mode->resolution > 0)
            mode->resolution = set_mouse_resolution(port, mode->resolution);
        set_mouse_scaling(port);
        set_mouse_mode(port);	
    }

    /* just check the status of the mouse */
    if (verbose) {
        get_mouse_status(port, stat);
        log(LOG_DEBUG, "psm%d: status %02x %02x %02x (reinitialized)\n",
            PSM_UNIT(dev), stat[0], stat[1], stat[2]);
    }

    return TRUE;
}

/* psm driver entry points */

#define endprobe(v)	{   if (bootverbose) \
				--verbose;   \
			    return (v);	     \
			}

static int
psmprobe(struct isa_device *dvp)
{
    int unit = dvp->id_unit;
    int ioport = dvp->id_iobase;
    struct psm_softc *sc;
    int stat[3];
    int setparams;
    int i;

    /* validate unit number */
    if (unit >= NPSM)
        return (0);

    psm_softc[unit] = NULL; 

    sc =  malloc(sizeof *sc, M_DEVBUF, M_NOWAIT);

    bzero(sc, sizeof *sc);

    sc->addr = ioport;
    if (bootverbose)
        ++verbose;

    /*
     * FIXME:XXX
     * the keyboard interrupt should be disabled while probing a
     * mouse?
     */

    /*
     * NOTE: two bits in the command byte controls the operation of the
     * aux port (mouse port): the aux port disable bit (bit 5) and the aux
     * port interrupt (IRQ 12) enable bit (bit 2). When this probe routine
     * is called, there are following possibilities about the presence of
     * the aux port and the PS/2 mouse.
     * 
     * Case 1: aux port disabled (bit 5:1), aux int. disabled (bit 2:0) The
     * aux port most certainly exists. A device may or may not be
     * connected to the port. No driver is probably installed yet.
     * 
     * Case 2: aux port enabled (bit 5:0), aux int. disabled (bit 2:0) Three
     * possibile situations here:
     * 
     * Case 2a: The aux port does not exist, therefore, is not explicitly
     * disabled. Case 2b: The aux port exists. A device and a driver may
     * exist, using the device in the polling(remote) mode. Case 2c: The
     * aux port exists. A device may exist, but someone who knows nothing
     * about the aux port has set the command byte this way (this is the
     * case with `syscons').
     * 
     * Case 3: aux port disabled (bit 5:1), aux int. enabled (bit 2:1) The
     * aux port exists, but someone is controlloing the device and
     * temporalily disabled the port.
     * 
     * Case 4: aux port enabled (bit 5:0), aux int. enabled (bit 2:1) The aux
     * port exists, a device is attached to the port, and someone is
     * controlling the device. Some BIOS set the bits this way after boot.
     * 
     * All in all, it is no use examing the bits for detecting the presence
     * of the port and the mouse device.
     */

    /* save the current command byte; it will be used later */
    if (!write_controller_command(ioport,KBDC_GET_COMMAND_BYTE)) {
        /* CONTROLLER ERROR */
        printf("psm%d: failed to get the current command byte value.\n",
            unit);
 	free(sc, M_DEVBUF);
        endprobe(0);
    }
    sc->command_byte = read_controller_data(ioport);
    if (verbose) {
        printf("psm%d: current command byte:%04x\n",
            unit, sc->command_byte);
    }
    if (sc->command_byte == -1) {
        /* CONTROLLER ERROR */
        printf("psm%d: unable to get the current command byte value.\n",
            unit);
	free(sc, M_DEVBUF);
        endprobe(0);
    }

    /*
     * disable the keyboard port while probing the aux port, which must be
     * enabled during this routine
     */
    if (!set_controller_command_byte(ioport,
  	    sc->command_byte & ~(KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS),
        KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
            | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
        /* 
	 * this is CONTROLLER ERROR; I don't know how to recover 
         * from this error... 
	 */
        restore_controller(ioport, sc->command_byte);
        printf("psm%d: unable to set the command byte.\n", unit);
 	free(sc, M_DEVBUF);
        endprobe(0);
    }

    /*
     * NOTE: `test_aux_port()' is designed to return with zero if the aux
     * port exists and is functioning. However, some controllers appears
     * to respond with zero even when the aux port doesn't exist. (It may
     * be that this is only the case when the controller DOES have the aux
     * port but the port is not wired on the motherboard.) The keyboard
     * controllers without the port, such as the original AT, are
     * supporsed to return with an error code or simply time out. In any
     * case, we have to continue probing the port even when the controller
     * passes this test.
     */
    switch ((i = test_aux_port(ioport))) {
    case 0:        /* no error */
        break;
    case -1:        /* time out */
    default:        /* error */
        recover_from_error(ioport);
        restore_controller(ioport, sc->command_byte);
        if (verbose)
            printf("psm%d: the aux port is not functioning (%d).\n",
                unit, i);
	free(sc, M_DEVBUF);
        endprobe(0);
    }

    /*
     * NOTE: some controllers appears to hang the `keyboard' when the aux
     * port doesn't exist and `PSMC_RESET_DEV' is issued.
     */
    if (!reset_aux_dev(ioport)) {
        recover_from_error(ioport);
        restore_controller(ioport, sc->command_byte);
        if (verbose)
            printf("psm%d: failed to reset the aux device.\n", unit);
	free(sc, M_DEVBUF);
        endprobe(0);
    }
    /*
     * both the aux port and the aux device is functioning, see if the
     * device can be enabled. NOTE: when enabled, the device will start
     * sending data; we shall immediately disable the device once we know
     * the device can be enabled.
     */
    if (!enable_aux_dev(ioport) || !disable_aux_dev(ioport)) {
	/* MOUSE ERROR */
        restore_controller(ioport, sc->command_byte);
        if (verbose)
            printf("psm%d: failed to enable the aux device.\n", unit);
	free(sc, M_DEVBUF);
        endprobe(0);
    }
    empty_both_buffers(ioport, 10);	/* remove stray data if any */

    /* save the default values after reset */
    if (get_mouse_status(ioport, stat)) {
	if (verbose)
            printf("psm%d: status after reset %02x %02x %02x\n",
                unit, stat[0], stat[1], stat[2]);
	sc->mode.rate = stat[2];
	sc->mode.resolution = PSMD_DEFAULT_RESOLUTION;
	for (i = 0; rescode[i].resolution > 0; ++i)
	    if (rescode[i].code == stat[1])
		sc->mode.resolution = rescode[i].resolution;
	setparams = TRUE;
    } else {
	sc->mode.rate = -1;
	sc->mode.resolution = -1;
	setparams = FALSE;
    }

    /* hardware information */
    sc->hw.iftype = MOUSE_IF_PS2;

    /* verify the device is a mouse */
    sc->hw.hwid = get_aux_id(ioport);
    if (!is_a_mouse(sc->hw.hwid)) {
        restore_controller(ioport, sc->command_byte);
        if (verbose)
            printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid);
	free(sc, M_DEVBUF);
        endprobe(0);
    }
    switch (sc->hw.hwid) {
    case PSM_BALLPOINT_ID:
        sc->hw.type = MOUSE_TRACKBALL;
        break;
    case PSM_MOUSE_ID:
        sc->hw.type = MOUSE_MOUSE;
        break;
    default:
        sc->hw.type = MOUSE_UNKNOWN;
        break;
    }

    /* # of buttons */
    sc->hw.buttons = (setparams) ? get_mouse_buttons(ioport) : 2;

    /* set mouse parameters */
    /* FIXME:XXX should we set them in `psmattach()' rather than here? */
    if (setparams) {
        if (sc->mode.rate > 0)
            sc->mode.rate = set_mouse_sampling_rate(ioport, sc->mode.rate);
        if (sc->mode.resolution > 0)
    sc->mode.resolution =
                set_mouse_resolution(ioport, sc->mode.resolution);
    }
    /* FIXME:XXX I don't know if these parameters are reasonable */
    set_mouse_scaling(ioport);    /* 1:1 scaling */
    set_mouse_mode(ioport);    /* stream mode */

    /* just check the status of the mouse */
    if (verbose) {
        get_mouse_status(ioport, stat);
        log(LOG_DEBUG, "psm%d: status %02x %02x %02x\n",
            unit, stat[0], stat[1], stat[2]);
    }

    /* disable the aux port for now... */

    /*
     * WARNING: we save the controller command byte and use it later
     * during `psmopen()' and `psmclose()'. This will be OK, so long as
     * the keyboard/console device driver won't change the command byte in
     * the course of its operation (this is the case with `syscons'). If
     * not,...
     */
    if (!set_controller_command_byte(ioport, 
	    sc->command_byte & ~KBD_AUX_CONTROL_BITS,
            KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
        /* 
	 * this is CONTROLLER ERROR; I don't know the proper way to 
         * recover from this error... 
	 */
        restore_controller(ioport, sc->command_byte);
        printf("psm%d: unable to set the command byte.\n", unit);
 	free(sc, M_DEVBUF);
        endprobe(0);
    }
    sc->command_byte &= ~KBD_AUX_CONTROL_BITS;

    /* done */
    psm_softc[unit] = sc;
    return (IO_PSMSIZE);
}

static int
psmattach(struct isa_device *dvp)
{
    int unit = dvp->id_unit;
    struct psm_softc *sc = psm_softc[unit];

    if (sc == NULL)    /* shouldn't happen */ 
	return (0); 

    /* initial operation mode */
    sc->mode.accelfactor = PSM_ACCEL;
    sc->mode.protocol = MOUSE_PROTO_PS2;
    sc->mkpacket = mkps2;

    /* Setup initial state */
    sc->state = PSM_VALID;

    /* Done */
#ifdef    DEVFS
    sc->devfs_token =
        devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, TRUE),
        DV_CHR, 0, 0, 0666, "psm%d", unit);
    sc->n_devfs_token =
        devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, FALSE),
        DV_CHR, 0, 0, 0666, "npsm%d", unit);
#endif

    printf("psm%d: device ID %d, %d buttons?\n",
        unit, sc->hw.hwid, sc->hw.buttons);

    if (bootverbose)
        --verbose;

    return (1);
}

static int
psmopen(dev_t dev, int flag, int fmt, struct proc *p)
{
    int unit = PSM_UNIT(dev);
    int ioport;
    struct psm_softc *sc;
    int stat[3];

    /* Validate unit number */
    if (unit >= NPSM)
        return (ENXIO);

    /* Get device data */
    sc = psm_softc[unit];
    if ((sc == NULL) || (sc->state & PSM_VALID) == 0)
	/* the device is no longer valid/functioning */
        return (ENXIO);
    ioport = sc->addr;

    /* Disallow multiple opens */
    if (sc->state & PSM_OPEN)
        return (EBUSY);

{'fraction_non_alphanumeric': 0.11931842568350573, 'fraction_numerical': 0.016738915833297566, 'mean_word_length': 2.6165308498253785, 'pattern_counts': {'":': 0, '<': 79, '<?xml version=': 0, '>': 227, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5587473', 'n_tokens_mistral': 16615, 'n_tokens_neox': 15630, 'n_words': 4941}