diff options
| -rw-r--r-- | bda.h | 21 | ||||
| -rw-r--r-- | dlog.h | 41 | ||||
| -rw-r--r-- | int15ps2.h | 7 | ||||
| -rw-r--r-- | mousetsr.c | 246 | ||||
| -rw-r--r-- | mousetsr.h | 39 | ||||
| -rw-r--r-- | mousmain.c | 306 | ||||
| -rw-r--r-- | nls/vbmouse.en | 8 | ||||
| -rw-r--r-- | pic.h | 59 | ||||
| -rw-r--r-- | serial.h | 117 | ||||
| -rw-r--r-- | sermouse.h | 38 | ||||
| -rw-r--r-- | version.h | 2 |
11 files changed, 782 insertions, 102 deletions
@@ -1,3 +1,22 @@ +/* + * VBMouse - BIOS data area access routines + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + #ifndef BDA_H #define BDA_H @@ -22,6 +41,8 @@ static inline uint8_t bda_get_byte(unsigned int offset) { #define bda_get_ebda_segment() bda_get_word(0x0e) +#define bda_get_equipment() bda_get_word(0x10) + #define bda_get_video_mode() bda_get_byte(0x49) #define bda_get_num_columns() bda_get_word(0x4a) #define bda_get_video_page_size() bda_get_word(0x4c) @@ -29,14 +29,18 @@ /** If 0, these routines become nops */ #define ENABLE_DLOG 0 /** 1 means target serial port, 0 means target IO port. */ -#define DLOG_TARGET_SERIAL 0 -/** IO port to target. +#define DLOG_TARGET DLOG_TARGET_IOPORT +/** When using DLOG_TARGET_IOPORT, which IO port to target. * VirtualBox uses 0x504, Bochs, DOSBox and descendants use 0xE9. - * When using DLOG_TARGET_SERIAL, use desired UART IO base port. (e.g. COM1 = 0x3F8). */ + * When using DLOG_TARGET_SERIAL, the desired UART IO base port. (e.g. COM1 = 0x3F8). */ #define DLOG_TARGET_PORT 0x504 // End of customizable defines +#define DLOG_TARGET_BIOSTTY 1 +#define DLOG_TARGET_SERIAL 2 +#define DLOG_TARGET_IOPORT 3 + #if ENABLE_DLOG /** Initializes the debug log port. */ @@ -45,7 +49,22 @@ static void dlog_init(); /** Logs a single character to the debug message IO port. */ static inline void dputc(char c); -#if DLOG_TARGET_SERIAL +#if DLOG_TARGET == DLOG_TARGET_BIOSTTY + +static inline void dlog_init() +{ + // No initialization required +} + +static inline void dputc(char c); +#pragma aux dputc = \ + "mov ah, 0x0E" \ + "xor bx, bx" \ + "int 0x10" \ + __parm [AL] \ + __modify __exact [AH AL BH BL]; + +#elif DLOG_TARGET == DLOG_TARGET_SERIAL static void dlog_init() { @@ -65,7 +84,7 @@ static inline void dputc(char c) outp(DLOG_TARGET_PORT, c); } -#else /* DLOG_TARGET_SERIAL */ +#elif DLOG_TARGET == DLOG_TARGET_IOPORT static inline void dlog_init() { @@ -77,7 +96,7 @@ static inline void dputc(char c) outp(DLOG_TARGET_PORT, c); } -#endif /* DLOG_TARGET_SERIAL */ +#endif /* DLOG_TARGET */ static void d_utoa(unsigned num, unsigned base) { @@ -265,6 +284,16 @@ static void dprintf(const char *fmt, ...) break; } break; + case 'b': + switch (size) { + case sizeof(int): + d_utoa(va_arg(va, int), 2); + break; + case sizeof(long): + d_ultoa(va_arg(va, long), 2); + break; + } + break; case 'p': if (is_far) { void __far *p = va_arg(va, void __far *); @@ -23,6 +23,7 @@ #include <stdbool.h> #include <stdint.h> #include "utils.h" +#include "bda.h" /** Standard PS/2 mouse IRQ. At least on VirtualBox. */ #define PS2_MOUSE_IRQ 12 @@ -95,6 +96,12 @@ enum ps2m_sample_rate { /** Invoked by the BIOS when there is a mouse event. */ typedef void (__far * LPFN_PS2CALLBACK)(); +static bool ps2m_installed() +{ + uint16_t equipment = bda_get_equipment(); + return equipment & (1 << 2); +} + static ps2m_err ps2m_init(uint8_t packet_size); #pragma aux ps2m_init = \ "stc" /* If nothing happens, assume failure */ \ @@ -27,6 +27,9 @@ #include "int16kbd.h" #include "int2fwin.h" #include "int33.h" +#include "pic.h" +#include "serial.h" +#include "sermouse.h" #include "vbox.h" #include "vmware.h" #include "mousetsr.h" @@ -737,11 +740,11 @@ static void handle_mouse_event(uint16_t buttons, bool absolute, int x, int y, ch static void handle_ps2_packet(void) { - unsigned status = data.ps2_packet[0]; + unsigned status = data.cur_packet[0]; // Decode basic PS/2 packet unsigned buttons = status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2 | PS2M_STATUS_BUTTON_3); - int x = data.ps2_packet[1], y = data.ps2_packet[2]; + int x = data.cur_packet[1], y = data.cur_packet[2]; // For the extended byte... bool abs = false; @@ -757,32 +760,32 @@ static void handle_ps2_packet(void) switch (data.device_id) { case PS2M_DEVICE_ID_IMPS2: // ImPS/2. The fourth packet is the (vertical) wheel movement. - z = (int8_t) data.ps2_packet[3]; + z = (int8_t) data.cur_packet[3]; break; #if USE_IMEX case PS2M_DEVICE_ID_IMEX: case PS2M_DEVICE_ID_IMEX_HORZ: // IntelliMouse Explorer. It can report either: - if (data.ps2_packet[3] & PS2M_IMEX_VERTICAL_SCROLL) { + if (data.cur_packet[3] & PS2M_IMEX_VERTICAL_SCROLL) { // Vertical scrolling, with 6 bits of precision. - z = sign_extend(data.ps2_packet[3], 6); + z = sign_extend(data.cur_packet[3], 6); // Assume 4th/5th buttons are still pressed if they were buttons |= data.buttons & (INT33_BUTTON_MASK_4TH|INT33_BUTTON_MASK_5TH); - } else if (data.ps2_packet[3] & PS2M_IMEX_HORIZONTAL_SCROLL) { + } else if (data.cur_packet[3] & PS2M_IMEX_HORIZONTAL_SCROLL) { // Horizontal scrolling, with 6 bits of precision. - z = sign_extend(data.ps2_packet[3], 6); + z = sign_extend(data.cur_packet[3], 6); wheeln = 1; buttons |= data.buttons & (INT33_BUTTON_MASK_4TH|INT33_BUTTON_MASK_5TH); } else { // Or 2 extra buttons (4, 5) - if (data.ps2_packet[3] & PS2M_IMEX_BUTTON_4) { + if (data.cur_packet[3] & PS2M_IMEX_BUTTON_4) { buttons |= INT33_BUTTON_MASK_4TH; } - if (data.ps2_packet[3] & PS2M_IMEX_BUTTON_5) { + if (data.cur_packet[3] & PS2M_IMEX_BUTTON_5) { buttons |= INT33_BUTTON_MASK_5TH; } // Plus (vertical) scrolling with 4 bits of precision. - z = sign_extend(data.ps2_packet[3], 4); + z = sign_extend(data.cur_packet[3], 4); } break; #endif /* USE_IMEX */ @@ -848,13 +851,13 @@ static void handle_ps2_packet(void) } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_LEFT) { - buttons |= PS2M_STATUS_BUTTON_1; + buttons |= INT33_BUTTON_MASK_LEFT; } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_RIGHT) { - buttons |= PS2M_STATUS_BUTTON_2; + buttons |= INT33_BUTTON_MASK_RIGHT; } if (vmw.status & VMWARE_ABSPOINTER_STATUS_BUTTON_MIDDLE) { - buttons |= PS2M_STATUS_BUTTON_3; + buttons |= INT33_BUTTON_MASK_CENTER; } } else { return; // Ignore the PS/2 packet otherwise, it is likely garbage @@ -876,9 +879,9 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui // Are we using the BIOS in 3-packet mode directly? if (data.bios_packet_size == PS2M_PACKET_SIZE_STD) { // Just forward it to the full packet handler. - data.ps2_packet[0] = word0; - data.ps2_packet[1] = word1; - data.ps2_packet[2] = word2; + data.cur_packet[0] = word0; + data.cur_packet[1] = word1; + data.cur_packet[2] = word2; (void) word3; handle_ps2_packet(); return; @@ -889,7 +892,7 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui // We have to compute synchronization ourselves. if (data.cur_packet_bytes && - ticks >= data.cur_packet_ticks + MAX_PS2_PACKET_DELAY) { + ticks >= data.cur_packet_ticks + MAX_PACKET_DELAY) { // Assume the start of a new packet dprintf("dropping packet! cur_bytes=%u prev_ticks=%u new_ticks=%u\n", data.cur_packet_bytes, data.cur_packet_ticks, ticks); @@ -900,11 +903,11 @@ static void ps2_mouse_handler(uint16_t word0, uint16_t word1, uint16_t word2, ui } #if TRACE_PROTO - dprintf("ps2 callback byte %d/%d = %x\n", + dprintf("ps2 callback byte %d/%d = %hx\n", 1 + data.cur_packet_bytes, data.packet_size, word0 & 0xFF); #endif /* TRACE_PROTO */ - data.ps2_packet[data.cur_packet_bytes] = word0; + data.cur_packet[data.cur_packet_bytes] = word0; data.cur_packet_bytes++; if (data.cur_packet_bytes >= data.packet_size) { @@ -985,13 +988,14 @@ static void set_absolute(bool enable) } #endif /* USE_INTEGRATION */ -static void reset_mouse_hardware() +static bool reset_ps2_mouse() { int err; // Stop receiving bytes... ps2m_enable(false); + data.port = 0; data.bios_packet_size = PS2M_PACKET_SIZE_STREAMING; // Default to use the BIOS in streaming mode data.packet_size = PS2M_PACKET_SIZE_STD; data.cur_packet_bytes = 0; @@ -1034,7 +1038,8 @@ static void reset_mouse_hardware() err = ps2m_init(data.bios_packet_size); } if (err) { - dputs("error on ps2m_init during reset, ignoring"); + dputs("error on ps2m_init during reset"); + return false; } #if USE_WHEEL @@ -1065,7 +1070,6 @@ static void reset_mouse_hardware() if (data.device_id == PS2M_DEVICE_ID_IMEX || data.device_id == PS2M_DEVICE_ID_IMEX_HORZ) { - dputs("ImEx detected"); data.num_wheels = MIN(2, MAX_WHEELS); data.num_buttons = MIN(5, MAX_BUTTONS); } @@ -1099,6 +1103,56 @@ static void reset_mouse_hardware() #endif ps2m_enable(true); + return true; +} + +#if USE_SERIAL +static bool reset_serial_mouse() +{ + // TODO: Currently this is not power-cycling nor redetecting the mouse + unsigned iobase = data.port_io; + + data.cur_packet_bytes = 0; + data.cur_packet_ticks = 0; + + while (serial_data_ready(iobase)) { + uint8_t byte = serial_read_data(iobase); + dprintf("got byte from serial %hx '%c'\n", byte, byte); + } + + // First, reconfigure the serial port to our liking + serial_configure_line(iobase, SERIAL_DIVISOR_1200, SERIAL_LCR_WL_7|SERIAL_LCR_PAR_NONE); + // Keep the mouse powered on + serial_set_modem_control(iobase, SERIAL_MCR_DTR | SERIAL_MCR_RTS); + // Reset and enable the FIFO + serial_set_fifo_control(iobase, SERIAL_FCR_FIFO_ENABLE|SERIAL_FCR_FIFO_RESET); + // And finally enable interrupts + serial_configure_interrupts(iobase, SERIAL_IER_ERBFI); + pic_unmask_irq(data.port_irq); + + dputs("Serial mouse reset done"); + + return true; +} +#endif + +static bool reset_mouse_hardware() +{ + dputs("Reset mouse hardware"); + + // I don't want to search for mouse again. + // So this won't switch from serial to PS/2. + // For that, reload the mouse driver. + + if (data.port == 0) { + return reset_ps2_mouse(); + } else { +#if USE_SERIAL + return reset_serial_mouse(); +#else + return false; +#endif + } } /** Reset "software" mouse settings, i.e. those configurable by the client program. */ @@ -1205,10 +1259,14 @@ static void int33_handler(union INTPACK r) dputs("Mouse reset"); reload_video_info(); reset_mouse_settings(); - reset_mouse_hardware(); reset_mouse_state(); - r.w.ax = INT33_MOUSE_FOUND; - r.w.bx = data.num_buttons; + if (reset_mouse_hardware()) { + r.w.ax = INT33_MOUSE_FOUND; + r.w.bx = data.num_buttons; + } else { + r.w.ax = 0; + r.w.bx = 0; + } break; case INT33_SHOW_CURSOR: if (data.hidden_count > 0) data.hidden_count--; @@ -1394,11 +1452,14 @@ static void int33_handler(union INTPACK r) dputs("Mouse reset settings"); reload_video_info(); reset_mouse_settings(); + reset_mouse_state(); if (!data.bios_packet_size || !data.packet_size) { // Someone is calling this without calling reset first - reset_mouse_hardware(); + if (!reset_mouse_hardware()) { + r.w.ax = 0; + break; + } } - reset_mouse_state(); r.w.ax = INT33_MOUSE_FOUND; r.w.bx = data.num_buttons; break; @@ -1409,8 +1470,10 @@ static void int33_handler(union INTPACK r) dputs("Mouse get driver info"); r.h.bh = REPORTED_VERSION_MAJOR; r.h.bl = REPORTED_VERSION_MINOR; - r.h.ch = INT33_MOUSE_TYPE_PS2; - r.h.cl = 0; + // TODO Seems that windows may use this, albeit only when the following has MSB set? + r.h.ch = data.port == 0 ? INT33_MOUSE_TYPE_PS2 : INT33_MOUSE_TYPE_SERIAL; + r.h.cl = data.port_irq; + r.h.al = data.port; break; case INT33_GET_MAX_COORDINATES: r.w.bx = 0; @@ -1482,6 +1545,121 @@ void __declspec(naked) __far int33_isr(void) } } +#if USE_SERIAL +static void handle_serial_ms_packet() +{ + int x = (int8_t) ((data.cur_packet[1] & SERMOUSE_MS_X_LO_MASK) + | ((data.cur_packet[0] & SERMOUSE_MS_X_HI_MASK) << SERMOUSE_MS_X_HI_SHIFT)); + int y = (int8_t) ((data.cur_packet[2] & SERMOUSE_MS_Y_LO_MASK) + | ((data.cur_packet[0] & SERMOUSE_MS_Y_HI_MASK) << SERMOUSE_MS_Y_HI_SHIFT)); + unsigned buttons = 0; + // Unused: + bool abs = false; + char wheeln = 0; + int z = 0; + + if (data.cur_packet[0] & SERMOUSE_MS_BUTTON_LEFT) + buttons |= INT33_BUTTON_MASK_LEFT; + if (data.cur_packet[0] & SERMOUSE_MS_BUTTON_RIGHT) + buttons |= INT33_BUTTON_MASK_RIGHT; + + +#if TRACE_PROTO + dprintf("serial decoded packet buttons=%x x=%d y=%d\n", buttons, x, y); +#endif /* TRACE_PROTO */ + + handle_mouse_event(buttons, abs, x, y, wheeln, z); +} + +static void handle_serial_data(uint8_t byte) +{ + // TODO: not checking cur_packet_ticks for now, don't think it necessary for serial + // due to different synchronization + // Depending on the serial protocol now... + switch (data.device_id) { + case 'M': + case '3': + // All these protocols use bit #6 (MSB since we use 7 data bits) as Start-of-Frame indicator + if (byte & (1 << 6)) { + data.cur_packet_bytes = 0; + } + +#if TRACE_PROTO + dprintf("serial byte %d/%d = %hx\n", + 1 + data.cur_packet_bytes, data.packet_size, byte); +#endif /* TRACE_PROTO */ + + data.cur_packet[data.cur_packet_bytes] = byte; + data.cur_packet_bytes++; + + if (data.cur_packet_bytes >= data.packet_size) { + handle_serial_ms_packet(); + data.cur_packet_bytes = 0; + } + + break; + } +} + +static void irq3_4_handler() +#pragma aux irq3_4_handler "*" parm caller [] modify [ax bx cx dx es] +{ + uint16_t iobase = data.port_io; + uint8_t intid, byte; + + pic_eoi_irq(data.port_irq); + + while ((intid = serial_read_pending_interrupt(iobase)) != SERIAL_IID_NONE) { + switch (intid) { + case SERIAL_IID_ERROR: + byte = serial_get_line_status(iobase); + // If there is an error, we should likely clear our packet buffer + if (byte & SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE) { + dputs("serial rx error"); + data.cur_packet_bytes = 0; + } + break; + case SERIAL_IID_DATA: + case SERIAL_IID_STALE: + while (serial_data_ready(iobase)) { + byte = serial_read_data(iobase); + handle_serial_data(byte); + } + break; + case SERIAL_IID_MODEM: + byte = serial_get_modem_status(iobase); // Read but ignore + break; + } + } +} + +void __declspec(naked) __far irq3_4_isr(void) +{ + __asm { + pusha + push ds + push es + push fs + push gs + + mov bp, sp + push cs + pop ds + + call irq3_4_handler + + pop gs + pop fs + pop es + pop ds + popa + + ; We don't chain to the previous handler + iret + } +} +#endif + #if USE_WIN386 /** Windows will call this function to notify events when we are inside a DOS box. */ static void windows_mouse_handler(int action, int x, int y, int buttons, int events) @@ -1597,7 +1775,7 @@ void __declspec(naked) __far int2f_isr(void) popa ; Jump to the next handler in the chain - jmp dword ptr cs:[data + 4] ; wasm doesn't support structs, this is data.prev_int2f_handler + jmp dword ptr cs:[data + (4*2)] ; wasm doesn't support structs, this is data.prev_int2f_handler } } #endif @@ -1615,7 +1793,13 @@ static LPTSRDATA int33_get_tsr_data(void); LPTSRDATA __far get_tsr_data(bool installed) { if (installed) { - return int33_get_tsr_data(); + // Check if int33 ISR actually points to something before calling it. + if (_dos_getvect(0x33)) { + // Call our magic function, which also returns our data segment + return int33_get_tsr_data(); + } else { + return 0; + } } else { // Get the TSR data of this instance, not the one currently installed // This is as simple as getting the data from this segment @@ -38,6 +38,8 @@ #define USE_WHEEL 1 /** Enable Intellimouse Explorer protocol (two wheels and 5 buttons). */ #define USE_IMEX 1 +/** Enable support for serial mouse. */ +#define USE_SERIAL 1 /** Trace mouse events verbosily. */ #define TRACE_EVENTS 0 /** Trace (noisy) API calls. */ @@ -53,11 +55,14 @@ #define USE_INTEGRATION (USE_VIRTUALBOX || USE_VMWARE) -/** Max size of PS/2 packet that we support. */ -#define MAX_PS2_PACKET_SIZE 4 +/** Max size of PS/2/serial packet that we support. */ +#define MAX_PACKET_SIZE 4 -/** Maximum number of 55ms ticks that may pass between two bytes of the same PS/2 packet */ -#define MAX_PS2_PACKET_DELAY 2 +/** Maximum number of 55ms ticks that may pass between two bytes of the same packet */ +#define MAX_PACKET_DELAY 2 + +/** Maximum COMn port. */ +#define MAX_PORTS 4 /** Maximum number of buttons supported by this driver. */ #define MAX_BUTTONS 5 @@ -97,11 +102,15 @@ enum { typedef struct tsrdata { // TSR installation data + // Note that because of WatcomC limitations the offsets of these + // are hardcoded in the inline ASM, so do not change them. /** Previous int33 ISR, storing it for uninstall. */ void (__interrupt __far *prev_int33_handler)(); -#if USE_WIN386 + /** Previous ISR for IRQ 3 or 4, depending on the used serial port. */ + void (__interrupt __far *prev_irq3_4_handler)(); + /** Previous int2f ISR, may need to chain to it. */ void (__interrupt __far *prev_int2f_handler)(); -#endif + // Settings configured via the command line #if USE_WHEEL /** Whether to enable & use wheel mouse. */ @@ -126,24 +135,28 @@ typedef struct tsrdata { struct point screen_granularity; // Detected mouse hardware & status - /** Current negotiated PS/2 device_id */ + /** Current mouse's port. 0 = PS/2 , 1...N serial. */ + uint8_t port; + /** Current negotiated PS/2 device_id (or serial mouse type) */ uint8_t device_id; /** Number of buttons of current device. */ uint8_t num_buttons; -#if USE_WHEEL /** Number of wheels of current device. */ uint8_t num_wheels; -#endif - /** Packet size that the BIOS is currently using. Either 1 (streaming) or 3 (plain). */ + /** Packet size that the PS/2 BIOS is currently using. Either 1 (streaming) or 3 (plain). */ uint8_t bios_packet_size; /** Packet size that we are currently expecting internally. Usually 3 (plain) or 4 (with wheel). */ uint8_t packet_size; /** For streaming mode: number of bytes received so far (< packet_size). */ uint8_t cur_packet_bytes; - /** Stores the bytes received so far (cur_bytes). */ - uint8_t ps2_packet[MAX_PS2_PACKET_SIZE]; + /** Stores the bytes received so far (cur_packet_bytes). */ + uint8_t cur_packet[MAX_PACKET_SIZE]; /** Number of ticks at the point when we started to receive this packet. */ uint16_t cur_packet_ticks; + /** IO base for the current port. */ + uint16_t port_io; + /** IRQ for the current port. */ + uint8_t port_irq; // Current mouse settings /** Mouse sensitivity/speed. */ @@ -255,6 +268,8 @@ typedef TSRDATA __far * LPTSRDATA; extern void __declspec(naked) __far int33_isr(void); +extern void __declspec(naked) __far irq3_4_isr(void); + extern void __declspec(naked) __far int2f_isr(void); extern LPTSRDATA __far get_tsr_data(bool installed); @@ -28,6 +28,7 @@ #include "int33.h" #include "int21dos.h" #include "int15ps2.h" +#include "serial.h" #include "vbox.h" #include "vmware.h" #include "dostsr.h" @@ -43,13 +44,15 @@ static bool detect_wheel(LPTSRDATA data) if (ps2m_detect_imps2()) { if (ps2m_detect_imex()) { data->num_wheels = 2; + data->num_buttons = 5; } else { data->num_wheels = 1; + data->num_buttons = 3; } - printf(_(1, 0, "Wheel mouse found and enabled\n")); return true; } else { data->num_wheels = 0; + data->num_buttons = 3; return false; } } @@ -124,7 +127,7 @@ static int set_hwheel_key(LPTSRDATA data, const char *keyname) #endif /* USE_WHEEL */ #if USE_VIRTUALBOX -static int set_virtualbox_integration(LPTSRDATA data, bool enable) +static int set_virtualbox_integration(LPTSRDATA data, bool enable, bool verbose) { if (enable) { int err; @@ -133,19 +136,19 @@ static int set_virtualbox_integration(LPTSRDATA data, bool enable) err = vbox_init_device(&data->vb); if (err) { - fprintf(stderr, _(3, 3, "Cannot find VirtualBox PCI device, err=%d\n"), err); + if (verbose) fprintf(stderr, _(3, 3, "Cannot find VirtualBox PCI device, err=%d\n"), err); return err; } err = vbox_init_buffer(&data->vb, VBOX_BUFFER_SIZE); if (err) { - fprintf(stderr, _(3, 4, "Cannot lock buffer used for VirtualBox communication, err=%d\n"), err); + if (verbose) fprintf(stderr, _(3, 4, "Cannot lock buffer used for VirtualBox communication, err=%d\n"), err); return err; } err = vbox_report_guest_info(&data->vb, VBOXOSTYPE_DOS); if (err) { - fprintf(stderr, _(3, 5, "VirtualBox communication is not working, err=%d\n"), err); + if (verbose) fprintf(stderr, _(3, 5, "VirtualBox communication is not working, err=%d\n"), err); return err; } @@ -162,7 +165,7 @@ static int set_virtualbox_integration(LPTSRDATA data, bool enable) data->vbavail = false; data->vbhaveabs = false; } else { - printf(_(1, 9, "VirtualBox integration already disabled or not available\n")); + if (verbose) printf(_(1, 9, "VirtualBox integration already disabled or not available\n")); } } @@ -179,7 +182,7 @@ static int set_virtualbox_host_cursor(LPTSRDATA data, bool enable) #endif #if USE_VMWARE -static int set_vmware_integration(LPTSRDATA data, bool enable) +static int set_vmware_integration(LPTSRDATA data, bool enable, bool verbose) { if (enable) { int32_t version; @@ -190,11 +193,11 @@ static int set_vmware_integration(LPTSRDATA data, bool enable) version = vmware_get_version(); if (version < 0) { - fprintf(stderr, _(3, 6, "Could not detect VMware, err=%ld\n"), version); + if (verbose) fprintf(stderr, _(3, 6, "Could not detect VMware, err=%ld\n"), version); return -1; } - printf(_(1, 11, "Found VMware protocol version %ld\n"), version); + if (verbose) printf(_(1, 11, "Found VMware protocol version %ld\n"), version); vmware_abspointer_cmd(VMWARE_ABSPOINTER_CMD_ENABLE); @@ -220,7 +223,7 @@ static int set_vmware_integration(LPTSRDATA data, bool enable) data->vmwavail = false; printf(_(1, 13, "Disabled VMware integration\n")); } else { - printf(_(1, 14, "VMware integration already disabled or not available\n")); + if (verbose) printf(_(1, 14, "VMware integration already disabled or not available\n")); } } @@ -228,7 +231,7 @@ static int set_vmware_integration(LPTSRDATA data, bool enable) } #endif -static int set_integration(LPTSRDATA data, bool enable) +static int set_integration(LPTSRDATA data, bool enable, bool verbose) { if (enable) { int err = -1; @@ -236,13 +239,13 @@ static int set_integration(LPTSRDATA data, bool enable) #if USE_VIRTUALBOX // First check if we can enable the VirtualBox integration, // since it's a PCI device it's easier to check if it's not present - err = set_virtualbox_integration(data, true); + err = set_virtualbox_integration(data, true, verbose); if (!err) return 0; #endif #if USE_VMWARE // Afterwards try VMWare integration - err = set_vmware_integration(data, true); + err = set_vmware_integration(data, true, verbose); if (!err) return 0; #endif @@ -251,12 +254,12 @@ static int set_integration(LPTSRDATA data, bool enable) } else { #if USE_VIRTUALBOX if (data->vbavail) { - set_virtualbox_integration(data, false); + set_virtualbox_integration(data, false, verbose); } #endif #if USE_VMWARE if (data->vmwavail) { - set_vmware_integration(data, false); + set_vmware_integration(data, false, verbose); } #endif return 0; @@ -274,29 +277,33 @@ static int set_host_cursor(LPTSRDATA data, bool enable) return -1; } -static int configure_driver(LPTSRDATA data) +static int configure_driver_ps2(LPTSRDATA data) { int err; - // Configure the debug logging port - dlog_init(); - // Check for PS/2 mouse BIOS availability if ((err = ps2m_init(PS2M_PACKET_SIZE_STD))) { - fprintf(stderr, _(3, 8, "Cannot init PS/2 mouse BIOS, err=%d\n"), err); - // Can't do anything without PS/2 return err; } + // OK, if BIOS gives no error, assume PS/2 mouse is present and use it + data->port = 0; + data->port_io = 0; + data->port_irq = 0; + #if USE_WHEEL // Let's utilize the wheel by default data->usewheel = true; - detect_wheel(data); // Prints message if wheel found + detect_wheel(data); +#else + // Without ImEX/wheel detection, hardcode number of buttons + data->num_buttons = 3; + data->num_wheels = 0; #endif #if USE_INTEGRATION // Enable integration by default - set_integration(data, true); + set_integration(data, true, false); #endif #if USE_VIRTUALBOX @@ -307,6 +314,168 @@ static int configure_driver(LPTSRDATA data) return 0; } +#if USE_SERIAL +static int configure_driver_serial(LPTSRDATA data, int port, unsigned iobase) +{ + struct serial_config serconfig; + unsigned delay; + char sig1 = 0, sig2 = 0; + + if (!iobase) { + dprintf("Skip search on COM%u because no IO base found\n", port); + return -1; + } + +#if ENABLE_DLOG && DLOG_TARGET == DLOG_TARGET_SERIAL + if (iobase == DLOG_TARGET_PORT) { + dprintf("Skip search on COM%u because it would overlap with dlog %x\n", port, iobase); + } +#endif + + dprintf("Detecting mouse on COM%d, iobase %xh\n", port, iobase); + serial_save_config(iobase, &serconfig); + serial_configure_interrupts(iobase, SERIAL_IER_NONE); // disable interrupts for now + // 1200 bps, word length = 7, parity = none + serial_configure_line(iobase, SERIAL_DIVISOR_1200, SERIAL_LCR_WL_7|SERIAL_LCR_PAR_NONE); + serial_set_fifo_control(iobase, SERIAL_FCR_FIFO_RESET|SERIAL_FCR_FIFO_ENABLE); + // Enables DRT, but clears RTS, so that mouse enters reset state + serial_set_modem_control(iobase, SERIAL_MCR_DTR); + dputs("DTR ON, RTS OFF"); + + // Wait for 3 ticks (100-150ms) for mouse to power off + bda_wait_ticks(3); + + // Now turn on the mouse by enabling RTS + serial_set_modem_control(iobase, SERIAL_MCR_DTR | SERIAL_MCR_RTS); + dputs("DTR ON, RTS ON"); + + // Wait up to 10 ticks (550ms) for the mouse to send something we understand + for (delay = 10; delay > 0; delay--) { + while (serial_data_ready(iobase)) { + uint8_t car = serial_read_data(iobase); + + // Is this something we recognize ? + dprintf("received serial data after reset: 0x%hx '%c'\n", car, car); + if (car == 'M') { + // Ok, got the MS signature, break out of this wait + sig1 = car; + break; + } + } + + if (sig1) break; + bda_wait_tick(); + dprintf("remaining ticks %u\n", delay); + } + + + if (sig1 == 'M') { + // We have a valid MS-style serial mouse + data->port = port; + data->device_id = 'M'; + data->port_io = iobase; + data->port_irq = serial_get_irq(port); + data->num_buttons = 2; +#if USE_WHEEL + data->num_wheels = 0; +#endif + data->packet_size = 3; + + // Should we wait for an extended signature? Let's do it + for (delay = 2; delay > 0; delay--) { + while (serial_data_ready(iobase)) { + uint8_t car = serial_read_data(iobase); + + dprintf("received serial data after '%c': 0x%hx '%c'\n", sig1, car, car); + if (car == '3') { + // Ok, got the MS signature, break out of this wait + sig2 = car; + break; + } + } + + if (sig2) break; + bda_wait_tick(); + dprintf("2nd loop remaining ticks %u\n", delay); + } + + if (sig2 == '3') { + data->device_id = '3'; + data->num_buttons = 3; + } + + // In any case, we got ourselves a mouse. + return 0; + } + + // No mouse found on this port. + serial_restore_config(iobase, &serconfig); + return -2; +} +#endif + +/// Auto-configure the TSR. Try to detect which mouse we have connected and enable all possible integrations. +static int configure_driver(LPTSRDATA data, unsigned num_ports, int ports[]) +{ + int errs[MAX_PORTS+1] = {0}; + unsigned portidx; + + // Configure the debug logging port + dlog_init(); + + for (portidx = 0; portidx < num_ports; portidx++) { + int port = ports[portidx]; + if (port == 0) { + // PS/2 + int err = configure_driver_ps2(data); + if (!err) { + unsigned num_wheels = 0; +#if USE_WHEEL + num_wheels = data->num_wheels; +#endif + printf(_(1, 0, "Found PS/2 mouse with %u buttons, %u wheels\n"), data->num_buttons, num_wheels); + return 0; + } else { + // Remember error to print out later only if no mouse was found + errs[portidx] = err; + } + } else { +#if USE_SERIAL + int err = configure_driver_serial(data, port, serial_get_iobase(port)); + if (!err) { + unsigned num_wheels = 0; +#if USE_WHEEL + num_wheels = data->num_wheels; +#endif + printf(_(1, 22, "Found serial mouse on COM%u with %u buttons, %u wheels\n"), port, data->num_buttons, num_wheels); + return 0; + } else { + errs[portidx] = err; + } +#endif + } + } + + // If we are here, we have neither serial nor PS/2. + // Print all error messages now. + for (portidx = 0; portidx < num_ports; portidx++) { + int port = ports[portidx]; + int err = errs[portidx]; + + if (err) { + if (port == 0) { + fprintf(stderr, _(3, 8, "Cannot init PS/2 mouse BIOS, err=%d\n"), err); + } else { + fprintf(stderr, _(3, 15, "Cannot find mouse in COM%u, err=%d\n"), port, err); + } + } + } + + fprintf(stderr, _(3, 14, "No mouse found\n")); + + return -1; +} + static int move_driver_to_umb(LPTSRDATA __far * data) { segment_t cur_seg = FP_SEG(*data); @@ -332,6 +501,16 @@ static __declspec(aborts) int install_driver(LPTSRDATA data, bool high) data->prev_int33_handler = _dos_getvect(0x33); _dos_setvect(0x33, data:>int33_isr); + data->prev_irq3_4_handler = 0; +#if USE_SERIAL + if (data->port_irq) { + dprintf("Hooking irq%hu (int%x) for serial\n", data->port_irq, 0x8 + data->port_irq); + data->prev_irq3_4_handler = _dos_getvect(0x8 + data->port_irq); + _dos_setvect(0x8 + data->port_irq, data:>irq3_4_isr); + } +#endif + + data->prev_int2f_handler = 0; #if USE_WIN386 data->prev_int2f_handler = _dos_getvect(0x2f); _dos_setvect(0x2f, data:>int2f_isr); @@ -359,16 +538,27 @@ static bool check_if_driver_uninstallable(LPTSRDATA data) // Compare the segment of the installed handler to see if its ours // or someone else's if (FP_SEG(cur_int33_handler) != FP_SEG(data)) { - fprintf(stderr, _(3, 9, "INT33 has been hooked by someone else, cannot safely remove\n")); + fprintf(stderr, _(3, 9, "INT%X has been hooked by someone else, cannot safely remove\n"), 0x33); return false; } +#if USE_SERIAL + if (data->port_irq) { + void (__interrupt __far *cur_irq3_4_handler)() = _dos_getvect(0x8 + data->port_irq); + + if (FP_SEG(cur_irq3_4_handler) != FP_SEG(data)) { + fprintf(stderr, _(3, 9, "INT%X has been hooked by someone else, cannot safely remove\n"), 0x8 + data->port_irq); + return false; + } + } +#endif + #if USE_WIN386 { void (__interrupt __far *cur_int2f_handler)() = _dos_getvect(0x2f); if (FP_SEG(cur_int2f_handler) != FP_SEG(data)) { - fprintf(stderr, _(3, 10, "INT2F has been hooked by someone else, cannot safely remove\n")); + fprintf(stderr, _(3, 9, "INT%X has been hooked by someone else, cannot safely remove\n"), 0x2F); return false; } } @@ -379,12 +569,28 @@ static bool check_if_driver_uninstallable(LPTSRDATA data) static int unconfigure_driver(LPTSRDATA data) { + if (data->port == 0) { + // PS/2 mode + + // Turn off PS2 BIOS, and remove callback + ps2m_enable(false); + ps2m_set_callback(0); + #if USE_INTEGRATION - set_integration(data, false); + set_integration(data, false, false); #endif + } else { +#if USE_SERIAL + // Serial mode + uint16_t iobase = data->port_io; - ps2m_enable(false); - ps2m_set_callback(0); + // Disable interrupts, and clear RTS/DTS, which should completely power down mouse + serial_configure_interrupts(iobase, 0); + serial_set_modem_control(iobase, 0); + + // TODO Should I mask the serial IRQ on the PIC too? +#endif + } return 0; } @@ -393,6 +599,12 @@ static int uninstall_driver(LPTSRDATA data) { _dos_setvect(0x33, data->prev_int33_handler); +#if USE_SERIAL + if (data->port_irq) { + _dos_setvect(0x8 + data->port_irq, data->prev_irq3_4_handler); + } +#endif + #if USE_WIN386 _dos_setvect(0x2f, data->prev_int2f_handler); #endif @@ -409,7 +621,7 @@ static int uninstall_driver(LPTSRDATA data) static int driver_reset(void) { printf(_(1, 19, "Reset mouse driver\n")); - return int33_reset() == 0xFFFF; + return int33_reset() == INT33_MOUSE_FOUND; } static int driver_not_found(void) @@ -476,6 +688,21 @@ static bool is_false(const char *s) || stricmp(s, "0") == 0; } +static int parse_port(const char *s) +{ + if (stricmp(s, "ps2") == 0) { + return 0; +#if USE_SERIAL + } else if (strnicmp(s, "com", 3) == 0 && s[4] == '\0' + && s[3] >= '0' && s[3] <= '9') { + // "COMn" string + return s[3] - '0'; +#endif + } else { + return -1; + } +} + int main(int argc, const char *argv[]) { LPTSRDATA data = get_tsr_data(true); @@ -487,6 +714,9 @@ int main(int argc, const char *argv[]) if (argi >= argc || stricmp(argv[argi], "install") == 0) { bool high = true; + int portidx = 0, port; + // Default search order (PS2, COM1, COM2, COM3...) + int ports[1+MAX_PORTS] = {-1}; argi++; for (; argi < argc; argi++) { @@ -494,11 +724,25 @@ int main(int argc, const char *argv[]) high = false; } else if (stricmp(argv[argi], "high") == 0) { high = true; + } else if ((port = parse_port(argv[argi])) >= 0) { + if (portidx > MAX_PORTS) return invalid_arg(argv[argi]); + if (port < 0 || port > MAX_PORTS) return invalid_arg(argv[argi]); + ports[portidx++] = port; } else { return invalid_arg(argv[argi]); } } + if (!portidx) { + // User has not specified custom search order, so init with default + const unsigned num_com_ports = MIN(serial_num_ports(), MAX_PORTS); + if (ps2m_installed()) ports[portidx++] = 0; // 1st. PS2 + // 2nd. COMn in order + for (port = 1; port <= num_com_ports; port++) { + ports[portidx++] = port; + } + } + if (data) { printf(_(1, 21, "VBMouse already installed\n")); print_help(); @@ -512,7 +756,7 @@ int main(int argc, const char *argv[]) } else { deallocate_environment(_psp); } - err = configure_driver(data); + err = configure_driver(data, portidx, ports); if (err) { if (high) cancel_reallocation(FP_SEG(data)); return EXIT_FAILURE; @@ -588,7 +832,7 @@ int main(int argc, const char *argv[]) if (is_false(argv[argi])) enable = false; } - return set_integration(data, enable); + return set_integration(data, enable, true); } else if (stricmp(argv[argi], "hostcur") == 0) { bool enable = true; diff --git a/nls/vbmouse.en b/nls/vbmouse.en index 3febaba..7272822 100644 --- a/nls/vbmouse.en +++ b/nls/vbmouse.en @@ -16,7 +16,7 @@ 0.9: integ <ON|OFF> Enable/disable VirtualBox integration. 0.10: hostcur <ON|OFF> Enable/disable mouse cursor rendering in the host. 0.11: reset Reset mouse driver. -1.0:Wheel mouse found and enabled\n +1.0:Found PS/2 mouse with %u buttons, %u wheels\n 1.1:Setting wheel support to %s\n 1.2:enabled 1.3:disabled @@ -38,6 +38,7 @@ 1.19:Reset mouse driver\n 1.20:\nVBMouse %x.%x (like MSMOUSE %x.%x)\n 1.21:VBMouse already installed\n +1.22:Found serial mouse on COM%u with %u buttons, %u wheels\n 3.0:Could not find PS/2 wheel mouse\n 3.1:Wheel not detected or support not enabled\n 3.2:Unknown key '%s'\n @@ -47,8 +48,9 @@ 3.6:Could not detect VMware, err=%ld\n 3.7:VMware absolute pointer error, err=0x%lx\n 3.8:Cannot init PS/2 mouse BIOS, err=%d\n -3.9:INT33 has been hooked by someone else, cannot safely remove\n -3.10:INT2F has been hooked by someone else, cannot safely remove\n +3.9:INT%X has been hooked by someone else, cannot safely remove\n 3.11:Driver data not found (driver not installed?)\n 3.12:Invalid argument '%s'\n 3.13:Argument required for '%s'\n +3.14:No mouse found\n +3.15:Cannot find mouse in COM%u, err=%d\n @@ -0,0 +1,59 @@ +/* + * VBMouse - Programmable Interrupt Controller routines + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PIC_H +#define PIC_H + +#include <conio.h> + +// TODO Currently do not support secondary PIC + +enum { + PIC1_CMD_IOPORT = 0x20, + PIC1_DATA_IOPORT = PIC1_CMD_IOPORT+1, + PIC2_CMD_IOPORT = 0xA0, + PIC2_DATA_IOPORT = PIC2_CMD_IOPORT+1, +}; + +enum PIC_CMDS { + /** End-Of-Interrupt command. */ + PIC_CMD_EOI = 0x20 +}; + +static void pic_mask_irq(unsigned irq) +{ + uint8_t mask = inp(PIC1_DATA_IOPORT); + mask |= (1 << irq); + outp(PIC1_DATA_IOPORT, mask); +} + +static void pic_unmask_irq(unsigned irq) +{ + uint8_t mask = inp(PIC1_DATA_IOPORT); + mask &= ~(1 << irq); + outp(PIC1_DATA_IOPORT, mask); +} + +static void pic_eoi_irq(unsigned irq) +{ + (void) irq; + outp(PIC1_CMD_IOPORT, PIC_CMD_EOI); +} + +#endif // PIC_H @@ -2,6 +2,7 @@ #define SERIAL_H #include <stdint.h> +#include <conio.h> #include "bda.h" @@ -12,6 +13,7 @@ enum SERIAL_UART_REGS { SERIAL_DIVISOR_LO = 0, /** (only with DLAB enabled) MSB of divisor/baud rate */ SERIAL_DIVISOR_HI = 1, + SERIAL_DIVISOR = SERIAL_DIVISOR_LO, /** Interrupt Enable Register(read/write) */ SERIAL_IER = 1, /** Fifo Control Register (write-only) */ @@ -45,6 +47,19 @@ enum SERIAL_DIVISOR { SERIAL_DIVISOR_1200 = 96 }; +enum SERIAL_IER { + /** No interrupts */ + SERIAL_IER_NONE = 0, + /** Enable Receiver Buffer Full Interrupt */ + SERIAL_IER_ERBFI = 1 << 0, + /** Enable Transmitter Buffer Empty Interrupt */ + SERIAL_IER_ETBEI = 1 << 1, + /** Enable Line Status Interrupt */ + SERIAL_IER_ELSI = 1 << 2, + /** Enable Delta Status Signals Interrupt */ + SERIAL_IER_EDSSI = 1 << 3, +}; + enum SERIAL_FCR { SERIAL_FCR_RX_TRIGER_1 = 0 << 6, SERIAL_FCR_RX_TRIGER_4 = 1 << 6, @@ -54,6 +69,31 @@ enum SERIAL_FCR { SERIAL_FCR_TX_RESET = 1 << 2, SERIAL_FCR_RX_RESET = 1 << 1, SERIAL_FCR_FIFO_ENABLE = 1 << 0, + SERIAL_FCR_FIFO_RESET = SERIAL_FCR_TX_RESET | SERIAL_FCR_RX_RESET, + SERIAL_FCR_FIFO_DISABLE = 0 +}; + +enum SERIAL_IIR { + /** Mask for retrieving the interrupt that occured. See enum SERIAL_IID. */ + SERIAL_IIR_ID = 0xF, + + SERIAL_IIR_FIFO1 = 1 << 6, + SERIAL_IIR_FIFO2 = 1 << 7, +}; + +enum SERIAL_IID { + /** No interrupt. */ + SERIAL_IID_NONE = 1, + /** Highest priority: line error. OE, PE, etc. Serviced by reading LSR. */ + SERIAL_IID_ERROR = 6, + /** Second highest priority: data received or trigger reached. Serviced by reading data. */ + SERIAL_IID_DATA = 4, + /** Second highest priority: stale data in FIFO. Serviced by reading data. */ + SERIAL_IID_STALE = 0xC, + /** Third priority: transmitter buffer has room. Serviced by reading this reg/writing. */ + SERIAL_IID_THRE = 2, + /** Fourth priority: MSR change. Serviced by reading MSR. */ + SERIAL_IID_MODEM = 0, }; enum SERIAL_LCR { @@ -115,14 +155,14 @@ enum SERIAL_MSR { struct serial_config { - uint8_t divisor_lo, divisor_hi; + uint16_t divisor; uint8_t ier, lcr, mcr; }; static unsigned serial_num_ports() { - uint8_t equipment = bda_get_byte(0x11); - uint8_t numports = (equipment & 0xE) >> 1; + uint16_t equipment = bda_get_equipment(); + uint8_t numports = (equipment & 0xE00) >> 9; return numports; } @@ -131,11 +171,31 @@ static uint16_t serial_get_iobase(unsigned port) uint16_t bdaport = bda_get_word((port - 1) * 2); if (bdaport > 10) { return bdaport; - } else if (port == 1) { - // Even if BIOS says no, hardcode the basic COM1 port at least. + } + + // Even if BIOS says no, hardcode basic COM1/COM2 at least. + switch (port) { + case 1: return 0x3F8; + case 2: + return 0x2F8; + default: + return 0; + } +} + +static uint8_t serial_get_irq(unsigned port) +{ + switch (port) { + case 1: + case 3: + return 4; + case 2: + case 4: + return 3; + default: + return 0; } - return 0; } static void serial_save_config(unsigned iobase, struct serial_config *config) @@ -145,8 +205,7 @@ static void serial_save_config(unsigned iobase, struct serial_config *config) config->mcr = inp(iobase + SERIAL_MCR); // Set DLAB so that we can read divisor outp(iobase + SERIAL_LCR, config->lcr | SERIAL_LCR_DLAB); - config->divisor_lo = inp(iobase + SERIAL_DIVISOR_LO); - config->divisor_hi = inp(iobase + SERIAL_DIVISOR_HI); + config->divisor = inpw(iobase + SERIAL_DIVISOR); // Restore DLAB to whatever it was outp(iobase + SERIAL_LCR, config->lcr); } @@ -154,22 +213,34 @@ static void serial_save_config(unsigned iobase, struct serial_config *config) static void serial_restore_config(unsigned iobase, const struct serial_config *config) { outp(iobase + SERIAL_LCR, config->lcr | SERIAL_LCR_DLAB); - outp(iobase + SERIAL_DIVISOR_LO, config->divisor_lo); - outp(iobase + SERIAL_DIVISOR_HI, config->divisor_hi); + outpw(iobase + SERIAL_DIVISOR, config->divisor); outp(iobase + SERIAL_LCR, config->lcr); outp(iobase + SERIAL_MCR, config->mcr); outp(iobase + SERIAL_IER, config->ier); } -static void serial_configure(unsigned iobase, uint16_t divisor, uint8_t lcr) +/// Configure line characteristics (speed, databytes, etc.) +/// Disables interrupts +static void serial_configure_line(unsigned iobase, uint16_t divisor, uint8_t lcr) { - outp(iobase + SERIAL_IER, 0); // Disable all interrupts outp(iobase + SERIAL_LCR, SERIAL_LCR_DLAB); // Enable DLAB and clear all other flags - outp(iobase + SERIAL_DIVISOR_LO, divisor); - outp(iobase + SERIAL_DIVISOR_HI, divisor >> 8); + outpw(iobase + SERIAL_DIVISOR, divisor); outp(iobase + SERIAL_LCR, lcr); // This also disables DLAB - // Enable and reset both FIFOs - outp(iobase + SERIAL_FCR, SERIAL_FCR_RX_TRIGER_14|SERIAL_FCR_FIFO_ENABLE|SERIAL_FCR_RX_RESET|SERIAL_FCR_TX_RESET); +} + +/// Configures when exceptions are going to be raised +/// it also clears all pending interrupts +/// and enables/disables the OUT2 gator here, which I'm not sure if is conceptually clean +static void serial_configure_interrupts(unsigned iobase, uint8_t ier) +{ + uint8_t mcr = inp(iobase + SERIAL_MCR); + outp(iobase + SERIAL_IER, ier); + if (ier) { + mcr |= SERIAL_MCR_OUT2; // Actually enables IRQ line for this UART + } else { + mcr &= ~SERIAL_MCR_OUT2; + } + outp(iobase + SERIAL_MCR, mcr); } static inline void serial_set_modem_control(unsigned iobase, uint8_t mcr) @@ -177,9 +248,14 @@ static inline void serial_set_modem_control(unsigned iobase, uint8_t mcr) outp(iobase + SERIAL_MCR, mcr); } -static inline void serial_set_modem_status(unsigned iobase, uint8_t msr) +static inline void serial_set_fifo_control(unsigned iobase, uint8_t fcr) { - outp(iobase + SERIAL_MSR, msr); + outp(iobase + SERIAL_FCR, fcr); +} + +static inline uint8_t serial_read_pending_interrupt(unsigned iobase) +{ + return inp(iobase + SERIAL_IIR) & SERIAL_IIR_ID; } static inline uint8_t serial_get_line_status(unsigned iobase) @@ -187,6 +263,11 @@ static inline uint8_t serial_get_line_status(unsigned iobase) return inp(iobase + SERIAL_LSR); } +static inline uint8_t serial_get_modem_status(unsigned iobase) +{ + return inp(iobase + SERIAL_MSR); +} + static inline bool serial_data_ready(unsigned iobase) { return serial_get_line_status(iobase) & SERIAL_LSR_DR; diff --git a/sermouse.h b/sermouse.h new file mode 100644 index 0000000..a120f26 --- /dev/null +++ b/sermouse.h @@ -0,0 +1,38 @@ +/* + * VBMouse - Constants for serial mouse protocols + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef SERMOUSE_H +#define SERMOUSE_H + +enum { + SERMOUSE_MS_BUTTON_LEFT = 1 << 5, + SERMOUSE_MS_BUTTON_RIGHT = 1 << 4, + + SERMOUSE_MS_X_LO_MASK = 0x3F, + SERMOUSE_MS_Y_LO_MASK = 0x3F, + + SERMOUSE_MS_X_HI_MASK = 0x3, + SERMOUSE_MS_X_HI_SHIFT = 6, + + SERMOUSE_MS_Y_HI_MASK = 0xC, + SERMOUSE_MS_Y_HI_SHIFT = 4, + +}; + +#endif // SERMOUSE_H @@ -2,6 +2,6 @@ #define VERSION_H #define VERSION_MAJOR 0 -#define VERSION_MINOR 0x70 +#define VERSION_MINOR 0x71 #endif // VERSION_H |
