Date: Fri, 31 Jul 1998 15:31:37 -0600 From: chanders@timing.com (Craig Anderson) To: Mike Smith <mike@smith.net.au> Cc: freebsd-hackers@FreeBSD.ORG Subject: Re: Using FreeBSD for data acquisition? (long) Message-ID: <199807312131.PAA04940@count.timing.com> In-Reply-To: Your message of "Fri, 31 Jul 1998 13:55:30 PDT." <199807312055.NAA00498@dingo.cdrom.com>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
I've attached all the source.
Thanks again.
> >
> > My company is considering using FreeBSD for a data acquisition
> > application. We want to know if an upper bound be placed on the
> > latency of an application reading data from a data acquisition
> > board? FreeBSD is being considered first and then commercial RTOS's
> > will be tried.
>
> No, there is no upper bound. Your response time will vary depending on
> a large number of factors, eg. swap activity, other driver activity,
> hardware errors, etc.
>
> > A test project is being done. The project uses an ISA data
> > acquisition board. The board interrupts 500 times a second, an 8
> > byte data block is read from the board on each interrupt.
> >
> > There is a driver lkm for the board: a) driver buffers 4k data
> > blocks. b) driver interrupt function reads 1 data block, stores
> > data block in buffer, wakes the reader. c) driver read function
> > waits for at least 1 data block, returns minimum of requested data
> > blocks and available data blocks.
> >
> > A test application: a) Loops forever requesting 4096 data blocks.
> > b) Saves the number of data blocks returned on each read to a file.
> >
> > Because the interrupts/data blocks arrive at 500 HZ, I am interpreting
> > the number of data blocks returned on a read as a measure of read
> > latency of the application. If the read returns 50 data blocks, it
> > has been .1 second since the last read. (50/500 = .1)
> >
> > Below are code fragments from the test application. The rtprio()
> > call is to put the process into RTP_PRIO_REALTIME. Should this
> > make a difference? Is the code correct?
>
> You haven't included enough code to be certain that your device driver
> is working properly. I suspect that it might not be.
>
> > At the end of the message is histogram data for a test run of about
> > 15 hours. I plot the data with gnuplot, using a logscale for y.
> > Note that the longest read is 480 (480/500 = .960 seconds). Note
> > that the most common read is 50 (50/500 = .100 seconds).
>
> This would tend to indicate a possible problem with the interaction
> between your interrupt handler and the read handler, but it's
> impossible to tell without seeing the code.
>
> > I'd like comments from FreeBSD hackers out there. Can an upper
> > bound be put on the read latency? An upper bound of about .100 seconds
> > is probably acceptable. Why is a read latency of .100 seconds (50
> > data blocks) so common? Should RTP_PRIO_REALTIME help? Does FreeBSD
> > 3 have realtime features that will help? Is there a problem with
> > the code or a better approach?
>
> In a soft-realtime project I was working on last year, we used the
> rtprio facility to expedite the data handling process. We were able to
> meet a hardware-mandated 100msec delay with light loading under some
> circumstances, but this was not sustainable if disk I/O was being
> performed (we were saving incoming data at >1MB/sec).
>
> There are enhanced realtime facilities in FreeBSD 3.0 which would help
> in some cases, however it's not clear from your message what your real
> goals are.
>
>
>
[-- Attachment #2 --]
#if !defined(gt401_h)
# define gt401_h
#include <sys/ioccom.h>
/* units of pt4 are 0.4 x 10^-6 sec */
#define D_TYPE_NONE 99
#define D_TYPE_ERROR 1
#define D_TYPE_TIMETAG 2
#define MIN_PERIOD_X_10000 1
#define MAX_PERIOD_X_10000 50000
typedef struct gt401_read_s
{
short overflow;
short d_type;
union
{
struct
{
unsigned int board_index;
long long board_time_tag_usec;
unsigned int driver_index;
long long driver_board_usec;
} tt;
struct
{
short err_code;
char err_str[58];
} ee;
} dd;
}
gt401_read;
#define GT401_START_EVENTS \
_IOW('G', 1, int)
#define GT401_STOP_EVENTS \
_IO('G', 2)
#define GT401_GET_HIGH_WATER \
_IOR('G', 3, int)
#define GT401_CLEAR_HIGH_WATER \
_IO('G', 4)
#endif /* #if !defined(gt401_h) */
[-- Attachment #3 --]
# $Id: Makefile,v 1.12 1998/07/30 17:19:23 chanders Exp $
.PATH: .
KMOD = gt401_mod
SRCS = gt401.c gt401.h ngt401.h
#MAN4 = gt401.4
#MAN8 = gt401in.8
LN = ln -f -s
CFLAGS += \
-I. \
-I/usr/src/sys \
-DGT401_MODULE
CLEANFILES += ngt401.h
ngt401.h:
echo "#define NGT401 1" > ngt401.h
r0: gt401_mod.o
cp gt401_mod.o \
/usr/local/tsc/rel/bin
test03: test03.c
cc -o test03 test03.c
test04: test04.c
cc -o test04 test04.c
test05: test05.c
cc -o test05 test05.c
test06: test06.c
cc -g -o test06 test06.c
test07: test07.c
cc -g -o test07 test07.c
test08: test08.c
cc -g -o test08 test08.c
log_read_counts_980726: log_read_counts_980726.c
cc -g -o log_read_counts_980726 log_read_counts_980726.c
counts_to_histogram: counts_to_histogram.c
cc -g -o counts_to_histogram counts_to_histogram.c
kmem_gt401: kmem_gt401.c
cc -g -o kmem_gt401 kmem_gt401.c
.include <bsd.kmod.mk>
[-- Attachment #4 --]
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/signalvar.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <machine/clock.h>
#include <machine/endian.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/timerreg.h>
#include <i386/isa/icu.h>
#include "ngt401.h"
#include "gt401.h"
#define UNIT(d) (minor(d) >> 16)
#define CDEV_MAJOR 90
#define MAX_GT401_RBUF (4*1024)
enum
{
io_addr_status_reg = 0,
io_addr_ram_addr_reg = 0,
io_addr_cont_reg_1 = 1,
io_addr_cont_reg_2 = 2,
io_addr_mux_addr_reg = 3,
io_addr_mux_data_reg = 4,
io_addr_8254_data_reg = 5,
io_addr_us_count0 = 6,
io_addr_us_count1 = 7,
io_addr_cmd_ready = 7,
io_addr_dp_ram = 8,
io_addr_err_reg = 13,
dp_ram_lus_page = 0,
dp_ram_command_first_page = 144,
dp_ram_status_page = 146,
mux_54_ctr_0 = 0,
mux_54_ctr_1 = 8,
mux_54_ctr_2 = 16,
mux_54_cont = 24,
cmd_poll_inner_loop = 512,
cmd_poll_sleep_loop = 20,
cmd_poll_usleep = 10000,
cmd_buf_sz = 20,
cmd_init = 1,
cmd_self_test = 2,
cmd_nop = 3,
cmd_ext_osc = 7,
cmd_pulse = 8,
cmd_tt = 9,
cmd_chan_off = 12,
cmd_tt_size = 13,
cmd_clr_tt = 15,
cmd_clr_err = 20,
cont_reg1_src_mask = 7,
cont_reg1_pulse_en = 0x08,
cont_reg1_sw_pulse = 0x10,
status_reg_ready_for_cmd = 1,
cont_reg2_irq_src_mask = 7,
cont_reg2_irq_src_chan0 = 0,
cont_reg2_irq_src_chan1 = 1,
cont_reg2_irq_src_chan2 = 2,
cont_reg2_irq_src_chan3 = 3,
cont_reg2_irq_src_board_proc = 4,
cont_reg2_irq_src_out0 = 5,
cont_reg2_irq_src_out1 = 6,
cont_reg2_irq_src_out2 = 7,
cont_reg2_intr_en_bit = 0x08,
cont_reg2_intr_edge_bit = 0x10,
conn_CLK0 = 0, /* All CLKn values must be consecutive */
conn_CLK1 = 1,
conn_CLK2 = 2,
conn_EXT_CLK0 = 3, /* All EXT_CLKn values must be consecutive */
conn_EXT_CLK1 = 4,
conn_EXT_CLK2 = 5,
conn_GATE0 = 6, /* All GATEn values must be consecutive */
conn_GATE1 = 7,
conn_GATE2 = 8,
conn_EXT_GATE0 = 9, /* All EXT_GATEn values must be consecutive */
conn_EXT_GATE1 = 10,
conn_EXT_GATE2 = 11,
conn_OUT0 = 12, /* All OUTn values must be consecutive */
conn_OUT1 = 13,
conn_OUT2 = 14,
conn_IO_CHAN0 = 15, /* All IO_CHANn values must be consecutive */
conn_IO_CHAN1 = 16,
conn_IO_CHAN2 = 17,
conn_IO_CHAN3 = 18,
conn_EXT_CHAN0 = 19, /* all EXT_CHANNn values must be consecutive */
conn_EXT_CHAN1 = 20,
conn_EXT_CHAN2 = 21,
conn_EXT_CHAN3 = 22,
conn_TEN_MHZ = 23,
conn_GATED_TEN_MHZ = 24,
conn_SW_LOW = 25, /* To force an input low */
conn_SW_HIGH = 26, /* To force an input high */
conn_SW_INTR = 27, /* For selecting interrupts from */
tt_ptr_reg = 12, /* in dpram status page */
err_reg = 13, /* in dpram status page */
tt_edge_neg = 0x08,
tt_edge_pos = 0x10,
ten_int = 2,
ten_int_out = 3,
ten_ext = 4,
err_success = 0,
err_no_io_fd = 1,
err_timeout = 2,
err_cmd_sz = 3,
err_invalid_8254_ctr = 4,
err_connect_dst = 5,
err_connect_src = 6,
err_io_open = 7,
err_ten_mhz = 8,
err_irq_src = 9
};
/* One dev per board */
static struct isa_device devices[NGT401];
/*
* Setup the per-device structure defining routines used at 'boot' time.
*/
static int gt401attach(struct isa_device* devices);
static int gt401probe(struct isa_device* devices);
void gt401intr(int unit);
static int get_lus (int base_io_port, long long *lus);
static int mux_matrix_connect (int unit,
u_short src,
u_short dst);
static int pulse_out (int unit,
int enable,
int src);
static int removeIntr(int irq);
static int send_cmd (int unit, u_char *cmd_buf, u_int size);
static int set_8254 (int unit,
u_short counter,
u_short mode,
u_short initial_count);
static int set_irq (int unit,
int enable,
int src,
int edge);
static int setupIntr(struct isa_device* device,
int irq,
inthand2_t* hand,
int unit,
u_int* maskp);
static int start_tt (int unit);
static int stop_tt (int unit);
static int get_tt_count (int unit);
static int get_tt (int unit, long long *tt);
static int get_status(int unit);
struct isa_driver gt401driver =
{
gt401probe,
gt401attach,
"gt401"
};
static d_open_t gt401open;
static d_close_t gt401close;
static d_read_t gt401read;
static d_ioctl_t gt401ioctl;
static d_select_t gt401select;
static struct cdevsw gt401_cdevsw =
{
gt401open,
gt401close,
gt401read,
nowrite,
gt401ioctl,
nostop,
nullreset,
nodevtotty,
gt401select,
nommap,
NULL,
"gt401",
NULL,
-1
};
struct gt401Unit
{
short inuse;
short tagging_active;
char * sleep_address;
u_char cont_reg1;
u_char cont_reg2;
u_char mux_control[8];
int counts_8254[2];
u_int driver_index;
u_int max_rbuf_count;
/* rbuf_XXX is a ring buffer for reads */
short rbuf_count;
short rbuf_first;
short rbuf_last;
gt401_read rbuf [MAX_GT401_RBUF];
};
#if 1 /* debug */
static char * save_sleep_address = 0;
#endif
static struct gt401Unit gt401Unit[NGT401];
static int
get_lus (int base_io_port, long long *lus)
{
char us_count[8] = {0};
char bb[8] = {0};
long long *ll_us = (long long *)&(us_count[0]);
long long *ll_bb = (long long *)&(bb[0]);
us_count [0] = inb (base_io_port + io_addr_us_count0);
outb((base_io_port + io_addr_ram_addr_reg), dp_ram_lus_page);
inb(base_io_port); /* need a 1us delay */
inb(base_io_port);
us_count [1] = inb (base_io_port + io_addr_us_count1);
bb[0] = inb (base_io_port + io_addr_dp_ram + 0);
bb[1] = inb (base_io_port + io_addr_dp_ram + 1);
bb[2] = inb (base_io_port + io_addr_dp_ram + 2);
bb[3] = inb (base_io_port + io_addr_dp_ram + 3);
bb[4] = inb (base_io_port + io_addr_dp_ram + 4);
bb[5] = inb (base_io_port + io_addr_dp_ram + 5);
bb[6] = inb (base_io_port + io_addr_dp_ram + 6);
bb[7] = inb (base_io_port + io_addr_dp_ram + 7);
*lus = *ll_bb + *ll_us;
return 0;
}
static int
gt401attach(struct isa_device* devices)
{
int ii,jj;
uprintf ("gt401attach\n");
for (ii=0; ii<NGT401; ++ii)
{
gt401Unit[ii].inuse = 0;
gt401Unit[ii].tagging_active = 0;
gt401Unit[ii].sleep_address = (char *)&(gt401Unit[ii].inuse);
gt401Unit[ii].cont_reg1 = 0;
gt401Unit[ii].cont_reg2 = 0;
for (jj=0; jj<8; ++jj)
gt401Unit[ii].mux_control[jj] = 0;
gt401Unit[ii].counts_8254[0] = 0;
gt401Unit[ii].counts_8254[1] = 0;
gt401Unit[ii].driver_index = 0;
gt401Unit[ii].max_rbuf_count = 0;
gt401Unit[ii].rbuf_count = 0;
gt401Unit[ii].rbuf_first = 0;
gt401Unit[ii].rbuf_last = MAX_GT401_RBUF - 1;
for (jj=0; jj<MAX_GT401_RBUF; ++jj)
{
gt401Unit[ii].rbuf[jj].d_type = D_TYPE_NONE;
gt401Unit[ii].rbuf[jj].overflow = 0;
}
}
#if 1 /* debug */
save_sleep_address = gt401Unit[0].sleep_address;
#endif
return 1;
}
static int
gt401close (dev_t dev,
int flags,
int fmt,
struct proc* p)
{
int unit = UNIT(dev);
#if 1 /* debug */
uprintf ("gt401close unit=%d\n",unit);
#endif
gt401Unit[unit].tagging_active = 0;
gt401Unit[unit].inuse = 0;
set_irq (unit,
0,
-1,
1);
return 0;
}
static int
gt401ioctl (dev_t dev,
int cmd,
caddr_t data,
int flag,
struct proc* p)
{
int unit = UNIT(dev);
if ((unit < 0) || (unit >= NGT401))
return (ENODEV);
if (!(devices[unit].id_alive))
return (ENODEV);
if (!gt401Unit[unit].inuse)
return (ENODEV);
switch (cmd)
{
case GT401_START_EVENTS:
{
int * count_x_10000_i;
int max_counter = 0xffff;
double period;
double period_x_10000;
double target_counts_d;
int target_counts_i;
#if 0
stop_tt(unit);
#endif
count_x_10000_i = ((int *)data);
#if 1 /* debug */
uprintf("gt401ioctl: GT401_START_EVENTS arg=%d\n",*count_x_10000_i);
#endif
if (*count_x_10000_i < MIN_PERIOD_X_10000)
{
return (EINVAL);
}
if (*count_x_10000_i > MAX_PERIOD_X_10000)
{
return (EINVAL);
}
period_x_10000 = (double)(*count_x_10000_i);
period = period_x_10000 / 10000.;
target_counts_d = period * 10e6;
target_counts_i = (int)target_counts_d;
if (target_counts_i < max_counter)
{
gt401Unit[unit].counts_8254[0] = target_counts_i;
gt401Unit[unit].counts_8254[1] = 0;
}
else
{
gt401Unit[unit].counts_8254[0] = target_counts_i / max_counter;
++gt401Unit[unit].counts_8254[0];
gt401Unit[unit].counts_8254[1] = (target_counts_i
/ gt401Unit[unit].counts_8254[0]);
}
#if 1 /* debug */
uprintf("gt401ioctl: 8254[0]=%d 8254[1]=%d\n",
gt401Unit[unit].counts_8254[0],
gt401Unit[unit].counts_8254[1]);
#endif
if (gt401Unit[unit].counts_8254[1] <= 0)
{ /* using one 8254 */
mux_matrix_connect (unit,
conn_TEN_MHZ,
conn_CLK1);
mux_matrix_connect (unit,
conn_SW_HIGH,
conn_GATE1);
set_8254 (unit,
1,
3,
gt401Unit[unit].counts_8254[0]);
set_irq (unit,
1,
conn_OUT1,
1);
} /* using one 8254 */
else
{ /* using two 8254's */
mux_matrix_connect (unit,
conn_TEN_MHZ,
conn_CLK0);
mux_matrix_connect (unit,
conn_SW_HIGH,
conn_GATE0);
set_8254 (unit,
0,
3,
gt401Unit[unit].counts_8254[0]);
mux_matrix_connect (unit,
conn_OUT0,
conn_CLK1);
mux_matrix_connect (unit,
conn_SW_HIGH,
conn_GATE1);
set_8254 (unit,
1,
3,
gt401Unit[unit].counts_8254[1]);
set_irq (unit,
1,
conn_OUT1,
1);
} /* using two 8254's */
#if 0 /* debug */
uprintf("gt401ioctl: disable 8254 clock for debug.\n");
mux_matrix_connect (unit,
conn_SW_LOW,
conn_GATE0);
mux_matrix_connect (unit,
conn_SW_LOW,
conn_GATE1);
#endif
gt401Unit[unit].tagging_active = 1;
#if 0 /* not yet */
start_tt(unit);
#endif
}
break;
case GT401_STOP_EVENTS:
gt401Unit[unit].tagging_active = 0;
#if 1 /* debug */
uprintf("gt401ioctl: GT401_STOP_EVENTS\n");
#endif
#if 0 /* not yet */
stop_tt(unit);
#endif
set_irq (unit,
0,
-1,
1);
break;
case GT401_GET_HIGH_WATER:
{
int * iarg = ((int *)data);
*iarg = gt401Unit[unit].max_rbuf_count;
}
break;
case GT401_CLEAR_HIGH_WATER:
gt401Unit[unit].max_rbuf_count = 0;
break;
default:
return (ENODEV);
break;
}
return 0;
}
void
gt401intr(int unit)
{
int base_io_port;
int io_cont_reg2;
long long btime_intr;
int rbuf_index;
unit = 0; /* debug */
base_io_port = devices[0].id_iobase;
get_lus(base_io_port,&btime_intr);
if (gt401Unit[0].tagging_active == 0)
return;
++gt401Unit[0].driver_index;
rbuf_index = gt401Unit[0].rbuf_last + 1;
if (rbuf_index >= MAX_GT401_RBUF)
rbuf_index = 0;
if (gt401Unit[0].rbuf_count >= MAX_GT401_RBUF)
{
gt401Unit[0].rbuf_count = MAX_GT401_RBUF - 1;
++gt401Unit[0].rbuf_first;
if(gt401Unit[0].rbuf_first >= MAX_GT401_RBUF)
gt401Unit[0].rbuf_first = 0;
gt401Unit[0].rbuf [gt401Unit[0].rbuf_first].overflow = 1;
}
gt401Unit[0].rbuf[rbuf_index].d_type = D_TYPE_TIMETAG;
gt401Unit[0].rbuf[rbuf_index].dd.tt.driver_index = gt401Unit[0].driver_index;
gt401Unit[0].rbuf[rbuf_index].dd.tt.driver_board_usec = btime_intr;
++gt401Unit[0].rbuf_count;
gt401Unit[0].rbuf_last = rbuf_index;
if (gt401Unit[0].max_rbuf_count < gt401Unit[0].rbuf_count)
gt401Unit[0].max_rbuf_count = gt401Unit[0].rbuf_count;
io_cont_reg2 = base_io_port + io_addr_cont_reg_2;
gt401Unit[0].cont_reg2 &= ~(cont_reg2_intr_en_bit);
outb (io_cont_reg2,
gt401Unit[0].cont_reg2);
send_cmd (unit, NULL, 0);
if (gt401Unit[0].tagging_active != 0)
{
gt401Unit[0].cont_reg2 |= cont_reg2_intr_en_bit;
outb (io_cont_reg2,
gt401Unit[0].cont_reg2);
}
#if 1 /* debug */
if (gt401Unit[0].sleep_address != save_sleep_address)
{
panic("gt401intr: sleep_address %x != %x",
gt401Unit[0].sleep_address,
save_sleep_address);
}
#endif
wakeup (gt401Unit[0].sleep_address);
}
static int
gt401open (dev_t dev,
int flags,
int fmt,
struct proc* p)
{
int unit = UNIT(dev);
int jj;
#if 1 /* debug */
uprintf("gt401open unit=%d\n",unit);
#endif
if ((unit < 0) || (unit >= NGT401))
return (ENXIO);
if (!(devices[unit].id_alive))
return (ENXIO);
if (gt401Unit[unit].inuse)
return (EBUSY);
gt401Unit[unit].inuse = 1;
gt401Unit[unit].tagging_active = 0;
gt401Unit[unit].rbuf_count = 0;
gt401Unit[unit].rbuf_first = 0;
gt401Unit[unit].rbuf_last = MAX_GT401_RBUF - 1;
for (jj=0; jj<MAX_GT401_RBUF; ++jj)
{
gt401Unit[unit].rbuf[jj].d_type = D_TYPE_NONE;
gt401Unit[unit].rbuf[jj].overflow = 0;
}
#if 1 /* debug */
{
int addr = devices[unit].id_iobase;
uprintf("gt401open unit=%d io_addr=0x%x\n",unit,addr);
}
#endif
return 0;
}
static int
gt401probe(struct isa_device* devices)
{
int addr = 0x100;
int ii;
long long time_check[9];
long long time_diffs[8];
int units = 0;
long long duration_max;
long long duration_min;
long long min_max_range;
long long min_max_range_check;
for (ii=0; ii<NGT401; ++ii)
{
devices[ii].id_alive = 0;
devices[ii].id_enabled = 0;
}
/* check for gt401 at addr by reading it's time */
for (ii=0; ii<9; ++ii)
{
get_lus (addr,&(time_check[ii]));
tsleep (&ii, PZERO, "gt401", 10);
}
for (ii=1; ii<9; ++ii)
{
time_diffs[ii-1] = time_check[ii] - time_check[ii-1];
}
duration_max = duration_min = time_diffs[0];
for (ii=0; ii<8; ++ii)
{
if (duration_max < time_diffs[ii])
duration_max = time_diffs[ii];
if (duration_min > time_diffs[ii])
duration_min = time_diffs[ii];
}
min_max_range_check = duration_min / 2;
min_max_range = duration_max - duration_min;
#if 1 /* debug */
uprintf("gt401probe addr 0x%x min %d max %d check %d range %d\n",
addr,
(int)duration_min,
(int)duration_max,
(int)min_max_range_check,
(int)min_max_range);
#endif
if (duration_min < 5000)
{
uprintf("gt401probe time diff too small\n");
return 0;
}
if (min_max_range > min_max_range_check)
{
uprintf("gt401probe time diff too variable\n");
return 0;
}
devices[0].id_unit = 0;
devices[0].id_iobase = addr;
devices[0].id_driver = >401driver;
devices[0].id_irq = 0;
devices[0].id_intr = gt401intr;
devices[0].id_drq = -1;
#if 0 /* debug */
devices[0].id_ri_flags = RI_FAST;
#endif
devices[0].id_alive = 1;
devices[0].id_enabled = 1;
++units;
return units;
}
static int
gt401read (dev_t dev,
struct uio* uio,
int flag)
{
int left_to_move = 0;
int status;
int unit = UNIT(dev);
int i_timeout = hz / 4;
if ((unit < 0) || (unit >= NGT401))
return (ENODEV);
if (!(devices[0].id_alive))
return (ENODEV);
if (!gt401Unit[0].inuse)
return (ENODEV);
disable_intr();
for (;;)
{
if (gt401Unit[0].rbuf_count > 0)
break;
enable_intr();
status = tsleep (gt401Unit[0].sleep_address,
(PSWP | PCATCH),
"gt401",
i_timeout);
if (!((status == 0)
|| (status == EWOULDBLOCK)))
{
return (status);
}
disable_intr();
}
left_to_move = uio->uio_resid / sizeof(struct gt401_read_s);
while (left_to_move > 0)
{
if (gt401Unit[0].rbuf_count <= 0)
break;
status = uiomove((caddr_t)&(gt401Unit[0].rbuf[gt401Unit[0].rbuf_first]),
sizeof(struct gt401_read_s),
uio);
if (status)
{
enable_intr();
return (EFAULT);
}
--left_to_move;
--gt401Unit[0].rbuf_count;
++gt401Unit[0].rbuf_first;
if(gt401Unit[0].rbuf_first >= MAX_GT401_RBUF)
gt401Unit[0].rbuf_first = 0;
if (gt401Unit[0].rbuf_count <= 0)
break;
}
enable_intr();
return 0;
}
static int
gt401select (dev_t dev,
int rw,
struct proc* p)
{
return 0;
}
/*
*
*/
#ifdef GT401_MODULE
#include <sys/exec.h>
#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/lkm.h>
MOD_DEV(gt401, LM_DT_CHAR, CDEV_MAJOR, >401_cdevsw);
int
gt401_load(struct lkm_table* lkmtp, int cmd)
{
int units;
int irq = 11;
int istat;
uprintf("Gt401 driver loading, capable of %d board(s)\n", NGT401);
units = gt401probe (&devices[0]);
if (units <= 0)
{
uprintf("gt401 driver: probe failed\n");
return (1);
}
gt401attach (&devices[0]);
istat = setupIntr (&devices[0],
irq,
gt401intr,
0,
NULL);
if (istat != irq)
{
uprintf ("gt401 driver: INT %d register failed\n",irq);
return (1);
}
return (0);
}
int
gt401_unload(struct lkm_table* lkmtp, int cmd)
{
int unit;
uprintf("Gt401 driver unloading...\n");
for (unit=0; unit<NGT401; ++unit)
{
if (devices[unit].id_alive == 1)
{
/** disable interrupts on board */
if (devices[unit].id_irq != 0)
{
uprintf ("removeINTR 0x%x\n",(ffs(devices[unit].id_irq) - 1));
removeIntr(ffs(devices[unit].id_irq) - 1);
}
}
}
return (0);
}
/** XXX: /usr/include/lkm.h fails to define this: */
int gt401_stat(struct lkm_table* lkmtp, int cmd);
int
gt401_stat(struct lkm_table* lkmtp, int cmd)
{
return (0);
}
int
gt401_mod(struct lkm_table* lkmtp, int cmd, int ver)
{
DISPATCH(lkmtp, cmd, ver, gt401_load, gt401_unload, gt401_stat);
}
#endif /* GT401_MODULE */
static int
mux_matrix_connect (int unit,
u_short src,
u_short dst)
{
int base_io_port;
int err;
u_short mux_addr_reg;
u_char mux_addr_reg_val;
u_short mux_data_reg;
u_char src_val;
base_io_port = devices[unit].id_iobase;
err = 0;
mux_addr_reg = base_io_port + io_addr_mux_addr_reg;
mux_data_reg = base_io_port + io_addr_mux_data_reg;
// verify that dst is valid
switch (dst)
{
case conn_IO_CHAN0:
case conn_IO_CHAN1:
case conn_IO_CHAN2:
case conn_IO_CHAN3:
case conn_EXT_CHAN0:
case conn_EXT_CHAN1:
case conn_EXT_CHAN2:
case conn_EXT_CHAN3:
case conn_CLK0:
case conn_CLK1:
case conn_CLK2:
case conn_GATE0:
case conn_GATE1:
case conn_GATE2:
break;
default:
err = err_connect_dst;
return err;
}
// verify that src is valid
switch (dst)
{
case conn_IO_CHAN0:
case conn_IO_CHAN1:
case conn_IO_CHAN2:
case conn_IO_CHAN3:
switch (src)
{
case conn_EXT_CHAN0:
src_val = 0;
break;
case conn_EXT_CHAN1:
src_val = 1;
break;
case conn_EXT_CHAN2:
src_val = 2;
break;
case conn_EXT_CHAN3:
src_val = 3;
break;
case conn_OUT0:
src_val = 4;
break;
case conn_OUT1:
src_val = 5;
break;
case conn_OUT2:
src_val = 6;
break;
case conn_SW_LOW:
src_val = 7;
break;
case conn_SW_HIGH:
src_val = 7;
break;
default:
err = err_connect_src;
return err;
}
break;
case conn_EXT_CHAN0:
if (src != conn_IO_CHAN0)
{
err = err_connect_src;
return err;
}
break;
case conn_EXT_CHAN1:
if (src != conn_IO_CHAN1)
{
err = err_connect_src;
return err;
}
break;
case conn_EXT_CHAN2:
if (src != conn_IO_CHAN2)
{
err = err_connect_src;
return err;
}
break;
case conn_EXT_CHAN3:
if (src != conn_IO_CHAN3)
{
err = err_connect_src;
return err;
}
break;
case conn_CLK0:
switch (src)
{
case conn_IO_CHAN0:
src_val = 0;
break;
case conn_IO_CHAN1:
src_val = 1;
break;
case conn_IO_CHAN2:
src_val = 2;
break;
case conn_IO_CHAN3:
src_val = 3;
break;
case conn_OUT2:
src_val = 4;
break;
case conn_TEN_MHZ:
src_val = 5;
break;
case conn_GATED_TEN_MHZ:
src_val = 6;
break;
case conn_EXT_CLK2:
src_val = 7;
break;
default:
err = err_connect_dst;
return err;
}
break;
case conn_CLK1:
switch (src)
{
case conn_IO_CHAN0:
src_val = 0;
break;
case conn_IO_CHAN1:
src_val = 1;
break;
case conn_IO_CHAN2:
src_val = 2;
break;
case conn_IO_CHAN3:
src_val = 3;
break;
case conn_OUT0:
src_val = 4;
break;
case conn_TEN_MHZ:
src_val = 5;
break;
case conn_GATED_TEN_MHZ:
src_val = 6;
break;
case conn_EXT_CLK2:
src_val = 7;
break;
default:
err = err_connect_dst;
return err;
}
break;
case conn_CLK2:
switch (src)
{
case conn_IO_CHAN0:
src_val = 0;
break;
case conn_IO_CHAN1:
src_val = 1;
break;
case conn_IO_CHAN2:
src_val = 2;
break;
case conn_IO_CHAN3:
src_val = 3;
break;
case conn_OUT1:
src_val = 4;
break;
case conn_TEN_MHZ:
src_val = 5;
break;
case conn_GATED_TEN_MHZ:
src_val = 6;
break;
case conn_EXT_CLK2:
src_val = 7;
break;
default:
err = err_connect_dst;
return err;
}
break;
case conn_GATE0:
switch (src)
{
case conn_IO_CHAN0:
src_val = 0;
break;
case conn_IO_CHAN1:
src_val = 1;
break;
case conn_IO_CHAN2:
src_val = 2;
break;
case conn_IO_CHAN3:
src_val = 3;
break;
case conn_OUT1:
src_val = 4;
break;
case conn_OUT2:
src_val = 5;
break;
case conn_SW_LOW:
src_val = 6;
break;
case conn_SW_HIGH:
src_val = 6;
break;
case conn_EXT_GATE0:
src_val = 7;
break;
default:
err = err_connect_dst;
return err;
}
break;
case conn_GATE1:
switch (src)
{
case conn_IO_CHAN0:
src_val = 0;
break;
case conn_IO_CHAN1:
src_val = 1;
break;
case conn_IO_CHAN2:
src_val = 2;
break;
case conn_IO_CHAN3:
src_val = 3;
break;
case conn_OUT0:
src_val = 5;
break;
case conn_OUT2:
src_val = 4;
break;
case conn_SW_LOW:
src_val = 6;
break;
case conn_SW_HIGH:
src_val = 6;
break;
case conn_EXT_GATE1:
src_val = 7;
break;
default:
err = err_connect_dst;
return err;
}
break;
case conn_GATE2:
switch (src)
{
case conn_IO_CHAN0:
src_val = 0;
break;
case conn_IO_CHAN1:
src_val = 1;
break;
case conn_IO_CHAN2:
src_val = 2;
break;
case conn_IO_CHAN3:
src_val = 3;
break;
case conn_OUT0:
src_val = 4;
break;
case conn_OUT1:
src_val = 5;
break;
case conn_SW_LOW:
src_val = 6;
break;
case conn_SW_HIGH:
src_val = 6;
break;
case conn_EXT_GATE2:
src_val = 7;
break;
default:
err = err_connect_dst;
return err;
}
break;
}
// handle sw set gate and chan
switch (src)
{
case conn_SW_LOW:
switch (dst)
{
case conn_IO_CHAN0:
mux_addr_reg_val = 6;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~1);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_IO_CHAN1:
mux_addr_reg_val = 6;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~2);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_IO_CHAN2:
mux_addr_reg_val = 6;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~4);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_IO_CHAN3:
mux_addr_reg_val = 6;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~8);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_GATE0:
mux_addr_reg_val = 3;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~1);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_GATE1:
mux_addr_reg_val = 3;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~2);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_GATE2:
mux_addr_reg_val = 3;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~4);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
}
break;
case conn_SW_HIGH:
switch (dst)
{
case conn_IO_CHAN0:
mux_addr_reg_val = 6;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 1;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_IO_CHAN1:
mux_addr_reg_val = 6;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 2;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_IO_CHAN2:
mux_addr_reg_val = 6;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 4;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_IO_CHAN3:
mux_addr_reg_val = 6;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 8;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_GATE0:
mux_addr_reg_val = 3;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 1;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_GATE1:
mux_addr_reg_val = 3;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 2;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_GATE2:
mux_addr_reg_val = 3;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 4;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
}
break;
}
// make the connection
switch (dst)
{
case conn_IO_CHAN0:
mux_addr_reg_val = 4;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= ~7;
gt401Unit[unit].mux_control[mux_addr_reg_val] |= src_val;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
mux_addr_reg_val = 7;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= ~1;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_IO_CHAN1:
mux_addr_reg_val = 4;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= ~0x38;
gt401Unit[unit].mux_control[mux_addr_reg_val] |= (src_val << 3);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
mux_addr_reg_val = 7;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= ~2;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_IO_CHAN2:
mux_addr_reg_val = 5;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= ~7;
gt401Unit[unit].mux_control[mux_addr_reg_val] |= src_val;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
mux_addr_reg_val = 7;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= ~4;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_IO_CHAN3:
mux_addr_reg_val = 5;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= ~0x38;
gt401Unit[unit].mux_control[mux_addr_reg_val] |= (src_val << 3);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
mux_addr_reg_val = 7;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= ~8;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_EXT_CHAN0:
mux_addr_reg_val = 7;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 1;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_EXT_CHAN1:
mux_addr_reg_val = 7;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 2;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_EXT_CHAN2:
mux_addr_reg_val = 7;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 4;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_EXT_CHAN3:
mux_addr_reg_val = 7;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= 8;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_CLK0:
mux_addr_reg_val = 0;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~7);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= src_val;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_CLK1:
mux_addr_reg_val = 1;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~7);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= src_val;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_CLK2:
mux_addr_reg_val = 2;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~7);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= src_val;
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_GATE0:
mux_addr_reg_val = 0;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~0x38);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= (src_val << 3);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_GATE1:
mux_addr_reg_val = 1;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~0x38);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= (src_val << 3);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
case conn_GATE2:
mux_addr_reg_val = 2;
outb (mux_addr_reg, mux_addr_reg_val);
gt401Unit[unit].mux_control[mux_addr_reg_val] &= (~0x38);
gt401Unit[unit].mux_control[mux_addr_reg_val] |= (src_val << 3);
outb(mux_data_reg, gt401Unit[unit].mux_control[mux_addr_reg_val]);
break;
}
return err;
}
static int
pulse_out (int unit,
int enable,
int src)
{
int base_io_port;
u_short cont_reg1;
int err;
u_char src_code;
base_io_port = devices[unit].id_iobase;
err = err_success;
cont_reg1 = base_io_port + io_addr_cont_reg_1;
if (enable == 0)
{ /* disable pulse output */
gt401Unit[unit].cont_reg1 &= (~cont_reg1_pulse_en);
outb (cont_reg1, gt401Unit[unit].cont_reg1);
return err;
}
// enable pulse output
switch (src)
{
case conn_IO_CHAN0:
src_code = 0;
break;
case conn_IO_CHAN1:
src_code = 1;
break;
case conn_IO_CHAN2:
src_code = 2;
break;
case conn_IO_CHAN3:
src_code = 3;
break;
case conn_SW_LOW:
case conn_SW_HIGH:
src_code = 4;
break;
case conn_OUT0:
src_code = 5;
break;
case conn_OUT1:
src_code = 6;
break;
case conn_OUT2:
src_code = 7;
break;
default:
err = err_connect_src;
return err;
}
gt401Unit[unit].cont_reg1 &= cont_reg1_src_mask;
gt401Unit[unit].cont_reg1 |= src_code;
switch (src)
{
case conn_SW_LOW:
gt401Unit[unit].cont_reg1 &= (~cont_reg1_sw_pulse);
break;
case conn_SW_HIGH:
gt401Unit[unit].cont_reg1 |= cont_reg1_sw_pulse;
break;
}
outb (cont_reg1,
gt401Unit[unit].cont_reg1);
tsleep (&err, PZERO, "gt401", 100);
gt401Unit[unit].cont_reg1 |= cont_reg1_pulse_en; // then enable pulse out
outb (cont_reg1,
gt401Unit[unit].cont_reg1);
return err;
}
static int
set_irq (int unit,
int enable,
int src,
int edge)
{
int base_io_port;
int err;
int io_cont_reg2;
int src_code;
base_io_port = devices[unit].id_iobase;
err = err_success;
io_cont_reg2 = base_io_port + io_addr_cont_reg_2;
if (enable == 0)
{ /* turn off interrupts */
gt401Unit[unit].cont_reg2 &= ~(cont_reg2_intr_en_bit);
#if 0 /* debug */
printf ("outb (%x,%x)\n",
io_cont_reg2,
gt401Unit[unit].cont_reg2);
#endif
outb (io_cont_reg2,
gt401Unit[unit].cont_reg2);
return err;
} /* turn off interrupts */
/* turn on interrupts */
switch (src)
{
case conn_IO_CHAN0:
src_code = cont_reg2_irq_src_chan0;
break;
case conn_IO_CHAN1:
src_code = cont_reg2_irq_src_chan1;
break;
case conn_IO_CHAN2:
src_code = cont_reg2_irq_src_chan2;
break;
case conn_IO_CHAN3:
src_code = cont_reg2_irq_src_chan3;
break;
case conn_OUT0:
src_code = cont_reg2_irq_src_out0;
break;
case conn_OUT1:
src_code = cont_reg2_irq_src_out1;
break;
case conn_OUT2:
src_code = cont_reg2_irq_src_out2;
break;
default:
err = err_irq_src;
return err;
}
gt401Unit[unit].cont_reg2 &= ~(cont_reg2_irq_src_mask);
gt401Unit[unit].cont_reg2 |= src_code;
if (edge == 0)
gt401Unit[unit].cont_reg2 &= ~(cont_reg2_intr_edge_bit);
else
gt401Unit[unit].cont_reg2 |= cont_reg2_intr_edge_bit;
gt401Unit[unit].cont_reg2 &= ~(cont_reg2_intr_en_bit);
#if 0 /* debug */
printf ("outb (%x,%x)\n",
io_cont_reg2,
gt401Unit[unit].cont_reg2);
#endif
outb (io_cont_reg2,
gt401Unit[unit].cont_reg2);
gt401Unit[unit].cont_reg2 |= cont_reg2_intr_en_bit;
#if 0 /* debug */
printf ("outb (%x,%x)\n",
io_cont_reg2,
gt401Unit[unit].cont_reg2);
#endif
outb (io_cont_reg2,
gt401Unit[unit].cont_reg2);
return err;
}
/*
* Register the appropriate INTerrupt.
*/
static int
setupIntr(struct isa_device* device,
int irq, inthand2_t* hand, int unit, u_int* maskp)
{
u_int mask;
u_int flags;
mask = 1ul << irq;
flags = device->id_ri_flags;
if (maskp)
INTRMASK(*maskp, mask);
if (register_intr(irq, 0, flags, hand, maskp, unit) == 0) {
device->id_irq = mask;
INTREN(mask);
return (irq);
}
if (maskp)
INTRUNMASK(*maskp, mask);
return (-1);
}
/*
* Unregister the appropriate INTerrupt.
*/
static int
removeIntr(int irq)
{
if (unregister_intr(irq, intr_handler[irq])) {
uprintf("\tINT #%d failed to unregister\n", irq);
return (EPERM);
}
else {
INTRDIS(1 << irq);
uprintf("\tINT #%d unregistered\n", irq);
return (0);
}
}
static int
send_cmd (int unit, u_char *cmd_buf, u_int size)
{
int base_io_port;
u_short cmd_ready_reg;
u_short dual_port_bank_index;
u_short dual_port_bank_reg;
u_short dual_port_data_reg;
int err;
int ii;
int jj;
u_short status_reg;
u_char status_val;
int ready;
int ready_loop_max = 5000;
err = err_success;
base_io_port = devices[unit].id_iobase;
// Wait for command ready bit
status_reg = base_io_port + io_addr_status_reg;
ready = 0;
for (ii=0; ii<cmd_poll_sleep_loop; ++ii)
{
for (jj=0; jj<cmd_poll_inner_loop; ++jj)
{
if (jj > ready_loop_max)
break;
status_val = inb(status_reg);
if ((status_val & status_reg_ready_for_cmd) != 0)
{
ready = 1;
break;
}
}
if (ready)
break;
}
if (!ready)
{
err = err_timeout;
return err;
}
if (size == 0)
{
return err;
}
if (size > cmd_buf_sz)
{
err = err_cmd_sz;
return err;
}
// place command bytes into dual port ram
dual_port_bank_reg = base_io_port + io_addr_ram_addr_reg;
dual_port_bank_index = dp_ram_command_first_page;
for (ii=0; ii<size; )
{
dual_port_data_reg = base_io_port + io_addr_dp_ram;
outb(dual_port_bank_reg, dual_port_bank_index);
for (jj=0; jj<8; ++jj)
{ // 8 bytes in each bank
outb(dual_port_data_reg, cmd_buf[ii]);
++ii;
if (ii >= size)
break;
++dual_port_data_reg;
}
if (ii >= size)
break;
++dual_port_bank_index;
}
// signal command
cmd_ready_reg = base_io_port + io_addr_cmd_ready;
outb(cmd_ready_reg, 0);
return err;
}
static int
set_8254 (int unit,
u_short counter,
u_short mode,
u_short initial_count)
{
u_short addr54;
int base_io_port;
u_char control_byte;
int err;
u_char ic_hi;
u_char ic_lo;
u_short i8254_data_reg;
u_short mux_addr_reg;
err = err_success;
base_io_port = devices[unit].id_iobase;
switch (counter)
{
case 0:
addr54 = mux_54_ctr_0;
break;
case 1:
addr54 = mux_54_ctr_1;
break;
case 2:
addr54 = mux_54_ctr_2;
break;
default:
err = err_invalid_8254_ctr;
return err;
}
mux_addr_reg = base_io_port + io_addr_mux_addr_reg;
i8254_data_reg = base_io_port + io_addr_8254_data_reg;
control_byte = 0;
control_byte |= ((counter & 3) << 6);
control_byte |= (3 << 4);
control_byte |= ((mode & 7) << 1);
// BCD bit is 0, meaning counter it 16 bit unsigned binary
outb(mux_addr_reg, mux_54_cont); // map in 8254 control register
outb(i8254_data_reg, control_byte);
outb(mux_addr_reg, addr54); // map in 8254 counter register.
ic_lo = initial_count & 0xff;
ic_hi = (initial_count >> 8) & 0xff;
outb(i8254_data_reg, ic_lo);
outb(i8254_data_reg, ic_hi);
return err;
}
static int start_tt (int unit)
{
u_char cmd_buf[8] = {0};
int istat;
cmd_buf[0] = cmd_clr_err;
send_cmd(unit,cmd_buf,1);
istat = get_status(unit);
printf ("start_tt after clr_err stat=%d\n",istat);
cmd_buf[0] = cmd_tt_size;
cmd_buf[1] = 12;
cmd_buf[2] = 0; /* no wrap */
send_cmd(unit, cmd_buf, 3);
istat = get_status(unit);
printf ("start_tt tt_size stat=%d\n",istat);
cmd_buf[0] = cmd_tt;
cmd_buf[1] = tt_edge_pos; /* chan=0 << 6 | edge */
send_cmd(unit, cmd_buf, 2);
istat = get_status(unit);
printf ("start_tt tt stat=%d\n",istat);
return 0;
}
static int stop_tt (int unit)
{
u_char cmd_buf[8] = {0};
int istat;
cmd_buf[0] = cmd_clr_err;
send_cmd(unit,cmd_buf,1);
istat = get_status(unit);
printf ("stop_tt stat after cmd_clr_err=%d\n",istat);
cmd_buf[0] = cmd_chan_off;
cmd_buf[1] = 0xc0;
send_cmd(unit, cmd_buf, 2);
istat = get_status(unit);
printf ("stop_tt stat after cmd_chan_off=%d\n",istat);
#if 0
cmd_buf[0] = cmd_clr_tt;
cmd_buf[1] = 0; /* clear time tag buffer and time tag counter */
send_cmd(unit, cmd_buf, 2);
istat = get_status(unit);
printf ("stop_tt stat after cmd_clr_tt=%d\n",istat);
#endif
return 0;
}
static int get_tt_count (int unit)
{
return 0;
}
static int get_tt (int unit, long long *tt)
{
int base_io_port;
int ram_addr_reg;
int next_tt;
send_cmd(unit,NULL,0);
base_io_port = devices[unit].id_iobase;
ram_addr_reg = base_io_port + io_addr_ram_addr_reg;
outb (ram_addr_reg, dp_ram_status_page);
{
int ii;
printf ("status page : ");
for (ii=0; ii<8; ++ii)
{
next_tt = inb(base_io_port + 8 + ii);
printf (" 0x%x");
}
printf ("\n");
}
{
int ii;
printf ("tag buf :");
outb (ram_addr_reg, 156);
for (ii=0; ii<8; ++ii)
{
next_tt = inb(base_io_port + 8 + ii);
printf (" 0x%x");
}
outb (ram_addr_reg, 157);
for (ii=0; ii<4; ++ii)
{
next_tt = inb(base_io_port + 8 + ii);
printf (" 0x%x");
}
printf ("\n");
}
return 0;
}
static int get_status(int unit)
{
int base_io_port;
int ram_addr_reg;
int status_reg;
int istat;
base_io_port = devices[unit].id_iobase;
ram_addr_reg = base_io_port + io_addr_ram_addr_reg;
send_cmd(unit,NULL,0);
outb (ram_addr_reg, dp_ram_status_page);
status_reg = base_io_port + err_reg;
istat = inb(status_reg) & 0xff;
return istat;
}
[-- Attachment #5 --]
#if 0
cc -o test04 test04.c
Experiment with /dev/gt401 not doing time tags
Run continuously
#endif
#include <sys/types.h>
#include <sys/rtprio.h>
#include <stdio.h>
#include <fcntl.h>
#include "gt401.h"
#define DEV_PATH "/dev/gt401"
int fd = -1;
int arg_ioctl = 0;
int read_count = 0;
#define MAX_TTAG_BUF (4*1024)
main (int argc, char **argv)
{
int istat;
int ii;
gt401_read * ttag;
gt401_read ttag_buf[MAX_TTAG_BUF], last_ttag;
int log_mod;
double period_d;
double freq_d;
int first_flag = 1;
int read_req = (sizeof(gt401_read) * MAX_TTAG_BUF);
int read_data_count;
int max_read_data_count = 0;
long long sample_period = -1;
long long sample_period_hi;
long long sample_period_low;
int read_dropped = 0;
int driver_dropped = 0;
long long index_diff_driver;
long long index_diff_read_error;
long long calc_usec_diff_driver;
long long calc_index_diff_driver;
long long index_diff_driver_error;
struct rtprio rtp;
int rtp_flag = 0;
#if 1
rtp_flag = 1;
rtp.type = RTP_PRIO_REALTIME;
rtp.prio = 0;
istat = rtprio (RTP_SET,
0,
&rtp);
if (istat != 0)
{
perror("rtprio");
exit (1);
}
fprintf (stderr,"rtprio\n");
#else
fprintf (stderr, "No rtprio\n");
#endif
fd = open (DEV_PATH, O_RDONLY, 0);
if (fd < 0)
{
fprintf(stderr,"open of %s failed\n",DEV_PATH);
exit (__LINE__);
}
arg_ioctl = 20;
period_d = ((double)arg_ioctl) / 10000.0;
freq_d = 1.0 / period_d;
log_mod = (int)(freq_d * 30.0);
sample_period = (long long)(period_d * 1e6);
sample_period_low = sample_period - (sample_period / 8);
sample_period_hi = sample_period + (sample_period / 8);
fprintf (stderr,"periodX10000=%d period=%f freq=%f\n",arg_ioctl,period_d,freq_d);
fprintf (stderr,"sizeof(gt401_read) = %d\n",sizeof(gt401_read));
for (ii=1; ii<argc; ++ii)
{
printf ("# %s\n",argv[ii]); /* gnuplot comment line */
}
printf ("# period=%f freq=%f",
period_d, freq_d);
if (rtp_flag)
printf(" RTPRIO");
else
printf(" NO RTPRIO");
printf ("\n");
istat = ioctl(fd,GT401_START_EVENTS,&arg_ioctl);
for (;;)
{
istat = read (fd,(void *)&(ttag_buf[0]),read_req);
if (istat <= 0)
{
fprintf (stderr,"read fail istat=%d\n",istat);
perror("read fail");
exit (__LINE__);
}
read_data_count = istat / sizeof(gt401_read);
printf ("%d\n",read_data_count);
fflush(stdout);
} /* for (;;) */
istat = ioctl(fd,GT401_STOP_EVENTS,&arg_ioctl);
return (0);
}
[-- Attachment #6 --]
#if 0
cc -o test04 test04.c
Experiment with /dev/gt401 not doing time tags
Run continuously
#endif
#include <sys/types.h>
#include <sys/rtprio.h>
#include <stdio.h>
#include <fcntl.h>
#include "gt401.h"
#define DEV_PATH "/dev/gt401"
int fd = -1;
int arg_ioctl = 0;
int read_count = 0;
#define MAX_TTAG_BUF (4*1024)
main (int argc, char **argv)
{
int istat;
int ii;
gt401_read * ttag;
gt401_read ttag_buf[MAX_TTAG_BUF], last_ttag;
int log_mod;
double period_d;
double freq_d;
int first_flag = 1;
int read_req = (sizeof(gt401_read) * MAX_TTAG_BUF);
int read_data_count;
int max_read_data_count = 0;
long long sample_period = -1;
long long sample_period_hi;
long long sample_period_low;
int read_dropped = 0;
int driver_dropped = 0;
long long index_diff_driver;
long long index_diff_read_error;
long long calc_usec_diff_driver;
long long calc_index_diff_driver;
long long index_diff_driver_error;
struct rtprio rtp;
int rtp_flag = 0;
#if 1
rtp_flag = 1;
rtp.type = RTP_PRIO_REALTIME;
rtp.prio = 0;
istat = rtprio (RTP_SET,
0,
&rtp);
if (istat != 0)
{
perror("rtprio");
exit (1);
}
fprintf (stderr,"rtprio\n");
#else
fprintf (stderr, "No rtprio\n");
#endif
fd = open (DEV_PATH, O_RDONLY, 0);
if (fd < 0)
{
fprintf(stderr,"open of %s failed\n",DEV_PATH);
exit (__LINE__);
}
arg_ioctl = 20;
period_d = ((double)arg_ioctl) / 10000.0;
freq_d = 1.0 / period_d;
log_mod = (int)(freq_d * 30.0);
sample_period = (long long)(period_d * 1e6);
sample_period_low = sample_period - (sample_period / 8);
sample_period_hi = sample_period + (sample_period / 8);
fprintf (stderr,"periodX10000=%d period=%f freq=%f\n",arg_ioctl,period_d,freq_d);
fprintf (stderr,"sizeof(gt401_read) = %d\n",sizeof(gt401_read));
for (ii=1; ii<argc; ++ii)
{
printf ("# %s\n",argv[ii]); /* gnuplot comment line */
}
printf ("# period=%f freq=%f",
period_d, freq_d);
if (rtp_flag)
printf(" RTPRIO");
else
printf(" NO RTPRIO");
printf ("\n");
istat = ioctl(fd,GT401_START_EVENTS,&arg_ioctl);
for (;;)
{
istat = read (fd,(void *)&(ttag_buf[0]),read_req);
if (istat <= 0)
{
fprintf (stderr,"read fail istat=%d\n",istat);
perror("read fail");
exit (__LINE__);
}
read_data_count = istat / sizeof(gt401_read);
printf ("%d\n",read_data_count);
fflush(stdout);
} /* for (;;) */
istat = ioctl(fd,GT401_STOP_EVENTS,&arg_ioctl);
return (0);
}
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199807312131.PAA04940>
