#include <asm/io.h>
#include "rtapi.h"
#include "rtapi_app.h"
#include <linux/pci.h>
#include "hal.h"
#include "hal_vti.h"
MODULE_AUTHOR("Eric Johnson");
MODULE_DESCRIPTION
("Driver for Vigilant Technologies ENCDAC 4 channel controller");
MODULE_LICENSE("GPL");
static int num_chan = MAX_CHANS;
RTAPI_MP_INT(num_chan, "number of channels");
static char *dio = "ii";
RTAPI_MP_STRING(dio, "dio config string - expects something like IOiooi");
typedef struct {
hal_bit_t *data;
union {
hal_bit_t *not;
hal_bit_t invert;
} io;
} io_pin;
typedef struct {
hal_s32_t *count[MAX_CHANS];
hal_float_t *pos[MAX_CHANS];
hal_float_t pos_scale[MAX_CHANS];
hal_float_t *dac_value[MAX_CHANS];
hal_float_t dac_offset[MAX_CHANS];
hal_float_t dac_gain[MAX_CHANS];
hal_float_t *adc_value[MAX_CHANS];
hal_float_t adc_offset[MAX_CHANS];
hal_float_t adc_gain[MAX_CHANS];
int adc_current_chan;
io_pin port[MAX_IO_PORTS][PINS_PER_PORT];
unsigned char dir_bits[MAX_IO_PORTS * 2];
unsigned char model;
} vti_struct;
static vti_struct *vti_driver;
struct pci_dev *dev = NULL;
struct pci_access *device;
volatile struct encoder *encoder = NULL;
volatile struct timer *timer = NULL;
volatile struct dac *dac = NULL;
volatile struct ip *ip = NULL;
static int comp_id;
static int outpinnum = 0, inputpinnum = 0;
static int diocount = 0;
static hal_s32_t enc_counts[MAX_CHANS];
static int export_counter(int num, vti_struct * addr);
static int export_dac(int num, vti_struct * addr);
static int export_dio_pins(int io_points);
static int export_pin(int num, int dir, vti_struct * addr);
static int export_input_pin(int pinnum, io_pin * pin);
static int export_output_pin(int pinnum, io_pin * pin);
static int vti_init_card(void);
static int vti_autodetect(void);
static int vti_counter_init(int channels);
static long vti_counter_read(int i);
static int vti_dac_init(int channels);
static int vti_dac_write(int ch, short value);
static int vti_adc_init(int channels);
static int vti_dio_init(int nibbles);
static int vti_parse_dio(void);
static void vti_adcs_read(void *arg, long period); static void vti_dacs_write(void *arg, long period); static void vti_counter_capture(void *arg, long period); static void vti_di_read(void *arg, long period); static void vti_do_write(void *arg, long period);
#define MAX_CHAN 8
int rtapi_app_main(void)
{
int retval;
if ((num_chan <= 0) || (num_chan > MAX_CHAN)) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: invalid num_chan: %d\n", num_chan);
return -1;
}
if ((dio == 0) || (dio[0] == '\0')) {
rtapi_print_msg(RTAPI_MSG_ERR, "VTI: ERROR: no dio config string\n");
return -1;
}
comp_id = hal_init("hal_vti");
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "VTI: ERROR: hal_init() failed\n");
return -1;
}
vti_driver = hal_malloc(num_chan * sizeof(vti_struct));
if (vti_driver == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "VTI: ERROR: hal_malloc() failed\n");
hal_exit(comp_id);
return -1;
}
if ((retval=vti_init_card()) != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: vti_init_card() failed\n");
hal_exit(comp_id);
return retval;
}
diocount = vti_parse_dio();
if (diocount == -1) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: bad config info for port.\n");
return -1;
}
export_dio_pins(diocount);
vti_dio_init(diocount / 4);
if (vti_counter_init(num_chan) == -1) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: bad config info counter.\n");
return -1;
}
vti_dac_init(num_chan);
vti_adc_init(0);
retval = hal_export_funct("vti.capture-position", vti_counter_capture,
vti_driver, 1, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: vti.counter-capture funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO,
"VTI: installed %d encoder counters\n", num_chan);
retval = hal_export_funct("vti.write-dacs", vti_dacs_write,
vti_driver, 1, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: vti.write-dacs funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO, "VTI: installed %d dacs\n", num_chan);
retval = hal_export_funct("vti.read-adcs", vti_adcs_read,
vti_driver, 1, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: vti.read-adcs funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO, "VTI: installed %d adcs\n", 0);
retval = hal_export_funct("vti.di-read", vti_di_read,
vti_driver, 0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: vti.di-read funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO,
"VTI: installed %d digital inputs\n", inputpinnum);
retval = hal_export_funct("vti.do-write", vti_do_write,
vti_driver, 0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: vti.do-write funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO,
"VTI: installed %d digital outputs\n", outpinnum);
hal_ready(comp_id);
return 0;
}
static int vti_parse_dio(void)
{
int i = 0, nibble = 0;
if (strlen(dio) == 0)
return 0;
while (i < strlen(dio)) {
switch (dio[i]) {
case 'I':
vti_driver->dir_bits[nibble] = 0;
vti_driver->dir_bits[nibble + 1] = 0;
break;
case 'O':
vti_driver->dir_bits[nibble] = 1;
vti_driver->dir_bits[nibble + 1] = 1;
break;
case 'i':
vti_driver->dir_bits[nibble] = 0;
i++;
if (dio[i] == 'i')
vti_driver->dir_bits[nibble + 1] = 0;
else if (dio[i] == 'o')
vti_driver->dir_bits[nibble + 1] = 1;
else
return -1;
break;
case 'o':
vti_driver->dir_bits[nibble] = 1;
i++;
if (dio[i] == 'i')
vti_driver->dir_bits[nibble + 1] = 0;
else if (dio[i] == 'o')
vti_driver->dir_bits[nibble + 1] = 1;
else
return -1;
break;
default:
return -1;
}
nibble += 2;
i++;
}
return nibble * 4; }
void rtapi_app_exit(void)
{
hal_exit(comp_id);
}
static void vti_counter_capture(void *arg, long period)
{
vti_struct *vti;
int i;
vti = arg;
for (i = 0; i < num_chan; i++) {
*(vti->count[i]) = vti_counter_read(i);
if (vti->pos_scale[i] < 0.0) {
if (vti->pos_scale[i] > -EPSILON)
vti->pos_scale[i] = -1.0;}
else {
if (vti->pos_scale[i] < EPSILON)
vti->pos_scale[i] = 1.0; }
*(vti->pos[i]) = *(vti->count[i]) / vti->pos_scale[i];
}
}
static void vti_dacs_write(void *arg, long period)
{
vti_struct *vti;
double volts;
unsigned short ncounts, i;
vti = arg;
for (i = 0; i < num_chan; i++) {
volts =
(*(vti->dac_value[i]) - vti->dac_offset[i]) * vti->dac_gain[i];
ncounts = ((volts / 10) * 0x7fff) + 0x8000;
vti_dac_write(i, ncounts);
}
}
static void vti_adcs_read(void *arg, long period)
{
return;
}
static void split_input(unsigned char data, io_pin * dest, int num)
{
int b;
unsigned char mask;
mask = 0x01;
for (b = 0; b < num; b++) {
if (data & mask) {
*(dest->data) = 0;
*(dest->io.not) = 1;
} else {
*(dest->data) = 1;
*(dest->io.not) = 0;
}
mask <<= 1;
dest++;
}
}
unsigned char build_output(io_pin * src, int num)
{
int b;
unsigned char data, mask;
data = 0x00;
mask = 0x01;
for (b = 0; b < num; b++) {
if (*(src->data)) {
if (!(src->io.invert)) {
data |= mask;
}
} else {
if ((src->io.invert)) {
data |= mask;
}
}
mask <<= 1;
src++;
}
return data;
}
static void vti_di_read(void *arg, long period) {
vti_struct *vti;
int i;
char latchedVal;
vti = arg;
if (diocount == 0) return;
latchedVal = encoder->DIO;
if (vti->dir_bits[0] == 0)
split_input(latchedVal, &(vti->port[0][0]), 4);
if (vti->dir_bits[1] == 0)
split_input(latchedVal, &(vti->port[0][4]), 4);
if (diocount <= 8) return; for (i = 1; i < (diocount / 8); i++) {
latchedVal = dac->DIO[i - 1];
if (vti->dir_bits[i * 2] == 0)
split_input(latchedVal, &(vti->port[i][0]), 4);
if (vti->dir_bits[i * 2 + 1] == 0)
split_input(latchedVal, &(vti->port[i][4]), 4);
}
}
static void vti_do_write(void *arg, long period) {
vti_struct *vti;
int i;
vti = arg;
if (diocount == 0) return; encoder->DIO = build_output(&(vti->port[0][0]), 8);
if (diocount <= 8) return;
for (i = 1; i < diocount / 8; i++) {
dac->DIO[i - 1] = build_output(&(vti->port[i][0]), 8);
}
}
static int vti_counter_init(int counters)
{
int i, retval=0;
for (i = 0; i < counters; i++) {
retval = export_counter(i, vti_driver);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: counter %d var export failed\n", i + 1);
hal_exit(comp_id);
return -1;
}
*(vti_driver->count[i]) = 0;
*(vti_driver->pos[i]) = 0.0;
vti_driver->pos_scale[i] = 1.0;
}
return 0;
}
static int vti_dac_init(int channels)
{
int retval, i;
encoder->DAC = DAC_IND_MODE; for (i = 0; i < channels; i++) {
retval = export_dac(i, vti_driver);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"VTI: ERROR: dac %d var export failed\n", i + 1);
hal_exit(comp_id);
return -1;
}
*(vti_driver->dac_value[i]) = 0;
vti_driver->dac_offset[i] = 0.0;
vti_driver->dac_gain[i] = 1.0;
vti_dac_write(i, DAC_ZERO_VOLTS);
}
return 0;
}
static int vti_adc_init(int channels)
{
return 0;
}
static int vti_dio_init(int nibbles)
{
unsigned int mask;
int i;
if (diocount == 0) return 0; encoder->DIO = 0; mask = encoder->Interrupt;
mask &= 0xfffffcff; if (vti_driver->dir_bits[0] == 1)
mask |= 0x00000100; if (vti_driver->dir_bits[1] == 1)
mask |= 0x00000200; encoder->Interrupt = mask;
if (diocount <= 8) return 0; for (i = 0; i < (diocount - 8) / 8; i++) {
dac->DIO[i] = 0; }
mask = 0;
for (i = 0; i < 8; i++) { if (vti_driver->dir_bits[(i * 2) + 2] == 1)
mask |= (1 << i);
}
dac->config0 = mask;
mask = 0;
for (i = 0; i < 8; i++) { if (vti_driver->dir_bits[(i * 2) + 18] == 1)
mask |= (1 << i);
}
dac->config1 = mask;
return 0;
}
static long vti_counter_read(int axis)
{
unsigned int status;
unsigned int count;
Longword EncData;
static long int lastCount;
if ((axis >= MAX_CHANS) || (axis < 0)) {
return 0x80000000; }
lastCount = enc_counts[axis];
status = encoder->Status; count = encoder->Counter[axis];
EncData.Long = (long int)enc_counts[axis];
if (status & (1 << axis)) {
if (status & (1 << (axis + 4))) {
EncData.Word[1] += 1;
}
else {
EncData.Word[1] -= 1;
}
}
EncData.Word[0] = count;
if ((EncData.Long - lastCount) > 0x7fff)
EncData.Word[1] -= 1;
else
if ((lastCount - EncData.Long) > 0x7fff)
EncData.Word[1] += 1;
enc_counts[axis] = EncData.Long;
return EncData.Long;
}
static int vti_dac_write(int axis, short value)
{
short junk;
if ((axis >= MAX_CHANS) || (axis < 0)) {
return -1;
}
junk = dac->mode; dac->dac[axis] = value;
return 0;
}
static int vti_init_card()
{
int retval=vti_autodetect();
if (retval == 0) {
encoder = (volatile struct encoder *)
ioremap(pci_resource_start(dev, 2), sizeof(encoder));
dac =
(volatile struct dac *) ioremap(pci_resource_start(dev,
4), sizeof(dac));
timer =
(volatile struct timer *) ioremap(pci_resource_start(dev,
3), sizeof(timer));
ip = (volatile struct ip *) ioremap(pci_resource_start(dev, 5),
sizeof(ip));
} else {
return (retval);
}
rtapi_print_msg(RTAPI_MSG_INFO, "VTI: Encoders mapped to : %p\n",
encoder);
rtapi_print_msg(RTAPI_MSG_INFO, "VTI: DACs mapped to : %p\n",
dac);
rtapi_print_msg(RTAPI_MSG_INFO, "VTI: Timers mapped to : %p\n",
timer);
rtapi_print_msg(RTAPI_MSG_INFO, "VTI: Industry pack mapped to : %p\n",
ip);
encoder->Status = 0;
encoder->Reset = 0;
return 0;
}
static int vti_autodetect()
{
dev = pci_get_device(VENDOR, DEVICE, dev);
if (dev) {
pci_dev_put(dev);
rtapi_print_msg(RTAPI_MSG_INFO,
"VTI: Card detected in slot: %2x\n", PCI_SLOT(dev->devfn));
return (0);
} else {
rtapi_print_msg(RTAPI_MSG_INFO, "VTI: Exiting with auto detect failed\n");
return (-ENODEV);
}
}
static int export_counter(int num, vti_struct * addr)
{
int retval, msg;
msg = rtapi_get_msg_level();
rtapi_set_msg_level(RTAPI_MSG_WARN);
retval = hal_pin_s32_newf(HAL_OUT, &addr->count[num],
comp_id, "vti.%d.counts", num);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &addr->pos[num],
comp_id, "vti.%d.position", num);
if (retval != 0) {
return retval;
}
retval = hal_param_float_newf(HAL_RW, &addr->pos_scale[num],
comp_id, "vti.%d.position-scale", num);
if (retval != 0) {
return retval;
}
rtapi_set_msg_level(msg);
return 0;
}
static int export_dac(int num, vti_struct * addr)
{
int retval, msg;
msg = rtapi_get_msg_level();
rtapi_set_msg_level(RTAPI_MSG_WARN);
retval = hal_pin_float_newf(HAL_IN, &addr->dac_value[num],
comp_id, "vti.%d.dac-value", num);
if (retval != 0) {
return retval;
}
retval = hal_param_float_newf(HAL_RW, &addr->dac_offset[num],
comp_id, "vti.%d.dac-offset", num);
if (retval != 0) {
return retval;
}
retval = hal_param_float_newf(HAL_RW, &addr->dac_gain[num],
comp_id, "vti.%d.dac-gain", num);
if (retval != 0) {
return retval;
}
rtapi_set_msg_level(msg);
return 0;
}
static int export_dio_pins(int io_points)
{
int i, msg, retval=0;
if (io_points == 0) return 0;
msg = rtapi_get_msg_level();
rtapi_set_msg_level(RTAPI_MSG_WARN);
for (i = 0; i < io_points; i++) {
retval |= export_pin(i, vti_driver->dir_bits[i / 4], vti_driver);
}
rtapi_set_msg_level(msg);
return retval;
}
static int export_pin(int num, int dir, vti_struct * addr)
{
int retval;
if (dir != 0)
retval =
export_output_pin(outpinnum++, &(addr->port[num / 8][num % 8]));
else
retval =
export_input_pin(inputpinnum++, &(addr->port[num / 8][num % 8]));
if (retval != 0)
return retval;
return 0;
}
static int export_input_pin(int pinnum, io_pin * pin)
{
int retval;
retval = hal_pin_bit_newf(HAL_OUT, &(pin->data),
comp_id, "vti.in-%02d", pinnum);
if (retval != 0)
return retval;
retval = hal_pin_bit_newf(HAL_OUT, &(pin->io.not),
comp_id, "vti.in-%02d-not", pinnum);
*(pin->data) = 0;
*(pin->io.not) = 1;
return retval;
}
static int export_output_pin(int pinnum, io_pin * pin)
{
int retval;
retval = hal_pin_bit_newf(HAL_IN, &(pin->data),
comp_id, "vti.out-%02d", pinnum);
if (retval != 0)
return retval;
retval = hal_param_bit_newf(HAL_RW, &(pin->io.invert),
comp_id, "vti.out-%02d-invert", pinnum);
*(pin->data) = 0;
pin->io.invert = 0;
return retval;
}