Why are there two incompatible ways of specifying a serial port baud rate?
One of my colleagues tracked down a bug in their code that communicates over a serial port. (Remember serial ports?)
DCB structure specifies the baud rate as an integer. To request 2400 baud, you set the
BaudRate to 2400. There are some convenient defined constants for this purpose.
#define CBR_110 110 #define CBR_300 300 #define CBR_600 600 #define CBR_1200 1200 #define CBR_2400 2400 #define CBR_4800 4800 #define CBR_9600 9600 #define CBR_14400 14400 #define CBR_19200 19200 #define CBR_38400 38400 #define CBR_56000 56000 #define CBR_57600 57600 #define CBR_115200 115200 #define CBR_128000 128000 #define CBR_256000 256000
COMMPROP structure also has a way of specifying the baud rate, but it is done by setting the
dwMaxBaud to a bitmask:
#define BAUD_075 ((DWORD)0x00000001) #define BAUD_110 ((DWORD)0x00000002) #define BAUD_134_5 ((DWORD)0x00000004) #define BAUD_150 ((DWORD)0x00000008) #define BAUD_300 ((DWORD)0x00000010) #define BAUD_600 ((DWORD)0x00000020) #define BAUD_1200 ((DWORD)0x00000040) #define BAUD_1800 ((DWORD)0x00000080) #define BAUD_2400 ((DWORD)0x00000100) #define BAUD_4800 ((DWORD)0x00000200) #define BAUD_7200 ((DWORD)0x00000400) #define BAUD_9600 ((DWORD)0x00000800) #define BAUD_14400 ((DWORD)0x00001000) #define BAUD_19200 ((DWORD)0x00002000) #define BAUD_38400 ((DWORD)0x00004000) #define BAUD_56K ((DWORD)0x00008000) #define BAUD_128K ((DWORD)0x00010000) #define BAUD_115200 ((DWORD)0x00020000) #define BAUD_57600 ((DWORD)0x00040000) #define BAUD_USER ((DWORD)0x10000000)
My colleague accidentally set the
DCB.BaudRate to a
BAUD_xxx value, and since these values are untyped integers, there was no compiler warning.
My colleague asked for the historical background behind why there are two easily-confused ways of doing the same thing.
DCB structure dates back to 16-bit Windows. It tracks the feature set of the 8250 UART, since that is what came with the IBM PC XT.¹ In particular, there is no need to ask what baud rates are supported by the serial chip because you already know what baud rates are supported by the serial chip: The 8250 and 16650 support baud rates that are divisors of 115200.²
Enter Windows NT. This operating system wanted to run on things that weren’t IBM PCs. Crazy. In particular, those systems may have serial communications chips that support a different set of baud rates. That’s where the
COMMPROP structure came in: It reports baud rates as a bitmask that is filled out by the
GetCommProperties function. That way, the program that wants to do serial communications can find out what baud rates are supported by the current hardware. And since it’s reporting a set of values, a bitmask seems the natural way of representing it.
The program inspects the bitmask, decides which of the available baud rates it wants to use, and puts the desired value (as an integer, not a bitmask) in the
BaudRate member of the
That’s my attempt to reverse-engineer the history of the two incompatible ways of representing baud rates.
¹ The PS/2 line introduced the 16550 UART which is backward-compatible with the 8250. In particular, it supports the same baud rates.
² Other baud rates like 110 are approximations. For example 110 is really 115200 ÷ 1048 = 109.92 baud. This article claims that microcontrollers “rarely offer an internal oscillator that has accuracy better than ±1.5%,” so an error of 0.07% is easily lost in the jitter.