device driver for serial com

Hello,

i’ m trying to implement a multithreaded serial port driver for multiple
devices. I have attached the sourcecode. The problems are:

  1. After recieving a full dataset another Interrupt raises and the read
    command blocks (there is definitely only one dataset sent by the device).

  2. If I try to mask the IRQ, I can’t unmask it in the handling thread
    (int_thread). After that every try to read from that port fails, until I
    restart my program with the mask command removed and several (!) unmask
    commands are called.

Any help would be greatly appreciated.

Valentin

Here is the code:

#include <fcntl.h> //open, write, read…
#include <pthread.h> //pthread_create…
#include <sched.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <termios.h>
#include <unistd.h> /* Support all standards /
#include <sys/mman.h> /
Memory locking functions */
#include <sys/neutrino.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>

#define RW_BUFFER 2048 //Size of Read
#define DATA_SIZE 2048
#define NAME_LEN 100
#define NBYTE_READ 1
#define COM2_IRQ 3
#define COM1_IRQ 4
#define COM1_NAME “/dev/ser1”
#define COM2_NAME “/dev/ser2”
#define COM3_NAME “/dev/ser3”
#define COM4_NAME “/dev/ser4”
#define FALSE 0
#define TRUE 1
#define MINCHAR 1 /* blocking read until “min” chars received */

//port identification and settings
typedef struct
{
int fd; //file descriptor
char dev[NAME_LEN]; //name of device
char buf[RW_BUFFER]; //read or write buffer
unsigned int num; //number of characters to read
} sPort;

//interrupt identification
typedef struct
{
int id;
unsigned int irq;
int mask;
} sISR;


struct sigevent event;


//
/* open and configurate a Port /
/
needs: /
/
const char* Port…a device name /
/
speed_t Baud…baudrate for reading and writing */
/
/
sPort openPort (const char* Port, speed_t Baud)
{
int fc, fd;
sPort msPort;
struct termios tio, oldtio; //struct settings of io-file

//printf(“Delete buffer before open. Size of Buffer: %d\n”,
sizeof(msPort.buf));
memset (msPort.buf, ‘\0’, sizeof(msPort.buf));
strcpy (msPort.dev, Port);

//open for read and write operation (O_RDWR),
msPort.fd = open(Port, O_RDWR | O_NOCTTY | O_NDELAY);//O_NOCTTY: this
program doesn’t want to be the “controlling terminal”
//O_NDELAY: program doesn’t care what state the DCD signal
line is in

if (msPort.fd > 0)
{
tcgetattr(msPort.fd, &oldtio); /* save current serial port settings /
bzero(&tio, sizeof(tio)); /
clear struct for new port settings */

/*
BAUDRATE: Set bps rate. You could also use cfsetispeed and
cfsetospeed.
CS8 : 8n1 (8bit,no parity,1 stopbit)
CLOCAL : local connection, no modem contol
CREAD : enable receiving characters
*/
cfsetispeed (&tio, Baud);
cfsetospeed (&tio, Baud);
tio.c_cflag = (CS8 | CLOCAL | CREAD);

//IGNPAR : ignore bytes with parity errors
tio.c_iflag = (IGNPAR);

//Raw output
tio.c_oflag = 0;

/* set input mode (non-canonical, no echo,…) */
tio.c_lflag = 0;

tio.c_cc[VTIME] = 0; /* inter-character timer unused /
tio.c_cc[VMIN] = MINCHAR; /
blocking read until “min” chars
received */

tcflush(msPort.fd, TCIFLUSH); //flush terminal
tcsetattr(msPort.fd, TCSANOW, &tio); //set attributes


fc = fcntl(msPort.fd, F_SETFL, 0);
printf(“Port opened %s.\n”,Port);
}

return (msPort);

}




//
/* write msPort.buf to Port described by msPort.fd /
/
needs: /
/
struct sPort…see above for definition */
/
/
int writePort (sPort* msPort)
{
int i;
int size_written; //number of characters writen

//write to port
size_written = write (msPort->fd, msPort->buf, strlen(msPort->buf));

return(1);
}




//
/* read on Interrupt from msPort.dev to msPort.buf /
/
needs: /
/
struct sPort…see above for definition */
/
/
int readPort (sPort* msPort)
{
pthread_t ptid; //ID of thread created

/POSIX 1003.1 (Threads); create a thread for interupt handling and pass
msPort to thread(IRQ4 only)
/
pthread_create (&ptid, NULL, int_thread, (void *) msPort);

printf(“Want to continue? (y/n)\n”);
while (getchar() != ‘n’)
printf(“Want to continue? (y/n)\n”);


return(1);
}




