Detecting Interrupt

Hi all,

I am having some trouble detecting the interrupt. My code would just stay at “InterruptWait()” and won’t proceed.

What I want is…from Hyperterminal, enter some keystrokes to cause an interrupt to QNX, and then QNX would then write into Hyperterminal.

I want to use com1.

Here’s a bit of my code…

int
main (int argc, char **argv)
{
int intId; // interrupt id
int iir; // interrupt identification register
int serial_msr; // saved contents of Modem Status Reg
int serial_rx; // saved contents of RX register
int serial_lsr; // saved contents of Line Status Reg
struct sigevent event;

// usual main() setup stuff...
ThreadCtl( _NTO_TCTL_IO, 0 );

// set up the event
intId = InterruptAttachEvent (HW_SERIAL_IRQ, &event, 0);
cout <<intId<<endl;
for (;;) 
{
	cout <<"I am in for loop"<<endl;
    // wait for an interrupt event (could use MsgReceive instead)
    cout <<"Waiting for interrupt"<<endl;
    InterruptWait (0, NULL);

This is where my code executes up to before waiting forever long=/

You must setup the serial port first.

If you can try to use the serial driver facility instead of dealing with the serial port directly.

Well, I actually would like to set up my own serial port instead of using the prescribed dev-ser8250 provided by QNX.

I now have hyperterminal(com6) and QEMU(an x86 emulator, com5) bridged together. I believe I have the I/O privity set, hmm…I am not too sure, but how do you enable hardware access?

I’m trying to implement a 8250 driver that would allow me to communicate between hyperterminal and qnx…and if I hit keystrokes to cause interrupts i would like qnx to pick that up and send a message to hyperterminal of what i stroked.

For what reason?

You already have access, but you must configure the serial port. Search the web for information on 8250. You will have to write in the chip’s register to obtain the result you want. You must also make sure that QNX driver isn’t running.

It is an assigned project that I have to work on.=)

Oh I see, but I realised that after “slaying” devc-ser8250 /dev/ser1 is no longer there.

And this surprises you because…?

Sometimes when works get assigned to you it’s good to question the motive behind the decision.

Sorry, I’m still a student trying to figure out QNX. It’s my first time touching this stuff and first time experiencing real-time embedded systems. It’s just that after /dev/ser1 is gone, I got confused as to how to communicate through a com port. I have googled about 8250, but still a little bit confused about the real chip and how to use the emulated chip using QNX. Do I write to it like a real chip with the register addresses according to the real chip’s specifications?

Sorry if I’m not very clear or don’t make any sense at all.

Well ok, now your confusion makes sense. A device name, for example /dev/ser1 is just an operating system construct. For a Linux system, a device driver associates such a name with itself, so open/read/write/ioctl calls are routed to its handlers. QNX ends up doing about the same thing, but via a process. The process associates the device name with itself, and thereafter the I/O calls are turned into QNX messages to the process. So when you slay the devc-ser8250 process, the device /dev/ser1 goes away.

Now as far as writing to an I/O port, this is just something you do in a program, eg: output8(PORT,value);
Generally speaking, writing to hardware this way gives your code the label “driver”, however in QNX the
distinction between a driver and a process can be vague. An application (in QNX) can talk to hardware directly
without the benefit of a separate driver, and there’s nothing particularly wrong with doing it this way.

If you are using the normal interface through /dev/ser1, you generally should not be reading or writing
directly to the 8250’s I/O ports. If you want to read and write to them directly, you probably should not
be running devc-ser8250. When you start accessing an 8250 directly via its I/O ports, you get a lot of responsibility.
For example, you need to set the baud rate, and various other serial features, like bits, parity, etc. You also
may need to setup the chip up so that it will interrupt when you want it to, and further catch those interrupts
via a handler.

Just as a minor note, for a lot of hardware, you can avoid having any code run in the interrupt handler the way you
have set yours up. That is, when the interrupt fires, your process is woken up. This isn’t generally how it is
done with serial ports due to the high ratio of interrupts to data bytes. Instead, the interrupt handler reads data
into a buffer, and only wakes up the process when appropriate. This avoids requiring a context
switch for every byte that comes in. QNX is so fast at context switches that you can probably get away with this, but it still isn’t the right way to do it.

Hmm okay, so I guess I’ll have to setup the 8250 myself then, because the whole goal is to not use the devc-ser8250. (This is possible right? Even though we’re dealing with an emulated chip here)

Thanks for the response, I’ll go think about what you said and see what I can achieve.

Yes it is possible. I don’t know what you mean by an emulated chip, but if responds to the same I/O ports in the same way as an 8250, then it should work. You might want to try to get ahold of the source to devc-ser8250 (assuming that it is not cheating) in order to see how it works.

Actually yea, I have been trying to find the source so I can gain better understanding, but haven’t been able to find one yet. And no it wouldn’t be cheating since it’s not for academic purposes.

Thanks for your help though.

Why do you use the word emulated, there is no emulation involved at all. devc-ser8250 doesn’t emulate the serial chip is actually take control of it and provide a POSIX interface (open/read/write) to the serial chip. That way program that need to use serial port only have to use these simple functions and don’t have to deal with the chip register and interrupt. Also program that use devc-ser8250 (/dev/ser1) do not have to be root, while program that will directly access the chip must be root and have IO privilege. Going through /dev/ser1 has the added benefit that if in the future if the hardware is changed for a different model of serial port, only the driver needs to be changed. The program using /dev/ser1 won’t have to be modified.

As finding the source to dev-ser8250 even if you find it, it’s not the best way to learn about the serial chip because the driver is full of QNX specific stuff that will probably confuse you. Just get the specification of the chip, it’s very easy to find on the web. Google it up.

I’m not sure I understand your argument about the cheating.

I have googled it up.

I am trying to configure the chip by…

out8(COM+offset, value);

Is that proper?

Yes if COM, offset and value are right ;-)

