Questions about writing a serial device using io-char

Hello!
I’m currently trying to write a serial device driver using the io-char
library. So far I’m able to read and write data but there are still
some things unclear to me. Maybe someone that has already done this
can help me…

\

  1. tte()-function:
    Can this function directly be used by the driver or is it just a
    io-char internal event handler (the documentation is a bit unclear at
    this but i assume the latter)?

    \
  2. Sending events:
    This question is related to question 1) and assumes that tte() is a
    io-char internal handler. So how does the driver sends events to
    io-char?
    I can see that the structure TTYCTRL has an “event”-member
    and also TTYDEV’s “flags”-member is used for events (at
    least in the 8250 example). But just setting the flags in TTYDEV
    will, of course, not do anything. As I see it only makes sense doing
    this inside the tto()-function, as the function that is calling it (
    kick() in io_write.c ) will queue the event in “ttyctrl”
    and send a message. How can it be done on other places?

Btw: Whats the difference, regarding EVENT_*, between the TTYCTRL
“event” and the TTYDEV “flags” member ?


3) Event queue:
TTYCTRL also contains an event queue
(“num_events”+“event_queue”). It is written in
the documentation that this is used by io-char AND the driver to
queue events. How is this related to sending events as desribed in 2)
? It seems conflicting to me.
I see that it is used in the interrupt-handler of the 8250-example.


4) Blocking read:
When read() is called on the character-device then it blocks when
there is no data in the “ibuf” of the device. How can the
driver indicate when data has been written to the “ibuf”,
so io-char will process it and deliver it to the caller ?


5) Drain:
What is the meaning of EVENT_DRAIN and the
“waiting_drain”-queue in TTYDEF ? What is the driver
supposed to do with that information ?


6) Instances:
This is a more general question regarding resource managers. How do I
distinguish between different instances of the serial device (two
processes call open() on the same device). I assume it will create
two instances in this case, will it ?!


Quick help on these issues would be appreciated :slight_smile:

ED209 <ans666@gmx-dot-de.no-spam.invalid> wrote:

Hello!
I’m currently trying to write a serial device driver using the io-char
library. So far I’m able to read and write data but there are still
some things unclear to me. Maybe someone that has already done this
can help me…


  1. tte()-function:
    Can this function directly be used by the driver or is it just a
    io-char internal event handler (the documentation is a bit unclear at
    this but i assume the latter)?

I don’t think you ever use this function.

  1. Sending events:
    This question is related to question 1) and assumes that tte() is a
    io-char internal handler. So how does the driver sends events to
    io-char?
    I can see that the structure TTYCTRL has an “event”-member
    and also TTYDEV’s “flags”-member is used for events (at
    least in the 8250 example). But just setting the flags in TTYDEV
    will, of course, not do anything. As I see it only makes sense doing
    this inside the tto()-function, as the function that is calling it (
    kick() in io_write.c ) will queue the event in “ttyctrl”
    and send a message. How can it be done on other places?

Either you return an event from the interrupt handler – this is what
devc-ser8250 does, or you call iochar_send_event( &ttydev ).

  1. Event queue:
    TTYCTRL also contains an event queue
    (“num_events”+“event_queue”). It is written in
    the documentation that this is used by io-char AND the driver to
    queue events. How is this related to sending events as desribed in 2)
    ? It seems conflicting to me.
    I see that it is used in the interrupt-handler of the 8250-example.

See above – either steal the devc-ser8250 interrupt code, or call
iochar_send_event() which properly manipulates the event queue for you.

  1. Blocking read:
    When read() is called on the character-device then it blocks when
    there is no data in the “ibuf” of the device. How can the
    driver indicate when data has been written to the “ibuf”,
    so io-char will process it and deliver it to the caller ?

By setting a “data available” flag then queueing an event to wake
up the io-char processing.

  1. Drain:
    What is the meaning of EVENT_DRAIN and the
    “waiting_drain”-queue in TTYDEF ? What is the driver
    supposed to do with that information ?

EVENT_DRAIN should, I think, be set when all the data in the
outgoing buffer has drained.

  1. Instances:
    This is a more general question regarding resource managers. How do I
    distinguish between different instances of the serial device (two
    processes call open() on the same device). I assume it will create
    two instances in this case, will it ?!

I’m not sure what you mean by instances.

From a general resmgr point of view, there is a client-tracking structure
called an OCB (open control block) which is allocated on each successful
open(), and used to track the client’s requests, state, etc. It is cleaned
up at final close. The ocb points to the device structure, which corresponds
to the registered name (/dev/ser1, /dev/ser2).

