ISR doesn't get called when the interrupt is triggered

I’m using a A/D ISA board (keithley DAS-1802HR) on QNX 6.3.0 service Pack 2 for data collection. The counter on this board is programmed to generate interrupt to the host. My problem is: the ISR which is attached to the IRQ never get called when the interrupt is generated, I can check from the status register on the board that the interrupt does occur. Here is my code related with the interrput process. What’s wrong with my code?

#define IRQ 5
#define base_add 0x300 //ISA board address
int count = 0;

extern const sigevent *send_control(void *arg, int id)
{
count += 1;
//check the status register on the board to interrupt
if(!(int8(base_add+7) & 0x1))
return NULL;
//do some A/D reading

}

int main()
{
int interpt_no;
int ttl_cnt = 1000;

if(ThreadCtrl(_NTO_TCTL_IO, 0) == -1)
    exit(1);

//initiate the board
......
//set interrupt routine
InterruptDisable();
interpt_no = InterruptAttach(IRQ, send_control,
    NULL, 0, _NTO_INTR_FLAGS_TRK_MSK);
if(interpt_no == -1)
    return -1;
out8(base_add+5,0xd0)    //set board interrupt level to IRQ:5
out8(base_add+7,0x80)    //start counter on the board
InterruptUnmask(IRQ, interpt_no);
InterruptEnable();

while(count < ttl_cnt)
{
    InterruptWait(0, NULL);
    printf("interrupt! count is %d\n", count);
    InterruptUnmask(IRQ, interpt_no);
}

out8(base_add+5,0x00)    //disbable board interrupt
if(InterruptDetach(interpt_no) == -1)
    printf("detach error\n");

return 0;

}

The ISR “send_control” never get called even if an interrupt occured, which I can check from the status register after the program running. Can somebody help me out?

Thanks!
Yicheng

Hi,

are you really sure that send_control () is never called?
Looking at your code sample, I assume that your program “hangs”,
i.e. does not terminate correctly. Even if this is not the case, i
suggest the following as a start:

Below, you call InterruptWait (), which will block until the thread
receives a signal (SIGEV_INTR).

But in your ISR send_control (), you always(?) return NULL, so your
main thread/process never gets unblocked! So for your thread to
run, you need to return a struct sigevent from your
ISR, send_control (). Like:

struct sigevent isr_event;

int main ([...])
   {
   int iid;

   SIGEV_INTR_INIT (&isr_event);
   iid = InterruptAttach (IRQ, send_control, NULL, 0,
       _NTO_INTR_FLAGS_TRK_MSK);
   /* BTW InterruptAttach () automatically unmasks the interrupt */

   for (;;) {
      InterruptWait (0, NULL);
      [...]
      }
   }

const sigevent *send_control (void *area, int id)
   {
   (void)InterruptMask (IRQ, id);   /* you'll want to mask the interrupt */
   count += 1;
   
   /* don't really get what this does though... */
   //check the status register on the board to interrupt
   if ( !(in8(base_add+7) & 0x1))
   //do some A/D reading

   return &isr_event;   /* this will unblock InterruptWait () */
   }

Soo, hope this helps you to get it right. On the other hand, it’s 2am
and I might have totally missed the point…
Also, re-reading the docs and the Programmer’s Guide about interrupt
handling can never be wrong ;-)

Ah, and your direct hardware access via in8 (), out8 (): Take a look
at mmap_device_io (). Had some troubles with this, too.

Good luck
qnxsa

I tried the method of returning a event in the ISR, but it has no effect. Actually I also tried setting a TIMEOUT to the InterruptWait, after timeout, I check the status register on the board, it shows that an interrupt does occur, but still the ISR doesn’t get called( I can check it from the “count” value, it always be zero ).

Any suggestion?

Try this:

typedef struct
{
   struct sigevent irqEvent;

   int count;
} IsrData_t;

IsrData_t isr_event;

int main ([...])
   {
   int iid;

   memset( &isr_event, 0, sizeof( isr_event));
   SIGEV_INTR_INIT (&isr_event.irqEvent);
   /* !! */
   /* NOTE: Need to pass a POINTER to the data you want the ISR to have access to */
   /* !! */
   iid = InterruptAttach (IRQ, &isr_event, NULL, 0,
       _NTO_INTR_FLAGS_TRK_MSK);
   /* BTW InterruptAttach () automatically unmasks the interrupt */

   for (;;) {
      InterruptWait (0, NULL);
      [...]
      }
   }

const sigevent *send_control (void *area, int id)
   {
   IsrData_t *isrData = (IsrData_t*)area;
   struct sigevent *event = NULL;
   
   (void)InterruptMask (IRQ, id);   /* you'll want to mask the interrupt */
   area->count += 1;
   
   /* don't really get what this does though... */
   //check the status register on the board to interrupt
   if ( !(in8(base_add+7) & 0x1))
   {
      //do some A/D reading
           /* !! */
           /* NOTE: Only return non-null if this was an interrupt from your A/D board */
           /* !! */
      event = &(isrData->irqEvent);
   }

   return event;   /* this will unblock InterruptWait () */
   } 

I tried this, it still has no effect. As I said, I set a timeout to the InterruptWait function. After timeout, I check the board, an interrupt does occur, but the “count” value always is 0, which should increase by 1 with every interrupt.

Well, at least your ISR should get called… Maybe somethings wrong with the ISA card
configuration, BIOS etc. I don’t know much about ISA tech, though.

In the Programmer’s Guide chapter about interrupt handling (or was it in Architecture Overview?),
there is example source code where an ISR is attached to the QNX hardware timer interrupt
and increments a counter, similar to the sample you provided. You could use this code
to check that the error is not in your implementation of the ISR etc. If your code works with
the hardware timer interrupt, then I’d guess it’s some configuration/hardware issue with
your ISA card which you have to figure out.

qnxsa

Thanks to all of you! You are right, the shared variable “count” should be passed into the ISR by the argument “void * area”, otherwise it could cause the kernel hang up! I never notice this before!

I think that if you added volatile to count then you might see it change. As it is the compiler can’t see a way that the count variable will increment, since you don’t call the interrupt event handler inline, and it would probably read the value once into a register and never check it again.

Did you check the BIOS setup to see if the IRQ is reserved for ISA? The IRQ is usually reserved for PCI PnP. You will have to set the setting for IRQ 5 in your case to ISA.

Just a thought in case you have not taken care of it already.