I run OpenBSD 4.6 (i386) on a PCEngines ALIX2c3, as a low power file/web/DHCP server. I would like to have this machine regularly retrieve data from an instrument which communicates over RS-232. I'm using a Prolific USB-RS232 converter (full dmesg for the ALIX below).
I have no protocol documentation but have been able to reverse engineer a subset of the instrument's protocol. I have written a code to extract data from the instrument which compiles cleanly on OpenBSD, Linux (gcc) and Solaris x86 (Sun Studio). On Linux and Solaris, the resulting binary always reads the data that I want. On OpenBSD, the code appears to hang on every second execution, its entry in the output of top looking similar to: 0 204K 496K idle ttyin 0:00 0.00% reader I've come up with a minimal example to demonstrate where the problem begins, and have pasted it below. The code sends an initialisation string to the instrument and should get an ACK (ASCII decimal 6) back. On Linux it works; on OpenBSD, it works the first time after insertion of the USB-RS232 converter but not subsequently. I don't believe this is likely to be a hardware problem with the USB-RS232 converter, since I successfully used the same one in Linux and Solaris. I also think it unlikely to be the USB host controller, since I have identical behaviour on another OpenBSD box, with a different USB controller, see lines from its dmesg: uhci0 at pci0 dev 7 function 2 "Intel 82371AB USB" rev 0x01: irq 11 usb0 at uhci0: USB revision 1.0 uhub0 at usb0 "Intel UHCI root hub" rev 1.00/1.00 addr 1 uplcom0 at uhub0 port 2 "Prolific Technology Inc. USB-Serial Controller D" rev 1.10/4.00 addr 2 ucom0 at uplcom0 Running on OpenBSD, either on the ALIX or on my other box, the behaviour I see is: # cc -Wall -o ex_obsd ex_obsd.c # ./ex_obsd; sleep 5; ./ex_obsd; sleep 5; ./ex_obsd; sleep 5; ./ex_obsd 1 bytes available, read: 6 1 bytes available, read: 10 Expecting <ACK>, got 10 1 bytes available, read: 10 Expecting <ACK>, got 10 1 bytes available, read: 10 Expecting <ACK>, got 10 Running identical code (save for a change of device name) on Linux (on another different machine), I get: $ ./ex_lin; sleep 5; ./ex_lin; sleep 5; ./ex_lin; sleep 5; ./ex_lin 1 bytes available, read: 6 1 bytes available, read: 6 1 bytes available, read: 6 1 bytes available, read: 6 On OpenBSD, I can get the desired behaviour if I manually unplug and re-plug the USB to serial converter during each sleep. I'd be very grateful if anyone has any advice on what I should change to get the desired behaviour from this code on OpenBSD. Many thanks in advance. Neil My code: #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <termios.h> #include <err.h> int open_port(void); /* Opens port, returns file descriptor */ void set_port(int fd); /* Sets port options on file descriptor fd */ int main(void) { int n,fd; char resp; char initString[] = {0x0D, 0x01, 0x39, 0x31, 0x38}; char ack = 0x06; char eot = 0x03; fd = open_port(); set_port(fd); if(DEBUG != 0) printf("Port has been opened and set up\n"); // send <CR><SOH>918 n = write(fd,initString,5); if (n != 5) {printf("Failed to write init string.\n"); return(-1);} // See if it replies - want ACK n = read(fd,&resp,1); printf("%i bytes available, read: %i\n",n,resp); if (resp != ack) printf("Expecting <ACK>, got %i\n",resp); // Send EOT n = write(fd,&eot,1); if (n != 1) {printf("Failed to write <EOT>.\n"); return(-1);} // Close the port n = close(fd); if (n != 0) { printf("failed to close port: close returned %d\n",n); return(-1); } return 0; } void set_port(int fd) { struct termios options; int status; // Get current settings for the port tcgetattr(fd, &options); // Set baud rate = 1200 cfsetispeed(&options, B1200); cfsetospeed(&options, B1200); // Set 7 bits, even parity options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS7; // Raw input options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Check and strip parity bit. options.c_iflag |= (INPCK | ISTRIP); // Disable software flow control options.c_iflag &= ~(IXON | IXOFF | IXANY); // Output to be raw options.c_oflag &= ~(OPOST | OLCUC | ONLCR | OCRNL ); // Set new options for port status = tcsetattr(fd, TCSANOW, &options); if (status < 0) { errx(1,"set_port(): failed."); } } int open_port(void) { int fd; fd = open("/dev/ttyU0", O_RDWR | O_NOCTTY | O_NDELAY); if (fd < 0) { errx(1,"open_port: Unable to open /dev/ttyS0."); } else { fcntl(fd, F_SETFL, 0); } return (fd); } dmesg from ALIX: OpenBSD 4.6 (GENERIC) #0: Thu Apr 8 14:17:46 BST 2010 r...@alix:/usr/src/sys/arch/i386/compile/GENERIC cpu0: Geode(TM) Integrated Processor by AMD PCS ("AuthenticAMD" 586-class) 499 MHz cpu0: FPU,DE,PSE,TSC,MSR,CX8,SEP,PGE,CMOV,CFLUSH,MMX real mem = 268009472 (255MB) avail mem = 250335232 (238MB) mainbus0 at root bios0 at mainbus0: AT/286+ BIOS, date 12/10/07, BIOS32 rev. 0 @ 0xfceb2 pcibios0 at bios0: rev 2.1 @ 0xf0000/0x10000 pcibios0: pcibios_get_intr_routing - function not supported pcibios0: PCI IRQ Routing information unavailable. pcibios0: PCI bus #0 is the last bus bios0: ROM list: 0xe0000/0xa800 cpu0 at mainbus0: (uniprocessor) pci0 at mainbus0 bus 0: configuration mode 1 (bios) pchb0 at pci0 dev 1 function 0 "AMD Geode LX" rev 0x33 glxsb0 at pci0 dev 1 function 2 "AMD Geode LX Crypto" rev 0x00: RNG AES vr0 at pci0 dev 9 function 0 "VIA VT6105M RhineIII" rev 0x96: irq 10, address 00:0d:b9:14:1c:2c ukphy0 at vr0 phy 1: Generic IEEE 802.3u media interface, rev. 3: OUI 0x004063, model 0x0034 vr1 at pci0 dev 10 function 0 "VIA VT6105M RhineIII" rev 0x96: irq 11, address 00:0d:b9:14:1c:2d ukphy1 at vr1 phy 1: Generic IEEE 802.3u media interface, rev. 3: OUI 0x004063, model 0x0034 vr2 at pci0 dev 11 function 0 "VIA VT6105M RhineIII" rev 0x96: irq 12, address 00:0d:b9:14:1c:2e ukphy2 at vr2 phy 1: Generic IEEE 802.3u media interface, rev. 3: OUI 0x004063, model 0x0034 glxpcib0 at pci0 dev 15 function 0 "AMD CS5536 ISA" rev 0x03: rev 0, 32-bit 3579545Hz timer, watchdog, gpio gpio0 at glxpcib0: 32 pins pciide0 at pci0 dev 15 function 2 "AMD CS5536 IDE" rev 0x01: DMA, channel 0 wired to compatibility, channel 1 wired to compatibility wd0 at pciide0 channel 0 drive 0: <ELITE PRO CF CARD 8GB> wd0: 1-sector PIO, LBA, 7695MB, 15761088 sectors wd1 at pciide0 channel 0 drive 1: <WDC WD2500BEVE-00WZT0> wd1: 16-sector PIO, LBA48, 238475MB, 488397168 sectors wd0(pciide0:0:0): using PIO mode 4, Ultra-DMA mode 2 wd1(pciide0:0:1): using PIO mode 4, Ultra-DMA mode 2 pciide0: channel 1 ignored (disabled) ohci0 at pci0 dev 15 function 4 "AMD CS5536 USB" rev 0x02: irq 15, version 1.0, legacy support ehci0 at pci0 dev 15 function 5 "AMD CS5536 USB" rev 0x02: irq 15 usb0 at ehci0: USB revision 2.0 uhub0 at usb0 "AMD EHCI root hub" rev 2.00/1.00 addr 1 isa0 at glxpcib0 isadma0 at isa0 com0 at isa0 port 0x3f8/8 irq 4: ns16550a, 16 byte fifo com0: console pcppi0 at isa0 port 0x61 midi0 at pcppi0: <PC speaker> spkr0 at pcppi0 npx0 at isa0 port 0xf0/16: reported by CPUID; using exception 16 usb1 at ohci0: USB revision 1.0 uhub1 at usb1 "AMD OHCI root hub" rev 1.00/1.00 addr 1 biomask e3ef netmask ffef ttymask ffff mtrr: K6-family MTRR support (2 registers) nvram: invalid checksum uplcom0 at uhub1 port 2 "Prolific Technology Inc. USB-Serial Controller D" rev 1.10/4.00 addr 2 ucom0 at uplcom0 softraid0 at root root on wd0a swap on wd0b dump on wd0b clock: unknown CMOS layout