Mailing List Archive

serial port handling question
G'day,

I'm porting some old DOS code to Linux for a medical device that is
being upgraded. Among other goodies, it has a sensor that sends data at
115KB to an onboard NS16550A (or equivalent).

The sensor is controlled (in part) by setting RTS on and off. I looked
high and low (pun intended) for an ioctl or similar call that would
allow this level of control and couldn't find anything. I finally ended
up using the ollowing lines of code:

outb(inportb(MCR) | 0x02, MCR); //DTR,RTS=ON
outb(inportb(MCR) & ~0x02, MCR); //DTR=ON,RTS=OFF

Directly tweaking the I/O port runs against the grain, but it's the
only thing I've found that works.

Is there a better way to control the chip?

Regards,

David
Re: serial port handling question [ In reply to ]
David Relson wrote:
> I'm porting some old DOS code to Linux for a medical device that is
> being upgraded.

Interesting, this business.


> The sensor is controlled (in part) by setting RTS on and off.

What is controlled, exactly? What is RTS being used for? If it is
indeed flow control then you are lucky and can simply enable hardware
flow control for the serial port, and Linux will then take care of
everything for you.

If not flow control and some other signalling, you have to write a
line discipline driver. I have done both this and serial drivers
(also related to DOS era equipment) and documentation is not the
greatest. Let me know if you would like some help.


> I looked high and low (pun intended) for an ioctl or similar call
> that would allow this level of control and couldn't find anything.

The best thing out there is tcsetattr() and friends.

By switching between baud rate 0 and something else you can reliably
and easily control both RTS and DTR, and nothing but RTS and DTR, but
always both at the same time.

Line disciplines can call the tty_throttle() and tty_unthrottle()
functions in the serial driver, which will then control RTS
accordingly, but the default TTY line discipline does not expose any
API that will result in throttle function calls.


> outb(inportb(MCR) | 0x02, MCR); //DTR,RTS=ON
> outb(inportb(MCR) & ~0x02, MCR); //DTR=ON,RTS=OFF
>
> Directly tweaking the I/O port runs against the grain, but it's the
> only thing I've found that works.

Not only against the grain, it can mess up internal state in the
kernel serial layer and worst case lead to a kernel BUG_ON (kernel
hangs) or best case serial port hang (unhang e.g. by closing all file
handles for the port and opening again). It is not at all nice to
change these signals behind Linux' back.


//Peter
Re: serial port handling question [ In reply to ]
I've never actually used the RTS on - always had it off. But just in
case you didn't find the details there's a setting in the termios
options structure for rts - have you tried that?

'man termios' - look for CRTSCTS.

I usually use a bit of code to set the flag off for my connections:

*******************************

termios options;
// Disable Flow control
#if defined(CRTSCTS)
    options.c_cflag &= ~CRTSCTS; // Disable hardware flow control (old)
#elif defined (CNEW_RTSCTS)
    options.c_cflag &= ~CNEW_RTSCTS; // Disable hardware flow control (new)
#endif
tcsetattr(file_descriptor,TCSAFLUSH,&options);

*******************************

Above, file_descriptor is the handle you get back from the previously
called open(...).

2010/1/14 David Relson <relson@osagesoftware.com>
>
> G'day,
>
> I'm porting some old DOS code to Linux for a medical device that is
> being upgraded.  Among other goodies, it has a sensor that sends data at
> 115KB to an onboard NS16550A (or equivalent).
>
> The sensor is controlled (in part) by setting RTS on and off. I looked
> high and low (pun intended) for an ioctl or similar call that would
> allow this level of control and couldn't find anything. I finally ended
> up using the ollowing lines of code:
>
>  outb(inportb(MCR) |  0x02, MCR);  //DTR,RTS=ON
>  outb(inportb(MCR) & ~0x02, MCR);  //DTR=ON,RTS=OFF
>
> Directly tweaking the I/O port runs against the grain, but it's the
> only thing I've found that works.
>
> Is there a better way to control the chip?
>
> Regards,
>
> David
>



--
Phone : +82-10-5400-3296 (010-5400-3296)
HomePage: http://snorriheim.dnsdojo.com/
Yujin Robot: http://www.yujinrobot.com/
Projects: http://snorriheim.dnsdojo.com/redmine/projects
Embedded Control Libraries: http://snorriheim.dnsdojo.com/redmine/wiki/ecl
Re: serial port handling question [ In reply to ]
Daniel Stonier wrote:
> there's a setting in the termios options structure for rts - have
> you tried that?
>
> 'man termios' - look for CRTSCTS.

