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 , got 10
1 bytes available, read: 10
Expecting , got 10
1 bytes available, read: 10
Expecting , 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
#include
#include
#include
#include
#include
#include
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 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 , got %i\n",resp);
// Send EOT
n = write(fd,&eot,1);
if (n != 1) {printf("Failed to write .\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 = 250