diff --git a/sys/i386/isa/psm.c b/sys/i386/isa/psm.c index 536046fb49d2..30b301de41a2 100644 --- a/sys/i386/isa/psm.c +++ b/sys/i386/isa/psm.c @@ -18,6 +18,8 @@ * 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$ */ /* @@ -43,189 +45,629 @@ * * Modified for PS/2 AUX mouse by Shoji Yuen * - 24 October 1993 + * + * Hardware access routines and probe logic rewritten by + * Kazutaka Yokota + * - 3 October 1996. + * - 14 October 1996. + * - 22 October 1996. + * - 28 October 1996. Start adding IOCTLs. + * - 12 November 1996. IOCTLs and rearranging `psmread', `psmioctl'... + * - 14 November 1996. Uses `kbdio.c'. */ #include "psm.h" #if NPSM > 0 +#include #include #include #include #include #include #include +#include #ifdef DEVFS #include #endif /*DEVFS*/ +/* #include #include +*/ +#include +#include +#include #include +#include -#define PSM_DATA 0x00 /* Offset for data port, read-write */ -#define PSM_CNTRL 0x04 /* Offset for control port, write-only */ -#define PSM_STATUS 0x04 /* Offset for status port, read-only */ +/* driver specific options: the following options may be set by + `options' statements in the kernel configuration file. */ -/* status bits */ -#define PSM_OUTPUT_ACK 0x02 /* output acknowledge */ +/* debugging */ +#ifndef PSM_DEBUG +#define PSM_DEBUG 2 /* controls debug logging: + 0: no logging, 1: brief, 2: verbose */ +#endif -/* controller commands */ -#define PSM_INT_ENABLE 0x47 /* enable controller interrupts */ -#define PSM_INT_DISABLE 0x65 /* disable controller interrupts */ -#define PSM_DISABLE 0xa7 /* disable auxiliary port */ -#define PSM_ENABLE 0xa8 /* enable auxiliary port */ -#define PSM_AUX_TEST 0xa9 /* test auxiliary port */ +/* version dependency */ +#define PSM_CURRENT /* compiles for FreeBSD-current, if defined. + otherwise, compiles for FreeBSD 2.1.x. */ -/* mouse commands */ -#define PSM_SET_SCALE11 0xe6 /* set 1:1 scaling */ -#define PSM_SET_SCALE21 0xe7 /* set 2:1 scaling */ -#define PSM_SET_RES 0xe8 /* set resolution */ -#define PSM_GET_SCALE 0xe9 /* set scaling factor */ -#define PSM_SET_STREAM 0xea /* set streaming mode */ -#define PSM_SET_SAMPLE 0xf3 /* set sampling rate */ -#define PSM_DEV_ENABLE 0xf4 /* mouse on */ -#define PSM_DEV_DISABLE 0xf5 /* mouse off */ -#define PSM_RESET 0xff /* reset */ +/* features */ +/* #define PSM_NOCHECKSYNC the driver does not check the header data + byte, if defined */ -#define PSMUNIT(dev) (minor(dev) >> 1) +#ifndef PSM_ACCEL +#define PSM_ACCEL 2 /* the default acceleration factor, must + be one or greater; acceleration will be + disabled if zero */ +#endif +/* #define PSM_NOEMULATION disables protocol emulation */ + +/* end of driver specific options */ + +/* default values */ +#define PSMD_DEFAULT_RESOLUTION 800 /* resolution: 800 ppi */ +#define PSMD_DEFAULT_RATE 100 /* report rate: 100 Hz */ + +/* misc */ +#define TRUE (-1) +#define FALSE 0 + +/* 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 min +#define min(x,y) ((x) < (y) ? (x) : (y)) +#endif -static int psmprobe(struct isa_device *); -static int psmattach(struct isa_device *); -static void psm_poll_status(int); +/* mouse status block */ -static int psmaddr[NPSM]; /* Base I/O port addresses per unit */ +typedef struct mousestatus { + int button; /* button status */ + int obutton; /* previous button status */ + int dx; /* x movement */ + int dy; /* y movement */ +} mousestatus_t; -#define PSM_CHUNK 128 /* chunk size for read */ -#define PSM_BSIZE 1024 /* buffer size */ +/* ring buffer */ -struct ringbuf { - int count, first, last; - char queue[PSM_BSIZE]; -}; +#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 ringbuf inq; /* Input queue */ - struct selinfo rsel; /* Process selecting for Input */ + struct selinfo rsel; /* Process selecting for Input */ unsigned char state; /* Mouse driver state */ - unsigned char status; /* Mouse button status */ - unsigned char button; /* Previous mouse button status bits */ - int x, y; /* accumulated motion in the X,Y axis */ + 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]; -#define PSM_OPEN 1 /* Device is open */ -#define PSM_ASLP 2 /* Waiting for mouse data */ +/* driver state flags (state) */ +#define PSM_VALID 0x80 +#define PSM_OPEN 1 /* Device is open */ +#define PSM_ASLP 2 /* Waiting for mouse data */ -struct isa_driver psmdriver = { psmprobe, psmattach, "psm" }; +/* function prototypes */ -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; +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 mkmsc __P((unsigned char *,int *,int,mousestatus_t *)); +static int mkps2 __P((unsigned char *,int *,int,mousestatus_t *)); -#define CDEV_MAJOR 21 -static struct cdevsw psm_cdevsw = - { psmopen, psmclose, psmread, nowrite, /*21*/ - psmioctl, nostop, nullreset, nodevtotty, - psmselect, nommap, NULL, "psm", NULL, -1 }; +#ifdef PSM_CURRENT +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; +#endif /* PSM_CURRENT */ + +/* device driver declarateion */ + +struct isa_driver psmdriver = { psmprobe, psmattach, "psm", FALSE }; + +#ifdef PSM_CURRENT +#define CDEV_MAJOR 21 + +static struct cdevsw psm_cdevsw = { + psmopen, psmclose, psmread, nowrite, /* 21 */ + psmioctl, nostop, nullreset, nodevtotty, + psmselect, nommap, NULL, "psm", NULL, -1 +}; +#endif /* PSM_CURRENT */ + +/* debug message level */ + +extern int bootverbose; /* `-v' option at `boot:' prompt */ +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); + 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 retry; + int id; + int c; + + for(retry = KBD_MAXRETRY; retry > 0; --retry) { + empty_both_buffers(port); + write_aux_command(port,PSMC_SEND_DEV_ID); + /* 10ms delay */ + DELAY(10000); + c = read_controller_data(port); + if (verbose >= 2) + log(LOG_DEBUG,"psm: SEND_DEV_ID return code:%04x\n",c); + if (c == PSM_ACK) + break; + } + if (retry <= 0) + return -1; + + 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 int +set_mouse_resolution(int port,int res) +{ + 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 }, + }; + int ret; + int i; + + if (res <= 0) + return FALSE; + 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 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)) + 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: 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 */ + }; + /* + int i; + + for(i = 0; valid_ids[i] >= 0; ++i) { + if (valid_ids[i] == id) + return TRUE; + } + return FALSE; + */ + return TRUE; +} static void -psm_write_dev(int ioport, u_char value) +recover_from_error(int port) { - psm_poll_status(ioport); - outb(ioport+PSM_CNTRL, 0xd4); - psm_poll_status(ioport); - outb(ioport+PSM_DATA, value); + /* discard anything left in the output buffer */ + empty_both_buffers(port); + + /* NOTE: KBDC_RESET_KBD may not restore the communication between + the keyboard and the controller. */ + /* reset_kbd(port); */ + /* NOTE: somehow diagnostic and keyboard port test commands bring + the keyboard back. */ + test_controller(port); + test_kbd_port(port); } -static inline void -psm_command(int ioport, u_char value) +static void +restore_controller(int port,int command_byte) { - psm_poll_status(ioport); - outb(ioport+PSM_CNTRL, 0x60); - psm_poll_status(ioport); - outb(ioport+PSM_DATA, value); + set_controller_command_byte(port,command_byte,0); } -static int +/* psm driver entry points */ + +static int psmprobe(struct isa_device *dvp) { - /* XXX: Needs a real probe routine. */ - int ioport, c, unit; + int unit = dvp->id_unit; + int ioport = dvp->id_iobase; + struct psm_softc *sc; + int stat[3]; + int i; - ioport=dvp->id_iobase; - unit=dvp->id_unit; + /* validate unit number */ + if (unit >= NPSM) + return (0); -#ifndef PSM_NO_RESET - psm_write_dev(ioport, PSM_RESET); /* Reset aux device */ -#endif - psm_poll_status(ioport); - outb(ioport+PSM_CNTRL, PSM_AUX_TEST); - psm_poll_status(ioport); -#if 0 - outb(ioport+PSM_CNTRL, 0xaa); -#endif - c = inb(ioport+PSM_DATA); - if(c & 0x04) { -/* printf("PS/2 AUX mouse is not found\n");*/ - psm_command(ioport, PSM_INT_DISABLE); - psmaddr[unit] = 0; /* Device not found */ + sc = &psm_softc[unit]; + sc->addr = ioport; + if (bootverbose) + ++verbose; + + /* FIXME: 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 */ + write_controller_command(ioport,KBDC_GET_COMMAND_BYTE); + 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) { + printf("psm%d: unable to get the current command byte value.\n", + unit); + return (0); + } + + /* disable the keyboard port while probing the aux port, which + must be enabled during this routine */ + write_controller_command(ioport,KBDC_DISABLE_KBD_PORT); + 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); + + /* 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); + if (bootverbose) + --verbose; return (0); } -/* printf("PS/2 AUX mouse found. Installing driver\n");*/ - return (4); + + /* 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); + return (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)) { + restore_controller(ioport,sc->command_byte); + if (verbose) + printf("psm%d: failed to enable the aux device.\n",unit); + if (bootverbose) + --verbose; + return (0); + } + empty_both_buffers(ioport); /* remove stray data if any */ + + /* 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); + if (bootverbose) + --verbose; + return (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 = get_mouse_buttons(ioport); + + /* set mouse parameters */ + /* FIXME: I don't know if these parameters are reasonable */ + /* FIXME: should we set them in `psmattach()' rather than here? */ + sc->mode.rate = set_mouse_sampling_rate(ioport,PSMD_DEFAULT_RATE); + sc->mode.resolution = + set_mouse_resolution(ioport,PSMD_DEFAULT_RESOLUTION); + 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,... */ + sc->command_byte &= ~KBD_AUX_CONTROL_BITS; + set_controller_command_byte(ioport,sc->command_byte, + KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT); + + /* done */ + return (IO_PSMSIZE); } static int psmattach(struct isa_device *dvp) { int unit = dvp->id_unit; - int ioport = dvp->id_iobase; struct psm_softc *sc = &psm_softc[unit]; - /* Save I/O base address */ - psmaddr[unit] = ioport; - - /* Disable mouse interrupts */ - psm_poll_status(ioport); - outb(ioport+PSM_CNTRL, PSM_ENABLE); -#ifdef 0 - psm_write(ioport, PSM_SET_RES); - psm_write(ioport, 0x03); /* 8 counts/mm */ - psm_write(ioport, PSM_SET_SCALE); - psm_write(ioport, 0x02); /* 2:1 */ - psm_write(ioport, PSM_SET_SCALE21); - psm_write(ioport, PSM_SET_SAMPLE); - psm_write(ioport, 0x64); /* 100 samples/sec */ - psm_write(ioport, PSM_SET_STREAM); -#endif - psm_poll_status(ioport); - outb(ioport+PSM_CNTRL, PSM_DISABLE); - psm_command(ioport, PSM_INT_DISABLE); + /* initial operation mode */ + sc->mode.accelfactor = PSM_ACCEL; + sc->mode.protocol = MOUSE_PROTO_PS2; + sc->mkpacket = mkps2; /* Setup initial state */ - sc->state = 0; + sc->state = PSM_VALID; /* Done */ - return (0); /* XXX eh? usually 1 indicates success */ +#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); + /* return (0); XXX eh? usually 1 indicates success */ } -static int +#ifdef PSM_CURRENT +static +#endif +int psmopen(dev_t dev, int flag, int fmt, struct proc *p) { - struct psm_softc *sc; + int unit = PSM_UNIT(dev); int ioport; - int unit = PSMUNIT(dev); + struct psm_softc *sc; + int stat[3]; /* Validate unit number */ if (unit >= NPSM) @@ -233,11 +675,9 @@ psmopen(dev_t dev, int flag, int fmt, struct proc *p) /* Get device data */ sc = &psm_softc[unit]; - ioport = psmaddr[unit]; - - /* If device does not exist */ - if (ioport == 0) + if ((sc->state & PSM_VALID) == 0) return (ENXIO); + ioport = sc->addr; /* Disallow multiple opens */ if (sc->state & PSM_OPEN) @@ -247,63 +687,70 @@ psmopen(dev_t dev, int flag, int fmt, struct proc *p) sc->state |= PSM_OPEN; sc->rsel.si_flags = 0; sc->rsel.si_pid = 0; - sc->status = 0; + + /* flush the event queue */ + sc->queue.count = 0; + sc->queue.head = 0; + sc->queue.tail = 0; sc->button = 0; - sc->x = 0; - sc->y = 0; - /* Allocate and initialize a ring buffer */ - sc->inq.count = sc->inq.first = sc->inq.last = 0; + /* empty input/output buffers */ + sc->inputbytes = 0; + sc->outputbytes = 0; + sc->outputhead = 0; - /* Enable Bus Mouse interrupts */ - psm_write_dev(ioport, PSM_DEV_ENABLE); - - psm_poll_status(ioport); - outb(ioport+PSM_CNTRL, PSM_ENABLE); - psm_command(ioport, PSM_INT_ENABLE); + /* enable the aux port and temporalily disable the keyboard */ + write_controller_command(ioport,KBDC_DISABLE_KBD_PORT); + set_controller_command_byte(ioport, + sc->command_byte & ~KBD_KBD_CONTROL_BITS, + KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT + | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT); - /* Successful open */ -#ifdef DEVFS - sc->devfs_token = - devfs_add_devswf(&psm_cdevsw, unit << 1, DV_CHR, 0, 0, 0666, - "psm%d", unit); - sc->n_devfs_token = - devfs_add_devswf(&psm_cdevsw, (unit<<1)+1, DV_CHR,0, 0, 0666, - "npsm%d", unit); -#endif + /* enable the mouse device */ + if (!enable_aux_dev(ioport)) { + set_controller_command_byte(ioport,sc->command_byte, + KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT); + log(LOG_ERR,"psm%d: unable to enable the pointing device.\n",unit); + return (EIO); + } + if (verbose >= 2) { + get_mouse_status(ioport,stat); + log(LOG_DEBUG,"psm%d: status %02x %02x %02x\n", + unit,stat[0],stat[1],stat[2]); + } + + /* enable the aux port and interrupt */ + set_controller_command_byte(ioport,sc->command_byte, + KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT); + + /* done */ return (0); } -static void -psm_poll_status(int ioport) -{ - u_char c; - - while(c = inb(ioport+PSM_STATUS) & 0x03) - if(c & PSM_OUTPUT_ACK == PSM_OUTPUT_ACK) { - /* XXX - Avoids some keyboard hangs during probe */ - DELAY(6); - inb(ioport+PSM_DATA); - } - return; -} - -static int +#ifdef PSM_CURRENT +static +#endif +int psmclose(dev_t dev, int flag, int fmt, struct proc *p) { - int unit, ioport; - struct psm_softc *sc; + struct psm_softc *sc = &psm_softc[PSM_UNIT(dev)]; + int ioport = sc->addr; - /* Get unit and associated info */ - unit = PSMUNIT(dev); - sc = &psm_softc[unit]; - ioport = psmaddr[unit]; + /* disable the aux interrupt */ + set_controller_command_byte(ioport,sc->command_byte, + KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT); - /* Disable further mouse interrupts */ - psm_command(ioport, PSM_INT_DISABLE); - psm_poll_status(ioport); - outb(ioport+PSM_CNTRL, PSM_DISABLE); + /* remove anything left in the output buffer */ + empty_aux_buffer(ioport); + + /* disable the aux device, port and interrupt */ + disable_aux_dev(ioport); + set_controller_command_byte(ioport,sc->command_byte, + KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT); + + /* remove anything left in the output buffer */ + empty_aux_buffer(ioport); /* Complete the close */ sc->state &= ~PSM_OPEN; @@ -313,109 +760,315 @@ psmclose(dev_t dev, int flag, int fmt, struct proc *p) } static int -psmread(dev_t dev, struct uio *uio, int flag) +mkms(unsigned char *buf,int *len,int maxlen,register mousestatus_t *status) { - int s; - int error = 0; /* keep compiler quiet, even though initialisation - is unnecessary */ - unsigned length; - struct psm_softc *sc; - unsigned char buffer[PSM_CHUNK]; + static int butmap[] = { + 0, MOUSE_MSS_BUTTON3DOWN, MOUSE_MSS_BUTTON2DOWN, + MOUSE_MSS_BUTTON3DOWN | MOUSE_MSS_BUTTON2DOWN, + MOUSE_MSS_BUTTON1DOWN, + MOUSE_MSS_BUTTON3DOWN | MOUSE_MSS_BUTTON1DOWN, + MOUSE_MSS_BUTTON2DOWN | MOUSE_MSS_BUTTON1DOWN, + MOUSE_MSS_BUTTON3DOWN | MOUSE_MSS_BUTTON2DOWN | MOUSE_MSS_BUTTON1DOWN, + }; + unsigned char delta; - /* Get device information */ - sc = &psm_softc[PSMUNIT(dev)]; + if (maxlen - *len < MOUSE_MSS_PACKETSIZE) + return FALSE; - /* Block until mouse activity occured */ - s = spltty(); - while (sc->inq.count == 0) { - if (minor(dev) & 0x1) { - splx(s); - return (EWOULDBLOCK); - } - sc->state |= PSM_ASLP; - error = tsleep((caddr_t)sc, PZERO | PCATCH, "psmrea", 0); - if (error != 0) { - splx(s); - return (error); - } - } + buf[0] = MOUSE_MSS_SYNC; + buf[0] |= butmap[status->button & BUTSTATMASK]; - /* Transfer as many chunks as possible */ - while (sc->inq.count > 0 && uio->uio_resid > 0) { - length = min(sc->inq.count, uio->uio_resid); - if (length > sizeof(buffer)) - length = sizeof(buffer); + if (status->dx < -128) + delta = 0x80; /* -128 */ + else if (status->dx > 127) + delta = 0x7f; /* 127 */ + else + delta = (unsigned char)status->dx; + buf[0] |= (delta & 0xc0) >> 6; /* bit 6-7 */ + buf[1] = delta & 0x3f; /* bit 0-5 */ - /* Remove a small chunk from input queue */ - if (sc->inq.first + length >= PSM_BSIZE) { - bcopy(&sc->inq.queue[sc->inq.first], - buffer, PSM_BSIZE - sc->inq.first); - bcopy(sc->inq.queue, &buffer[PSM_BSIZE - sc->inq.first], - length - (PSM_BSIZE - sc->inq.first)); - } - else - bcopy(&sc->inq.queue[sc->inq.first], buffer, length); + if (status->dy < -128) + delta = 0x80; /* -128 */ + else if (status->dy > 127) + delta = 0x7f; /* 127 */ + else + delta = (unsigned char)status->dy; + buf[0] |= (delta & 0xc0) >> 4; /* bit 6-7 */ + buf[2] = delta & 0x3f; /* bit 0-5 */ - sc->inq.first = (sc->inq.first + length) % PSM_BSIZE; - sc->inq.count -= length; + *len += MOUSE_MSS_PACKETSIZE; - /* Copy data to user process */ - error = uiomove(buffer, length, uio); - if (error) - break; - } - sc->x = sc->y = 0; - - /* Allow interrupts again */ - splx(s); - return (error); + return TRUE; } static int +mkmsc(unsigned char *buf,int *len,int maxlen,register mousestatus_t *status) +{ + static int butmap[] = { + 0, MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON2UP, + MOUSE_MSC_BUTTON3UP | MOUSE_MSC_BUTTON2UP, + MOUSE_MSC_BUTTON1UP, + MOUSE_MSC_BUTTON3UP | MOUSE_MSC_BUTTON1UP, + MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON1UP, + MOUSE_MSC_BUTTON3UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON1UP, + }; + unsigned char delta; + + if (maxlen - *len < MOUSE_PS2_PACKETSIZE) + return FALSE; + + buf[0] = MOUSE_MSC_SYNC; + buf[0] |= ~butmap[status->button & BUTSTATMASK] & MOUSE_MSC_BUTTONS; + + if (status->dx < -128) + delta = 0x80; /* -128 */ + else if (status->dx > 127) + delta = 0x7f; /* 127 */ + else + delta = (unsigned char)status->dx; + buf[1] = delta >> 2; + buf[3] = delta - buf[1]; + + if (status->dy < -128) + delta = 0x80; /* -128 */ + else if (status->dy > 127) + delta = 0x7f; /* 127 */ + else + delta = (unsigned char)status->dy; + buf[2] = delta >> 2; + buf[4] = delta - buf[2]; + + *len += MOUSE_MSC_PACKETSIZE; + + return TRUE; +} + +static int +mkps2(unsigned char *buf,int *len,int maxlen,register mousestatus_t *status) +{ + static int butmap[] = { + 0, MOUSE_PS2_BUTTON3DOWN, MOUSE_PS2_BUTTON2DOWN, + MOUSE_PS2_BUTTON3DOWN | MOUSE_PS2_BUTTON2DOWN, + MOUSE_PS2_BUTTON1DOWN, + MOUSE_PS2_BUTTON3DOWN | MOUSE_PS2_BUTTON1DOWN, + MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON1DOWN, + MOUSE_PS2_BUTTON3DOWN | MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON1DOWN, + }; + register int delta; + + if (maxlen - *len < MOUSE_PS2_PACKETSIZE) + return FALSE; + + buf[0] = MOUSE_PS2_SYNC; + buf[0] |= butmap[status->button & BUTSTATMASK]; + + if (status->dx < -128) + delta = -128; + else if (status->dx > 127) + delta = 127; + else + delta = status->dx; + if (delta < 0) + buf[0] |= MOUSE_PS2_XNEG; + buf[1] = delta; + + if (status->dy < -128) + delta = -128; + else if (status->dy > 127) + delta = 127; + else + delta = status->dy; + if (delta < 0) + buf[0] |= MOUSE_PS2_YNEG; + buf[2] = delta; + + *len += MOUSE_PS2_PACKETSIZE; + + return TRUE; +} + +#ifdef PSM_CURRENT +static +#endif +int +psmread(dev_t dev, struct uio *uio, int flag) +{ + register struct psm_softc *sc = &psm_softc[PSM_UNIT(dev)]; + unsigned int length; + int error; + int s; + int i; + + /* block until mouse activity occured */ + s = spltty(); + if ((sc->outputbytes <= 0) && (sc->queue.count <= 0)) { + while (sc->queue.count <= 0) { + if (PSM_NBLOCKIO(dev)) { + splx(s); + return (EWOULDBLOCK); + } + sc->state |= PSM_ASLP; + error = tsleep((caddr_t)sc, PZERO | PCATCH, + "psmread", 0); + if (error) { + splx(s); + return (error); + } + } + } + + if (sc->outputbytes >= uio->uio_resid) { + /* nothing to be done */ + } else { + if (sc->outputbytes > 0) { + bcopy(&sc->opacket[sc->outputhead],sc->opacket, + sc->outputbytes); + } + sc->outputhead = 0; + for(i = sc->queue.head; sc->queue.count > 0; + i = (i + 1) % PSM_BUFSIZE, --sc->queue.count) { + if (!(*sc->mkpacket)(&sc->opacket[sc->outputbytes], + &sc->outputbytes,PSM_BUFSIZE,&sc->queue.buf[i])) + break; + } + sc->queue.head = i; + } + + /* allow interrupts again */ + splx(s); + + /* copy data to user process */ + length = min(sc->outputbytes,uio->uio_resid); + error = uiomove(&sc->opacket[sc->outputhead], length, uio); + if (error) + return (error); + sc->outputhead += length; + sc->outputbytes -= length; + + return (error); +} + +#ifdef PSM_CURRENT +static +#endif +int psmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p) { - struct psm_softc *sc; - struct mouseinfo info; - int s, error; - - /* Get device information */ - sc = &psm_softc[PSMUNIT(dev)]; + struct psm_softc *sc = &psm_softc[PSM_UNIT(dev)]; + mouseinfo_t info; + mousestatus_t *ms; + packetfunc_t func; + int error = 0; + int s; /* Perform IOCTL command */ switch (cmd) { - case MOUSEIOCREAD: - /* Don't modify info while calculating */ - s = spltty(); + case MOUSE_GETINFO: + *(mousehw_t *)addr = sc->hw; + break; - /* Build mouse status octet */ - info.status = sc->status; - if (sc->x || sc->y) - info.status |= MOVEMENT; + case MOUSE_GETMODE: + *(mousemode_t *)addr = sc->mode; + break; - /* Encode X and Y motion as good as we can */ - if (sc->x > 127) - info.xmotion = 127; - else if (sc->x < -128) - info.xmotion = -128; - else - info.xmotion = sc->x; + case MOUSE_SETMODE: + if (((mousemode_t *)addr)->rate < 0) { + error = EINVAL; + break; + } + if (((mousemode_t *)addr)->resolution < 0) { + error = EINVAL; + break; + } +#ifndef PSM_NOEMULATION + switch (((mousemode_t *)addr)->protocol) { + case MOUSE_PROTO_MS: + func = mkms; + break; + case MOUSE_PROTO_MSC: + func = mkmsc; + break; + case MOUSE_PROTO_PS2: + func = mkps2; + break; + default: + error = EINVAL; + func = (packetfunc_t)NULL; + break; + } + if (error) + break; +#endif /* PSM_NOEMULATION */ + if (((mousemode_t *)addr)->accelfactor < 0) { + error = EINVAL; + break; + } - if (sc->y > 127) - info.ymotion = 127; - else if (sc->y < -128) - info.ymotion = -128; - else - info.ymotion = sc->y; + s = spltty(); /* disable interrupt while updating */ + sc->mode.rate = (((mousemode_t *)addr)->rate == 0) ? + PSMD_DEFAULT_RATE : + min(((mousemode_t *)addr)->rate,PSMD_MAX_RATE); + sc->mode.resolution = (((mousemode_t *)addr)->resolution == 0) ? + PSMD_DEFAULT_RESOLUTION : + ((mousemode_t *)addr)->resolution; - /* Reset historical information */ - sc->x = 0; - sc->y = 0; - sc->status &= ~BUTCHNGMASK; + /* temporalily disable the keyboard */ + write_controller_command(sc->addr,KBDC_DISABLE_KBD_PORT); + set_controller_command_byte(sc->addr, + sc->command_byte & ~KBD_KBD_CONTROL_BITS, + KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT + | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT); + + /* program the mouse */ + sc->mode.rate = set_mouse_sampling_rate(sc->addr,sc->mode.rate); + sc->mode.resolution = + set_mouse_resolution(sc->addr,sc->mode.resolution); + + /* enable the aux port and interrupt */ + set_controller_command_byte(sc->addr,sc->command_byte, + KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT); + +#ifndef PSM_NOEMULATION + sc->mode.protocol = ((mousemode_t *)addr)->protocol; + sc->mkpacket = func; + sc->outputbytes = 0; + sc->outputhead = 0; +#endif /* PSM_NOEMULATION */ + sc->mode.accelfactor = ((mousemode_t *)addr)->accelfactor; - /* Allow interrupts and copy result buffer */ splx(s); - error = copyout(&info, addr, sizeof(struct mouseinfo)); + break; + + case MOUSEIOCREAD: /* FIXME: this should go... */ + error = EINVAL; + break; + + case MOUSE_GETSTATE: + info.status = 0; + info.xmotion = 0; + info.ymotion = 0; + + s = spltty(); + if (sc->queue.count > 0) { + ms = &sc->queue.buf[sc->queue.head]; + + /* button status */ + info.status = ms->button; /* BUT?STAT bits */ + info.status |= /* BUT?CHNG bits */ + ((ms->button ^ ms->obutton) << 3); + /* mouse motion */ + info.xmotion = ms->dx; + info.ymotion = ms->dy; + if ((info.xmotion != 0) || (info.ymotion != 0)) + info.status |= MOVEMENT; + + sc->queue.head = (sc->queue.head + 1) % PSM_BUFSIZE; + --sc->queue.count; + } + splx(s); + + *(mouseinfo_t *)addr = info; break; default: @@ -430,23 +1083,111 @@ psmioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc *p) void psmintr(int unit) { - struct psm_softc *sc = &psm_softc[unit]; - int ioport = psmaddr[unit]; + /* the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN) + into `mouseinfo' button bits (BUT?STAT). */ + static butmap[8] = { + 0, BUT1STAT, BUT3STAT, BUT1STAT | BUT3STAT, + BUT2STAT, BUT1STAT | BUT2STAT, BUT2STAT | BUT3STAT, + BUT1STAT | BUT2STAT | BUT3STAT + }; + register struct psm_softc *sc = &psm_softc[unit]; + int ioport = sc->addr; + mousestatus_t *ms; + unsigned char c; + int x, y; - sc->inq.queue[sc->inq.last++ % PSM_BSIZE] = inb(ioport+PSM_DATA); - sc->inq.count++; - if (sc -> state & PSM_ASLP) { + /* is this really for us? */ + if ((inb(ioport + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) + != KBDS_AUX_BUFFER_FULL) + return; + + /* read a byte */ + c = inb(ioport + KBD_DATA_PORT); + + /* discard the byte if the device is not open */ + if ((sc->state & PSM_OPEN) == 0) + return; + + /* interpret data bytes + FIXME: there seems no way to reliably re-synchronize with + the PS/2 mouse once we are out of sync. Sure, there is + sync bits in the first data byte, but the second and the + third bytes may have these bits on (they are not functioning + as sync bits then!). There need to be two consequtive + bytes with these bits off to re-sync. (This can be done + if the user clicks buttons without moving the mouse?) + */ + if (sc->inputbytes == 0) { +#ifndef PSM_NOCHECKSYNC + if ((c & MOUSE_PS2_SYNCMASK) == MOUSE_PS2_SYNC) +#endif /* PSM_NOCHECKSYNC */ + sc->ipacket[sc->inputbytes++] = c; + } else { + sc->ipacket[sc->inputbytes++] = c; + if (sc->inputbytes >= MOUSE_PS2_PACKETSIZE) { + if (sc->queue.count >= PSM_BUFSIZE) { + /* no room in the queue */ + sc->inputbytes = 0; + return; + } + if (sc->mode.accelfactor >= 1) { + x = (sc->ipacket[0] & MOUSE_PS2_XOVERFLOW) ? + 128 : sc->ipacket[1]; + if (x != 0) { + x = x*x/sc->mode.accelfactor; + if (x == 0) + x = 1; + if (sc->ipacket[0] & MOUSE_PS2_XNEG) + x = -x; + } + y = (sc->ipacket[0] & MOUSE_PS2_YOVERFLOW) ? + 128 : sc->ipacket[2]; + if (y != 0) { + y = y*y/sc->mode.accelfactor; + if (y == 0) + y = 1; + if (sc->ipacket[0] & MOUSE_PS2_YNEG) + y = -y; + } + } else { /* sc->mode.accelfactor <= 0 */ + x = (sc->ipacket[0] & MOUSE_PS2_XOVERFLOW) ? + ((sc->ipacket[0] & MOUSE_PS2_XNEG) ? + -128 : 127) : sc->ipacket[1]; + y = (sc->ipacket[0] & MOUSE_PS2_YOVERFLOW) ? + ((sc->ipacket[0] & MOUSE_PS2_YNEG) ? + -128 : 127) : sc->ipacket[2]; + } + + /* FIXME: we shouldn't store data if no movement + and no button status change is detected? */ + ms = &sc->queue.buf[sc->queue.tail]; + ms->dx = x; + ms->dy = y; + ms->obutton = sc->button; /* previous button state */ + sc->button = ms->button = /* latest button state */ + butmap[sc->ipacket[0] & MOUSE_PS2_BUTTONS]; + sc->queue.tail = + (sc->queue.tail + 1) % PSM_BUFSIZE; + ++sc->queue.count; + sc->inputbytes = 0; + } + } + + if (sc->state & PSM_ASLP) { sc->state &= ~PSM_ASLP; wakeup((caddr_t)sc); } selwakeup(&sc->rsel); } -static int +#ifdef PSM_CURRENT +static +#endif +int psmselect(dev_t dev, int rw, struct proc *p) { + struct psm_softc *sc = &psm_softc[PSM_UNIT(dev)]; int s, ret; - struct psm_softc *sc = &psm_softc[PSMUNIT(dev)]; /* Silly to select for output */ if (rw == FWRITE) @@ -454,9 +1195,9 @@ psmselect(dev_t dev, int rw, struct proc *p) /* Return true if a mouse event available */ s = spltty(); - if (sc->inq.count) + if ((sc->outputbytes > 0) || (sc->queue.count > 0)) { ret = 1; - else { + } else { selrecord(p, &sc->rsel); ret = 0; } @@ -465,21 +1206,24 @@ psmselect(dev_t dev, int rw, struct proc *p) return (ret); } +#ifdef PSM_CURRENT -static psm_devsw_installed = 0; +static int psm_devsw_installed = FALSE; static void psm_drvinit(void *unused) { dev_t dev; - if( ! psm_devsw_installed ) { + if (!psm_devsw_installed) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&psm_cdevsw, NULL); - psm_devsw_installed = 1; + psm_devsw_installed = TRUE; } } -SYSINIT(psmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,psm_drvinit,NULL) +SYSINIT(psmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE + CDEV_MAJOR,psm_drvinit,NULL) + +#endif /* PSM_CURRENT */ #endif /* NPSM > 0 */