//
/* Thread waiting for interrupt on IRQ4 and read to buffer /
/
needed argument: /
/
struct sPort…see above for definition */
/
/
void* int_thread (void *arg)
{
int size_read, n_mask, id;
sPort *psPort = (sPort *)arg;
sISR msISR;
struct sched_param param;

msISR.id = 0; //init ISR struct
msISR.irq= 0;
msISR.mask =0;

ThreadCtl (_NTO_TCTL_IO, NULL); /*Request I/O privaty; ThreadCtl is QNX6
standard */

/****** set IRQ - Nummber /
if (strcmp(COM1_NAME,psPort->dev) == 0)
msISR.irq = COM1_IRQ;
else if (strcmp(COM2_NAME,psPort->dev) == 0)
msISR.irq = COM2_IRQ;
else
{
printf(“Error in int_thread: Device or IRQ unknown.\n”);
exit(EXIT_FAILURE);
}
/
**************************/

/**** Attach an ISR to the thread so the trhread can Wait for an Interrupt
*********/
id = InterruptAttach (msISR.irq, isr_handler, (void *) &msISR, 0,
_NTO_INTR_FLAGS_END | _NTO_INTR_FLAGS_TRK_MSK); /*InterruptAttach is QNX6
standard */
msISR.id = id;

//boost the priority of the thread

param.sched_priority = 30;
pthread_setschedparam(pthread_self(), SCHED_FIFO, &param);

printf(“In thread with priority %d device %s\n”, getprio(0), psPort->dev);
/getprio is QNX4 standart/

while (1)
{
size_read = 0;

//Waiting for a Interrupt
InterruptWait (NULL, NULL); /*InterruptWait is QNX6 standard */

while (size_read < psPort->num)
{
size_read += read(psPort->fd, psPort->buf + size_read,
NBYTE_READ);
tcflush(psPort->fd, TCIFLUSH); //flush
terminal
}

n_mask = InterruptUnmask(msISR.irq, msISR.id);

}
InterruptDetach (msISR.id);
return (NULL);
}



// this is the Interrupt Service Routine
const struct sigevent * isr_handler (void *arg, int id)
{
sISR *psISR = (sISR *)arg;
memset(&event, 0, sizeof(event)); //Init of interrupt
signal for int_thread
event.sigev_notify = SIGEV_INTR;

psISR->mask = InterruptMask(psISR->irq, psISR->id);


// in a level-sensitive environment, clear the cause of !!from
QNX-Doc.!!
// the interrupt, or at least issue InterruptMask to
// disable the PIC from reinterrupting the kernel

// return a pointer to an event structure (preinitialized
// by main) that contains SIGEV INTR as its notification type.
// This causes the InterruptWait in “int thread” to unblock.
return (&event); // call int_thread
}


int main()
{
int baudrate;
sPort msPort;

/Initialize sPort - struct *******************************************/
msPort.fd = -1;
memset (msPort.dev, 0, sizeof(msPort.dev));
memset (msPort.buf, 0, sizeof(msPort.buf));
msPort.num = -1;
/
***********************************************************************/

baudrate = 9600;

msPort = openPort (COM1_NAME, baudrate); //open and configurate Commport

strcpy(msPort.buf, “*99P\r”); //set command
msPort.num = 7;

writePort(&msPort); //write to port

readPort(&msPort); //read from port

return (0);
}

Valentin Schlegel <valentin.schlegel@web.de> wrote:

Hello,

i’ m trying to implement a multithreaded serial port driver for multiple
devices. I have attached the sourcecode. The problems are:

I’ve looked at this code – you seem to be combining
open/read/write for serial devices /dev/serx with
InterruptAttach for the serial interrupts. The fact that
you are using the open/read/write, and call them COMx suggests
that you are running devc-ser8250. devc-ser8250 already has a
hardware interrupt handler, and does all the low-level hardware
work. You probalby shouldn’t combine them.

If you want to get some sort of asynchronous notification of
incoming data from the serial port driver, I would suggest
looking at ionotify().

