Initial commit.
This commit is contained in:
commit
f666825417
19 changed files with 4261 additions and 0 deletions
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Git LFS - binary files
|
||||||
|
*.bmp filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.DRV filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
||||||
|
# Force LF line endings on source files
|
||||||
|
*.c text eol=lf
|
||||||
|
*.h text eol=lf
|
||||||
|
*.rc text eol=lf
|
||||||
|
*.def text eol=lf
|
||||||
|
makefile text eol=lf
|
||||||
|
*.TXT text eol=lf
|
||||||
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# MSVC 1.52 build artifacts
|
||||||
|
*.obj
|
||||||
|
*.res
|
||||||
|
*.map
|
||||||
|
*.lib
|
||||||
|
*.pdb
|
||||||
|
*.sbr
|
||||||
|
*.bsc
|
||||||
|
*.pch
|
||||||
|
*.ilk
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
*.vbx
|
||||||
|
*.drv
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
# Backup files
|
||||||
|
*.bak
|
||||||
|
*~
|
||||||
BIN
drivers/CYBERCOM.DRV
(Stored with Git LFS)
Normal file
BIN
drivers/CYBERCOM.DRV
(Stored with Git LFS)
Normal file
Binary file not shown.
101
drivers/CYBERCOM.TXT
Normal file
101
drivers/CYBERCOM.TXT
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
CyberCom V1.1.0.0P
|
||||||
|
|
||||||
|
CYBERCOM.DRV 9264 11-26-93 1:06p
|
||||||
|
|
||||||
|
The High Speed Serial Communications Driver for Windows 3.1
|
||||||
|
Designed for 386 (and above) systems using the 16550 serial port
|
||||||
|
chip. (C) CyberSoft Corp 1993
|
||||||
|
|
||||||
|
Requires Windows 3.1, Enhanced Mode, a 16550 UART
|
||||||
|
|
||||||
|
|
||||||
|
INTRODUCTION.
|
||||||
|
-------------
|
||||||
|
CyberCom is a direct replacement for the standard Windows
|
||||||
|
Communications Driver (COMM.DRV).
|
||||||
|
|
||||||
|
* Transfer at up to 115,200 KB with a 16550 serial port chip
|
||||||
|
* Great for V.FAST and Voice modems that require 57,600 Kb
|
||||||
|
* More reliable Transfer with less overhead on your system - Better
|
||||||
|
background operation.
|
||||||
|
* Fewer (if any) over/under runs.
|
||||||
|
|
||||||
|
|
||||||
|
HOW DOES IT WORK?
|
||||||
|
-----------------
|
||||||
|
Don't worry if the following sounds too complicated - just skip over
|
||||||
|
it and move on to the Installation...
|
||||||
|
|
||||||
|
* CyberCom enables the FIFO buffer on the 16550 to be enabled for both
|
||||||
|
receive AND transmit - COMM.DRV only enables the FIFO for receive.
|
||||||
|
|
||||||
|
* The 'interrupt trigger level' has been set to 8 for both transmit and
|
||||||
|
receive. This gives your applications a lot more time to process
|
||||||
|
incoming information. COMM.DRV sets the trigger level to 14 which
|
||||||
|
means that your application only has 2 characters in which read from
|
||||||
|
the FIFO buffer.
|
||||||
|
|
||||||
|
What this means is that your communications applications will get far
|
||||||
|
fewer (if any) 'under runs' or 'over runs' when sending or receiving.
|
||||||
|
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
------------
|
||||||
|
1. Copy CYBERCOM.DRV into your Windows\System directory.
|
||||||
|
|
||||||
|
Edit the Windows\SYSTEM.INI file and change the following line:
|
||||||
|
|
||||||
|
From comm.drv=comm.drv
|
||||||
|
To comm.drv=cybercom.drv
|
||||||
|
|
||||||
|
2. If you previously have not taken advantage of the 16550 installed
|
||||||
|
in your computer then ensure that the Windows\SYSTEM.INI file has the
|
||||||
|
following information:
|
||||||
|
|
||||||
|
[386Enh]
|
||||||
|
COMnFIFO=1
|
||||||
|
|
||||||
|
where n is the number of the COM port.
|
||||||
|
|
||||||
|
if your 16550 is installed on, say, COM 1 then
|
||||||
|
|
||||||
|
[386Enh]
|
||||||
|
COM1FIFO=1
|
||||||
|
|
||||||
|
3. Start or Restart windows.
|
||||||
|
|
||||||
|
|
||||||
|
LICENCE
|
||||||
|
-------
|
||||||
|
CyberCom is provided free for non-commercial use.
|
||||||
|
|
||||||
|
You may not distribute CyberCom for profit. If you wish to include
|
||||||
|
CyberCom with your applications then contact CyberSoft at the address
|
||||||
|
below for details about royalty-free licences.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
For further information, upgrades, problems etc please contact us at:
|
||||||
|
|
||||||
|
Attention: Douglas I. Scadlock
|
||||||
|
CyberSoft Corporation Pty Ltd,
|
||||||
|
PO BOX 407 Vaucluse,
|
||||||
|
New South Wales
|
||||||
|
2030 Australia
|
||||||
|
Phone +61 2 805 1077
|
||||||
|
Fax +61 2 805 0897
|
||||||
|
CIS 100033,1723
|
||||||
|
|
||||||
|
|
||||||
|
CAVEAT
|
||||||
|
------
|
||||||
|
THE INFORMATION AND COMMUNICATIONS DRIVER PROVIDED HEREUNDER
|
||||||
|
(COLLECTIVELY REFERRED TO AS "SOFTWARE") IS PROVIDED AS IS WITHOUT
|
||||||
|
WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||||
|
LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
(C) CyberSoft Corp 1993
|
||||||
|
(C) Microsoft Corp
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
Sydney 2000 - Home of the Millennium Games
|
||||||
1302
drv/commdrv.c
Normal file
1302
drv/commdrv.c
Normal file
File diff suppressed because it is too large
Load diff
27
drv/commdrv.def
Normal file
27
drv/commdrv.def
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
LIBRARY COMM
|
||||||
|
DESCRIPTION 'High-Speed Serial Communications Driver'
|
||||||
|
EXETYPE WINDOWS
|
||||||
|
CODE PRELOAD FIXED
|
||||||
|
DATA PRELOAD FIXED SINGLE
|
||||||
|
HEAPSIZE 1024
|
||||||
|
EXPORTS
|
||||||
|
INICOM @1
|
||||||
|
SETCOM @2
|
||||||
|
SETQUE @3
|
||||||
|
RECCOM @4
|
||||||
|
SNDCOM @5
|
||||||
|
CTX @6
|
||||||
|
TRMCOM @7
|
||||||
|
STACOM @8
|
||||||
|
CEXTFCN @9
|
||||||
|
CFLUSH @10
|
||||||
|
CEVT @11
|
||||||
|
CEVTGET @12
|
||||||
|
CSETBRK @13
|
||||||
|
CCLRBRK @14
|
||||||
|
GETDCB @15
|
||||||
|
SUSPENDOPENCOMMPORTS @17
|
||||||
|
REACTIVATEOPENCOMMPORTS @18
|
||||||
|
COMMWRITESTRING @19
|
||||||
|
READCOMMSTRING @20
|
||||||
|
ENABLENOTIFICATION @100
|
||||||
373
drv/commdrv.h
Normal file
373
drv/commdrv.h
Normal file
|
|
@ -0,0 +1,373 @@
|
||||||
|
// commdrv.h - High-speed COMM.DRV replacement
|
||||||
|
//
|
||||||
|
// Types, UART register definitions, port state structure, and prototypes.
|
||||||
|
// Drop-in replacement for Windows 3.1 stock COMM.DRV with proper 16550
|
||||||
|
// FIFO management for reliable operation at 57600 and 115200 baud.
|
||||||
|
|
||||||
|
#ifndef COMMDRV_H
|
||||||
|
#define COMMDRV_H
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// stdint types for MSVC 1.52 (no stdint.h available)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#ifndef _STDINT_DEFINED
|
||||||
|
typedef short int16_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef long int32_t;
|
||||||
|
typedef unsigned long uint32_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef signed char int8_t;
|
||||||
|
#define _STDINT_DEFINED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE 1
|
||||||
|
#endif
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Maximum ports supported
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define MAX_PORTS 4
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Default buffer sizes
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define DEFAULT_RX_SIZE 4096
|
||||||
|
#define DEFAULT_TX_SIZE 4096
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// UART register offsets from base address
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define UART_RBR 0 // Receive Buffer Register (read, DLAB=0)
|
||||||
|
#define UART_THR 0 // Transmit Holding Register (write, DLAB=0)
|
||||||
|
#define UART_DLL 0 // Divisor Latch Low (DLAB=1)
|
||||||
|
#define UART_IER 1 // Interrupt Enable Register (DLAB=0)
|
||||||
|
#define UART_DLM 1 // Divisor Latch High (DLAB=1)
|
||||||
|
#define UART_IIR 2 // Interrupt Identification Register (read)
|
||||||
|
#define UART_FCR 2 // FIFO Control Register (write)
|
||||||
|
#define UART_LCR 3 // Line Control Register
|
||||||
|
#define UART_MCR 4 // Modem Control Register
|
||||||
|
#define UART_LSR 5 // Line Status Register
|
||||||
|
#define UART_MSR 6 // Modem Status Register
|
||||||
|
#define UART_SCR 7 // Scratch Register
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// IER bits - Interrupt Enable Register
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define IER_RDA 0x01 // Received Data Available
|
||||||
|
#define IER_THRE 0x02 // Transmitter Holding Register Empty
|
||||||
|
#define IER_LSI 0x04 // Line Status Interrupt
|
||||||
|
#define IER_MSI 0x08 // Modem Status Interrupt
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// IIR bits - Interrupt Identification Register
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define IIR_PENDING 0x01 // 0=interrupt pending, 1=no interrupt
|
||||||
|
#define IIR_ID_MASK 0x0E // Interrupt ID mask
|
||||||
|
#define IIR_MSR 0x00 // Modem Status change
|
||||||
|
#define IIR_THRE 0x02 // Transmitter Holding Register Empty
|
||||||
|
#define IIR_RDA 0x04 // Received Data Available
|
||||||
|
#define IIR_LSR 0x06 // Line Status (error/break)
|
||||||
|
#define IIR_TIMEOUT 0x0C // Character Timeout (16550 FIFO)
|
||||||
|
#define IIR_FIFO_MASK 0xC0 // FIFO enabled bits
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// FCR bits - FIFO Control Register (write-only)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define FCR_ENABLE 0x01 // Enable FIFOs
|
||||||
|
#define FCR_RX_RESET 0x02 // Reset RX FIFO
|
||||||
|
#define FCR_TX_RESET 0x04 // Reset TX FIFO
|
||||||
|
#define FCR_DMA_MODE 0x08 // DMA mode select
|
||||||
|
#define FCR_TRIG_1 0x00 // RX trigger level: 1 byte
|
||||||
|
#define FCR_TRIG_4 0x40 // RX trigger level: 4 bytes
|
||||||
|
#define FCR_TRIG_8 0x80 // RX trigger level: 8 bytes
|
||||||
|
#define FCR_TRIG_14 0xC0 // RX trigger level: 14 bytes
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// LCR bits - Line Control Register
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define LCR_WLS_MASK 0x03 // Word Length Select mask
|
||||||
|
#define LCR_WLS_5 0x00 // 5 data bits
|
||||||
|
#define LCR_WLS_6 0x01 // 6 data bits
|
||||||
|
#define LCR_WLS_7 0x02 // 7 data bits
|
||||||
|
#define LCR_WLS_8 0x03 // 8 data bits
|
||||||
|
#define LCR_STB 0x04 // Number of Stop Bits (0=1, 1=2)
|
||||||
|
#define LCR_PEN 0x08 // Parity Enable
|
||||||
|
#define LCR_EPS 0x10 // Even Parity Select
|
||||||
|
#define LCR_SPAR 0x20 // Stick Parity
|
||||||
|
#define LCR_SBRK 0x40 // Set Break
|
||||||
|
#define LCR_DLAB 0x80 // Divisor Latch Access Bit
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// MCR bits - Modem Control Register
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define MCR_DTR 0x01 // Data Terminal Ready
|
||||||
|
#define MCR_RTS 0x02 // Request To Send
|
||||||
|
#define MCR_OUT1 0x04 // Output 1
|
||||||
|
#define MCR_OUT2 0x08 // Output 2 (master interrupt enable)
|
||||||
|
#define MCR_LOOP 0x10 // Loopback mode
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// LSR bits - Line Status Register
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define LSR_DR 0x01 // Data Ready
|
||||||
|
#define LSR_OE 0x02 // Overrun Error
|
||||||
|
#define LSR_PE 0x04 // Parity Error
|
||||||
|
#define LSR_FE 0x08 // Framing Error
|
||||||
|
#define LSR_BI 0x10 // Break Interrupt
|
||||||
|
#define LSR_THRE 0x20 // Transmitter Holding Register Empty
|
||||||
|
#define LSR_TEMT 0x40 // Transmitter Empty (shift register too)
|
||||||
|
#define LSR_FIFO 0x80 // Error in RX FIFO (16550)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// MSR bits - Modem Status Register
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define MSR_DCTS 0x01 // Delta CTS
|
||||||
|
#define MSR_DDSR 0x02 // Delta DSR
|
||||||
|
#define MSR_TERI 0x04 // Trailing Edge Ring Indicator
|
||||||
|
#define MSR_DDCD 0x08 // Delta DCD
|
||||||
|
#define MSR_CTS 0x10 // Clear To Send
|
||||||
|
#define MSR_DSR 0x20 // Data Set Ready
|
||||||
|
#define MSR_RI 0x40 // Ring Indicator
|
||||||
|
#define MSR_DCD 0x80 // Data Carrier Detect
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// PIC constants
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define PIC_CMD 0x20 // PIC command port
|
||||||
|
#define PIC_DATA 0x21 // PIC data (mask) port
|
||||||
|
#define PIC_EOI 0x20 // End of Interrupt command
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Standard port base addresses and IRQs
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define COM1_BASE 0x03F8
|
||||||
|
#define COM2_BASE 0x02F8
|
||||||
|
#define COM3_BASE 0x03E8
|
||||||
|
#define COM4_BASE 0x02E8
|
||||||
|
|
||||||
|
#define COM1_IRQ 4
|
||||||
|
#define COM2_IRQ 3
|
||||||
|
#define COM3_IRQ 4
|
||||||
|
#define COM4_IRQ 3
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Baud rate divisor (115200 / baud)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define BAUD_DIVISOR_BASE 115200UL
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// CBR_* baud rate index constants
|
||||||
|
//
|
||||||
|
// The 16-bit DCB BaudRate field is a UINT (16 bits). Since 115200
|
||||||
|
// exceeds 65535, high baud rates use CBR_* index constants instead
|
||||||
|
// of raw values. Values >= 0xFF00 are indices, not raw rates.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#ifndef CBR_110
|
||||||
|
#define CBR_110 0xFF10
|
||||||
|
#define CBR_300 0xFF11
|
||||||
|
#define CBR_600 0xFF12
|
||||||
|
#define CBR_1200 0xFF13
|
||||||
|
#define CBR_2400 0xFF14
|
||||||
|
#define CBR_4800 0xFF15
|
||||||
|
#define CBR_9600 0xFF16
|
||||||
|
#define CBR_14400 0xFF17
|
||||||
|
#define CBR_19200 0xFF18
|
||||||
|
#define CBR_38400 0xFF1B
|
||||||
|
#define CBR_56000 0xFF1F
|
||||||
|
#define CBR_115200 0xFF24
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// 16550 FIFO depth
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define FIFO_DEPTH 16
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Comm error flags (CE_* -- matches Windows SDK definitions)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define CE_RXOVER 0x0001 // Receive Queue overflow
|
||||||
|
#define CE_OVERRUN 0x0002 // Hardware overrun
|
||||||
|
#define CE_RXPARITY 0x0004 // Parity error
|
||||||
|
#define CE_FRAME 0x0008 // Framing error
|
||||||
|
#define CE_BREAK 0x0010 // Break detected
|
||||||
|
#define CE_TXFULL 0x0020 // TX Queue is full
|
||||||
|
#define CE_MODE 0x8000 // Requested mode unsupported
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Comm event flags (EV_* -- matches Windows SDK definitions)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define EV_RXCHAR 0x0001 // Any character received
|
||||||
|
#define EV_RXFLAG 0x0002 // Event character received
|
||||||
|
#define EV_TXEMPTY 0x0004 // TX buffer empty
|
||||||
|
#define EV_CTS 0x0008 // CTS changed
|
||||||
|
#define EV_DSR 0x0010 // DSR changed
|
||||||
|
#define EV_RLSD 0x0020 // DCD/RLSD changed
|
||||||
|
#define EV_BREAK 0x0040 // Break received
|
||||||
|
#define EV_ERR 0x0080 // Line status error
|
||||||
|
#define EV_RING 0x0100 // Ring indicator
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// CN_* notification codes (lParam for WM_COMMNOTIFY)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#ifndef CN_RECEIVE
|
||||||
|
#define CN_RECEIVE 0x0001
|
||||||
|
#define CN_TRANSMIT 0x0002
|
||||||
|
#define CN_EVENT 0x0004
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// WM_COMMNOTIFY message
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#ifndef WM_COMMNOTIFY
|
||||||
|
#define WM_COMMNOTIFY 0x0044
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Handshaking modes
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define HS_NONE 0
|
||||||
|
#define HS_XONXOFF 1
|
||||||
|
#define HS_RTSCTS 2
|
||||||
|
#define HS_BOTH 3
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// EscapeCommFunction codes (matches Windows SDK)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#ifndef SETXON
|
||||||
|
#define SETXON 1
|
||||||
|
#define SETXOFF 2
|
||||||
|
#define SETRTS 3
|
||||||
|
#define CLRRTS 4
|
||||||
|
#define SETDTR 5
|
||||||
|
#define CLRDTR 6
|
||||||
|
#define RESETDEV 7
|
||||||
|
#define SETBREAK 8
|
||||||
|
#define CLRBREAK 9
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Flush queue selectors
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define FLUSH_RX 0
|
||||||
|
#define FLUSH_TX 1
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Port state structure
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
typedef struct {
|
||||||
|
uint16_t baseAddr; // UART base I/O address
|
||||||
|
uint8_t irq; // IRQ number (3 or 4)
|
||||||
|
int16_t commId; // Port ID (0=COM1, 1=COM2, ...)
|
||||||
|
uint8_t isOpen; // Port open flag
|
||||||
|
uint8_t is16550; // 16550 FIFO detected
|
||||||
|
uint8_t fifoEnabled; // FIFO enabled (COMnFIFO setting)
|
||||||
|
uint8_t fifoTrigger; // RX FIFO trigger FCR bits (FCR_TRIG_*)
|
||||||
|
|
||||||
|
// Ring buffers (GlobalAlloc'd)
|
||||||
|
uint8_t FAR *rxBuf; // Receive ring buffer
|
||||||
|
uint16_t rxSize; // Buffer size
|
||||||
|
uint16_t rxHead; // Write position (ISR writes)
|
||||||
|
uint16_t rxTail; // Read position (app reads)
|
||||||
|
uint16_t rxCount; // Bytes in buffer
|
||||||
|
|
||||||
|
uint8_t FAR *txBuf; // Transmit ring buffer
|
||||||
|
uint16_t txSize; // Buffer size
|
||||||
|
uint16_t txHead; // Write position (app writes)
|
||||||
|
uint16_t txTail; // Read position (ISR reads)
|
||||||
|
uint16_t txCount; // Bytes in buffer
|
||||||
|
|
||||||
|
// DCB shadow
|
||||||
|
uint16_t baudRate; // Current baud rate
|
||||||
|
uint8_t byteSize; // Data bits (5-8)
|
||||||
|
uint8_t parity; // Parity mode
|
||||||
|
uint8_t stopBits; // Stop bits
|
||||||
|
|
||||||
|
// Flow control state
|
||||||
|
uint8_t hsMode; // Handshaking mode
|
||||||
|
uint8_t txStopped; // TX halted by flow control
|
||||||
|
uint8_t rxStopped; // We sent XOFF / dropped RTS
|
||||||
|
uint8_t xonChar; // XON character (default 0x11)
|
||||||
|
uint8_t xoffChar; // XOFF character (default 0x13)
|
||||||
|
uint16_t xoffLim; // Send XOFF when rxCount > rxSize - xoffLim
|
||||||
|
uint16_t xonLim; // Send XON when rxCount < xonLim
|
||||||
|
|
||||||
|
// Modem control shadow
|
||||||
|
uint8_t dtrState; // DTR line state
|
||||||
|
uint8_t rtsState; // RTS line state (when not flow-controlled)
|
||||||
|
|
||||||
|
// Error accumulator
|
||||||
|
uint16_t errorFlags; // CE_* error flags (sticky until read)
|
||||||
|
|
||||||
|
// Event notification
|
||||||
|
uint16_t evtMask; // Event enable mask
|
||||||
|
uint16_t evtWord; // Accumulated events
|
||||||
|
HWND hwndNotify; // Window for WM_COMMNOTIFY
|
||||||
|
int16_t rxNotifyThresh; // CN_RECEIVE threshold (-1=disabled)
|
||||||
|
int16_t txNotifyThresh; // CN_TRANSMIT threshold (-1=disabled)
|
||||||
|
|
||||||
|
// ISR state
|
||||||
|
void (FAR *prevIsr)(void); // Previous ISR in chain
|
||||||
|
uint8_t irqMask; // PIC mask bit for this IRQ
|
||||||
|
uint8_t breakState; // Break signal active
|
||||||
|
|
||||||
|
// Priority transmit
|
||||||
|
int16_t txImmediate; // -1=none, else char to send immediately
|
||||||
|
|
||||||
|
// DCB copy for GetCommState
|
||||||
|
DCB dcb; // Full DCB for GETDCB/SETCOM
|
||||||
|
} PortStateT;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Global port state array
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
extern PortStateT ports[MAX_PORTS];
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Exported function prototypes (COMM.DRV API)
|
||||||
|
//
|
||||||
|
// These use the Windows COMM.DRV calling convention: FAR PASCAL
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t FAR PASCAL _export inicom(DCB FAR *dcb, char FAR *rxBuf, int16_t rxSize);
|
||||||
|
int16_t FAR PASCAL _export setcom(DCB FAR *dcb);
|
||||||
|
int16_t FAR PASCAL _export setque(int16_t commId, char FAR *rxBuf, int16_t rxSize, char FAR *txBuf, int16_t txSize);
|
||||||
|
int16_t FAR PASCAL _export reccom(int16_t commId, void FAR *buf, int16_t len);
|
||||||
|
int16_t FAR PASCAL _export sndcom(int16_t commId, void FAR *buf, int16_t len);
|
||||||
|
int16_t FAR PASCAL _export ctx(int16_t commId, int16_t ch);
|
||||||
|
int16_t FAR PASCAL _export trmcom(int16_t commId);
|
||||||
|
int16_t FAR PASCAL _export stacom(int16_t commId, COMSTAT FAR *stat);
|
||||||
|
int32_t FAR PASCAL _export cevt(int16_t commId, int16_t evtMask);
|
||||||
|
uint16_t FAR PASCAL _export cevtget(int16_t commId, int16_t evtMask);
|
||||||
|
int16_t FAR PASCAL _export cextfcn(int16_t commId, int16_t func);
|
||||||
|
int16_t FAR PASCAL _export cflush(int16_t commId, int16_t queue);
|
||||||
|
int16_t FAR PASCAL _export csetbrk(int16_t commId);
|
||||||
|
int16_t FAR PASCAL _export cclrbrk(int16_t commId);
|
||||||
|
int16_t FAR PASCAL _export getdcb(int16_t commId, DCB FAR *dcb);
|
||||||
|
void FAR PASCAL _export suspendOpenCommPorts(void);
|
||||||
|
void FAR PASCAL _export reactivateOpenCommPorts(void);
|
||||||
|
int16_t FAR PASCAL _export commWriteString(int16_t commId, void FAR *buf, int16_t len);
|
||||||
|
int16_t FAR PASCAL _export readCommString(int16_t commId, void FAR *buf, int16_t len);
|
||||||
|
int16_t FAR PASCAL _export enableNotification(int16_t commId, HWND hwnd, int16_t rxThresh, int16_t txThresh);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// ISR prototypes (isr.c)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t hookIsr(PortStateT *port);
|
||||||
|
void unhookIsr(PortStateT *port);
|
||||||
|
void isrDispatch(PortStateT *port);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Internal helpers
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t detect16550(uint16_t baseAddr);
|
||||||
|
void applyBaudRate(PortStateT *port, uint16_t baud);
|
||||||
|
void applyLineParams(PortStateT *port, uint8_t byteSize, uint8_t parity, uint8_t stopBits);
|
||||||
|
void enableFifo(PortStateT *port);
|
||||||
|
void primeTx(PortStateT *port);
|
||||||
|
|
||||||
|
#endif // COMMDRV_H
|
||||||
507
drv/isr.c
Normal file
507
drv/isr.c
Normal file
|
|
@ -0,0 +1,507 @@
|
||||||
|
// isr.c - Interrupt service routines for COMM.DRV replacement
|
||||||
|
//
|
||||||
|
// ISR entry points for IRQ3 (COM2/4) and IRQ4 (COM1/3), DPMI interrupt
|
||||||
|
// vector hooking/unhooking, and interrupt dispatch with 16550 FIFO support.
|
||||||
|
|
||||||
|
#include "commdrv.h"
|
||||||
|
#include <dos.h>
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Prototypes
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void checkFlowRx(PortStateT *port);
|
||||||
|
static void checkNotify(PortStateT *port);
|
||||||
|
static void handleLsr(PortStateT *port, uint8_t lsr);
|
||||||
|
static void handleMsr(PortStateT *port);
|
||||||
|
static void handleRx(PortStateT *port, uint8_t lsr);
|
||||||
|
static void handleTx(PortStateT *port);
|
||||||
|
|
||||||
|
void _far _interrupt isr3(void);
|
||||||
|
void _far _interrupt isr4(void);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Saved previous ISR vectors for IRQ3 and IRQ4
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void (_far _interrupt *prevIsr3)(void) = NULL;
|
||||||
|
static void (_far _interrupt *prevIsr4)(void) = NULL;
|
||||||
|
|
||||||
|
// Track how many ports are using each IRQ so we know when to unhook
|
||||||
|
static int16_t irq3RefCount = 0;
|
||||||
|
static int16_t irq4RefCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// checkFlowRx - Check RX buffer level and assert/deassert flow control
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void checkFlowRx(PortStateT *port)
|
||||||
|
{
|
||||||
|
uint16_t base;
|
||||||
|
|
||||||
|
base = port->baseAddr;
|
||||||
|
|
||||||
|
if (port->hsMode == HS_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we need to stop remote sender
|
||||||
|
if (!port->rxStopped && port->rxCount >= (port->rxSize - port->xoffLim)) {
|
||||||
|
port->rxStopped = TRUE;
|
||||||
|
|
||||||
|
if (port->hsMode == HS_XONXOFF || port->hsMode == HS_BOTH) {
|
||||||
|
// Send XOFF - queue as immediate character
|
||||||
|
port->txImmediate = port->xoffChar;
|
||||||
|
// Enable THRE to get it sent
|
||||||
|
_outp(base + UART_IER, (uint8_t)(_inp(base + UART_IER) | IER_THRE));
|
||||||
|
}
|
||||||
|
if (port->hsMode == HS_RTSCTS || port->hsMode == HS_BOTH) {
|
||||||
|
// Drop RTS
|
||||||
|
_outp(base + UART_MCR, (uint8_t)(_inp(base + UART_MCR) & ~MCR_RTS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we can resume remote sender
|
||||||
|
if (port->rxStopped && port->rxCount <= port->xonLim) {
|
||||||
|
port->rxStopped = FALSE;
|
||||||
|
|
||||||
|
if (port->hsMode == HS_XONXOFF || port->hsMode == HS_BOTH) {
|
||||||
|
// Send XON
|
||||||
|
port->txImmediate = port->xonChar;
|
||||||
|
_outp(base + UART_IER, (uint8_t)(_inp(base + UART_IER) | IER_THRE));
|
||||||
|
}
|
||||||
|
if (port->hsMode == HS_RTSCTS || port->hsMode == HS_BOTH) {
|
||||||
|
// Raise RTS
|
||||||
|
_outp(base + UART_MCR, (uint8_t)(_inp(base + UART_MCR) | MCR_RTS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// checkNotify - Post WM_COMMNOTIFY if threshold conditions met
|
||||||
|
//
|
||||||
|
// Called at end of ISR dispatch, outside the IIR loop.
|
||||||
|
// Uses PostMessage to avoid reentrancy issues.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void checkNotify(PortStateT *port)
|
||||||
|
{
|
||||||
|
uint16_t notifyBits;
|
||||||
|
|
||||||
|
if (!port->hwndNotify) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyBits = 0;
|
||||||
|
|
||||||
|
// CN_RECEIVE: rxCount crossed threshold from below
|
||||||
|
if (port->rxNotifyThresh >= 0 && port->rxCount >= (uint16_t)port->rxNotifyThresh) {
|
||||||
|
notifyBits |= CN_RECEIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CN_TRANSMIT: space available crossed threshold
|
||||||
|
if (port->txNotifyThresh >= 0) {
|
||||||
|
uint16_t txFree = port->txSize - port->txCount;
|
||||||
|
if (txFree >= (uint16_t)port->txNotifyThresh) {
|
||||||
|
notifyBits |= CN_TRANSMIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CN_EVENT: any event bits accumulated
|
||||||
|
if (port->evtWord & port->evtMask) {
|
||||||
|
notifyBits |= CN_EVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notifyBits) {
|
||||||
|
PostMessage(port->hwndNotify, WM_COMMNOTIFY, (WPARAM)port->commId, (LPARAM)notifyBits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// handleLsr - Process line status errors
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void handleLsr(PortStateT *port, uint8_t lsr)
|
||||||
|
{
|
||||||
|
if (lsr & LSR_OE) {
|
||||||
|
port->errorFlags |= CE_OVERRUN;
|
||||||
|
}
|
||||||
|
if (lsr & LSR_PE) {
|
||||||
|
port->errorFlags |= CE_RXPARITY;
|
||||||
|
}
|
||||||
|
if (lsr & LSR_FE) {
|
||||||
|
port->errorFlags |= CE_FRAME;
|
||||||
|
}
|
||||||
|
if (lsr & LSR_BI) {
|
||||||
|
port->errorFlags |= CE_BREAK;
|
||||||
|
port->evtWord |= EV_BREAK;
|
||||||
|
}
|
||||||
|
if (lsr & (LSR_OE | LSR_PE | LSR_FE)) {
|
||||||
|
port->evtWord |= EV_ERR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// handleMsr - Process modem status changes
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void handleMsr(PortStateT *port)
|
||||||
|
{
|
||||||
|
uint8_t msr;
|
||||||
|
|
||||||
|
msr = (uint8_t)_inp(port->baseAddr + UART_MSR);
|
||||||
|
|
||||||
|
if (msr & MSR_DCTS) {
|
||||||
|
port->evtWord |= EV_CTS;
|
||||||
|
|
||||||
|
// RTS/CTS flow control: stop/start TX based on CTS
|
||||||
|
if (port->hsMode == HS_RTSCTS || port->hsMode == HS_BOTH) {
|
||||||
|
if (msr & MSR_CTS) {
|
||||||
|
port->txStopped = FALSE;
|
||||||
|
// Resume TX
|
||||||
|
if (port->txCount > 0) {
|
||||||
|
handleTx(port);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
port->txStopped = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msr & MSR_DDSR) {
|
||||||
|
port->evtWord |= EV_DSR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msr & MSR_TERI) {
|
||||||
|
port->evtWord |= EV_RING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msr & MSR_DDCD) {
|
||||||
|
port->evtWord |= EV_RLSD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// handleRx - Read all available bytes from UART into RX ring buffer
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void handleRx(PortStateT *port, uint8_t lsr)
|
||||||
|
{
|
||||||
|
uint16_t base;
|
||||||
|
uint8_t ch;
|
||||||
|
|
||||||
|
base = port->baseAddr;
|
||||||
|
|
||||||
|
while (lsr & LSR_DR) {
|
||||||
|
// Check for line errors on this byte
|
||||||
|
if (lsr & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) {
|
||||||
|
handleLsr(port, lsr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ch = (uint8_t)_inp(base + UART_RBR);
|
||||||
|
|
||||||
|
// Check for XON/XOFF if software flow control enabled
|
||||||
|
if (port->hsMode == HS_XONXOFF || port->hsMode == HS_BOTH) {
|
||||||
|
if (ch == port->xonChar) {
|
||||||
|
port->txStopped = FALSE;
|
||||||
|
// Resume TX if we have data
|
||||||
|
if (port->txCount > 0) {
|
||||||
|
handleTx(port);
|
||||||
|
}
|
||||||
|
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch == port->xoffChar) {
|
||||||
|
port->txStopped = TRUE;
|
||||||
|
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store in ring buffer
|
||||||
|
if (port->rxCount < port->rxSize) {
|
||||||
|
port->rxBuf[port->rxHead] = ch;
|
||||||
|
port->rxHead++;
|
||||||
|
if (port->rxHead >= port->rxSize) {
|
||||||
|
port->rxHead = 0;
|
||||||
|
}
|
||||||
|
port->rxCount++;
|
||||||
|
} else {
|
||||||
|
// Buffer overflow
|
||||||
|
port->errorFlags |= CE_RXOVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set event bit
|
||||||
|
port->evtWord |= EV_RXCHAR;
|
||||||
|
|
||||||
|
// Read LSR again for next byte
|
||||||
|
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we need to assert flow control
|
||||||
|
checkFlowRx(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// handleTx - Transmit bytes from TX ring buffer to UART
|
||||||
|
//
|
||||||
|
// For 16550: burst up to 16 bytes per THRE interrupt.
|
||||||
|
// For 8250: send 1 byte at a time.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void handleTx(PortStateT *port)
|
||||||
|
{
|
||||||
|
uint16_t base;
|
||||||
|
uint16_t burst;
|
||||||
|
uint16_t i;
|
||||||
|
|
||||||
|
if (port->txStopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base = port->baseAddr;
|
||||||
|
|
||||||
|
// Priority character first
|
||||||
|
if (port->txImmediate >= 0) {
|
||||||
|
_outp(base + UART_THR, (uint8_t)port->txImmediate);
|
||||||
|
port->txImmediate = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port->txCount == 0) {
|
||||||
|
// Nothing to send -- disable THRE interrupt
|
||||||
|
_outp(base + UART_IER, (uint8_t)(_inp(base + UART_IER) & ~IER_THRE));
|
||||||
|
// TX empty event
|
||||||
|
port->evtWord |= EV_TXEMPTY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Burst size: 16 for FIFO, 1 for non-FIFO or FIFO disabled
|
||||||
|
burst = (port->is16550 && port->fifoEnabled) ? FIFO_DEPTH : 1;
|
||||||
|
if (burst > port->txCount) {
|
||||||
|
burst = port->txCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < burst; i++) {
|
||||||
|
_outp(base + UART_THR, port->txBuf[port->txTail]);
|
||||||
|
port->txTail++;
|
||||||
|
if (port->txTail >= port->txSize) {
|
||||||
|
port->txTail = 0;
|
||||||
|
}
|
||||||
|
port->txCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// hookIsr - Hook the interrupt vector for a port's IRQ via DPMI
|
||||||
|
//
|
||||||
|
// Uses INT 31h AX=0204h to get and AX=0205h to set protected-mode
|
||||||
|
// interrupt vectors.
|
||||||
|
//
|
||||||
|
// Returns 0 on success, -1 on error.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t hookIsr(PortStateT *port)
|
||||||
|
{
|
||||||
|
uint8_t intNum;
|
||||||
|
uint8_t picMask;
|
||||||
|
int16_t *refCount;
|
||||||
|
void (_far _interrupt **prevPtr)(void);
|
||||||
|
void (_far _interrupt *newIsr)(void);
|
||||||
|
void _far *oldVector;
|
||||||
|
uint16_t oldSeg;
|
||||||
|
uint16_t oldOff;
|
||||||
|
|
||||||
|
intNum = port->irq + 8;
|
||||||
|
port->irqMask = (uint8_t)(1 << port->irq);
|
||||||
|
|
||||||
|
if (port->irq == 3) {
|
||||||
|
prevPtr = &prevIsr3;
|
||||||
|
refCount = &irq3RefCount;
|
||||||
|
newIsr = isr3;
|
||||||
|
} else {
|
||||||
|
prevPtr = &prevIsr4;
|
||||||
|
refCount = &irq4RefCount;
|
||||||
|
newIsr = isr4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only hook the vector on first use of this IRQ
|
||||||
|
if (*refCount == 0) {
|
||||||
|
// INT 31h AX=0204h: Get Protected Mode Interrupt Vector
|
||||||
|
// BL = interrupt number
|
||||||
|
// Returns: CX:DX = selector:offset of handler
|
||||||
|
_asm {
|
||||||
|
mov ax, 0204h
|
||||||
|
mov bl, intNum
|
||||||
|
int 31h
|
||||||
|
mov oldSeg, cx
|
||||||
|
mov oldOff, dx
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVector = (void _far *)((uint32_t)oldSeg << 16 | oldOff);
|
||||||
|
*prevPtr = (void (_far _interrupt *)(void))oldVector;
|
||||||
|
|
||||||
|
// INT 31h AX=0205h: Set Protected Mode Interrupt Vector
|
||||||
|
// BL = interrupt number
|
||||||
|
// CX:DX = selector:offset of new handler
|
||||||
|
{
|
||||||
|
uint16_t newSeg = _FP_SEG(newIsr);
|
||||||
|
uint16_t newOff = _FP_OFF(newIsr);
|
||||||
|
|
||||||
|
_asm {
|
||||||
|
mov ax, 0205h
|
||||||
|
mov bl, intNum
|
||||||
|
mov cx, newSeg
|
||||||
|
mov dx, newOff
|
||||||
|
int 31h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*refCount)++;
|
||||||
|
port->prevIsr = (void (FAR *)(void))*prevPtr;
|
||||||
|
|
||||||
|
// Unmask IRQ at PIC
|
||||||
|
picMask = _inp(PIC_DATA);
|
||||||
|
picMask &= ~port->irqMask;
|
||||||
|
_outp(PIC_DATA, picMask);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// isrDispatch - Main interrupt dispatch loop
|
||||||
|
//
|
||||||
|
// Called from the ISR entry points with a pointer to the port state.
|
||||||
|
// Reads IIR in a loop until no more interrupts are pending.
|
||||||
|
// Handles in priority order: LSR > RX > TX > MSR.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
void isrDispatch(PortStateT *port)
|
||||||
|
{
|
||||||
|
uint8_t iir;
|
||||||
|
uint8_t lsr;
|
||||||
|
uint16_t base;
|
||||||
|
|
||||||
|
base = port->baseAddr;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
iir = (uint8_t)_inp(base + UART_IIR);
|
||||||
|
|
||||||
|
// Bit 0 set = no interrupt pending
|
||||||
|
if (iir & IIR_PENDING) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (iir & IIR_ID_MASK) {
|
||||||
|
case IIR_LSR:
|
||||||
|
// Line status: read LSR, accumulate errors
|
||||||
|
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||||
|
handleLsr(port, lsr);
|
||||||
|
// If data ready, also handle RX
|
||||||
|
if (lsr & LSR_DR) {
|
||||||
|
handleRx(port, lsr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_RDA:
|
||||||
|
case IIR_TIMEOUT:
|
||||||
|
// Received data available or FIFO timeout
|
||||||
|
lsr = (uint8_t)_inp(base + UART_LSR);
|
||||||
|
handleRx(port, lsr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_THRE:
|
||||||
|
// Transmitter holding register empty
|
||||||
|
handleTx(port);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IIR_MSR:
|
||||||
|
// Modem status change
|
||||||
|
handleMsr(port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNotify(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// unhookIsr - Restore previous interrupt vector via DPMI
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
void unhookIsr(PortStateT *port)
|
||||||
|
{
|
||||||
|
uint8_t intNum;
|
||||||
|
uint8_t picMask;
|
||||||
|
int16_t *refCount;
|
||||||
|
void (_far _interrupt **prevPtr)(void);
|
||||||
|
|
||||||
|
intNum = port->irq + 8;
|
||||||
|
|
||||||
|
if (port->irq == 3) {
|
||||||
|
prevPtr = &prevIsr3;
|
||||||
|
refCount = &irq3RefCount;
|
||||||
|
} else {
|
||||||
|
prevPtr = &prevIsr4;
|
||||||
|
refCount = &irq4RefCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask IRQ at PIC
|
||||||
|
picMask = _inp(PIC_DATA);
|
||||||
|
picMask |= port->irqMask;
|
||||||
|
_outp(PIC_DATA, picMask);
|
||||||
|
|
||||||
|
(*refCount)--;
|
||||||
|
|
||||||
|
// Only restore vector when last user of this IRQ unhooks
|
||||||
|
if (*refCount <= 0) {
|
||||||
|
uint16_t oldSeg = _FP_SEG(*prevPtr);
|
||||||
|
uint16_t oldOff = _FP_OFF(*prevPtr);
|
||||||
|
|
||||||
|
_asm {
|
||||||
|
mov ax, 0205h
|
||||||
|
mov bl, intNum
|
||||||
|
mov cx, oldSeg
|
||||||
|
mov dx, oldOff
|
||||||
|
int 31h
|
||||||
|
}
|
||||||
|
|
||||||
|
*prevPtr = NULL;
|
||||||
|
*refCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// isr3 - ISR entry point for IRQ3 (COM2 and COM4)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
void _far _interrupt isr3(void)
|
||||||
|
{
|
||||||
|
int16_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_PORTS; i++) {
|
||||||
|
if (ports[i].isOpen && ports[i].irq == 3) {
|
||||||
|
isrDispatch(&ports[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send EOI to PIC
|
||||||
|
_outp(PIC_CMD, PIC_EOI);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// isr4 - ISR entry point for IRQ4 (COM1 and COM3)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
void _far _interrupt isr4(void)
|
||||||
|
{
|
||||||
|
int16_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_PORTS; i++) {
|
||||||
|
if (ports[i].isOpen && ports[i].irq == 4) {
|
||||||
|
isrDispatch(&ports[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send EOI to PIC
|
||||||
|
_outp(PIC_CMD, PIC_EOI);
|
||||||
|
}
|
||||||
72
drv/makefile
Normal file
72
drv/makefile
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
# makefile - High-speed COMM.DRV replacement for MSVC 1.52
|
||||||
|
#
|
||||||
|
# Build: nmake
|
||||||
|
# Clean: nmake clean
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - MSVC 1.52 (cl, link in PATH)
|
||||||
|
#
|
||||||
|
# Output is COMM.DRV -- drop-in replacement for stock Windows 3.1 driver.
|
||||||
|
#
|
||||||
|
# Install:
|
||||||
|
# 1. Copy COMM.DRV to \WINDOWS\SYSTEM
|
||||||
|
# 2. Edit SYSTEM.INI [boot] section: comm.drv=comm.drv
|
||||||
|
# 3. Add to [386Enh] section: COM1FIFO=1 (for each port in use)
|
||||||
|
# 4. Restart Windows
|
||||||
|
#
|
||||||
|
# Key improvements over stock COMM.DRV:
|
||||||
|
# - Both RX and TX FIFOs enabled (stock: RX only)
|
||||||
|
# - RX trigger level 8 (stock: 14 -- only 2 bytes headroom)
|
||||||
|
# - TX burst writes up to 16 bytes per THRE interrupt (stock: 1 byte)
|
||||||
|
|
||||||
|
CC = cl
|
||||||
|
LINK = link
|
||||||
|
|
||||||
|
# Compiler flags:
|
||||||
|
# -c Compile only
|
||||||
|
# -W3 Warning level 3
|
||||||
|
# -ASw Small model, SS!=DS (Windows DLL)
|
||||||
|
# -Gsw No stack probes, Windows prolog/epilog
|
||||||
|
# -Ow Safe optimizations for Windows
|
||||||
|
# -Zp1 Pack structures on 1-byte boundaries (hardware register layouts)
|
||||||
|
# -Ze Enable Microsoft extensions
|
||||||
|
CFLAGS = -c -W3 -ASw -Gsw -Ow -Zp1 -Ze
|
||||||
|
|
||||||
|
# Linker flags:
|
||||||
|
# /NOD No default libraries
|
||||||
|
# /NOE No extended dictionary search
|
||||||
|
# /AL:16 Segment alignment 16
|
||||||
|
LFLAGS = /NOD /NOE /AL:16
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
# sdllcew Small model DLL C runtime (emulated math, Windows)
|
||||||
|
# libw Windows API import library
|
||||||
|
LIBS = sdllcew libw
|
||||||
|
|
||||||
|
# Output
|
||||||
|
TARGET = comm.drv
|
||||||
|
|
||||||
|
# Objects
|
||||||
|
OBJS = commdrv.obj isr.obj
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Build rules
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJS) commdrv.def
|
||||||
|
$(LINK) $(LFLAGS) $(OBJS), $(TARGET),,$(LIBS), commdrv.def
|
||||||
|
|
||||||
|
commdrv.obj: commdrv.c commdrv.h
|
||||||
|
$(CC) $(CFLAGS) commdrv.c
|
||||||
|
|
||||||
|
isr.obj: isr.c commdrv.h
|
||||||
|
$(CC) $(CFLAGS) isr.c
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Clean
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
-del *.obj
|
||||||
|
-del *.drv
|
||||||
|
-del *.map
|
||||||
90
vbx/makefile
Normal file
90
vbx/makefile
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
# makefile - MSComm VBX control for MSVC 1.52
|
||||||
|
#
|
||||||
|
# Build: nmake
|
||||||
|
# Clean: nmake clean
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - MSVC 1.52 (cl, link, rc in PATH)
|
||||||
|
# - VBAPI.LIB (from VBX CDK, or generate with: implib vbapi.lib vbapi.def)
|
||||||
|
#
|
||||||
|
# High-speed serial note:
|
||||||
|
# The stock Windows 3.1 COMM.DRV enables the 16550 FIFO for receive only,
|
||||||
|
# with a trigger level of 14 (leaving only 2 bytes of headroom). This causes
|
||||||
|
# overruns at baud rates above 9600 under load. For reliable operation at
|
||||||
|
# 57600 or 115200, install CyberCom V1.1.0.0P -- a freeware drop-in
|
||||||
|
# replacement included in ..\drivers\CYBERCOM.DRV. It enables both RX and TX
|
||||||
|
# FIFOs with a trigger level of 8, dramatically reducing interrupt overhead.
|
||||||
|
#
|
||||||
|
# Install:
|
||||||
|
# 1. Copy ..\drivers\CYBERCOM.DRV to \WINDOWS\SYSTEM
|
||||||
|
# 2. Edit SYSTEM.INI [boot] section: comm.drv=cybercom.drv
|
||||||
|
# 3. Add to [386Enh] section: COM1FIFO=1 (for each port in use)
|
||||||
|
# 4. Restart Windows
|
||||||
|
#
|
||||||
|
# CyberCom is free for non-commercial use. (C) CyberSoft Corp 1993
|
||||||
|
|
||||||
|
CC = cl
|
||||||
|
LINK = link
|
||||||
|
RC = rc
|
||||||
|
|
||||||
|
# Compiler flags:
|
||||||
|
# -c Compile only
|
||||||
|
# -W3 Warning level 3
|
||||||
|
# -ASw Small model, SS!=DS (Windows DLL)
|
||||||
|
# -Gsw No stack probes, Windows prolog/epilog
|
||||||
|
# -Ow Safe optimizations for Windows
|
||||||
|
# -Zp2 Pack structures on 2-byte boundaries
|
||||||
|
# -Ze Enable Microsoft extensions
|
||||||
|
CFLAGS = -c -W3 -ASw -Gsw -Ow -Zp2 -Ze
|
||||||
|
|
||||||
|
# Linker flags:
|
||||||
|
# /NOD No default libraries
|
||||||
|
# /NOE No extended dictionary search
|
||||||
|
# /AL:16 Segment alignment 16
|
||||||
|
LFLAGS = /NOD /NOE /AL:16
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
# sdllcew Small model DLL C runtime (emulated math, Windows)
|
||||||
|
# libw Windows API import library
|
||||||
|
# commdlg Common dialog import library
|
||||||
|
# vbapi VB API import library
|
||||||
|
LIBS = sdllcew libw commdlg vbapi
|
||||||
|
|
||||||
|
# Output
|
||||||
|
TARGET = mscomm.vbx
|
||||||
|
|
||||||
|
# Objects
|
||||||
|
OBJS = mscomm.obj serial.obj
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Build rules
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJS) mscomm.def mscomm.res
|
||||||
|
$(LINK) $(LFLAGS) $(OBJS), $(TARGET),,$(LIBS), mscomm.def
|
||||||
|
$(RC) mscomm.res $(TARGET)
|
||||||
|
|
||||||
|
mscomm.obj: mscomm.c mscomm.h vbapi.h serial.h
|
||||||
|
$(CC) $(CFLAGS) mscomm.c
|
||||||
|
|
||||||
|
serial.obj: serial.c serial.h
|
||||||
|
$(CC) $(CFLAGS) serial.c
|
||||||
|
|
||||||
|
mscomm.res: mscomm.rc mscomm.h mscomm.bmp
|
||||||
|
$(RC) -r mscomm.rc
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Generate VBAPI.LIB from vbapi.def if not present
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
vbapi.lib: vbapi.def
|
||||||
|
implib vbapi.lib vbapi.def
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
# Clean
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
-del *.obj
|
||||||
|
-del *.res
|
||||||
|
-del *.vbx
|
||||||
|
-del *.map
|
||||||
BIN
vbx/mscomm.bmp
(Stored with Git LFS)
Normal file
BIN
vbx/mscomm.bmp
(Stored with Git LFS)
Normal file
Binary file not shown.
935
vbx/mscomm.c
Normal file
935
vbx/mscomm.c
Normal file
|
|
@ -0,0 +1,935 @@
|
||||||
|
// mscomm.c - MSComm VBX control implementation
|
||||||
|
//
|
||||||
|
// Provides high-speed serial communications for 16-bit Visual Basic 4.
|
||||||
|
// Implements MODEL, control procedure, property/event handling, and
|
||||||
|
// WM_COMMNOTIFY dispatch via hidden notification windows.
|
||||||
|
//
|
||||||
|
// IMPORTANT: VBDerefControl() returns a pointer into VB's compacting heap.
|
||||||
|
// Any VB API call (VBCreateHsz, VBDestroyHsz, VBFireEvent, etc.) can
|
||||||
|
// trigger heap compaction and invalidate the pointer. All code must
|
||||||
|
// re-deref after such calls before accessing control data again.
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "vbapi.h"
|
||||||
|
#include "serial.h"
|
||||||
|
#include "mscomm.h"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Global data
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
HANDLE ghInstance = NULL;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Convenience macro for re-dereferencing control data after VB API calls
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define DEREF(hctl) ((MsCommDataT FAR *)VBDerefControl(hctl))
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Forward declarations - Control procedure
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
LONG FAR PASCAL _export MsCommCtlProc(HCTL hctl, HWND hwnd, USHORT msg, USHORT wp, LONG lp);
|
||||||
|
LRESULT FAR PASCAL _export NotifyWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Forward declarations - Internal functions (alphabetical)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void closePort(HCTL hctl, MsCommDataT FAR *pData);
|
||||||
|
static void fireOnComm(HCTL hctl, MsCommDataT FAR *pData, int16_t eventCode);
|
||||||
|
static LONG handleGetProperty(HCTL hctl, MsCommDataT FAR *pData, USHORT iProp);
|
||||||
|
static LONG handleSetProperty(HCTL hctl, MsCommDataT FAR *pData, USHORT iProp, LONG lp);
|
||||||
|
static void initControlData(HCTL hctl);
|
||||||
|
static int16_t openPort(HCTL hctl, MsCommDataT FAR *pData);
|
||||||
|
static void processEventNotify(HCTL hctl, int16_t commId);
|
||||||
|
static void processReceiveNotify(HCTL hctl, int16_t commId);
|
||||||
|
static void processTransmitNotify(HCTL hctl, int16_t commId);
|
||||||
|
static BOOL registerNotifyClass(HANDLE hInstance);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Forward declarations - DLL entry points
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine);
|
||||||
|
BOOL FAR PASCAL _export VBINITCC(USHORT usVersion, BOOL fRuntime);
|
||||||
|
void FAR PASCAL _export VBTERMCC(void);
|
||||||
|
void FAR PASCAL _export WEP(int nParam);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Enum value lists (null-separated, double-null terminated by compiler)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static char szHandshaking[] = "None\0XonXoff\0RtsCts\0Both";
|
||||||
|
static char szInputMode[] = "Text\0Binary";
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Property descriptors
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// offset default enum list enumMax
|
||||||
|
static PROPINFO propCommPort = { "CommPort", PF_fSetMsg | PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, commPort), 0, 1L, NULL, 0 };
|
||||||
|
static PROPINFO propSettings = { "Settings", PF_fSetMsg | PF_fGetMsg | DT_HSZ, 0, 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propPortOpen = { "PortOpen", PF_fSetMsg | PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, portOpen), 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propInput = { "Input", PF_fGetMsg | PF_fNoShow | DT_HSZ, 0, 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propOutput = { "Output", PF_fSetMsg | PF_fNoShow | DT_HSZ, 0, 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propInBufferSize = { "InBufferSize", PF_fSetMsg | PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, inBufferSize), 0, 4096L, NULL, 0 };
|
||||||
|
static PROPINFO propOutBufferSize = { "OutBufferSize", PF_fSetMsg | PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, outBufferSize), 0, 4096L, NULL, 0 };
|
||||||
|
static PROPINFO propInBufferCount = { "InBufferCount", PF_fGetMsg | PF_fNoShow | DT_SHORT, 0, 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propOutBufferCount= { "OutBufferCount",PF_fGetMsg | PF_fNoShow | DT_SHORT, 0, 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propRThreshold = { "RThreshold", PF_fSetData| PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, rThreshold), 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propSThreshold = { "SThreshold", PF_fSetData| PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, sThreshold), 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propHandshaking = { "Handshaking", PF_fSetMsg | PF_fGetData | DT_ENUM, OFFSETIN(MsCommDataT, handshaking), 0, 0L, szHandshaking, 3 };
|
||||||
|
static PROPINFO propInputLen = { "InputLen", PF_fSetData| PF_fGetData | DT_SHORT, OFFSETIN(MsCommDataT, inputLen), 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propInputMode = { "InputMode", PF_fSetData| PF_fGetData | DT_ENUM, OFFSETIN(MsCommDataT, inputMode), 0, 0L, szInputMode, 1 };
|
||||||
|
static PROPINFO propDTREnable = { "DTREnable", PF_fSetMsg | PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, dtrEnable), 0, 1L, NULL, 0 };
|
||||||
|
static PROPINFO propRTSEnable = { "RTSEnable", PF_fSetMsg | PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, rtsEnable), 0, 1L, NULL, 0 };
|
||||||
|
static PROPINFO propCDHolding = { "CDHolding", PF_fGetMsg | PF_fNoShow | DT_BOOL, 0, 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propCTSHolding = { "CTSHolding", PF_fGetMsg | PF_fNoShow | DT_BOOL, 0, 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propDSRHolding = { "DSRHolding", PF_fGetMsg | PF_fNoShow | DT_BOOL, 0, 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propBreak = { "Break", PF_fSetMsg | PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, breakState), 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propCommEvent = { "CommEvent", PF_fGetData| PF_fNoShow | DT_SHORT, OFFSETIN(MsCommDataT, commEvent), 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propNullDiscard = { "NullDiscard", PF_fSetMsg | PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, nullDiscard), 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propEOFEnable = { "EOFEnable", PF_fSetData| PF_fGetData | DT_BOOL, OFFSETIN(MsCommDataT, eofEnable), 0, 0L, NULL, 0 };
|
||||||
|
static PROPINFO propParityReplace = { "ParityReplace", PF_fSetMsg | PF_fGetMsg | DT_HSZ, 0, 0, 0L, NULL, 0 };
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Property list (NULL-terminated, order must match IPROP_* indices)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static PPROPINFO propList[] = {
|
||||||
|
PPROPINFO_STD_CTLNAME, // 0 - Name
|
||||||
|
PPROPINFO_STD_INDEX, // 1 - Index
|
||||||
|
PPROPINFO_STD_TAG, // 2 - Tag
|
||||||
|
&propCommPort, // 3
|
||||||
|
&propSettings, // 4
|
||||||
|
&propPortOpen, // 5
|
||||||
|
&propInput, // 6
|
||||||
|
&propOutput, // 7
|
||||||
|
&propInBufferSize, // 8
|
||||||
|
&propOutBufferSize, // 9
|
||||||
|
&propInBufferCount, // 10
|
||||||
|
&propOutBufferCount, // 11
|
||||||
|
&propRThreshold, // 12
|
||||||
|
&propSThreshold, // 13
|
||||||
|
&propHandshaking, // 14
|
||||||
|
&propInputLen, // 15
|
||||||
|
&propInputMode, // 16
|
||||||
|
&propDTREnable, // 17
|
||||||
|
&propRTSEnable, // 18
|
||||||
|
&propCDHolding, // 19
|
||||||
|
&propCTSHolding, // 20
|
||||||
|
&propDSRHolding, // 21
|
||||||
|
&propBreak, // 22
|
||||||
|
&propCommEvent, // 23
|
||||||
|
&propNullDiscard, // 24
|
||||||
|
&propEOFEnable, // 25
|
||||||
|
&propParityReplace, // 26
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Event descriptors
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static EVENTINFO eventOnComm = { "OnComm", 0, 0, NULL, "", 0 };
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Event list (NULL-terminated)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static PEVENTINFO eventList[] = {
|
||||||
|
&eventOnComm, // 0 - OnComm
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// MODEL definition
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static char szCtlName[] = "MSComm";
|
||||||
|
|
||||||
|
MODEL modelMsComm = {
|
||||||
|
VB300_VERSION, // usVersion
|
||||||
|
MODEL_fInitMsg | MODEL_fInvisAtRun,// fl
|
||||||
|
(PCTLPROC)MsCommCtlProc, // pctlproc
|
||||||
|
0, // fsClassStyle
|
||||||
|
WS_CHILD, // flWndStyle
|
||||||
|
sizeof(MsCommDataT), // cbCtlExtra
|
||||||
|
IDB_MSCOMM, // idBmpPalette
|
||||||
|
szCtlName, // npszDefCtlName
|
||||||
|
NULL, // npszClassName
|
||||||
|
NULL, // npszParentClassName
|
||||||
|
propList, // npproplist
|
||||||
|
eventList, // npeventlist
|
||||||
|
IPROP_COMMPORT, // nDefProp
|
||||||
|
IEVENT_ONCOMM, // nDefEvent
|
||||||
|
0, // nValueProp
|
||||||
|
0x0100 // usCtlVersion (1.00)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// Internal helper functions (alphabetical)
|
||||||
|
// =======================================================================
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// closePort - Close the serial port and destroy notification window
|
||||||
|
//
|
||||||
|
// No VB API calls here, so pData remains valid throughout.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void closePort(HCTL hctl, MsCommDataT FAR *pData)
|
||||||
|
{
|
||||||
|
if (pData->commId >= 0) {
|
||||||
|
if (pData->breakState) {
|
||||||
|
serialEscape(pData->commId, SERIAL_CLRBREAK);
|
||||||
|
pData->breakState = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialClose(pData->commId);
|
||||||
|
pData->commId = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pData->hwndNotify != NULL) {
|
||||||
|
DestroyWindow(pData->hwndNotify);
|
||||||
|
pData->hwndNotify = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pData->portOpen = FALSE;
|
||||||
|
pData->ctsState = FALSE;
|
||||||
|
pData->dsrState = FALSE;
|
||||||
|
pData->cdState = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// fireOnComm - Set commEvent and fire OnComm event to VB
|
||||||
|
//
|
||||||
|
// WARNING: After this function returns, any previously obtained pData
|
||||||
|
// pointer is INVALID. Callers must re-deref via DEREF(hctl).
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void fireOnComm(HCTL hctl, MsCommDataT FAR *pData, int16_t eventCode)
|
||||||
|
{
|
||||||
|
pData->commEvent = eventCode;
|
||||||
|
VBFireEvent(hctl, IEVENT_ONCOMM, NULL);
|
||||||
|
// pData is now stale - caller must re-deref
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// handleGetProperty - Process VBM_GETPROPERTY for message-based props
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static LONG handleGetProperty(HCTL hctl, MsCommDataT FAR *pData, USHORT iProp)
|
||||||
|
{
|
||||||
|
switch (iProp) {
|
||||||
|
case IPROP_SETTINGS:
|
||||||
|
return (LONG)pData->hszSettings;
|
||||||
|
|
||||||
|
case IPROP_INPUT: {
|
||||||
|
COMSTAT stat;
|
||||||
|
int16_t bytesToRead;
|
||||||
|
int16_t bytesRead;
|
||||||
|
int16_t commId;
|
||||||
|
int16_t inputLen;
|
||||||
|
HGLOBAL hMem;
|
||||||
|
char FAR *buf;
|
||||||
|
HSZ hsz;
|
||||||
|
|
||||||
|
if (!pData->portOpen || pData->commId < 0) {
|
||||||
|
return (LONG)VBCreateHsz((_segment)0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save values to locals before any VB API calls
|
||||||
|
commId = pData->commId;
|
||||||
|
inputLen = pData->inputLen;
|
||||||
|
|
||||||
|
serialGetStatus(commId, &stat);
|
||||||
|
bytesToRead = (int16_t)stat.cbInQue;
|
||||||
|
|
||||||
|
if (inputLen > 0 && bytesToRead > inputLen) {
|
||||||
|
bytesToRead = inputLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesToRead <= 0) {
|
||||||
|
return (LONG)VBCreateHsz((_segment)0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use GlobalAlloc for potentially large buffers (DLL local heap is small)
|
||||||
|
hMem = GlobalAlloc(GMEM_MOVEABLE, (DWORD)bytesToRead + 1);
|
||||||
|
if (hMem == NULL) {
|
||||||
|
return (LONG)VBCreateHsz((_segment)0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = (char FAR *)GlobalLock(hMem);
|
||||||
|
bytesRead = serialRead(commId, buf, bytesToRead);
|
||||||
|
if (bytesRead <= 0) {
|
||||||
|
GlobalUnlock(hMem);
|
||||||
|
GlobalFree(hMem);
|
||||||
|
return (LONG)VBCreateHsz((_segment)0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[bytesRead] = '\0';
|
||||||
|
hsz = VBCreateHsz((_segment)0, buf);
|
||||||
|
GlobalUnlock(hMem);
|
||||||
|
GlobalFree(hMem);
|
||||||
|
// pData is stale after VBCreateHsz, but we just return the HSZ
|
||||||
|
return (LONG)hsz;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IPROP_INBUFFERCOUNT: {
|
||||||
|
COMSTAT stat;
|
||||||
|
if (!pData->portOpen || pData->commId < 0) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
serialGetStatus(pData->commId, &stat);
|
||||||
|
return (LONG)(int16_t)stat.cbInQue;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IPROP_OUTBUFFERCOUNT: {
|
||||||
|
COMSTAT stat;
|
||||||
|
if (!pData->portOpen || pData->commId < 0) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
serialGetStatus(pData->commId, &stat);
|
||||||
|
return (LONG)(int16_t)stat.cbOutQue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modem line status: return shadow state maintained by processEventNotify.
|
||||||
|
// The 16-bit comm API only provides transition events (GetCommEventMask),
|
||||||
|
// not current line levels. Shadow state is toggled on each transition.
|
||||||
|
case IPROP_CDHOLDING:
|
||||||
|
return (LONG)(BOOL)pData->cdState;
|
||||||
|
|
||||||
|
case IPROP_CTSHOLDING:
|
||||||
|
return (LONG)(BOOL)pData->ctsState;
|
||||||
|
|
||||||
|
case IPROP_DSRHOLDING:
|
||||||
|
return (LONG)(BOOL)pData->dsrState;
|
||||||
|
|
||||||
|
case IPROP_PARITYREPLACE:
|
||||||
|
return (LONG)pData->hszParityReplace;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// handleSetProperty - Process VBM_SETPROPERTY for message-based props
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static LONG handleSetProperty(HCTL hctl, MsCommDataT FAR *pData, USHORT iProp, LONG lp)
|
||||||
|
{
|
||||||
|
int16_t val;
|
||||||
|
|
||||||
|
switch (iProp) {
|
||||||
|
case IPROP_COMMPORT:
|
||||||
|
val = (int16_t)lp;
|
||||||
|
if (val < 1 || val > 16) {
|
||||||
|
return (LONG)ERR_InvPropVal;
|
||||||
|
}
|
||||||
|
if (pData->portOpen) {
|
||||||
|
return (LONG)ERR_InvPropSet;
|
||||||
|
}
|
||||||
|
pData->commPort = val;
|
||||||
|
return 0L;
|
||||||
|
|
||||||
|
case IPROP_SETTINGS: {
|
||||||
|
HSZ oldHsz;
|
||||||
|
HSZ newHsz;
|
||||||
|
LPSTR lpstr;
|
||||||
|
int16_t commId;
|
||||||
|
int16_t port;
|
||||||
|
|
||||||
|
// Deref the incoming HSZ (VBDerefHsz does not compact)
|
||||||
|
lpstr = VBDerefHsz((HSZ)lp);
|
||||||
|
if (lpstr == NULL) {
|
||||||
|
return (LONG)ERR_InvPropVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save old HSZ, create new one
|
||||||
|
oldHsz = pData->hszSettings;
|
||||||
|
newHsz = VBCreateHsz((_segment)0, lpstr);
|
||||||
|
// pData may be stale after VBCreateHsz - re-deref
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
pData->hszSettings = newHsz;
|
||||||
|
|
||||||
|
if (oldHsz) {
|
||||||
|
VBDestroyHsz(oldHsz);
|
||||||
|
// pData may be stale after VBDestroyHsz - re-deref
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If port is open, reconfigure immediately
|
||||||
|
if (pData->portOpen && pData->commId >= 0) {
|
||||||
|
commId = pData->commId;
|
||||||
|
port = pData->commPort;
|
||||||
|
lpstr = VBDerefHsz(pData->hszSettings);
|
||||||
|
if (serialConfigure(commId, port, lpstr) != 0) {
|
||||||
|
return (LONG)ERR_InvPropVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IPROP_PORTOPEN:
|
||||||
|
if ((BOOL)lp) {
|
||||||
|
if (pData->portOpen) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
if (openPort(hctl, pData) != 0) {
|
||||||
|
return (LONG)ERR_InvPropVal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!pData->portOpen) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
closePort(hctl, pData);
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
|
||||||
|
case IPROP_OUTPUT: {
|
||||||
|
int16_t commId;
|
||||||
|
LPSTR lpstr;
|
||||||
|
|
||||||
|
if (!pData->portOpen || pData->commId < 0) {
|
||||||
|
return (LONG)ERR_InvPropSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save commId before VB API call
|
||||||
|
commId = pData->commId;
|
||||||
|
lpstr = VBDerefHsz((HSZ)lp);
|
||||||
|
if (lpstr != NULL) {
|
||||||
|
int16_t len = (int16_t)lstrlen(lpstr);
|
||||||
|
if (len > 0) {
|
||||||
|
int16_t written = serialWrite(commId, lpstr, len);
|
||||||
|
if (written < 0) {
|
||||||
|
// Re-deref before fireOnComm since pData may be stale
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
fireOnComm(hctl, pData, COM_EVT_TXFULL);
|
||||||
|
// pData stale after fireOnComm, but we just return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IPROP_INBUFFERSIZE:
|
||||||
|
val = (int16_t)lp;
|
||||||
|
if (val < 64) {
|
||||||
|
return (LONG)ERR_InvPropVal;
|
||||||
|
}
|
||||||
|
if (pData->portOpen) {
|
||||||
|
return (LONG)ERR_InvPropSet;
|
||||||
|
}
|
||||||
|
pData->inBufferSize = val;
|
||||||
|
return 0L;
|
||||||
|
|
||||||
|
case IPROP_OUTBUFFERSIZE:
|
||||||
|
val = (int16_t)lp;
|
||||||
|
if (val < 64) {
|
||||||
|
return (LONG)ERR_InvPropVal;
|
||||||
|
}
|
||||||
|
if (pData->portOpen) {
|
||||||
|
return (LONG)ERR_InvPropSet;
|
||||||
|
}
|
||||||
|
pData->outBufferSize = val;
|
||||||
|
return 0L;
|
||||||
|
|
||||||
|
case IPROP_HANDSHAKING:
|
||||||
|
pData->handshaking = (int16_t)lp;
|
||||||
|
if (pData->portOpen && pData->commId >= 0) {
|
||||||
|
serialSetHandshaking(pData->commId, pData->handshaking);
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
|
||||||
|
case IPROP_DTRENABLE:
|
||||||
|
pData->dtrEnable = (BOOL)lp;
|
||||||
|
if (pData->portOpen && pData->commId >= 0) {
|
||||||
|
serialEscape(pData->commId, pData->dtrEnable ? SERIAL_SETDTR : SERIAL_CLRDTR);
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
|
||||||
|
case IPROP_RTSENABLE:
|
||||||
|
pData->rtsEnable = (BOOL)lp;
|
||||||
|
if (pData->portOpen && pData->commId >= 0) {
|
||||||
|
serialEscape(pData->commId, pData->rtsEnable ? SERIAL_SETRTS : SERIAL_CLRRTS);
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
|
||||||
|
case IPROP_BREAK:
|
||||||
|
pData->breakState = (BOOL)lp;
|
||||||
|
if (pData->portOpen && pData->commId >= 0) {
|
||||||
|
serialEscape(pData->commId, pData->breakState ? SERIAL_SETBREAK : SERIAL_CLRBREAK);
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
|
||||||
|
case IPROP_NULLDISCARD: {
|
||||||
|
int16_t commId;
|
||||||
|
BOOL nullDiscard;
|
||||||
|
LPSTR lpstr;
|
||||||
|
|
||||||
|
pData->nullDiscard = (BOOL)lp;
|
||||||
|
if (pData->portOpen && pData->commId >= 0) {
|
||||||
|
// Save locals before VBDerefHsz
|
||||||
|
commId = pData->commId;
|
||||||
|
nullDiscard = pData->nullDiscard;
|
||||||
|
lpstr = VBDerefHsz(pData->hszParityReplace);
|
||||||
|
serialSetOptions(commId, nullDiscard, lpstr);
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IPROP_PARITYREPLACE: {
|
||||||
|
HSZ oldHsz;
|
||||||
|
HSZ newHsz;
|
||||||
|
int16_t commId;
|
||||||
|
BOOL nullDiscard;
|
||||||
|
LPSTR lpstr;
|
||||||
|
|
||||||
|
lpstr = VBDerefHsz((HSZ)lp);
|
||||||
|
oldHsz = pData->hszParityReplace;
|
||||||
|
newHsz = VBCreateHsz((_segment)0, lpstr ? lpstr : "");
|
||||||
|
// Re-deref after VBCreateHsz
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
pData->hszParityReplace = newHsz;
|
||||||
|
|
||||||
|
if (oldHsz) {
|
||||||
|
VBDestroyHsz(oldHsz);
|
||||||
|
// Re-deref after VBDestroyHsz
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pData->portOpen && pData->commId >= 0) {
|
||||||
|
commId = pData->commId;
|
||||||
|
nullDiscard = pData->nullDiscard;
|
||||||
|
lpstr = VBDerefHsz(pData->hszParityReplace);
|
||||||
|
serialSetOptions(commId, nullDiscard, lpstr);
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// initControlData - Initialize per-instance control data
|
||||||
|
//
|
||||||
|
// Re-derefs after each VBCreateHsz to avoid stale pointers.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void initControlData(HCTL hctl)
|
||||||
|
{
|
||||||
|
MsCommDataT FAR *pData;
|
||||||
|
HSZ hsz;
|
||||||
|
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
pData->commId = -1;
|
||||||
|
pData->hwndNotify = NULL;
|
||||||
|
pData->commPort = 1;
|
||||||
|
pData->inBufferSize = 4096;
|
||||||
|
pData->outBufferSize = 4096;
|
||||||
|
pData->rThreshold = 0;
|
||||||
|
pData->sThreshold = 0;
|
||||||
|
pData->handshaking = HS_NONE;
|
||||||
|
pData->inputLen = 0;
|
||||||
|
pData->inputMode = INPUT_TEXT;
|
||||||
|
pData->commEvent = 0;
|
||||||
|
pData->portOpen = FALSE;
|
||||||
|
pData->dtrEnable = TRUE;
|
||||||
|
pData->rtsEnable = TRUE;
|
||||||
|
pData->breakState = FALSE;
|
||||||
|
pData->nullDiscard = FALSE;
|
||||||
|
pData->eofEnable = FALSE;
|
||||||
|
pData->ctsState = FALSE;
|
||||||
|
pData->dsrState = FALSE;
|
||||||
|
pData->cdState = FALSE;
|
||||||
|
|
||||||
|
// VBCreateHsz may compact VB's heap - re-deref after each call
|
||||||
|
hsz = VBCreateHsz((_segment)0, "9600,N,8,1");
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
pData->hszSettings = hsz;
|
||||||
|
|
||||||
|
hsz = VBCreateHsz((_segment)0, "?");
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
pData->hszParityReplace = hsz;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// openPort - Open serial port and set up notification window
|
||||||
|
//
|
||||||
|
// Saves all needed control data to locals before VB API calls to avoid
|
||||||
|
// stale pointer issues. Re-derefs when writing results back.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static int16_t openPort(HCTL hctl, MsCommDataT FAR *pData)
|
||||||
|
{
|
||||||
|
int16_t commId;
|
||||||
|
int16_t port;
|
||||||
|
int16_t inBufSize;
|
||||||
|
int16_t outBufSize;
|
||||||
|
int16_t handshaking;
|
||||||
|
int16_t rThreshold;
|
||||||
|
int16_t sThreshold;
|
||||||
|
BOOL dtrEnable;
|
||||||
|
BOOL rtsEnable;
|
||||||
|
BOOL nullDiscard;
|
||||||
|
HSZ hszSettings;
|
||||||
|
HSZ hszParityReplace;
|
||||||
|
HWND hwndNotify;
|
||||||
|
LPSTR lpstr;
|
||||||
|
|
||||||
|
// Snapshot all values we need (pData may become stale after VB API calls)
|
||||||
|
port = pData->commPort;
|
||||||
|
inBufSize = pData->inBufferSize;
|
||||||
|
outBufSize = pData->outBufferSize;
|
||||||
|
handshaking = pData->handshaking;
|
||||||
|
rThreshold = pData->rThreshold;
|
||||||
|
sThreshold = pData->sThreshold;
|
||||||
|
dtrEnable = pData->dtrEnable;
|
||||||
|
rtsEnable = pData->rtsEnable;
|
||||||
|
nullDiscard = pData->nullDiscard;
|
||||||
|
hszSettings = pData->hszSettings;
|
||||||
|
hszParityReplace = pData->hszParityReplace;
|
||||||
|
|
||||||
|
// Open the comm port
|
||||||
|
commId = serialOpen(port, inBufSize, outBufSize);
|
||||||
|
if (commId < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure baud/parity/data/stop
|
||||||
|
lpstr = VBDerefHsz(hszSettings);
|
||||||
|
if (serialConfigure(commId, port, lpstr) != 0) {
|
||||||
|
serialClose(commId);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply handshaking
|
||||||
|
serialSetHandshaking(commId, handshaking);
|
||||||
|
|
||||||
|
// Apply null-discard and parity-replace
|
||||||
|
lpstr = VBDerefHsz(hszParityReplace);
|
||||||
|
serialSetOptions(commId, nullDiscard, lpstr);
|
||||||
|
|
||||||
|
// Set DTR and RTS lines
|
||||||
|
serialEscape(commId, dtrEnable ? SERIAL_SETDTR : SERIAL_CLRDTR);
|
||||||
|
serialEscape(commId, rtsEnable ? SERIAL_SETRTS : SERIAL_CLRRTS);
|
||||||
|
|
||||||
|
// Create hidden notification window
|
||||||
|
hwndNotify = CreateWindow(
|
||||||
|
NOTIFY_CLASS,
|
||||||
|
"",
|
||||||
|
WS_POPUP,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
ghInstance,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hwndNotify == NULL) {
|
||||||
|
serialClose(commId);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store HCTL in notification window for message dispatch
|
||||||
|
SetWindowWord(hwndNotify, 0, (WORD)hctl);
|
||||||
|
|
||||||
|
// Enable comm notifications
|
||||||
|
serialEnableNotify(commId, hwndNotify,
|
||||||
|
rThreshold > 0 ? rThreshold : -1,
|
||||||
|
sThreshold > 0 ? sThreshold : -1);
|
||||||
|
|
||||||
|
// Re-deref to write results back to control data
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
pData->commId = commId;
|
||||||
|
pData->hwndNotify = hwndNotify;
|
||||||
|
pData->portOpen = TRUE;
|
||||||
|
pData->ctsState = FALSE;
|
||||||
|
pData->dsrState = FALSE;
|
||||||
|
pData->cdState = FALSE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// processEventNotify - Handle CN_EVENT notification
|
||||||
|
//
|
||||||
|
// Takes commId as a stable local value. Re-derefs pData after every
|
||||||
|
// fireOnComm call since VBFireEvent can trigger heap compaction.
|
||||||
|
// Updates modem line shadow state on CTS/DSR/CD transitions.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void processEventNotify(HCTL hctl, int16_t commId)
|
||||||
|
{
|
||||||
|
MsCommDataT FAR *pData;
|
||||||
|
UINT evtMask;
|
||||||
|
|
||||||
|
evtMask = serialGetEventMask(commId, EV_CTS | EV_DSR | EV_RLSD | EV_RING | EV_ERR | EV_BREAK);
|
||||||
|
|
||||||
|
if (evtMask & EV_BREAK) {
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
fireOnComm(hctl, pData, COM_EVT_BREAK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evtMask & EV_CTS) {
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
pData->ctsState = !pData->ctsState;
|
||||||
|
fireOnComm(hctl, pData, COM_EV_CTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evtMask & EV_DSR) {
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
pData->dsrState = !pData->dsrState;
|
||||||
|
fireOnComm(hctl, pData, COM_EV_DSR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evtMask & EV_RLSD) {
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
pData->cdState = !pData->cdState;
|
||||||
|
fireOnComm(hctl, pData, COM_EV_CD);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evtMask & EV_RING) {
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
fireOnComm(hctl, pData, COM_EV_RING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evtMask & EV_ERR) {
|
||||||
|
COMSTAT stat;
|
||||||
|
int16_t errFlags;
|
||||||
|
|
||||||
|
errFlags = serialGetStatus(commId, &stat);
|
||||||
|
|
||||||
|
if (errFlags & CE_FRAME) {
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
fireOnComm(hctl, pData, COM_EVT_FRAME);
|
||||||
|
}
|
||||||
|
if (errFlags & CE_OVERRUN) {
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
fireOnComm(hctl, pData, COM_EVT_OVERRUN);
|
||||||
|
}
|
||||||
|
if (errFlags & CE_RXOVER) {
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
fireOnComm(hctl, pData, COM_EVT_RXOVER);
|
||||||
|
}
|
||||||
|
if (errFlags & CE_RXPARITY) {
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
fireOnComm(hctl, pData, COM_EVT_RXPARITY);
|
||||||
|
}
|
||||||
|
if (errFlags & CE_TXFULL) {
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
fireOnComm(hctl, pData, COM_EVT_TXFULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// processReceiveNotify - Handle CN_RECEIVE notification
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void processReceiveNotify(HCTL hctl, int16_t commId)
|
||||||
|
{
|
||||||
|
MsCommDataT FAR *pData;
|
||||||
|
COMSTAT stat;
|
||||||
|
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
|
||||||
|
if (pData->rThreshold <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialGetStatus(commId, &stat);
|
||||||
|
|
||||||
|
if ((int16_t)stat.cbInQue >= pData->rThreshold) {
|
||||||
|
fireOnComm(hctl, pData, COM_EV_RECEIVE);
|
||||||
|
// pData stale after fireOnComm, but we just return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// processTransmitNotify - Handle CN_TRANSMIT notification
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static void processTransmitNotify(HCTL hctl, int16_t commId)
|
||||||
|
{
|
||||||
|
MsCommDataT FAR *pData;
|
||||||
|
COMSTAT stat;
|
||||||
|
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
|
||||||
|
if (pData->sThreshold <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialGetStatus(commId, &stat);
|
||||||
|
|
||||||
|
if ((int16_t)stat.cbOutQue <= pData->sThreshold) {
|
||||||
|
fireOnComm(hctl, pData, COM_EV_SEND);
|
||||||
|
// pData stale after fireOnComm, but we just return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// registerNotifyClass - Register hidden notification window class
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
static BOOL registerNotifyClass(HANDLE hInstance)
|
||||||
|
{
|
||||||
|
WNDCLASS wc;
|
||||||
|
|
||||||
|
wc.style = 0;
|
||||||
|
wc.lpfnWndProc = NotifyWndProc;
|
||||||
|
wc.cbClsExtra = 0;
|
||||||
|
wc.cbWndExtra = sizeof(WORD); // Room for HCTL
|
||||||
|
wc.hInstance = hInstance;
|
||||||
|
wc.hIcon = NULL;
|
||||||
|
wc.hCursor = NULL;
|
||||||
|
wc.hbrBackground = NULL;
|
||||||
|
wc.lpszMenuName = NULL;
|
||||||
|
wc.lpszClassName = NOTIFY_CLASS;
|
||||||
|
|
||||||
|
return RegisterClass(&wc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// Hidden notification window procedure
|
||||||
|
//
|
||||||
|
// Receives WM_COMMNOTIFY from the comm driver. Saves commId to a local
|
||||||
|
// variable and passes it to process* functions, which independently
|
||||||
|
// deref the control data (avoiding stale pointer chains across VBFireEvent).
|
||||||
|
// =======================================================================
|
||||||
|
|
||||||
|
LRESULT FAR PASCAL _export NotifyWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
||||||
|
{
|
||||||
|
if (msg == WM_COMMNOTIFY) {
|
||||||
|
HCTL hctl;
|
||||||
|
MsCommDataT FAR *pData;
|
||||||
|
int16_t commId;
|
||||||
|
UINT notifyCode;
|
||||||
|
|
||||||
|
hctl = (HCTL)GetWindowWord(hwnd, 0);
|
||||||
|
if (hctl == 0) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
if (pData == NULL || pData->commId < 0) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save commId to local - stable across VBFireEvent calls
|
||||||
|
commId = pData->commId;
|
||||||
|
notifyCode = LOWORD(lp);
|
||||||
|
|
||||||
|
// Each process* function independently derefs and re-derefs as needed
|
||||||
|
if (notifyCode & CN_RECEIVE) {
|
||||||
|
processReceiveNotify(hctl, commId);
|
||||||
|
}
|
||||||
|
if (notifyCode & CN_TRANSMIT) {
|
||||||
|
processTransmitNotify(hctl, commId);
|
||||||
|
}
|
||||||
|
if (notifyCode & CN_EVENT) {
|
||||||
|
processEventNotify(hctl, commId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(hwnd, msg, wp, lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// VBX Control procedure
|
||||||
|
// =======================================================================
|
||||||
|
|
||||||
|
LONG FAR PASCAL _export MsCommCtlProc(HCTL hctl, HWND hwnd, USHORT msg, USHORT wp, LONG lp)
|
||||||
|
{
|
||||||
|
MsCommDataT FAR *pData;
|
||||||
|
|
||||||
|
switch (msg) {
|
||||||
|
case VBM_INITIALIZE:
|
||||||
|
initControlData(hctl);
|
||||||
|
return 0L;
|
||||||
|
|
||||||
|
case VBM_SETPROPERTY:
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
return handleSetProperty(hctl, pData, wp, lp);
|
||||||
|
|
||||||
|
case VBM_GETPROPERTY:
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
return handleGetProperty(hctl, pData, wp);
|
||||||
|
|
||||||
|
case WM_DESTROY: {
|
||||||
|
HSZ hszSettings;
|
||||||
|
HSZ hszParityReplace;
|
||||||
|
|
||||||
|
pData = DEREF(hctl);
|
||||||
|
|
||||||
|
// Close port if open
|
||||||
|
if (pData->portOpen) {
|
||||||
|
closePort(hctl, pData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save HSZ handles to locals, then destroy them.
|
||||||
|
// VBDestroyHsz may compact the heap, so don't access pData after.
|
||||||
|
hszSettings = pData->hszSettings;
|
||||||
|
hszParityReplace = pData->hszParityReplace;
|
||||||
|
pData->hszSettings = 0;
|
||||||
|
pData->hszParityReplace = 0;
|
||||||
|
|
||||||
|
if (hszSettings) {
|
||||||
|
VBDestroyHsz(hszSettings);
|
||||||
|
}
|
||||||
|
if (hszParityReplace) {
|
||||||
|
VBDestroyHsz(hszParityReplace);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return VBDefControlProc(hctl, hwnd, msg, wp, lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =======================================================================
|
||||||
|
// DLL entry points
|
||||||
|
// =======================================================================
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// LibMain - DLL initialization
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine)
|
||||||
|
{
|
||||||
|
ghInstance = hInstance;
|
||||||
|
|
||||||
|
if (wHeapSize > 0) {
|
||||||
|
UnlockData(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// VBINITCC - Called by VB to register the control
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
BOOL FAR PASCAL _export VBINITCC(USHORT usVersion, BOOL fRuntime)
|
||||||
|
{
|
||||||
|
if (!registerNotifyClass(ghInstance)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VBRegisterModel(ghInstance, &modelMsComm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// VBTERMCC - Called by VB when unloading the control
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
void FAR PASCAL _export VBTERMCC(void)
|
||||||
|
{
|
||||||
|
UnregisterClass(NOTIFY_CLASS, ghInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// WEP - DLL termination (required for 16-bit Windows DLLs)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
void FAR PASCAL _export WEP(int nParam)
|
||||||
|
{
|
||||||
|
(void)nParam;
|
||||||
|
}
|
||||||
15
vbx/mscomm.def
Normal file
15
vbx/mscomm.def
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
; mscomm.def - Module definition for MSComm VBX control
|
||||||
|
|
||||||
|
LIBRARY MSCOMM
|
||||||
|
DESCRIPTION 'MSComm Serial Communications Control for VB4'
|
||||||
|
EXETYPE WINDOWS
|
||||||
|
|
||||||
|
CODE PRELOAD MOVEABLE DISCARDABLE
|
||||||
|
DATA PRELOAD MOVEABLE SINGLE
|
||||||
|
|
||||||
|
HEAPSIZE 1024
|
||||||
|
|
||||||
|
EXPORTS
|
||||||
|
WEP @1 RESIDENTNAME
|
||||||
|
VBINITCC @2
|
||||||
|
VBTERMCC @3
|
||||||
141
vbx/mscomm.h
Normal file
141
vbx/mscomm.h
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
// mscomm.h - MSComm VBX control definitions
|
||||||
|
//
|
||||||
|
// Property/event IDs, comm event constants, and control instance data.
|
||||||
|
|
||||||
|
#ifndef MSCOMM_H
|
||||||
|
#define MSCOMM_H
|
||||||
|
|
||||||
|
#include "vbapi.h"
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// stdint types for MSVC 1.52 (shared with serial.h)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#ifndef _STDINT_DEFINED
|
||||||
|
typedef short int16_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef long int32_t;
|
||||||
|
typedef unsigned long uint32_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef signed char int8_t;
|
||||||
|
#define _STDINT_DEFINED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Resource IDs
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define IDB_MSCOMM 8000
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Property indices (position in npproplist array)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define IPROP_CTLNAME 0
|
||||||
|
#define IPROP_INDEX 1
|
||||||
|
#define IPROP_TAG 2
|
||||||
|
#define IPROP_COMMPORT 3
|
||||||
|
#define IPROP_SETTINGS 4
|
||||||
|
#define IPROP_PORTOPEN 5
|
||||||
|
#define IPROP_INPUT 6
|
||||||
|
#define IPROP_OUTPUT 7
|
||||||
|
#define IPROP_INBUFFERSIZE 8
|
||||||
|
#define IPROP_OUTBUFFERSIZE 9
|
||||||
|
#define IPROP_INBUFFERCOUNT 10
|
||||||
|
#define IPROP_OUTBUFFERCOUNT 11
|
||||||
|
#define IPROP_RTHRESHOLD 12
|
||||||
|
#define IPROP_STHRESHOLD 13
|
||||||
|
#define IPROP_HANDSHAKING 14
|
||||||
|
#define IPROP_INPUTLEN 15
|
||||||
|
#define IPROP_INPUTMODE 16
|
||||||
|
#define IPROP_DTRENABLE 17
|
||||||
|
#define IPROP_RTSENABLE 18
|
||||||
|
#define IPROP_CDHOLDING 19
|
||||||
|
#define IPROP_CTSHOLDING 20
|
||||||
|
#define IPROP_DSRHOLDING 21
|
||||||
|
#define IPROP_BREAK 22
|
||||||
|
#define IPROP_COMMEVENT 23
|
||||||
|
#define IPROP_NULLDISCARD 24
|
||||||
|
#define IPROP_EOFENABLE 25
|
||||||
|
#define IPROP_PARITYREPLACE 26
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Event indices (position in npeventlist array)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define IEVENT_ONCOMM 0
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// CommEvent constants - Communication events
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define COM_EV_RECEIVE 1 // Received RThreshold bytes
|
||||||
|
#define COM_EV_SEND 2 // Transmit buffer has SThreshold space
|
||||||
|
#define COM_EV_CTS 3 // CTS line changed
|
||||||
|
#define COM_EV_DSR 4 // DSR line changed
|
||||||
|
#define COM_EV_CD 5 // CD (RLSD) line changed
|
||||||
|
#define COM_EV_RING 6 // Ring indicator detected
|
||||||
|
#define COM_EV_EOF 7 // EOF character received
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// CommEvent constants - Error events
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define COM_EVT_BREAK 1001 // Break signal received
|
||||||
|
#define COM_EVT_FRAME 1004 // Framing error
|
||||||
|
#define COM_EVT_OVERRUN 1006 // Hardware overrun
|
||||||
|
#define COM_EVT_RXOVER 1008 // Receive buffer overflow
|
||||||
|
#define COM_EVT_RXPARITY 1009 // Parity error
|
||||||
|
#define COM_EVT_TXFULL 1010 // Transmit buffer full
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Handshaking enum values
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define HS_NONE 0
|
||||||
|
#define HS_XONXOFF 1
|
||||||
|
#define HS_RTSCTS 2
|
||||||
|
#define HS_BOTH 3
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// InputMode enum values
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define INPUT_TEXT 0
|
||||||
|
#define INPUT_BINARY 1
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Control instance data
|
||||||
|
//
|
||||||
|
// Allocated by VB as cbCtlExtra bytes per control instance.
|
||||||
|
// Accessed via VBDerefControl(hctl).
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
typedef struct {
|
||||||
|
int16_t commId; // OpenComm handle (-1 = closed)
|
||||||
|
HWND hwndNotify; // Hidden notification window
|
||||||
|
int16_t commPort; // COM port number (1-16)
|
||||||
|
HSZ hszSettings; // Settings string "baud,parity,data,stop"
|
||||||
|
int16_t inBufferSize; // Receive buffer size in bytes
|
||||||
|
int16_t outBufferSize; // Transmit buffer size in bytes
|
||||||
|
int16_t rThreshold; // Receive event threshold (0=disabled)
|
||||||
|
int16_t sThreshold; // Send event threshold (0=disabled)
|
||||||
|
int16_t handshaking; // Handshaking mode (HS_*)
|
||||||
|
int16_t inputLen; // Bytes to read per Input (0=all)
|
||||||
|
int16_t inputMode; // Input mode (INPUT_TEXT/INPUT_BINARY)
|
||||||
|
int16_t commEvent; // Last comm event code
|
||||||
|
BOOL portOpen; // Port open state
|
||||||
|
BOOL dtrEnable; // DTR line enable
|
||||||
|
BOOL rtsEnable; // RTS line enable
|
||||||
|
BOOL breakState; // Break signal active
|
||||||
|
BOOL nullDiscard; // Discard null characters
|
||||||
|
BOOL eofEnable; // Watch for EOF character
|
||||||
|
BOOL ctsState; // Shadow state: CTS line level
|
||||||
|
BOOL dsrState; // Shadow state: DSR line level
|
||||||
|
BOOL cdState; // Shadow state: CD (RLSD) line level
|
||||||
|
HSZ hszParityReplace; // Parity error replacement character
|
||||||
|
} MsCommDataT;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Hidden notification window class name
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define NOTIFY_CLASS "MsCommNotify"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Global instance handle (set in LibMain)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
extern HANDLE ghInstance;
|
||||||
|
|
||||||
|
#endif // MSCOMM_H
|
||||||
7
vbx/mscomm.rc
Normal file
7
vbx/mscomm.rc
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
// mscomm.rc - MSComm VBX resource script
|
||||||
|
//
|
||||||
|
// Defines the toolbox bitmap displayed in the VB IDE custom controls palette.
|
||||||
|
|
||||||
|
#include "mscomm.h"
|
||||||
|
|
||||||
|
IDB_MSCOMM BITMAP mscomm.bmp
|
||||||
253
vbx/serial.c
Normal file
253
vbx/serial.c
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
// serial.c - Serial port abstraction layer
|
||||||
|
//
|
||||||
|
// Wraps Windows 3.1 comm API for use by the MSComm VBX control.
|
||||||
|
// All functions operate on a comm ID returned by serialOpen().
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "serial.h"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Prototypes
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialClose(int16_t commId);
|
||||||
|
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings);
|
||||||
|
int16_t serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold);
|
||||||
|
int16_t serialEscape(int16_t commId, int16_t func);
|
||||||
|
int16_t serialFlush(int16_t commId, int16_t queue);
|
||||||
|
UINT serialGetEventMask(int16_t commId, UINT mask);
|
||||||
|
int16_t serialGetStatus(int16_t commId, COMSTAT FAR *stat);
|
||||||
|
int16_t serialOpen(int16_t port, int16_t inBufSize, int16_t outBufSize);
|
||||||
|
int16_t serialRead(int16_t commId, char FAR *buf, int16_t len);
|
||||||
|
int16_t serialSetHandshaking(int16_t commId, int16_t mode);
|
||||||
|
int16_t serialSetOptions(int16_t commId, BOOL nullDiscard, const char FAR *parityReplace);
|
||||||
|
int16_t serialWrite(int16_t commId, const char FAR *buf, int16_t len);
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialClose - Close an open comm port
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialClose(int16_t commId)
|
||||||
|
{
|
||||||
|
// Drop DTR and RTS before closing
|
||||||
|
EscapeCommFunction(commId, CLRDTR);
|
||||||
|
EscapeCommFunction(commId, CLRRTS);
|
||||||
|
|
||||||
|
return (int16_t)CloseComm(commId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialConfigure - Set baud, parity, data bits, stop bits
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings)
|
||||||
|
{
|
||||||
|
DCB dcb;
|
||||||
|
char buf[64];
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
// Get current state to preserve existing settings
|
||||||
|
rc = GetCommState(commId, &dcb);
|
||||||
|
if (rc != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build DCB from "COMn:baud,parity,data,stop" string
|
||||||
|
wsprintf(buf, "COM%d:%s", (int)port, (LPSTR)settings);
|
||||||
|
rc = BuildCommDCB(buf, &dcb);
|
||||||
|
if (rc != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure binary mode for reliable operation
|
||||||
|
dcb.fBinary = 1;
|
||||||
|
|
||||||
|
// Ensure comm ID matches our open port
|
||||||
|
dcb.Id = (BYTE)commId;
|
||||||
|
|
||||||
|
rc = SetCommState(&dcb);
|
||||||
|
if (rc != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialEnableNotify - Enable WM_COMMNOTIFY messages
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
BOOL serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold)
|
||||||
|
{
|
||||||
|
// Enable event mask for modem status line changes, errors, and breaks
|
||||||
|
SetCommEventMask(commId, EV_CTS | EV_DSR | EV_RLSD | EV_RING | EV_ERR | EV_BREAK | EV_RXCHAR);
|
||||||
|
|
||||||
|
return EnableCommNotification(commId, hwnd, rxThreshold, txThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialEscape - Execute escape function (DTR, RTS, break control)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialEscape(int16_t commId, int16_t func)
|
||||||
|
{
|
||||||
|
LONG rc;
|
||||||
|
|
||||||
|
rc = EscapeCommFunction(commId, func);
|
||||||
|
return (rc == 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialFlush - Flush receive or transmit buffer
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialFlush(int16_t commId, int16_t queue)
|
||||||
|
{
|
||||||
|
return (int16_t)FlushComm(commId, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialGetEventMask - Get and clear event mask bits
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
UINT serialGetEventMask(int16_t commId, UINT mask)
|
||||||
|
{
|
||||||
|
return GetCommEventMask(commId, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialGetStatus - Get error status and buffer counts
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialGetStatus(int16_t commId, COMSTAT FAR *stat)
|
||||||
|
{
|
||||||
|
return (int16_t)GetCommError(commId, stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialOpen - Open a COM port with specified buffer sizes
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialOpen(int16_t port, int16_t inBufSize, int16_t outBufSize)
|
||||||
|
{
|
||||||
|
char name[8];
|
||||||
|
int commId;
|
||||||
|
|
||||||
|
wsprintf(name, "COM%d", (int)port);
|
||||||
|
commId = OpenComm(name, (UINT)inBufSize, (UINT)outBufSize);
|
||||||
|
|
||||||
|
return (int16_t)commId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialRead - Read data from receive buffer
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialRead(int16_t commId, char FAR *buf, int16_t len)
|
||||||
|
{
|
||||||
|
return (int16_t)ReadComm(commId, (LPSTR)buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialSetHandshaking - Apply handshaking mode
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialSetHandshaking(int16_t commId, int16_t mode)
|
||||||
|
{
|
||||||
|
DCB dcb;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = GetCommState(commId, &dcb);
|
||||||
|
if (rc != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear all flow control settings
|
||||||
|
dcb.fOutxCtsFlow = 0;
|
||||||
|
dcb.fOutxDsrFlow = 0;
|
||||||
|
dcb.fOutX = 0;
|
||||||
|
dcb.fInX = 0;
|
||||||
|
dcb.fRtsDisable = 0;
|
||||||
|
dcb.fRtsflow = 0;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case HANDSHAKE_XONXOFF:
|
||||||
|
dcb.fOutX = 1;
|
||||||
|
dcb.fInX = 1;
|
||||||
|
dcb.XonChar = 0x11; // Ctrl-Q
|
||||||
|
dcb.XoffChar = 0x13; // Ctrl-S
|
||||||
|
dcb.XonLim = 256;
|
||||||
|
dcb.XoffLim = 256;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HANDSHAKE_RTSCTS:
|
||||||
|
dcb.fOutxCtsFlow = 1;
|
||||||
|
dcb.fRtsflow = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HANDSHAKE_BOTH:
|
||||||
|
dcb.fOutxCtsFlow = 1;
|
||||||
|
dcb.fRtsflow = 1;
|
||||||
|
dcb.fOutX = 1;
|
||||||
|
dcb.fInX = 1;
|
||||||
|
dcb.XonChar = 0x11;
|
||||||
|
dcb.XoffChar = 0x13;
|
||||||
|
dcb.XonLim = 256;
|
||||||
|
dcb.XoffLim = 256;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HANDSHAKE_NONE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dcb.Id = (BYTE)commId;
|
||||||
|
rc = SetCommState(&dcb);
|
||||||
|
if (rc != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialSetOptions - Apply null-discard and parity-replace settings
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialSetOptions(int16_t commId, BOOL nullDiscard, const char FAR *parityReplace)
|
||||||
|
{
|
||||||
|
DCB dcb;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = GetCommState(commId, &dcb);
|
||||||
|
if (rc != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dcb.fNull = nullDiscard ? 1 : 0;
|
||||||
|
|
||||||
|
if (parityReplace != NULL && parityReplace[0] != '\0') {
|
||||||
|
dcb.fParity = 1;
|
||||||
|
dcb.PeChar = parityReplace[0];
|
||||||
|
} else {
|
||||||
|
dcb.fParity = 0;
|
||||||
|
dcb.PeChar = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
dcb.Id = (BYTE)commId;
|
||||||
|
rc = SetCommState(&dcb);
|
||||||
|
if (rc != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// serialWrite - Write data to transmit buffer
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
int16_t serialWrite(int16_t commId, const char FAR *buf, int16_t len)
|
||||||
|
{
|
||||||
|
return (int16_t)WriteComm(commId, (LPSTR)buf, len);
|
||||||
|
}
|
||||||
106
vbx/serial.h
Normal file
106
vbx/serial.h
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
// serial.h - Serial port abstraction header
|
||||||
|
//
|
||||||
|
// Wraps Windows 3.1 comm API (OpenComm, ReadComm, WriteComm, etc.)
|
||||||
|
// for use by the MSComm VBX control.
|
||||||
|
|
||||||
|
#ifndef SERIAL_H
|
||||||
|
#define SERIAL_H
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// stdint types for MSVC 1.52 (no stdint.h available)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#ifndef _STDINT_DEFINED
|
||||||
|
typedef short int16_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef long int32_t;
|
||||||
|
typedef unsigned long uint32_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef signed char int8_t;
|
||||||
|
#define _STDINT_DEFINED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Handshaking modes
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define HANDSHAKE_NONE 0
|
||||||
|
#define HANDSHAKE_XONXOFF 1
|
||||||
|
#define HANDSHAKE_RTSCTS 2
|
||||||
|
#define HANDSHAKE_BOTH 3
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Flush queue selectors
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define FLUSH_RX 0
|
||||||
|
#define FLUSH_TX 1
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Escape function constants (mirrors EscapeCommFunction values)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define SERIAL_SETDTR SETDTR
|
||||||
|
#define SERIAL_CLRDTR CLRDTR
|
||||||
|
#define SERIAL_SETRTS SETRTS
|
||||||
|
#define SERIAL_CLRRTS CLRRTS
|
||||||
|
#define SERIAL_SETBREAK SETBREAK
|
||||||
|
#define SERIAL_CLRBREAK CLRBREAK
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Function prototypes
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Open a COM port with specified buffer sizes.
|
||||||
|
// Returns comm ID (>= 0) on success, negative on error.
|
||||||
|
int16_t serialOpen(int16_t port, int16_t inBufSize, int16_t outBufSize);
|
||||||
|
|
||||||
|
// Close an open comm port. Drops DTR and RTS before closing.
|
||||||
|
// Returns 0 on success, negative on error.
|
||||||
|
int16_t serialClose(int16_t commId);
|
||||||
|
|
||||||
|
// Configure baud, parity, data bits, stop bits from a settings string.
|
||||||
|
// settings format: "baud,parity,data,stop" (e.g., "9600,N,8,1")
|
||||||
|
// Returns 0 on success, negative on error.
|
||||||
|
int16_t serialConfigure(int16_t commId, int16_t port, const char FAR *settings);
|
||||||
|
|
||||||
|
// Apply handshaking mode to an open port.
|
||||||
|
// mode: HANDSHAKE_NONE, HANDSHAKE_XONXOFF, HANDSHAKE_RTSCTS, HANDSHAKE_BOTH
|
||||||
|
// Returns 0 on success, negative on error.
|
||||||
|
int16_t serialSetHandshaking(int16_t commId, int16_t mode);
|
||||||
|
|
||||||
|
// Apply null-discard and parity-replace settings to an open port.
|
||||||
|
// Returns 0 on success, negative on error.
|
||||||
|
int16_t serialSetOptions(int16_t commId, BOOL nullDiscard, const char FAR *parityReplace);
|
||||||
|
|
||||||
|
// Read data from receive buffer.
|
||||||
|
// Returns number of bytes read, or negative on error.
|
||||||
|
int16_t serialRead(int16_t commId, char FAR *buf, int16_t len);
|
||||||
|
|
||||||
|
// Write data to transmit buffer.
|
||||||
|
// Returns number of bytes written, or negative on error.
|
||||||
|
int16_t serialWrite(int16_t commId, const char FAR *buf, int16_t len);
|
||||||
|
|
||||||
|
// Get comm error status and buffer counts. Clears the error state.
|
||||||
|
// Returns error flags, fills stat with buffer counts.
|
||||||
|
int16_t serialGetStatus(int16_t commId, COMSTAT FAR *stat);
|
||||||
|
|
||||||
|
// Execute an escape function (DTR, RTS, break control).
|
||||||
|
// func: SERIAL_SETDTR, SERIAL_CLRDTR, SERIAL_SETRTS, etc.
|
||||||
|
// Returns 0 on success, negative on error.
|
||||||
|
int16_t serialEscape(int16_t commId, int16_t func);
|
||||||
|
|
||||||
|
// Get modem status lines via GetCommEventMask.
|
||||||
|
// Returns event mask bits (EV_CTS, EV_DSR, EV_RLSD, EV_RING).
|
||||||
|
UINT serialGetEventMask(int16_t commId, UINT mask);
|
||||||
|
|
||||||
|
// Enable WM_COMMNOTIFY messages to the specified window.
|
||||||
|
// rxThreshold: fire CN_RECEIVE when this many bytes available (-1=disable)
|
||||||
|
// txThreshold: fire CN_TRANSMIT when this much space free (-1=disable)
|
||||||
|
// Returns TRUE on success.
|
||||||
|
BOOL serialEnableNotify(int16_t commId, HWND hwnd, int16_t rxThreshold, int16_t txThreshold);
|
||||||
|
|
||||||
|
// Flush receive and/or transmit buffers.
|
||||||
|
// queue: FLUSH_RX or FLUSH_TX
|
||||||
|
// Returns 0 on success, negative on error.
|
||||||
|
int16_t serialFlush(int16_t commId, int16_t queue);
|
||||||
|
|
||||||
|
#endif // SERIAL_H
|
||||||
29
vbx/vbapi.def
Normal file
29
vbx/vbapi.def
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
; vbapi.def - VB API import library definition
|
||||||
|
;
|
||||||
|
; Defines the VB API functions exported by the VBAPI module.
|
||||||
|
; Use with IMPLIB to generate the import library:
|
||||||
|
;
|
||||||
|
; implib vbapi.lib vbapi.def
|
||||||
|
;
|
||||||
|
; The VBAPI module is the VB runtime that exports these functions.
|
||||||
|
; When VB loads a VBX, it resolves imports against this module.
|
||||||
|
|
||||||
|
LIBRARY VBAPI
|
||||||
|
|
||||||
|
EXPORTS
|
||||||
|
VBRegisterModel @100
|
||||||
|
VBDefControlProc @101
|
||||||
|
VBFireEvent @102
|
||||||
|
VBCreateHsz @103
|
||||||
|
VBDestroyHsz @104
|
||||||
|
VBDerefHsz @105
|
||||||
|
VBCreateHlstr @106
|
||||||
|
VBDestroyHlstr @107
|
||||||
|
VBGetHlstr @108
|
||||||
|
VBSetHlstr @109
|
||||||
|
VBDerefControl @110
|
||||||
|
VBGetMode @111
|
||||||
|
VBSetControlProperty @112
|
||||||
|
VBGetControlProperty @113
|
||||||
|
VBGetControlHwnd @114
|
||||||
|
VBGetHInstance @115
|
||||||
266
vbx/vbapi.h
Normal file
266
vbx/vbapi.h
Normal file
|
|
@ -0,0 +1,266 @@
|
||||||
|
// vbapi.h - Reconstructed VBX CDK header
|
||||||
|
//
|
||||||
|
// Reconstructed from the published Visual Basic Custom Control (VBX)
|
||||||
|
// specification for use with MSVC 1.52 targeting 16-bit Visual Basic 4.
|
||||||
|
// Types, structures, constants, and API prototypes match the VBX CDK.
|
||||||
|
|
||||||
|
#ifndef VBAPI_H
|
||||||
|
#define VBAPI_H
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Packing: VBX CDK structures use byte packing
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#pragma pack(1)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Core types
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
typedef WORD HCTL; // Handle to a VBX control instance
|
||||||
|
typedef WORD HSZ; // Handle to a VB-managed string
|
||||||
|
typedef LONG HLSTR; // Handle to a VB long string
|
||||||
|
typedef USHORT ERR; // Error code
|
||||||
|
|
||||||
|
// Segment type for MSVC 1.52
|
||||||
|
#ifndef _SEGMENT_DEFINED
|
||||||
|
typedef unsigned short _segment;
|
||||||
|
#define _SEGMENT_DEFINED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Flag type
|
||||||
|
typedef DWORD FL;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Forward declarations and pointer types
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
typedef struct tagMODEL MODEL;
|
||||||
|
typedef struct tagPROPINFO PROPINFO;
|
||||||
|
typedef struct tagEVENTINFO EVENTINFO;
|
||||||
|
typedef struct tagPARMINFO PARMINFO;
|
||||||
|
|
||||||
|
typedef MODEL FAR *LPMODEL;
|
||||||
|
typedef PROPINFO *PPROPINFO; // Near pointer (DLL data segment)
|
||||||
|
typedef EVENTINFO *PEVENTINFO; // Near pointer (DLL data segment)
|
||||||
|
typedef PARMINFO *PPARMINFO; // Near pointer (DLL data segment)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Control procedure type
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
typedef LONG (FAR PASCAL *PCTLPROC)(HCTL, HWND, USHORT, USHORT, LONG);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// VB version constants
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define VB_VERSION 0x0300
|
||||||
|
#define VB300_VERSION 0x0300
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Data types (bits 0-7 of PROPINFO.fl)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define DT_HSZ 0x01
|
||||||
|
#define DT_SHORT 0x02
|
||||||
|
#define DT_LONG 0x03
|
||||||
|
#define DT_BOOL 0x04
|
||||||
|
#define DT_COLOR 0x05
|
||||||
|
#define DT_ENUM 0x06
|
||||||
|
#define DT_REAL 0x07
|
||||||
|
#define DT_XPOS 0x08
|
||||||
|
#define DT_XSIZE 0x09
|
||||||
|
#define DT_YPOS 0x0A
|
||||||
|
#define DT_YSIZE 0x0B
|
||||||
|
#define DT_PICTURE 0x0C
|
||||||
|
#define DT_HLSTR 0x0D
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Property flags (bits 8+ of PROPINFO.fl)
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define PF_datatype 0x000000FFL // Mask for data type field
|
||||||
|
#define PF_fPropArray 0x00000100L
|
||||||
|
#define PF_fSetData 0x00000200L
|
||||||
|
#define PF_fSetMsg 0x00000400L
|
||||||
|
#define PF_fNoShow 0x00000800L
|
||||||
|
#define PF_fNoRuntimeW 0x00001000L
|
||||||
|
#define PF_fGetData 0x00002000L
|
||||||
|
#define PF_fGetMsg 0x00004000L
|
||||||
|
#define PF_fGetHszMsg 0x00008000L
|
||||||
|
#define PF_fUpdateOnEdit 0x00010000L
|
||||||
|
#define PF_fEditable 0x00020000L
|
||||||
|
#define PF_fPreHwnd 0x00040000L
|
||||||
|
#define PF_fDefVal 0x00080000L
|
||||||
|
#define PF_fNoInitDef 0x00100000L
|
||||||
|
#define PF_fNoRuntimeR 0x00200000L
|
||||||
|
#define PF_fSaveData 0x00400000L
|
||||||
|
#define PF_fSaveMsg 0x00800000L
|
||||||
|
#define PF_fLoadDataOnly 0x01000000L
|
||||||
|
#define PF_fLoadMsgOnly 0x02000000L
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// MODEL flags
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define MODEL_fArrows 0x00000001L
|
||||||
|
#define MODEL_fFocusOk 0x00000002L
|
||||||
|
#define MODEL_fMnemonic 0x00000004L
|
||||||
|
#define MODEL_fDesInteract 0x00000008L
|
||||||
|
#define MODEL_fInitMsg 0x00000010L
|
||||||
|
#define MODEL_fLoadMsg 0x00000020L
|
||||||
|
#define MODEL_fInvisAtRun 0x00000040L
|
||||||
|
#define MODEL_fGraphical 0x00000080L
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// VBX control messages
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define VBM__BASE (WM_USER + 0x0600)
|
||||||
|
#define VBM_INITIALIZE (VBM__BASE + 0)
|
||||||
|
#define VBM_SETPROPERTY (VBM__BASE + 1)
|
||||||
|
#define VBM_GETPROPERTY (VBM__BASE + 2)
|
||||||
|
#define VBM_CHECKPROPERTY (VBM__BASE + 3)
|
||||||
|
#define VBM_MNEMONIC (VBM__BASE + 4)
|
||||||
|
#define VBM_CREATED (VBM__BASE + 5)
|
||||||
|
#define VBM_LOADED (VBM__BASE + 6)
|
||||||
|
#define VBM_SAVEPROPERTY (VBM__BASE + 7)
|
||||||
|
#define VBM_LOADPROPERTY (VBM__BASE + 8)
|
||||||
|
#define VBM_METHOD (VBM__BASE + 9)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Error codes
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define ERR_None 0
|
||||||
|
#define ERR_InvPropVal 380 // Invalid property value
|
||||||
|
#define ERR_InvPropSet 383 // Can't set property at this time
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Standard property pointers
|
||||||
|
//
|
||||||
|
// Magic values recognized by VB as built-in properties.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define PPROPINFO_STD_CTLNAME ((PPROPINFO)0)
|
||||||
|
#define PPROPINFO_STD_INDEX ((PPROPINFO)1)
|
||||||
|
#define PPROPINFO_STD_HWND ((PPROPINFO)2)
|
||||||
|
#define PPROPINFO_STD_TAG ((PPROPINFO)5)
|
||||||
|
#define PPROPINFO_STD_LEFT ((PPROPINFO)6)
|
||||||
|
#define PPROPINFO_STD_TOP ((PPROPINFO)7)
|
||||||
|
#define PPROPINFO_STD_WIDTH ((PPROPINFO)8)
|
||||||
|
#define PPROPINFO_STD_HEIGHT ((PPROPINFO)9)
|
||||||
|
#define PPROPINFO_STD_ENABLED ((PPROPINFO)17)
|
||||||
|
#define PPROPINFO_STD_VISIBLE ((PPROPINFO)18)
|
||||||
|
#define PPROPINFO_STD_PARENT ((PPROPINFO)21)
|
||||||
|
#define PPROPINFO_STD_DRAGMODE ((PPROPINFO)22)
|
||||||
|
#define PPROPINFO_STD_DRAGICON ((PPROPINFO)23)
|
||||||
|
#define PPROPINFO_STD_NONE ((PPROPINFO)NULL)
|
||||||
|
|
||||||
|
// Aliases matching the plan's naming convention
|
||||||
|
#define PPROPINFO_STD_NAME PPROPINFO_STD_CTLNAME
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// PROPINFO - Property descriptor
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
struct tagPROPINFO {
|
||||||
|
PSTR npszName; // Property name (near pointer, DLL DS)
|
||||||
|
FL fl; // Data type (bits 0-7) | Property flags (bits 8+)
|
||||||
|
BYTE offsetData; // Byte offset in control's cbCtlExtra data
|
||||||
|
BYTE infoData; // Type-specific info (e.g., max string length)
|
||||||
|
LONG dataDefault; // Default value
|
||||||
|
PSTR npszEnumList; // DT_ENUM: null-separated, double-null terminated
|
||||||
|
BYTE enumMax; // DT_ENUM: maximum valid value
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// PARMINFO - Event parameter descriptor
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
struct tagPARMINFO {
|
||||||
|
PSTR npszName; // Parameter name
|
||||||
|
FL fl; // Data type
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// EVENTINFO - Event descriptor
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
struct tagEVENTINFO {
|
||||||
|
PSTR npszName; // Event name
|
||||||
|
USHORT cParms; // Number of parameters
|
||||||
|
USHORT cwParms; // Size of parameters in words
|
||||||
|
PPARMINFO npParmInfo; // Parameter info array (NULL if no params)
|
||||||
|
PSTR npszParmProf; // Parameter profile string (e.g., "value As Integer")
|
||||||
|
FL fl; // Event flags
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// MODEL - Control model definition
|
||||||
|
//
|
||||||
|
// VB3+ version (usVersion = VB300_VERSION) for VB4 compatibility.
|
||||||
|
// Registered with VBRegisterModel() in VBINITCC.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
struct tagMODEL {
|
||||||
|
USHORT usVersion; // VB_VERSION (0x0300)
|
||||||
|
FL fl; // MODEL_* flags
|
||||||
|
PCTLPROC pctlproc; // Control procedure
|
||||||
|
USHORT fsClassStyle; // Window class style bits
|
||||||
|
USHORT flWndStyle; // Window style bits
|
||||||
|
USHORT cbCtlExtra; // Bytes of per-instance data
|
||||||
|
USHORT idBmpPalette; // Toolbox bitmap resource ID
|
||||||
|
PSTR npszDefCtlName; // Default control name (near)
|
||||||
|
PSTR npszClassName; // Window class name (NULL = default)
|
||||||
|
PSTR npszParentClassName; // Parent class name (NULL = default)
|
||||||
|
PPROPINFO *npproplist; // Property list (NULL-terminated array)
|
||||||
|
PEVENTINFO *npeventlist; // Event list (NULL-terminated array)
|
||||||
|
BYTE nDefProp; // Default property index
|
||||||
|
BYTE nDefEvent; // Default event index
|
||||||
|
BYTE nValueProp; // Value property index
|
||||||
|
USHORT usCtlVersion; // Control version (BCD, user-defined)
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Utility macro: byte offset of a field within a structure
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#define OFFSETIN(type, field) ((BYTE)&(((type *)0)->field))
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// VB API function prototypes
|
||||||
|
//
|
||||||
|
// These functions are exported by the VB runtime (VBAPI module) and
|
||||||
|
// resolved at load time when VB loads the VBX.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Control registration
|
||||||
|
BOOL FAR PASCAL VBRegisterModel(HANDLE hmodule, MODEL FAR *lpmodel);
|
||||||
|
|
||||||
|
// Default control procedure
|
||||||
|
LONG FAR PASCAL VBDefControlProc(HCTL hctl, HWND hwnd, USHORT msg, USHORT wp, LONG lp);
|
||||||
|
|
||||||
|
// Event firing
|
||||||
|
ERR FAR PASCAL VBFireEvent(HCTL hctl, USHORT iEvent, LPVOID lpparams);
|
||||||
|
|
||||||
|
// String handle management
|
||||||
|
HSZ FAR PASCAL VBCreateHsz(_segment seg, LPSTR lpsz);
|
||||||
|
void FAR PASCAL VBDestroyHsz(HSZ hsz);
|
||||||
|
LPSTR FAR PASCAL VBDerefHsz(HSZ hsz);
|
||||||
|
|
||||||
|
// Long string handle management
|
||||||
|
HLSTR FAR PASCAL VBCreateHlstr(LPVOID lpdata, USHORT cbLen);
|
||||||
|
void FAR PASCAL VBDestroyHlstr(HLSTR hlstr);
|
||||||
|
LPSTR FAR PASCAL VBGetHlstr(HLSTR hlstr, USHORT FAR *lpcbLen);
|
||||||
|
ERR FAR PASCAL VBSetHlstr(HLSTR FAR *phlstr, LPVOID lpdata, USHORT cbLen);
|
||||||
|
|
||||||
|
// Control data access
|
||||||
|
LPVOID FAR PASCAL VBDerefControl(HCTL hctl);
|
||||||
|
|
||||||
|
// Runtime mode query (TRUE = runtime, FALSE = design)
|
||||||
|
BOOL FAR PASCAL VBGetMode(void);
|
||||||
|
|
||||||
|
// Control property access
|
||||||
|
ERR FAR PASCAL VBSetControlProperty(HCTL hctl, USHORT iProp, LONG data);
|
||||||
|
LONG FAR PASCAL VBGetControlProperty(HCTL hctl, USHORT iProp);
|
||||||
|
|
||||||
|
// Control window handle
|
||||||
|
HWND FAR PASCAL VBGetControlHwnd(HCTL hctl);
|
||||||
|
|
||||||
|
// Instance handle
|
||||||
|
HANDLE FAR PASCAL VBGetHInstance(void);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Restore default packing
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
#pragma pack()
|
||||||
|
|
||||||
|
#endif // VBAPI_H
|
||||||
Loading…
Add table
Reference in a new issue