Note that this flag does not directly control RTS, but rather it
instructs the kernel whether RTS/CTS signalling should be used for
flow control or not.

As David noticed, there is no API for directly controlling RTS in
Linux, it can only be done by the serial driver itself or a line
discipline driver.


//Peter
Re: serial port handling question [ In reply to ]
On Thu, 14 Jan 2010 04:47:53 +0100
Peter Stuge wrote:

> Daniel Stonier wrote:
> > there's a setting in the termios options structure for rts - have
> > you tried that?
> >
> > 'man termios' - look for CRTSCTS.
>
> Note that this flag does not directly control RTS, but rather it
> instructs the kernel whether RTS/CTS signalling should be used for
> flow control or not.
>
> As David noticed, there is no API for directly controlling RTS in
> Linux, it can only be done by the serial driver itself or a line
> discipline driver.
>
>
> //Peter

Hello Peter and Daniel,

Information greatly appreciated. I'll digest it tomorrow when I'm more
alert.

After bashing my head against the wall (including experimentation with
CRTSCTS), I spent a while reading drivers/serial/8250.c and found that
CRTSCTS used only for enabling AFE (automatic flowcontrol enabled).
Not having adequate specs for the protocol or hardware involved,
there's some mystery as to exactly why RTS is used as it is.

For certain, it'll be interesting tomorrow to learn more of termios and
line discipline drivers (a new term for me).

Ciao,

David
Re: serial port handling question [ In reply to ]
On Thu, 14 Jan 2010 03:55:07 +0100
Peter Stuge wrote:

> David Relson wrote:
> > I'm porting some old DOS code to Linux for a medical device that is
> > being upgraded.
>
> Interesting, this business.

And it feels so good when one stops banging one's head against the wall.


> > The sensor is controlled (in part) by setting RTS on and off.
>
> What is controlled, exactly? What is RTS being used for? If it is
> indeed flow control then you are lucky and can simply enable hardware
> flow control for the serial port, and Linux will then take care of
> everything for you.

Not sure (insufficient documentation). The functions setting and
clearing RTS have names like RS485_RTS_Receiver_Enable and
RS485_RTS_Transmitter_Enable. My query as to the meaning/purpose of
the routines is awaiting an answer..

> If not flow control and some other signalling, you have to write a
> line discipline driver. I have done both this and serial drivers
> (also related to DOS era equipment) and documentation is not the
> greatest. Let me know if you would like some help.
>
>
> > I looked high and low (pun intended) for an ioctl or similar call
> > that would allow this level of control and couldn't find anything.
>
> The best thing out there is tcsetattr() and friends.
>
> By switching between baud rate 0 and something else you can reliably
> and easily control both RTS and DTR, and nothing but RTS and DTR, but
> always both at the same time.

The RS485 routines mentionned above only change RTS. DTR remains on.
Attempts to change both (using CRTSCTS and tcsetattr()) didn't work.

> Line disciplines can call the tty_throttle() and tty_unthrottle()
> functions in the serial driver, which will then control RTS
> accordingly, but the default TTY line discipline does not expose any
> API that will result in throttle function calls.
>
> > outb(inportb(MCR) | 0x02, MCR); //DTR,RTS=ON
> > outb(inportb(MCR) & ~0x02, MCR); //DTR=ON,RTS=OFF
> >
> > Directly tweaking the I/O port runs against the grain, but it's the
> > only thing I've found that works.
>
> Not only against the grain, it can mess up internal state in the
> kernel serial layer and worst case lead to a kernel BUG_ON (kernel
> hangs) or best case serial port hang (unhang e.g. by closing all file
> handles for the port and opening again). It is not at all nice to
> change these signals behind Linux' back.

I'm well aware of the hackish nature of my "solution". It'll be
interesting to see what unwanted side effects show up to bite me.

David
Re: serial port handling question [ In reply to ]
David Relson wrote:
> > > The sensor is controlled (in part) by setting RTS on and off.
> >
> > What is controlled, exactly? What is RTS being used for? If it is
> > indeed flow control then you are lucky and can simply enable
> > hardware flow control for the serial port, and Linux will then
> > take care of everything for you.
>
> Not sure (insufficient documentation). The functions setting and
> clearing RTS have names like RS485_RTS_Receiver_Enable and
> RS485_RTS_Transmitter_Enable.