You may also find readcond() or the tcsetattr() parameters in
noncanonical mode (VMIN and VTIME elements of the c_cc array
in termios structure) to be helpful. The “Terminal IO” chapter
of Advanced Programming in the UNIX Environment by W. Richard
Stevens (ISBN 0-201-56317-7) is quite helpful for dealing with
serial ports in a standard way. (Actually, this is a good book
on a lot of Unix programming topics.)

  1. After recieving a full dataset another Interrupt raises

Are you dealing with custom serial hardware? Generally a
uart will raise an interrupt on each byte it receives – or,
sometimes, if you have a FIFO on the chip, it may be after somewhere
between 2 and 16 bytes. It doesn’t know anything about “datasets”.

and the read command blocks (there is definitely only one dataset
sent by the device).

Unless you set O_NONBLOCK, if there is no date, read() will block.
This is normal and expected. (Or, at the lower level, you are
playing with MIN and TIME – you have set MIN=1 and TIME=0, this
again requests that if there is no data, the read() call should
block.)

-David

QNX Training Services
http://www.qnx.com/support/training/
Please followup in this newsgroup if you have further questions.

Hello David,

thanks for your help. I’ll not use InterruptAttach in connection with open,
read … anymore.
Another question is, how can I trace parallel function calls on a time axis?
Especially if (e.g.) in a handler function an output to stdout (or a
filestream) is not possible because critical.

Thanks again for your help,

Valentin


“David Gibbs” <dagibbs@qnx.com> schrieb im Newsbeitrag
news:b4b6qo$hgq$1@nntp.qnx.com

Valentin Schlegel <> valentin.schlegel@web.de> > wrote:
Hello,

i’ m trying to implement a multithreaded serial port driver for multiple
devices. I have attached the sourcecode. The problems are:

I’ve looked at this code – you seem to be combining
open/read/write for serial devices /dev/serx with
InterruptAttach for the serial interrupts. The fact that
you are using the open/read/write, and call them COMx suggests
that you are running devc-ser8250. devc-ser8250 already has a
hardware interrupt handler, and does all the low-level hardware
work. You probalby shouldn’t combine them.

If you want to get some sort of asynchronous notification of
incoming data from the serial port driver, I would suggest
looking at ionotify().

You may also find readcond() or the tcsetattr() parameters in
noncanonical mode (VMIN and VTIME elements of the c_cc array
in termios structure) to be helpful. The “Terminal IO” chapter
of Advanced Programming in the UNIX Environment by W. Richard
Stevens (ISBN 0-201-56317-7) is quite helpful for dealing with
serial ports in a standard way. (Actually, this is a good book
on a lot of Unix programming topics.)

  1. After recieving a full dataset another Interrupt raises

Are you dealing with custom serial hardware? Generally a
uart will raise an interrupt on each byte it receives – or,
sometimes, if you have a FIFO on the chip, it may be after somewhere
between 2 and 16 bytes. It doesn’t know anything about “datasets”.

and the read command blocks (there is definitely only one dataset
sent by the device).

Unless you set O_NONBLOCK, if there is no date, read() will block.
This is normal and expected. (Or, at the lower level, you are
playing with MIN and TIME – you have set MIN=1 and TIME=0, this
again requests that if there is no data, the read() call should
block.)

-David

QNX Training Services
http://www.qnx.com/support/training/
Please followup in this newsgroup if you have further questions.

Valentin Schlegel <valentin.schlegel@web.de> wrote:

Hello David,

Another question is, how can I trace parallel function calls on a time axis?

I’m not sure what you mean by trace. Are you talking the ability
to watch from outside? Or, are you talking the ability for the
threads internally to hand-shake with each other?

For the internal access, there are a bunch of synchronisation objects
that QNX supports, the most commonly used of which are mutexes and
condvars. (pthread_mutex* and pthread_cond* in our library).

Especially if (e.g.) in a handler function an output to stdout (or a
filestream) is not possible because critical.

stdout (printf, etc) do have locking built in to handle the access from
multiple threads case properly. I know they use mutexes, they may also
use condvars.

-David

QNX Training Services
http://www.qnx.com/support/training/
Please followup in this newsgroup if you have further questions.

David Gibbs <dagibbs@qnx.com> wrote in message
news:b4njg2$fbd$1@nntp.qnx.com

stdout (printf, etc) do have locking built in to handle the access from
multiple threads case properly. I know they use mutexes, they may also
use condvars.

They use condvars (which have a mutex associated of course).

-Adam