Do I need to alter the code (below) because it shares IRQs with another
card? I have the following PCI cards installed:
Slot Description IRQ
1 AHA2940 10
2 DIO-48 10
3
4 DIO-48 11
5
6 DIO-48 12
7
8 Pamux n/a
9 Pamux n/a
10 Ethernet (905) 11
11 Ethernet (82557) 10
12 Rocketport (serial) 10
Onboard:
Serial I/O 4/3
IDE (cd-rom) 15
Floppy 6
The code I’m starting is for the DIO-48 cards, one per card.
Thanks in advance for the help,
~ Jeffrey Jordan
#include <i86.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <fcntl.h>
#include <errno.h>
#include <conio.h>
#include <sys/irqinfo.h>
#include <sys/mman.h>
#include <sys/name.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/proxy.h>
#include <sys/kernel.h>
#define MAIN
#include “basic.h”
#include “config.h”
#include “defs.h”
#include “health_api.h”
#include “messages.h”
#include “system.h”
#include “timecvt.h”
#include “dio_api.h”
#include “bithelpers.h”
#include “yd_io.h”
#include “ipc.h”
#include “timer.h”
#include “IOcard_obj.h”
//
// TASK CONTROL VARIABLES
//
nid_t My_nid; /* return value from getnid() /
pid_t My_pid; / return value from getpid() /
pid_t My_nameid; / return value from qnx_name_attach() */
int board_num;
int logging = FALSE;
/*
NOTE: The following defines support the change from performing two
conpiles
to get dio0 and dio1 to a single generic task which uses parameters to
support multiple boards.
*/
IO_CARD_OBJ Dio;
int BASE;
#define IRQ (Dio.irq)
#define NUM (board_num)
// max number of stacked interrupts
#define NUM_BUFFERS 200
// definition of package prepared on dio interrupt
typedef struct
{
UBYTE port0a;
UBYTE port0b;
UBYTE port0c;
UBYTE port1a;
UBYTE port1b;
UBYTE port1c;
int64 cycles; // machine cycles @ interrupt
// TIME nanotime; // Djenkins - nanotime stamp
UDWORD sequence; // sequence number
} DIO48_POINTS;
DIO48_POINTS dio48_points[NUM_BUFFERS];
// pragma for instructions to read machine cycle counter - 586 processor
only!!
void rdtsc64( int64 *ptr);
#pragma aux rdtsc64 = “db 0fh,31h” “mov [ebx],eax” “mov [ebx+4],edx”
parm nomemory [ebx] modify exact nomemory [eax edx];
unsigned rdtsc32( void );
#pragma aux rdtsc32 = “db 0fh,31h”
parm nomemory [] modify exact nomemory [eax edx];
UDWORD han_sequence;
// local working variables
pid_t proxy; // main is a proxy of the interrupt handler
pid_t dio_in_distrib_pid; // pid of cyc task
UDWORD int_sequence;
UDWORD ii;
DIO_EVENT dio_event;
char task_name[33];
// DELAY IN MACHINE CYCLES IN INTERRUPT BEFORE READ:
// 3600 = 40musec; 3300 = 37musec; 4995=55.5 musec @90MHz
//
// TBD MZ
// DELAY is bad. We need to be able to convert from machine cycles to
// musec via HZ, which should be available on the system. Where???
// TBD MZ
#define DELAY 3600
int delay_end,
delay_cycles;
// OPERATION: DIO0 ATTACHES TO THE INTERRUPT OF THE DIO BOARD. WHEN AN
INTERRUPT OCCURS,
// THE HANDLER RECORDS THE MACHINE CYCLE COUNTER AND CURRENT DIO POINT
VALUES IN A ROTATING
// STACK OF BUFFERS (FUNCTION CALLS ARE LIMITED WHILE IN INTERRUPT
STATE). THE RETURN FROM
// THE INT HANDLER TRIGGERS MAIN VIA PROXY TO PROCESS THE NEXT BUFFER.
CORRECT OPERATION
// RELIES ON QNX TO GENERATE ONE TRIGGER PER INTERRUPT HANDLER RETURN.
AS LONG AS THIS IS
// RELIABLE, THE INT HANDLER AND MAIN WILL STAY IN SYNC, WITH REGARD TO
REFERENCING THE
// CORRECT BUFFER ITEM.
// ONCE MAIN RECEIVE A TRIGGER, IT CONVERTS MACHINE CYCLES TO
HIGH-PRECISION TIME AND SENDS
// A COPY OF THE DIO POINTS TO AN IOC PROCESS AND A CYC PROCESS VIA TWO
MESSAGES. NOTE THAT
// THERE ARE TWO DIO HANDLERS (ONE PER BOARD) AND THEY SHARE THE PAIR OF
FIFOS TO THE IOC
// AND CYC CONSUMER PROCESSES.
//
/ INTERRUPT HANDLER /
//
#pragma off( check_stack);
void read_points ( )
{
// RE-ENABLE INTERRUPTS ON THE DIO48 BOARD
outp( BASE+0x000F, 0x00 );
// DETERMINE BUFFER TO USE
ii = int_sequence % NUM_BUFFERS ;
// GET THE MACHINE CYCLE COUNTER AT TIME OF INTERRUPT (CONVERTED LATER
TO TIME)
rdtsc64( &dio48_points[ii].cycles );
//
// Djenkins
//
// dio48_points[ii].nanotime = seconds_since_startup ();
//
// Djenkins
//
// DELAY TO ALLOW TRAPPING OF POINTS NOT DETECTED BY THIS INTERRUPT
(HARDWARE PROBLEM)
// delay_end = rdtsc32() + DELAY;
do
{
delay_cycles = rdtsc32();
}
while(delay_cycles < delay_end);
// READ THE DIO48 POINTS AND SAVE IN BUFFERED ARRAY
dio48_points[ii].port0a = inp( BASE+0x0000 );
dio48_points[ii].port0b = inp( BASE+0x0001 );
dio48_points[ii].port0c = inp( BASE+0x0002 );
dio48_points[ii].port1a = inp( BASE+0x0004 );
dio48_points[ii].port1b = inp( BASE+0x0005 );
dio48_points[ii].port1c = inp( BASE+0x0006 );
// INCREMENT THE SEQUENCE COUNTER - IGNORE ROLLOVER
dio48_points[ii].sequence = int_sequence ;
// INCREMENT BUFFER FOR NEXT INTERRUPT
//
// SCuM, 9/24/97 – added rollover logic so 1st time thru is truly
// unique, and I/O distributor(s) can toss the packet it necessary.
//
if (++int_sequence == IO_RESTART_SEQUENCE)
{
int_sequence++;
}
} // read_points
pid_t far handler()
{
read_points( );
return( proxy ); // will trigger main
}
#pragma on( check_stack );
BOOL HotBackup_Initialize_HealthRegister(char *task_name)
{
if (health_init (task_name) != SUCCESS)
{
fprintf (stderr, “%s: Can’t perform health_init()\n”, task_name);
return (TRUE);
}
return (FALSE);
}
int init_dio(int argc, char ** argv )
//
// Procedure: init_dio
//
// Purpose: Perfrom command line parsing and assoicated attach actions.
//
// Inputs: argc – argument counter
// argv – pointer to an array of string arguments of size argc
//
// Side Effects: Everything – This task initializes global data.
//
// Return: 0 – ok
// -1 – init failed
//
{
int j;
struct sigaction act; // signal handling structure
sigset_t set; // set of process specified signals
if (Dio.Type == TYPE_ISA)
board_num = atoi(argv[6]);
else
board_num = atoi(argv[4]);
if((board_num > 3) || (board_num < 0))
{
fprintf( stderr,
“dio task number: %d. Value must be 0 - 3. \n”,
board_num);
fflush( stderr );
return(-1);
}
sprintf(task_name, “dio%d”, board_num);
#ifdef HOT_BACKUP
if (HotBackup_Initialize_HealthRegister(task_name))
return (FAILURE);
#endif
printf("%s: Running at BASE: 0x%x, IRQ: %d\n",
task_name, BASE, IRQ);
logging = FALSE;
for (j=0; j<argc; j++)
if (stricmp(argv[j], “-L” ) == 0)
{
logging = TRUE;
break;
} //
//
/ INITIALIZATION /
//
// RE-SPECIFY THE SIGNALS RECIEVED BY MAIN TO IGNORE A BROKEN FIFO/PIPE
sigaddset( &set, SIGPIPE );
act.sa_flags = 0;
act.sa_mask = set;
act.sa_handler = SIG_IGN; //signal handler set to ‘ignore’
sigaction( SIGPIPE, &act, NULL ); //ignore fifo broken pipe signal
// GET A PROXY TO MAIN FOR THE INTERRUPT HANDLER TO KICK
if( (proxy = qnx_proxy_attach( 0, 0, 0, 0 ) ) == -1 )
{
fprintf( stderr,"%s: Unable to attach proxy. \n%s\n",
task_name, strerror(errno) );
return(-1);
}
My_pid = getpid();
My_nid = getnid();
// ATTACH THE HANDLER TO THE DIO INTERRUPT
if( qnx_hint_attach( IRQ, &handler, FP_SEG( &int_sequence)) == -1 )
{
fprintf( stderr,"%s: fatal error: unable to attach interrupt
handler\n",
task_name );
return(-1);
}
return(0);
} // init_dio
void process_dio_board(DIO_EVENT *p_dio_event, UBYTE * invert_mask )
{
static UDWORD seq_delta= 0; // difference between sequences
UBYTE han_idx;
han_idx = han_sequence % NUM_BUFFERS;
p_dio_event->nanotime = cycles_to_double
(dio48_points[han_idx].cycles);
// Djenkins - cycles should removed
// p_dio_event->cycles.h = dio48_points[han_idx].cycles.h;
// p_dio_event->cycles.l = dio48_points[han_idx].cycles.l;
p_dio_event->headroom = int_sequence - han_sequence;
p_dio_event->sequence = (UDWORD) dio48_points[han_idx].sequence;
p_dio_event->points[0] = (UBYTE)dio48_points[han_idx].port0a;
p_dio_event->points[1] = (UBYTE)dio48_points[han_idx].port0b;
p_dio_event->points[2] = (UBYTE)dio48_points[han_idx].port0c;
p_dio_event->points[3] = (UBYTE)dio48_points[han_idx].port1a;
p_dio_event->points[4] = (UBYTE)dio48_points[han_idx].port1b;
p_dio_event->points[5] = (UBYTE)dio48_points[han_idx].port1c;
perform_logic_inversions(&p_dio_event->points[0],
invert_mask,
NUM_DIO_ADDRESS_PER_BOARD);
clock_gettime(CLOCK_REALTIME, &p_dio_event->rtclock);
// check sequence & post error to stderr if mismatch
if((han_sequence + seq_delta) != p_dio_event->sequence)
{
fprintf(stderr,
“%11.5f: %s: INTERRUPT STACK OVERFLOW - int %ld handler %ld delta
%ld\n”,
p_dio_event->nanotime, task_name, han_sequence,
p_dio_event->sequence, seq_delta );
fflush( stderr );
seq_delta = p_dio_event->sequence - han_sequence;
}
} // process_dio_board
display_dio_event(DIO_EVENT * p_dio_event)
{
// static BOOL first_in = FALSE;
// static DIO_EVENT old_event;
int i, j;
int dio_pt_idx;
// if (!first_in)
// {
// memcpy(&old_event, p_dio_event, sizeof(DIO_EVENT));
// first_in = TRUE;
// }
printf("\fDIO%02d Sequence %d\n\n", p_dio_event->dio,
p_dio_event->sequence);
for (i = 0; i < NUM_DIO_ADDRESS_PER_BOARD; i++)
{
for (j = 0; j < 8; j++)
{
dio_pt_idx = NUM * 8 * NUM_DIO_ADDRESS_PER_BOARD + i*8+j;
if (dio_pt_idx < I_DIO_EOL)
printf(":%-8s %1s", dio_attrib[dio_pt_idx].pt_name,
(test_bit(i *8 + j, &p_dio_event->points[0]) ? “Y” : “N”));
}
printf(":\n");
} // for i
} //
void print_usage_and_exit()
{
printf ("\n\n");
printf (“ISA usage: dio ISA BASE_ADDR 0x140 INTERRUPT 4 0 &\n”);
printf (“PCI usage: dio PCI ICS DIO-48 0 &\n”);
printf (" base and address can be either hex (0x) or decimal\n");
printf ("\n\n");
exit (-1);
}
//
/* MAIN */
//
void main(int argc, char ** argv)
{
pid_t rpid = INV_PID;
pid_t timer_proxy;
int status; // local scratch
static UBYTE invert_mask[NUM_DIO_ADDRESS_PER_BOARD];
int message[6]; // to keep health ping happy
//
// SCuM, 9/24/97 – added rollover logic so 1st time thru is truly
// unique, and I/O distributor(s) can toss the packet it necessary.
//
int_sequence = IO_RESTART_SEQUENCE; // set so CYC can assume IOC
restarted
if (argc >= 5)
{
if (strcmp(“ISA”,argv[1]) ==0)
{
SetISA_CardObject(&Dio, argc, argv, TRUE); // True yields
verbose
BASE = Dio.BaseAddr1;
}
if (strcmp(“PCI”,argv[1]) ==0)
{
SetPCI_CardObject(&Dio, argc, argv, TRUE); // True yields
verbose
BASE = Dio.BaseAddr2;
}
}
if (BASE == 0)
{
printf ("\ndio: base_address not assigned!\n");
print_usage_and_exit();
}
status = init_dio( argc, argv);
if (status == -1)
{
exit(0);
} // if
// INIT HANDLER INDEX TO BUFFERS
han_sequence = 0;
init_invert_logic_mask( &dio_attrib[(board_num *
NUM_DIO_ADDRESS_PER_BOARD*8)],
&invert_mask[0], NUM_DIO_ADDRESS_PER_BOARD);
// SETUP THE DIO48 TO GENERATE INTERRUPTS (DO AS LAST ITEM BEFORE INT
LOOP)
outp( BASE+0x0003, 0x9b ); // port 0 as input
outp( BASE+0x0007, 0x9b ); // port 1 as input
outp( BASE+0x000b, 0x00 ); // enable change of state intrpt
// RE-ENABLE INTERRUPTS ON THE DIO48 BOARD
outp( BASE+0x000F, 0x00 );
//
/* PROCESS INTERRUPT DATA LOOP - DO FOREVER */
//
// PACK THE DATA FOR THE CONSUMERS
dio_event.msg_id = DIO_UPDATE; // define message as dio update
dio_event.node = getnid();
dio_event.dio = NUM; // indicator of which dio was sender
read_points( );
process_dio_board(&dio_event, &invert_mask[0] );
han_sequence++;
while (send_message( &dio_in_distrib_pid, NODE_LOCAL,
DIO_IN_DISTRIB_PROCESS,
&dio_event, NULL, sizeof(DIO_EVENT), 0) != SUCCESS)
{
// printf(“DIO%d – waiting for dioin_distrib\n”, NUM);
sleep(2);
}
if (logging)
display_dio_event(&dio_event);
if ((status = timer_init(&timer_proxy, 2, 0, 2, 0)) != SUCCESS)
{
fprintf(stderr, “%s: timer_init() error[%d], task ABEND\n”,
task_name, status);
exit(-1);
}
while (1)
{
if (get_next_message (&rpid, &message, sizeof(message)) != SUCCESS )
continue;
if (rpid == timer_proxy)
continue;
if (rpid != proxy)
continue;
process_dio_board(&dio_event, &invert_mask[0] );
if (logging)
{
display_dio_event(&dio_event);
}
//
/ NOTIFY IOC CONSUMERS /
//
/-----------------------------------------------------------/
/* LOCATE AND SEND A COPY OF THE UPDATE TO THE IOC PROCESSOR /
/ USING TASK ‘dio_in_distrib’ /
/-----------------------------------------------------------*/
send_message( &dio_in_distrib_pid, NODE_LOCAL, DIO_IN_DISTRIB_PROCESS,
&dio_event, NULL, sizeof(DIO_EVENT), 0);
// ---------------------------------
// INCREMENT SEQUENCE FOR NEXT EVENT
// ---------------------------------
han_sequence++;
} // while
// qnx_hint_detach( id );
}