That definately suggests that RTS/CTS would be used for flow control.


> My query as to the meaning/purpose of the routines is awaiting an
> answer..

Hopefully they will confirm that it's for flow control.

Then you can simply ignore everything related to RTS, as Linux will
take care of it for you. Just read from the opened tty device and
you'll get data when there is some. Linux also buffers writes, so if
a write() call succeeds then data will eventually go out on the port.


> The RS485 routines mentionned above only change RTS. DTR remains on.
> Attempts to change both (using CRTSCTS and tcsetattr()) didn't work.

With tcsetattr() you'd use B0 in the CBAUD field to unset both
signals, and any B value other than B0 to set them. You can use
cfsetispeed() and cfsetospeed() to conveniently change only the
baudrate in a struct termios, but since it will also affect DTR I
don't think that this will work.


> I'm well aware of the hackish nature of my "solution".

What happens if you remove the code that touches the registers and
simply let Linux handle flow control? I suspect you could remove some
of the code surrounding the outb() calls as well, since the Linux
serial layer implements very thorough flow control.


> It'll be interesting to see what unwanted side effects show up to
> bite me.

Unfortunately the problems may not show up until far into the future,
with lots of installations possibly out in the field..


//Peter
Re: serial port handling question [ In reply to ]
On Wed, Jan 13 at 06:52, David Relson wrote:
...
> Directly tweaking the I/O port runs against the grain, but it's the
> only thing I've found that works.
>
> Is there a better way to control the chip?


I know others have commented on using automatic settings for flow control
etc, but if you need to control the lines directly there are an often
neglected set of ioctls to do this.

Some snippets of code, last used on x86 four years ago but it looks like
the hooks are still in the kernel and a fair number of device drivers.

unsigned int flags;

/* Raise RTS and DTR.
* Linux will have already done this but some Unix system don't and
* some wait for DCD before doing so, so make it explicit.
*/
flags = TIOCM_RTS | TIOCM_DTR;
if ( ioctl( fd, TIOCMBIS, &flags ) != 0 )
{
fprintf( stderr,"Failed to raise RTS and DTR. Errno %d\n", errno );
/* Possibly not fatal so we continue */
}

...

/* Drop RTS */
flags = TIOCM_RTS;
if ( ioctl( fd, TIOCMBIC, &flags ) != 0 )
{
fprintf( stderr,"Failed to clear RTS. Errno %d\n", errno );
}

As well as set and clear there is a get (TIOCMGET) useful for checking DCD.

--
Bob Dunlop
RE: serial port handling question [ In reply to ]
RS485 is a half-duplex bus, so this looks like a transmit enable rather than
flow control - and generally the transmit enable timing is quite critical,
since you have to leave it asserted until the last bit is sent from the UART
(so the normal "interrupt of THRE or FIFO low" is useless - you have to use
a TDRE interrupt), and then needs to be very quickly turned around before
the addressed device comes back with it's response.

If you don't want to bitbash the control registers in userland you are just
going to have to add a special mode to the driver, I'm afraid - none of the
RS485 implementations I've ever seen will work correctly using the flow
control semantics that the kernel implements.

Pete
-----Original Message-----
From: Peter Stuge [mailto:peter@stuge.se]
Sent: Thursday, January 14, 2010 1:08 PM
To: gentoo-embedded@lists.gentoo.org
Subject: Re: [gentoo-embedded] serial port handling question

David Relson wrote:
> > > The sensor is controlled (in part) by setting RTS on and off.
> >
> > What is controlled, exactly? What is RTS being used for? If it is
> > indeed flow control then you are lucky and can simply enable
> > hardware flow control for the serial port, and Linux will then
> > take care of everything for you.
>
> Not sure (insufficient documentation). The functions setting and
> clearing RTS have names like RS485_RTS_Receiver_Enable and
> RS485_RTS_Transmitter_Enable.

That definately suggests that RTS/CTS would be used for flow control.


> My query as to the meaning/purpose of the routines is awaiting an
> answer..

Hopefully they will confirm that it's for flow control.

Then you can simply ignore everything related to RTS, as Linux will
take care of it for you. Just read from the opened tty device and
you'll get data when there is some. Linux also buffers writes, so if
a write() call succeeds then data will eventually go out on the port.


