From c4a2854d9bcaf0f5895c5518320c01980b19c716 Mon Sep 17 00:00:00 2001 From: Jordi Sanfeliu Date: Wed, 25 Sep 2024 09:25:37 +0200 Subject: [PATCH] added PS/2 mouse support with the new device /dev/psaux #94 --- docs/devices.txt | 3 + drivers/char/ps2.c | 3 + drivers/char/psaux.c | 294 +++++++++++++++++++++++++++++++++++++++++ include/fiwix/config.h | 1 + include/fiwix/ps2.h | 4 + include/fiwix/psaux.h | 40 ++++++ 6 files changed, 345 insertions(+) create mode 100644 drivers/char/psaux.c create mode 100644 include/fiwix/psaux.h diff --git a/docs/devices.txt b/docs/devices.txt index d9e8e50d..e310f573 100644 --- a/docs/devices.txt +++ b/docs/devices.txt @@ -32,6 +32,9 @@ Major | Minor | Device name | Description 6 Parallel printer devices 0 lp0 first parallel printer port +10 Non-serial mice + 1 psaux PS/2-style mouse port + 29 Universal framebuffer 0 fb0 first framebuffer diff --git a/drivers/char/ps2.c b/drivers/char/ps2.c index 0f2106b8..ae051923 100644 --- a/drivers/char/ps2.c +++ b/drivers/char/ps2.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -214,9 +215,11 @@ void ps2_init(void) if(supp_ports) { ps2_write(PS2_COMMAND, PS2_CMD_ENABLE_CH1); keyboard_init(); +#ifdef CONFIG_PSAUX if(supp_ports > 1) { ps2_write(PS2_COMMAND, PS2_CMD_ENABLE_CH2); psaux_init(); } +#endif /* CONFIG_PSAUX */ } } diff --git a/drivers/char/psaux.c b/drivers/char/psaux.c new file mode 100644 index 00000000..b6034307 --- /dev/null +++ b/drivers/char/psaux.c @@ -0,0 +1,294 @@ +/* + * fiwix/drivers/char/psaux.c + * + * Copyright 2024, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PSAUX +static struct fs_operations psaux_driver_fsop = { + 0, + 0, + + psaux_open, + psaux_close, + psaux_read, + psaux_write, + NULL, /* ioctl */ + NULL, /* llseek */ + NULL, /* readdir */ + NULL, /* readdir64 */ + NULL, /* mmap */ + psaux_select, + + NULL, /* readlink */ + NULL, /* followlink */ + NULL, /* bmap */ + NULL, /* lockup */ + NULL, /* rmdir */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* mknod */ + NULL, /* truncate */ + NULL, /* create */ + NULL, /* rename */ + + NULL, /* read_block */ + NULL, /* write_block */ + + NULL, /* read_inode */ + NULL, /* write_inode */ + NULL, /* ialloc */ + NULL, /* ifree */ + NULL, /* statfs */ + NULL, /* read_superblock */ + NULL, /* remount_fs */ + NULL, /* write_superblock */ + NULL /* release_superblock */ +}; + +static struct device psaux_device = { + "psaux", + PSAUX_MAJOR, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + 0, + NULL, + &psaux_driver_fsop, + NULL, + NULL, + NULL +}; + +struct psaux psaux_table; + +static struct interrupt irq_config_psaux = { 0, "psaux", &irq_psaux, NULL }; + +extern volatile unsigned char ack; +static unsigned char status[3] = { 0, 0, 0}; +static unsigned char is_ps2 = 0; +static char id = -1; + +static void ps2_command_write(const unsigned char byte) +{ + ps2_write(PS2_COMMAND, PS2_CMD_CH2_PREFIX); + ps2_write(PS2_DATA, byte); + if(ps2_wait_ack()) { + printk("WARNING: %s(): ACK not received on %x command!\n", __FUNCTION__, byte); + } +} + +static void psaux_identify(void) +{ + /* disable */ + ps2_command_write(PS2_AUX_DISABLE); + + /* status information */ + ps2_command_write(PS2_DEV_GETINFO); + status[0] = ps2_read(PS2_DATA); /* status */ + status[2] = ps2_read(PS2_DATA); /* resolution */ + status[2] = ps2_read(PS2_DATA); /* sample rate */ + + /* identify */ + ps2_command_write(PS2_DEV_RATE); + ps2_command_write(200); + ps2_command_write(PS2_DEV_RATE); + ps2_command_write(100); + ps2_command_write(PS2_DEV_RATE); + ps2_command_write(80); + ps2_command_write(PS2_DEV_IDENTIFY); + id = ps2_read(PS2_DATA); + ps2_clear_buffer(); + + /* enable */ + ps2_command_write(PS2_DEV_ENABLE); +} + +void irq_psaux(int num, struct sigcontext *sc) +{ + unsigned char ch; + + ch = inport_b(PS2_DATA); + + /* aux controller said 'acknowledge!' */ + if(ch == DEV_ACK) { + ack = 1; + } + if(!psaux_table.count) { + return; + } + charq_putchar(&psaux_table.read_q, ch); + wakeup(&psaux_read); + wakeup(&do_select); +} + +int psaux_open(struct inode *i, struct fd *fd_table) +{ + int minor; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(psaux_device.minors, minor)) { + return -ENXIO; + } + if(psaux_table.count++) { + return 0; + } + memset_b(&psaux_table.read_q, 0, sizeof(struct clist)); + memset_b(&psaux_table.write_q, 0, sizeof(struct clist)); + return 0; +} + +int psaux_close(struct inode *i, struct fd *fdtable) +{ + int minor; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(psaux_device.minors, minor)) { + return -ENXIO; + } + psaux_table.count--; + return 0; +} + +int psaux_read(struct inode *i, struct fd *fdtable, char *buffer, __size_t count) +{ + int minor, bytes_read; + unsigned char ch; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(psaux_device.minors, minor)) { + return -ENXIO; + } + + for(;;) { + if(!psaux_table.read_q.count) { + if(fd_table->flags & O_NONBLOCK) { + return -EAGAIN; + } + if(sleep(&psaux_read, PROC_INTERRUPTIBLE)) { + return -EINTR; + } + continue; + } + break; + } + bytes_read = 0; + while(bytes_read < count) { + if(psaux_table.read_q.count) { + ch = charq_getchar(&psaux_table.read_q); + buffer[bytes_read++] = ch; + continue; + } + break; + } + if(bytes_read) { + i->i_atime = CURRENT_TIME; + } + return bytes_read; +} + +int psaux_write(struct inode *i, struct fd *fdtable, const char *buffer, __size_t count) +{ + int minor, bytes_written; + unsigned char ch; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(psaux_device.minors, minor)) { + return -ENXIO; + } + bytes_written = 0; + while(bytes_written < count) { + ch = buffer[bytes_written++]; + ps2_command_write(ch); + } + if(bytes_written) { + i->i_mtime = CURRENT_TIME; + } + return bytes_written; +} + +int psaux_select(struct inode *i, int flag) +{ + int minor; + + minor = MINOR(i->rdev); + if(!TEST_MINOR(psaux_device.minors, minor)) { + return -ENXIO; + } + + switch(flag) { + case SEL_R: + if(psaux_table.read_q.count) { + return 1; + } + break; + } + return 0; +} + +void psaux_init(void) +{ + int errno; + + /*add_bh(&psaux_bh);*/ + if(!register_irq(PSAUX_IRQ, &irq_config_psaux)) { + enable_irq(PSAUX_IRQ); + } + + /* reset device */ + ps2_command_write(PS2_DEV_RESET); + if((errno = ps2_read(PS2_DATA)) != DEV_RESET_OK) { + printk("WARNING: %s(): psaux returned 0x%x on reset.\n", __FUNCTION__, errno); + } + if(ps2_read(PS2_DATA) == 0) { + is_ps2 = 1; + } + + ps2_clear_buffer(); + psaux_identify(); + printk("psaux 0x%04x-0x%04x %d", 0x60, 0x64, PSAUX_IRQ); + printk("\ttype=%s", is_ps2 ? "PS/2" : "unknown"); + switch(id) { + case -1: + printk(", unknown ID %x", id & 0xFF); + break; + case 0: + printk(", standard mouse"); + break; + case 2: + printk(", track ball"); + break; + case 3: + printk(", 3-button wheel mouse"); + break; + case 4: + printk(", 5-button wheel mouse"); + break; + default: + printk(", unknown mouse"); + break; + } + printk("\n"); + memset_b(&psaux_table, 0, sizeof(struct psaux)); + SET_MINOR(psaux_device.minors, 1); + if(register_device(CHR_DEV, &psaux_device)) { + printk("WARNING: %s(): unable to register psaux device.\n", __FUNCTION__); + } +} +#endif /* CONFIG_PSAUX */ diff --git a/include/fiwix/config.h b/include/fiwix/config.h index 2e2f7643..ee31de7d 100644 --- a/include/fiwix/config.h +++ b/include/fiwix/config.h @@ -52,6 +52,7 @@ #undef CONFIG_MMAP2 #define CONFIG_NET #define CONFIG_PRINTK64 +#define CONFIG_PSAUX /* configuration options to help debugging */ diff --git a/include/fiwix/ps2.h b/include/fiwix/ps2.h index c6016cae..9190ca28 100644 --- a/include/fiwix/ps2.h +++ b/include/fiwix/ps2.h @@ -31,6 +31,7 @@ #define PS2_CMD_DISABLE_CH1 0xAD /* disable first channel */ #define PS2_CMD_ENABLE_CH1 0xAE /* enable first channel */ #define PS2_CMD_GET_IFACE 0xCA /* get the current interface */ +#define PS2_CMD_CH2_PREFIX 0xD4 /* second channel (mouse) prefix */ #define PS2_CMD_HOTRESET 0xFE /* Hot Reset */ /* device commands */ @@ -42,11 +43,14 @@ #define PS2_DEV_RATE 0xF3 /* set typematic rate/delay */ #define PS2_DEV_ENABLE 0xF4 /* keyboard enable scanning */ #define PS2_KB_DISABLE 0xF5 /* keyboard disable scanning */ +#define PS2_AUX_DISABLE 0xF6 /* mouse disable scanning */ #define PS2_DEV_RESET 0xFF /* device reset */ #define DEV_RESET_OK 0xAA /* self-test passed */ #define DEV_ACK 0xFA /* acknowledge */ +#define PS2_TIMEOUT 500000 + extern volatile unsigned char ack; int ps2_wait_ack(void); diff --git a/include/fiwix/psaux.h b/include/fiwix/psaux.h new file mode 100644 index 00000000..d38fdee0 --- /dev/null +++ b/include/fiwix/psaux.h @@ -0,0 +1,40 @@ +/* + * fiwix/include/fiwix/psaux.h + * + * Copyright 2024, Jordi Sanfeliu. All rights reserved. + * Distributed under the terms of the Fiwix License. + */ + +#ifdef CONFIG_PSAUX + +#ifndef _FIWIX_PSAUX_H +#define _FIWIX_PSAUX_H + +#include +#include +#include + +#define PSAUX_IRQ 12 + +#define PSAUX_MAJOR 10 /* major number for /dev/psaux */ +#define PSAUX_MINORS 1 + +struct psaux { + int count; + struct clist read_q; + struct clist write_q; +}; +extern struct psaux psaux_table; + +int psaux_open(struct inode *, struct fd *); +int psaux_close(struct inode *, struct fd *); +int psaux_read(struct inode *, struct fd *, char *, __size_t); +int psaux_write(struct inode *, struct fd *, const char *, __size_t); +int psaux_select(struct inode *, int); + +void irq_psaux(int num, struct sigcontext *); +void psaux_init(void); + +#endif /* _FIWIX_PSAUX_H */ + +#endif /* CONFIG_PSAUX */