In theory the address “COM+offset” should be obtain by calling device_map_io()

Well,
int ioId;
ioId = mmap_device_io (0x3f8, 8 );//8 byte length since I’m doing 8-1
//serial sequence
cout << ioId<<endl;

My return value for ioId is 8, is that what I write/read to using out8 or in8 when I specify the port address? Although COM1 really is 3f8?

I suggest you read mmap_device_io documentation.

Okay, I have configured the chip.

However, when I tried to hit keystrokes in hyperterminal to cause interrupt, it is not detected by the code. (I’ve “slay” the devc-ser8250 process already so hopefully my code will take over)

Are there any commonly made mistakes while coding an interrupt handler? I don’t have much experience in such area so I was wondering if anyone know what a beginner like me might of missed out in my code. On the chip, I’ve enabled all possible interrupts; MSR, THE, etc., and yet still nothing.(Assuming my configuration of the chip is correct)

I have enabled hardware I/O privilege using the line

 ThreadCtl (_NTO_TCTL_IO, NULL);

and also did interruptattach() to an ISR and declared a signal event variable called “event” and set the sigev_notify to SIGEV_INTR.

But, my code will still hang at “InterruptWait()”.

Oh, and yes I’ve also freed the device registers using mmap_device_io function before writing to them.

The most common mistake in an interrupt handler is code that causes the OS to crash. You would know immediately if that was the case. This often happens when a programmer has a call to an illegal routine in the handler, for example MsgSend().

It sounds like you are not getting an interrupt at all. My vague recollection (it’s probably been 10 years) is that the 8250 interrupts are enabled with at least two levels. There is a mask that controls what events cause an event, eg. TX drained, RX received, CTRL-Lines changes, etc. and also a master interrupt switch. I also recall that there is an interrupt status register, so you can see if the chip has detected some event, but it hasn’t been reset. You should be able to read this register and see if the event occured.

While I doubt the 8250 has this issue, most of the hardware I’ve worked with had an additional level, an off-chip register that connected the chip to the interrupt line on the bus. A proprietary serial board, especially one that allowed multiple 8250’s to share the same interrupt might have this issue.

This all sounds good, can you post your code?

What do you mean be freed? In theory you should unmap them only when you are finish with accessing the hardware?