> The RS485 routines mentionned above only change RTS. DTR remains on.
> Attempts to change both (using CRTSCTS and tcsetattr()) didn't work.

With tcsetattr() you'd use B0 in the CBAUD field to unset both
signals, and any B value other than B0 to set them. You can use
cfsetispeed() and cfsetospeed() to conveniently change only the
baudrate in a struct termios, but since it will also affect DTR I
don't think that this will work.


> I'm well aware of the hackish nature of my "solution".

What happens if you remove the code that touches the registers and
simply let Linux handle flow control? I suspect you could remove some
of the code surrounding the outb() calls as well, since the Linux
serial layer implements very thorough flow control.


> It'll be interesting to see what unwanted side effects show up to
> bite me.

Unfortunately the problems may not show up until far into the future,
with lots of installations possibly out in the field..


//Peter


__________ Information from ESET NOD32 Antivirus, version of virus signature
database 4770 (20100114) __________

The message was checked by ESET NOD32 Antivirus.

http://www.eset.com
Re: serial port handling question [ In reply to ]
On Thu, 14 Jan 2010 10:05:31 +0000
Bob Dunlop wrote:

> On Wed, Jan 13 at 06:52, David Relson wrote:
> ...
> > Directly tweaking the I/O port runs against the grain, but it's the
> > only thing I've found that works.
> >
> > Is there a better way to control the chip?
>
>
> I know others have commented on using automatic settings for flow
> control etc, but if you need to control the lines directly there are
> an often neglected set of ioctls to do this.
>
> Some snippets of code, last used on x86 four years ago but it looks
> like the hooks are still in the kernel and a fair number of device
> drivers.
>
> unsigned int flags;
>
> /* Raise RTS and DTR.
> * Linux will have already done this but some Unix system don't
> and
> * some wait for DCD before doing so, so make it explicit.
> */
> flags = TIOCM_RTS | TIOCM_DTR;
> if ( ioctl( fd, TIOCMBIS, &flags ) != 0 )
> {
> fprintf( stderr,"Failed to raise RTS and DTR. Errno %d\n",
> errno ); /* Possibly not fatal so we continue */
> }
>
> ...
>
> /* Drop RTS */
> flags = TIOCM_RTS;
> if ( ioctl( fd, TIOCMBIC, &flags ) != 0 )
> {
> fprintf( stderr,"Failed to clear RTS. Errno %d\n", errno );
> }
>
> As well as set and clear there is a get (TIOCMGET) useful for
> checking DCD.
>
> --
> Bob Dunlop

Sweet! Very sweet!

This sounds exactly like what I want. I saw the TIOCM_xxx symbols
being used in serial8250_get_mctrl() and serial8250_set_mctrl(), but
didn't know how to access those functions.

I'll test it later this morning when I get to work.

Regards,

David
Re: serial port handling question [ In reply to ]
David Relson wrote:
> > but if you need to control the lines directly there are an often
> > neglected set of ioctls to do this.

Nice! I thought I had looked at all paths into uart_update_mctrl()
but clearly not. Thanks for pointing it out!


> This sounds exactly like what I want.
> I'll test it later this morning when I get to work.

It should work well.


//Peter
RE: serial port handling question [ In reply to ]
'Tis working perfectly for /dev/ttyS2.

/dev/ttyS4 is seg-faulting for unknown reasons -- most certainly a flaw
unrelated to the ioctl.

Regards,

David
-----Original Message-----
From: Peter Stuge [mailto:peter@stuge.se]
Sent: Thursday, January 14, 2010 11:18 AM
To: gentoo-embedded@lists.gentoo.org
Subject: Re: [gentoo-embedded] serial port handling question

David Relson wrote:
> > but if you need to control the lines directly there are an often
> > neglected set of ioctls to do this.

Nice! I thought I had looked at all paths into uart_update_mctrl()
but clearly not. Thanks for pointing it out!


> This sounds exactly like what I want.
> I'll test it later this morning when I get to work.

It should work well.


//Peter
Re: serial port handling question [ In reply to ]
Relson, David wrote:
> 'Tis working perfectly for /dev/ttyS2.
>
> /dev/ttyS4 is seg-faulting for unknown reasons -- most certainly a
> flaw unrelated to the ioctl.

Is ttyS2 and ttyS4 using the same driver? What does

cat /proc/tty/driver/*

give?


//Peter