So…

fd = open("/dev/ser1") → ocb → dev_attr <–> /dev/ser1.

The device attr is a “TTYDEV” in the devc-ser8250, in general it is an
iofunc_attr_t, or an extension of this type.

But, in the serial port case, all this per-client tracking is completely
hidden from you in the io-char library.

Quick help on these issues would be appreciated > :slight_smile:

Sorry, not quick, got here when I could.

I’ll also email you a “sample” serial port driver – it does not do
real hardware, but tx/rxs fake data driven by the timer interrupt. This
may more clearly illustrate the architecture. (Well, if your email
address is valid, I will.)

-David

David Gibbs
QNX Training Services
dagibbs@qnx.com

Hello,

thanks for the reply and for the sample code also. My driver is
working now.But i still have one question:

Is there a better description how to handle the different actions in
tto() besides TTO_DATA ? I only found this in the ser8250-docs:
TTO_STTY – an stty command was received. It’s called by io-char when
the stty command is performed on the device. This action calls
ser_stty(); the argument is ignored.

TTO_CTRL – set the characteristics of the port i.e. control RS-232
modem lines.
arg1 _SERCTL_BRK_CHG – called by io-char when the application
requests a break such as tcsendbreak() to be sent
arg1 _SERCTL_DTR_CHG – changes the DTR line
arg1 _SERCTL_RTS_CHG – used to change the RTS line; io-char calls
this to assert hardware flow control when the input buffer is filling
up (based on the highwater level)
TTO_LINESTATUS – a request for line status. Returns the status of the
Modem Status and Modem Control registers when the user performs a
devctl() with DCMD_CHR_LINESTATUS; the argument is ignored.
TTO_DATA – output transmit data.
TTO_EVENT – ignored.

accidentally hit submit… (isn’t there an edit here?)

What i want to know especially is:

  • what exactly is tto() supposed to return for TTO_LINESTATUS ?
  • where are the parameters for TTO_STTY coming from ?
  • whats the meaning of the arg1-parameters for TTO_CTRL (see code of
  1. ?

if(arg1 & _SERCTL_BRK_CHG)
set_port(port[REG_LC], LCR_BREAK, arg1 &_SERCTL_BRK ?
LCR_BREAK : 0);

if(arg1 & _SERCTL_DTR_CHG)
set_port(port[REG_MC], MCR_DTR, arg1 & _SERCTL_DTR ? MCR_DTR
: 0);

if(arg1 & _SERCTL_RTS_CHG)
set_port(port[REG_MC], MCR_RTS, arg1 & _SERCTL_RTS ? MCR_RTS
: 0);

Thanks

ED209 <ans666@gmx-dot-de.no-spam.invalid> wrote:

accidentally hit submit… (isn’t there an edit here?)

What i want to know especially is:

  • what exactly is tto() supposed to return for TTO_LINESTATUS ?

A set of line status bits. The exact bits will depend on the
device you are handling. The client issued a DCMD_CHR_LINESTATUS,
and you’re responding. Take a look at <sys/dcmd_chr.h> for examples.
You’ll see that parallel ports have different answers from serial ports,
and from others. You need to determine what makes sense for your device,
and provide (usually) a set of masks to that.

  • where are the parameters for TTO_STTY coming from ?

tcsetattr() or devctl() calls – often from a command line stty command,
but could be programmatically, too.

The io-char library will update your TTYDEV structure to reflect the
needed changes, you just have to implement those changes. devc-ser8250
just completely resets the hardware to reflect the new configuration.

Since these are, generally, only used at start/end of sessions, rather
than on the fly, the reset is a reasonable approach.

  • whats the meaning of the arg1-parameters for TTO_CTRL (see code of
  1. ?

I think these are the result of a DCMD_CHR_LINECTRL devctl(), used to
manually toggle the line states. I haven’t looked deeply enough at
the io-char library to see what they are from, exactly. But, you should
have a copy of the io-char library as part of the character driver DDK,
and could take a look.

Also… devc-ser8250 overloads tto() as both “io-char calls this to
tell me to do stuff” and “I call this at interrupt time to move data
out to the hardware”. I prefer (architecturally, in my drivers) to
split those two ideas out.

(Actually, the char DDK/io-char library overloads way too many things,
especially the ttc() function, that sings, dances, and goes for soda.)

-David

David Gibbs
QNX Training Services
dagibbs@qnx.com