#include <linux/pci.h>
#include "rtapi.h"
#include "rtapi_app.h"
#include "hal.h"
#include "motenc.h"
MODULE_AUTHOR("Pete Vavaroutsos");
MODULE_DESCRIPTION("Driver for Vital Systems MOTENC-100 for EMC HAL");
MODULE_LICENSE("GPL");
typedef struct {
hal_s32_t *pCount; hal_float_t *pPosition; hal_bit_t *pIndex; hal_bit_t *pIndexEnable; hal_bit_t *pReset;
hal_float_t scale;
double oldScale; double scaleRecip; } EncoderPinsParams;
typedef struct {
hal_float_t *pValue;
hal_float_t offset;
hal_float_t gain;
} DacPinsParams;
typedef struct {
hal_float_t *pValue;
hal_float_t offset;
hal_float_t gain;
} AdcPinsParams;
typedef struct {
hal_bit_t *pValue;
hal_bit_t *pValueNot;
} DigitalInPinsParams;
typedef struct {
hal_bit_t *pValue;
hal_bit_t invert;
} DigitalOutPinsParams;
typedef struct {
hal_bit_t *pEstopIn;
hal_bit_t *pEstopInNot;
hal_bit_t *pWatchdogReset;
hal_u32_t watchdogControl;
} MiscPinsParams;
typedef struct {
MotencRegMap *pCard;
int boardType;
char *pTypeName;
int boardID;
int numFpga;
int adcState;
hal_u32_t watchdogControl;
EncoderPinsParams encoder[MOTENC_NUM_ENCODER_CHANNELS];
DacPinsParams dac[MOTENC_NUM_DAC_CHANNELS];
AdcPinsParams adc[MOTENC_NUM_ADC_CHANNELS];
DigitalInPinsParams in[MOTENC_NUM_DIGITAL_INPUTS];
DigitalOutPinsParams out[MOTENC_NUM_DIGITAL_OUTPUTS];
MiscPinsParams misc;
} Device;
static int Device_Init(Device *this, MotencRegMap *pCard);
static int Device_ExportPinsParametersFunctions(Device *this, int componentId);
static int Device_ExportEncoderPinsParametersFunctions(Device *this, int componentId, int boardId);
static int Device_ExportDacPinsParametersFunctions(Device *this, int componentId, int boardId);
static int Device_ExportAdcPinsParametersFunctions(Device *this, int componentId, int boardId);
static int Device_ExportDigitalInPinsParametersFunctions(Device *this, int componentId, int boardId);
static int Device_ExportDigitalOutPinsParametersFunctions(Device *this, int componentId, int boardId);
static int Device_ExportMiscPinsParametersFunctions(Device *this, int componentId, int boardId);
static void Device_EncoderRead(void *this, long period);
static void Device_DacWrite(void *this, long period);
static void Device_AdcRead(void *this, long period);
static void Device_DigitalInRead(void *this, long period);
static void Device_DigitalOutWrite(void *this, long period);
static void Device_MiscUpdate(void *this, long period);
static int Device_AdcRead4(Device *this, int startChannel);
#define MAX_DEVICES 4
typedef struct {
int componentId; Device *deviceTable[MAX_DEVICES];
unsigned char idPresent[MAX_DEVICES];
} Driver;
static Driver driver;
int
rtapi_app_main(void)
{
int i, j;
struct pci_dev *pDev = NULL;
MotencRegMap *pCard = NULL;
Device *pDevice;
driver.componentId = hal_init("hal_motenc");
if (driver.componentId < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: ERROR: hal_init() failed\n");
return(-EINVAL);
}
for(i = 0; i < MAX_DEVICES; i++){
driver.deviceTable[i] = NULL;
driver.idPresent[i] = 0;
}
i = 0;
while((i < MAX_DEVICES) && ((pDev = pci_get_device(MOTENC_VENDOR_ID, MOTENC_DEVICE_ID, pDev)) != NULL)){
pDevice = hal_malloc(sizeof(Device));
if (pDevice == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: ERROR: hal_malloc() failed\n");
hal_exit(driver.componentId);
return(-ENOMEM);
}
driver.deviceTable[i++] = pDevice;
pCard = (MotencRegMap *)ioremap_nocache(pci_resource_start(pDev, 2), pci_resource_len(pDev, 2));
rtapi_print_msg(RTAPI_MSG_INFO, "MOTENC: Card detected in slot %2x\n", PCI_SLOT(pDev->devfn));
rtapi_print_msg(RTAPI_MSG_INFO, "MOTENC: Card address @ %p, Len = %d\n", pCard, (int)pci_resource_len(pDev, 2));
Device_Init(pDevice, pCard);
rtapi_print_msg(RTAPI_MSG_INFO, "MOTENC: Card is %s, ID: %d\n", pDevice->pTypeName, pDevice->boardID);
if ( pDevice->boardType == 0 ) {
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: ERROR, unknown card detected\n");
hal_exit(driver.componentId);
return(-ENODEV);
}
if ( driver.idPresent[pDevice->boardID] != 0 ) {
j = 0;
while ( driver.idPresent[j] != 0 ) {
j++;
if ( j >= MAX_DEVICES ) {
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: ERROR, duplicate ID, can't remap\n");
hal_exit(driver.componentId);
return(-EINVAL);
}
}
pDevice->boardID = j;
rtapi_print_msg(RTAPI_MSG_WARN, "MOTENC: WARNING, duplicate ID, remapped to %d\n", j);
}
driver.idPresent[pDevice->boardID] = 1;
if(Device_ExportPinsParametersFunctions(pDevice, driver.componentId)){
hal_exit(driver.componentId);
return(-EINVAL);
}
}
if(pCard == NULL){
rtapi_print_msg(RTAPI_MSG_WARN, "MOTENC: **** No MOTENC card detected ****\n");
hal_exit(driver.componentId);
return -ENODEV;
}
hal_ready(driver.componentId);
return(0);
}
void
rtapi_app_exit(void)
{
int i, j;
Device *pDevice;
hal_exit(driver.componentId);
for(i = 0; i < MAX_DEVICES; i++){
if((pDevice = driver.deviceTable[i]) != NULL){
for(j = 0; j < pDevice->numFpga; j++){
pDevice->pCard->fpga[j].digitalIo = MOTENC_DIGITAL_OUT;
}
for(j = 0; j < MOTENC_NUM_DAC_CHANNELS; j++){
pDevice->pCard->dac[j] = MOTENC_DAC_COUNT_ZERO;
}
iounmap((void *)(pDevice->pCard));
}
}
}
static int
Device_Init(Device *this, MotencRegMap *pCard)
{
int i, status;
this->pCard = pCard;
this->adcState = 0;
this->watchdogControl = 0;
status = pCard->fpga[0].boardVersion;
if ( status == 0 ) {
this->boardType = 1;
this->pTypeName = "MOTENC-100";
this->numFpga = 2;
} else if ( status == 1 ) {
this->boardType = 2;
this->pTypeName = "MOTENC-Lite";
this->numFpga = 1;
} else {
this->boardType = 2;
this->pTypeName = "unknown";
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: unknown board type - %i\n", status);
this->numFpga = 0;
}
status = pCard->fpga[0].statusControl;
this->boardID = (status & MOTENC_STATUS_BOARD_ID) >> MOTENC_STATUS_BOARD_ID_SHFT;
for(i = 0; i < this->numFpga; i++){
pCard->fpga[i].digitalIo = MOTENC_DIGITAL_OUT;
pCard->fpga[i].statusControl = MOTENC_CONTROL_ENCODER_RESET;
}
for(i = 0; i < MOTENC_NUM_DAC_CHANNELS; i++){
pCard->dac[i] = MOTENC_DAC_COUNT_ZERO;
}
pCard->timerIrqDisable = 1;
pCard->watchdogControl = this->watchdogControl;
return(0);
}
static int
Device_ExportPinsParametersFunctions(Device *this, int componentId)
{
int msgLevel, boardId, error;
msgLevel = rtapi_get_msg_level();
rtapi_set_msg_level(RTAPI_MSG_WARN);
boardId = this->boardID;
error = Device_ExportEncoderPinsParametersFunctions(this, componentId, boardId);
if(!error) error = Device_ExportDacPinsParametersFunctions(this, componentId, boardId);
if(!error) error = Device_ExportAdcPinsParametersFunctions(this, componentId, boardId);
if(!error) error = Device_ExportDigitalInPinsParametersFunctions(this, componentId, boardId);
if(!error) error = Device_ExportDigitalOutPinsParametersFunctions(this, componentId, boardId);
if(!error) error = Device_ExportMiscPinsParametersFunctions(this, componentId, boardId);
rtapi_set_msg_level(msgLevel);
return(error);
}
static int
Device_ExportEncoderPinsParametersFunctions(Device *this, int componentId, int boardId)
{
int halError, channel;
char name[HAL_NAME_LEN + 1];
halError = 0;
for(channel = 0; channel < this->numFpga * MOTENC_FPGA_NUM_ENCODER_CHANNELS; channel++){
if((halError = hal_pin_s32_newf(HAL_OUT, &(this->encoder[channel].pCount),
componentId, "motenc.%d.enc-%02d-count", boardId, channel)) != 0)
break;
if((halError = hal_pin_float_newf(HAL_OUT, &(this->encoder[channel].pPosition),
componentId, "motenc.%d.enc-%02d-position", boardId, channel)) != 0)
break;
if((halError = hal_pin_bit_newf(HAL_OUT, &(this->encoder[channel].pIndex),
componentId, "motenc.%d.enc-%02d-index", boardId, channel)) != 0)
break;
if((halError = hal_pin_bit_newf(HAL_IO, &(this->encoder[channel].pIndexEnable),
componentId, "motenc.%d.enc-%02d-index-enable", boardId, channel)) != 0)
break;
if((halError = hal_pin_bit_newf(HAL_IN, &(this->encoder[channel].pReset),
componentId, "motenc.%d.enc-%02d-reset", boardId, channel)) != 0)
break;
if((halError = hal_param_float_newf(HAL_RW, &(this->encoder[channel].scale),
componentId, "motenc.%d.enc-%02d-scale", boardId, channel)) != 0)
break;
*(this->encoder[channel].pCount) = 0;
*(this->encoder[channel].pPosition) = 0.0;
*(this->encoder[channel].pIndex) = 0;
*(this->encoder[channel].pIndexEnable) = 0;
*(this->encoder[channel].pReset) = 0;
this->encoder[channel].scale = 1.0;
this->encoder[channel].oldScale = 1.0;
this->encoder[channel].scaleRecip = 1.0 / this->encoder[channel].scale;
}
if(!halError){
rtapi_snprintf(name, sizeof(name), "motenc.%d.encoder-read", boardId);
halError = hal_export_funct(name, Device_EncoderRead, this, 1, 0, componentId);
}
if(halError){
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: ERROR: export encoder failed\n");
return(-1);
}
return(0);
}
static int
Device_ExportDacPinsParametersFunctions(Device *this, int componentId, int boardId)
{
int halError, channel;
char name[HAL_NAME_LEN + 1];
halError = 0;
for(channel = 0; channel < MOTENC_NUM_DAC_CHANNELS; channel++){
if((halError = hal_pin_float_newf(HAL_IN, &(this->dac[channel].pValue),
componentId, "motenc.%d.dac-%02d-value", boardId, channel)) != 0)
break;
if((halError = hal_param_float_newf(HAL_RW, &(this->dac[channel].offset),
componentId, "motenc.%d.dac-%02d-offset", boardId, channel)) != 0)
break;
if((halError = hal_param_float_newf(HAL_RW, &(this->dac[channel].gain),
componentId, "motenc.%d.dac-%02d-gain", boardId, channel)) != 0)
break;
*(this->dac[channel].pValue) = 0.0;
this->dac[channel].offset = 0.0;
this->dac[channel].gain = 1.0;
}
if(!halError){
rtapi_snprintf(name, sizeof(name), "motenc.%d.dac-write", boardId);
halError = hal_export_funct(name, Device_DacWrite, this, 1, 0, componentId);
}
if(halError){
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: ERROR: export DAC failed\n");
return(-1);
}
return(0);
}
static int
Device_ExportAdcPinsParametersFunctions(Device *this, int componentId, int boardId)
{
int halError, channel;
char name[HAL_NAME_LEN + 1];
halError = 0;
for(channel = 0; channel < MOTENC_NUM_ADC_CHANNELS; channel++){
if((halError = hal_pin_float_newf(HAL_OUT, &(this->adc[channel].pValue),
componentId, "motenc.%d.adc-%02d-value", boardId, channel)) != 0)
break;
if((halError = hal_param_float_newf(HAL_RW, &(this->adc[channel].offset),
componentId, "motenc.%d.adc-%02d-offset", boardId, channel)) != 0)
break;
if((halError = hal_param_float_newf(HAL_RW, &(this->adc[channel].gain),
componentId, "motenc.%d.adc-%02d-gain", boardId, channel)) != 0)
break;
*(this->adc[channel].pValue) = 0.0;
this->adc[channel].offset = 0.0;
this->adc[channel].gain = 1.0;
}
if(!halError){
rtapi_snprintf(name, sizeof(name), "motenc.%d.adc-read", boardId);
halError = hal_export_funct(name, Device_AdcRead, this, 1, 0, componentId);
}
if(halError){
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: ERROR: export ADC failed\n");
return(-1);
}
return(0);
}
static int
Device_ExportDigitalInPinsParametersFunctions(Device *this, int componentId, int boardId)
{
int halError, channel;
char name[HAL_NAME_LEN + 1];
halError = 0;
for(channel = 0; channel < (this->numFpga * MOTENC_FPGA_NUM_DIGITAL_INPUTS - 4); channel++){
if((halError = hal_pin_bit_newf(HAL_OUT, &(this->in[channel].pValue),
componentId, "motenc.%d.in-%02d", boardId, channel)) != 0)
break;
if((halError = hal_pin_bit_newf(HAL_OUT, &(this->in[channel].pValueNot),
componentId, "motenc.%d.in-%02d-not", boardId, channel)) != 0)
break;
*(this->in[channel].pValue) = 0;
*(this->in[channel].pValueNot) = 1;
}
if(!halError){
rtapi_snprintf(name, sizeof(name), "motenc.%d.digital-in-read", boardId);
halError = hal_export_funct(name, Device_DigitalInRead, this, 0, 0, componentId);
}
if(halError){
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: ERROR: export digital in failed\n");
return(-1);
}
return(0);
}
static int
Device_ExportDigitalOutPinsParametersFunctions(Device *this, int componentId, int boardId)
{
int halError, channel;
char name[HAL_NAME_LEN + 1];
halError = 0;
for(channel = 0; channel < this->numFpga * MOTENC_FPGA_NUM_DIGITAL_OUTPUTS; channel++){
if((halError = hal_pin_bit_newf(HAL_IN, &(this->out[channel].pValue),
componentId, "motenc.%d.out-%02d", boardId, channel)) != 0)
break;
if((halError = hal_param_bit_newf(HAL_RW, &(this->out[channel].invert),
componentId, "motenc.%d.out-%02d-invert", boardId, channel)) != 0)
break;
*(this->out[channel].pValue) = 0;
this->out[channel].invert = 0;
}
if(!halError){
rtapi_snprintf(name, sizeof(name), "motenc.%d.digital-out-write", boardId);
halError = hal_export_funct(name, Device_DigitalOutWrite, this, 0, 0, componentId);
}
if(halError){
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: ERROR: export digital out failed\n");
return(-1);
}
return(0);
}
static int
Device_ExportMiscPinsParametersFunctions(Device *this, int componentId, int boardId)
{
int halError;
char name[HAL_NAME_LEN + 1];
halError = hal_pin_bit_newf(HAL_OUT, &(this->misc.pEstopIn), componentId,
"motenc.%d.estop-in", boardId);
if(!halError){
halError = hal_pin_bit_newf(HAL_OUT, &(this->misc.pEstopInNot), componentId,
"motenc.%d.estop-in-not", boardId);
}
if(!halError){
halError = hal_pin_bit_newf(HAL_IO, &(this->misc.pWatchdogReset), componentId,
"motenc.%d.watchdog-reset", boardId);
}
if(!halError){
halError = hal_param_u32_newf(HAL_RW, &(this->misc.watchdogControl), componentId,
"motenc.%d.watchdog-control", boardId);
}
if(!halError){
*(this->misc.pEstopIn) = 0;
*(this->misc.pEstopInNot) = 1;
*(this->misc.pWatchdogReset) = 0;
this->misc.watchdogControl = this->watchdogControl;
}
if(!halError){
rtapi_snprintf(name, sizeof(name), "motenc.%d.misc-update", boardId);
halError = hal_export_funct(name, Device_MiscUpdate, this, 0, 0, componentId);
}
if(halError){
rtapi_print_msg(RTAPI_MSG_ERR, "MOTENC: ERROR: export miscellaneous failed\n");
return(-1);
}
return(0);
}
static void
Device_EncoderRead(void *arg, long period)
{
Device *this = (Device *)arg;
MotencRegMap *pCard = this->pCard;
EncoderPinsParams *pEncoder;
int i, j;
hal_u32_t status;
pEncoder = &this->encoder[0];
for(i = 0; i < this->numFpga; i++){
status = pCard->fpga[i].statusControl;
for(j = 0; j < MOTENC_FPGA_NUM_ENCODER_CHANNELS; j++, pEncoder++){
if(*(pEncoder->pReset)){
pCard->fpga[i].statusControl = 1 << (MOTENC_CONTROL_ENCODER_RESET_SHFT + j);
}
*(pEncoder->pIndex) = (status >> (MOTENC_STATUS_INDEX_SHFT + j)) & 1;
if((status >> (MOTENC_STATUS_INDEX_LATCH_SHFT + j)) & 1){
*(pEncoder->pIndexEnable) = 0;
}
if(*(pEncoder->pIndexEnable)){
pCard->fpga[i].encoderCount[j] = 1;
} else {
pCard->fpga[i].encoderCount[j] = 0;
}
*(pEncoder->pCount) = pCard->fpga[i].encoderCount[j];
if ( pEncoder->scale != pEncoder->oldScale ) {
pEncoder->oldScale = pEncoder->scale;
if ((pEncoder->scale < 1e-20) && (pEncoder->scale > -1e-20)) {
pEncoder->scale = 1.0;
}
pEncoder->scaleRecip = 1.0 / pEncoder->scale;
}
*(pEncoder->pPosition) = *(pEncoder->pCount) * pEncoder->scaleRecip;
}
}
}
static void
Device_DacWrite(void *arg, long period)
{
Device *this = (Device *)arg;
MotencRegMap *pCard = this->pCard;
DacPinsParams *pDac;
int i;
hal_float_t volts;
hal_u32_t dacCount;
pDac = &this->dac[0];
for(i = 0; i < MOTENC_NUM_DAC_CHANNELS; i++, pDac++){
volts = (*(pDac->pValue) - pDac->offset) * pDac->gain;
if(volts > MOTENC_DAC_VOLTS_MAX){
volts = MOTENC_DAC_VOLTS_MAX;
}else if(volts < MOTENC_DAC_VOLTS_MIN){
volts = MOTENC_DAC_VOLTS_MIN;
}
dacCount = (hal_u32_t)(volts * MOTENC_DAC_SCALE_MULTIPLY /
MOTENC_DAC_SCALE_DIVIDE + MOTENC_DAC_COUNT_ZERO);
pCard->dac[i] = dacCount;
}
}
static void
Device_AdcRead(void *arg, long period)
{
Device *this = (Device *)arg;
MotencRegMap *pCard = this->pCard;
switch(this->adcState){
case 0:
this->adcState++;
pCard->adcDataCommand = MOTENC_ADC_COMMAND_CHN_0_1_2_3;
pCard->adcStartConversion = 1;
break;
case 1:
if(Device_AdcRead4(this, 0)){
this->adcState++;
pCard->adcDataCommand = MOTENC_ADC_COMMAND_CHN_4_5_6_7;
pCard->adcStartConversion = 1;
}
break;
case 2:
if(Device_AdcRead4(this, 4)){
this->adcState = 1;
pCard->adcDataCommand = MOTENC_ADC_COMMAND_CHN_0_1_2_3;
pCard->adcStartConversion = 1;
}
break;
default:
this->adcState = 0;
}
}
static int
Device_AdcRead4(Device *this, int startChannel)
{
MotencRegMap *pCard = this->pCard;
AdcPinsParams *pAdc;
int i;
hal_s32_t adcCount;
hal_float_t volts;
if(pCard->fpga[0].statusControl & MOTENC_STATUS_ADC_DONE)
return(0);
pAdc = &this->adc[startChannel];
for(i = 0; i < 4; i++, pAdc++){
adcCount = pCard->adcDataCommand;
if(adcCount & MOTENC_ADC_SIGN_BIT){
adcCount |= MOTENC_ADC_SIGN_EXTEND;
}
volts = adcCount * MOTENC_ADC_SCALE_MULTIPLY / MOTENC_ADC_SCALE_DIVIDE;
volts = volts * pAdc->gain - pAdc->offset;
*(pAdc->pValue) = volts;
}
return(1);
}
static void
Device_DigitalInRead(void *arg, long period)
{
Device *this = (Device *)arg;
MotencRegMap *pCard = this->pCard;
DigitalInPinsParams *pDigitalIn;
int i, j, n;
hal_u32_t pins;
pDigitalIn = &this->in[0];
for(i = 0; i < this->numFpga; i++){
pins = ~pCard->fpga[i].digitalIo >> MOTENC_DIGITAL_IN_SHFT;
for(j = 0; j < 16; j++, pDigitalIn++){
*(pDigitalIn->pValue) = pins & 1;
*(pDigitalIn->pValueNot) = !*(pDigitalIn->pValue);
pins >>= 1;
}
pins = ~pCard->fpga[i].statusControl >> MOTENC_STATUS_DIGITAL_IN_SHFT;
n = (i)? 20: 16;
for(j = 0; j < n; j++, pDigitalIn++){
*(pDigitalIn->pValue) = pins & 1;
*(pDigitalIn->pValueNot) = !*(pDigitalIn->pValue);
pins >>= 1;
}
}
}
static void
Device_DigitalOutWrite(void *arg, long period)
{
Device *this = (Device *)arg;
MotencRegMap *pCard = this->pCard;
DigitalOutPinsParams *pDigitalOut;
int i, j;
hal_u32_t pins, mask;
pDigitalOut = &this->out[0];
for(i = 0; i < this->numFpga; i++){
pins = 0;
mask = 1;
for(j = 0; j < MOTENC_FPGA_NUM_DIGITAL_OUTPUTS; j++, pDigitalOut++){
if(*(pDigitalOut->pValue) != pDigitalOut->invert){
pins |= mask;
}
mask <<=1;
}
pCard->fpga[i].digitalIo = ~pins << MOTENC_DIGITAL_OUT_SHFT;
}
}
static void
Device_MiscUpdate(void *arg, long period)
{
Device *this = (Device *)arg;
MotencRegMap *pCard = this->pCard;
if(this->watchdogControl != this->misc.watchdogControl){
this->watchdogControl = this->misc.watchdogControl;
pCard->watchdogControl = this->watchdogControl;
}
if(*(this->misc.pWatchdogReset)){
*(this->misc.pWatchdogReset) = 0;
pCard->watchdogReset = MOTENC_WATCHDOG_RESET_VALUE;
}
*(this->misc.pEstopIn) = (pCard->fpga[0].statusControl & MOTENC_STATUS_ESTOP)? 1: 0;
*(this->misc.pEstopInNot) = !*(this->misc.pEstopIn);
}