#include <linux/pci.h>
#include "rtapi.h"
#include "rtapi_app.h"
#include "hal.h"
#include "opto_ac5.h"
#ifndef MODULE
#define MODULE
#endif
#ifdef MODULE
MODULE_AUTHOR("Chris Morley");
MODULE_DESCRIPTION("Driver for opto22 pci AC5 for EMC HAL");
MODULE_LICENSE("GPL");
#endif
#define opto22_VENDOR_ID 0x148a
#define opto22_pci_AC5_DEVICE_ID 0xac05
#define MAX_BOARDS 4
#define CONFIG_WRITE_OFFSET_0 0x00000004
#define CONFIG_READ_OFFSET_0 0x00000100
#define DATA_WRITE_OFFSET_0 0X00000008
#define DATA_READ_OFFSET_0 0X00000300
#define CONFIG_WRITE_OFFSET_1 0x00000014
#define CONFIG_READ_OFFSET_1 0x00000500
#define DATA_WRITE_OFFSET_1 0X00000018
#define DATA_READ_OFFSET_1 0X00000700
static int Device_Init(board_data_t *pboard);
static int Device_ExportPinsParametersFunctions(board_data_t *pboard, int comp_id, int boardId);
static int Device_ExportDigitalInPinsParametersFunctions(board_data_t *pboard, int comp_id, int boardId);
static int Device_ExportDigitalOutPinsParametersFunctions(board_data_t *pboard, int comp_id, int boardId);
static void Device_DigitalInRead(void *this, long period);
static void Device_DigitalOutWrite(void *this, long period);
typedef struct {
int comp_id;
board_data_t *boards[MAX_BOARDS];
int num_of_boards;
} Driver_t;
static Driver_t driver;
static unsigned long portconfig0 = 0Xc0fff000;
RTAPI_MP_LONG(portconfig0, "port 0 i/o configuration number");
static unsigned long portconfig1 = 0Xc0fff000;
RTAPI_MP_LONG(portconfig1, "port 1 i/o configuration number");
int rtapi_app_main(void)
{
int n;
board_data_t *pboard;
struct pci_dev *pDev;
driver.comp_id = hal_init("opto_ac5");
if (driver.comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, " ERROR OPTO_AC5--- hal_init() failed\n");
return(-1);
}
for ( n = 0 ; n < MAX_BOARDS ; n++ ) {
driver.boards[n] = NULL;
}
pDev = NULL;
for ( n = 0 ; n < MAX_BOARDS ; n++ )
{
pDev = pci_get_device(opto22_VENDOR_ID, opto22_pci_AC5_DEVICE_ID, pDev);
if ( pDev == NULL ) { break;}
pboard = (board_data_t *)(hal_malloc(sizeof(board_data_t)));
if ( pboard == NULL ) {
rtapi_print_msg(RTAPI_MSG_ERR, "ERROR OPTO_AC5--- hal_malloc() failed\n");
rtapi_app_exit();
return -1;
}
driver.boards[n] = pboard;
pboard->pci_dev = pDev;
pboard->slot = PCI_SLOT(pDev->devfn);
pboard->num = n;
rtapi_print("INFO OPTO_AC5--- Board %d detected in PCI Slot: %2x\n", pboard->num, pboard->slot);
pboard->len = pci_resource_len(pDev, 0);
pboard->base = ioremap_nocache(pci_resource_start(pDev, 0), pboard->len);
if ( pboard->base == NULL ) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ERROR OPTO_AC5--- could not find board %d PCI base address\n", pboard->num );
rtapi_app_exit();
return -1;
} else {
rtapi_print(
"INFO OPTO_AC5--- board %d mapped to %08lx, Len = %ld\n",
pboard->num, (long)pboard->base,(long)pboard->len);
}
if(Device_Init(pboard)){
hal_exit(driver.comp_id);
return(-1);
}
if(Device_ExportPinsParametersFunctions(pboard, driver.comp_id, n)){
hal_exit(driver.comp_id);
return(-1);
}
}
if(n == 0){
rtapi_print ("ERROR OPTO_AC5--- No opto PCI-AC5 card(s) detected\n");
rtapi_app_exit();
return(-1);
}
hal_ready(driver.comp_id);
return(0);
}
void rtapi_app_exit(void)
{
int n;
board_data_t *pDevice;
pDevice = driver.boards[0];
hal_exit(driver.comp_id);
for ( n = 0; n < MAX_BOARDS; n++ )
{
if ( driver.boards[n] != NULL)
{
rtapi_print ("INFO OPTO_AC5--- board %i driver removed.\n",n);
writel(0XC0FFFFFF, driver.boards[n]->base + (DATA_WRITE_OFFSET_0));
writel(0XC0FFFFFF, driver.boards[n]->base + (DATA_WRITE_OFFSET_1));
if ( driver.boards[n]->base != NULL )
{
iounmap(driver.boards[n]->base);
}
}
}
}
static int Device_Init(board_data_t *pboard)
{
if ((portconfig0 & 0x80000000) ==0) { portconfig0 |=0x80000000; }
if ((portconfig0 & 0x40000000) ==0) { portconfig0 |=0x40000000; }
if ((portconfig1 & 0x80000000) ==0) { portconfig1 |=0x80000000; }
if ((portconfig1 & 0x40000000) ==0) { portconfig1 |=0x40000000; }
writel(portconfig0, pboard->base + (CONFIG_WRITE_OFFSET_0));
writel(portconfig1, pboard->base + (CONFIG_WRITE_OFFSET_1));
pboard->port[0].mask = portconfig0;
pboard->port[1].mask = portconfig1;
return(0);
}
static int Device_ExportPinsParametersFunctions(board_data_t *this, int componentId, int boardId)
{
int msgLevel, error=0;
msgLevel = rtapi_get_msg_level();
rtapi_set_msg_level(RTAPI_MSG_WARN);
if(!error) error = Device_ExportDigitalInPinsParametersFunctions(this, componentId, boardId);
if(!error) error = Device_ExportDigitalOutPinsParametersFunctions(this, componentId, boardId);
rtapi_set_msg_level(msgLevel);
return(error);
}
static int Device_ExportDigitalInPinsParametersFunctions(board_data_t *this, int comp_id, int boardId)
{
int halError=0, channel,mask,portnum=0;
char name[HAL_NAME_LEN + 1];
while (portnum<2)
{
mask=1;
for(channel = 0; channel < 24; channel++)
{
if ((this->port[portnum].mask & mask)==0) {
if((halError = hal_pin_bit_newf(HAL_OUT, &(this->port[portnum].io[channel].pValue),
comp_id, "opto-ac5.%d.port%d.in-%02d", boardId, portnum, channel)) != 0)
break;
if((halError = hal_pin_bit_newf(HAL_OUT, &(this->port[portnum].io[channel].pValueNot),
comp_id, "opto-ac5.%d.port%d.in-%02d-not", boardId, portnum, channel)) != 0)
break;
*(this->port[portnum].io[channel].pValue) = 0;
*(this->port[portnum].io[channel].pValueNot) = 1;
}
mask <<=1;
}
portnum ++;
}
if(!halError){
rtapi_snprintf(name, sizeof(name), "opto-ac5.%d.digital-read", boardId);
halError = hal_export_funct(name, Device_DigitalInRead, this, 0, 0, comp_id);
}
if(halError){
rtapi_print_msg(RTAPI_MSG_ERR, "ERROR OPTO22_AC5---exporting digital inputs to HAL failed\n");
return(-1);
}
return(0);
}
static int Device_ExportDigitalOutPinsParametersFunctions(board_data_t *this, int comp_id, int boardId)
{
int halError=0, channel,mask,portnum=0;
char name[HAL_NAME_LEN + 1];
while (portnum<2)
{
mask=1;
for(channel = 0; channel < 24; channel++)
{
if ((this->port[portnum].mask & mask)!=0) {
if((halError = hal_pin_bit_newf(HAL_IN, &(this->port[portnum].io[channel].pValue),
comp_id, "opto-ac5.%d.port%d.out-%02d", boardId, portnum, channel)) != 0)
break;
if((halError = hal_param_bit_newf(HAL_RW, &(this->port[portnum].io[channel].invert),
comp_id, "opto-ac5.%d.port%d.out-%02d-invert", boardId, portnum, channel)) != 0)
break;
*(this->port[portnum].io[channel].pValue) = 0;
this->port[portnum].io[channel].invert = 0;
}
mask <<=1;
}
portnum++;
}
portnum=0;
for(channel = 0; channel < 2; channel++)
{
if((halError = hal_pin_bit_newf(HAL_IN, &(this->port[portnum].io[24].pValue),
comp_id, "opto-ac5.%d.led%d", boardId, channel+portnum)) != 0)
break;
if((halError = hal_pin_bit_newf(HAL_IN, &(this->port[portnum].io[25].pValue),
comp_id, "opto-ac5.%d.led%d", boardId, channel+portnum+1)) != 0)
break;
portnum++;
}
if(!halError){
rtapi_snprintf(name, sizeof(name), "opto-ac5.%d.digital-write", boardId);
halError = hal_export_funct(name, Device_DigitalOutWrite, this, 0, 0, comp_id);
}
if(halError){
rtapi_print_msg(RTAPI_MSG_ERR, "ERROR OPTO_AC5---exporting digital outputs to HAL failed\n");
return(-1);
}
return(0);
}
static void
Device_DigitalInRead(void *arg, long period)
{
board_data_t *pboard = (board_data_t *)arg;
DigitalPinsParams *pDigital;
int i, portnum=0;
unsigned long pins, offset=DATA_READ_OFFSET_0, mask;
while (portnum<2)
{
mask=1;
pDigital = &pboard->port[portnum].io[0];
pins= readl (pboard->base + (offset));
for(i = 0; i < 24; i++,pDigital++)
{
if ((pboard->port[portnum].mask & mask) ==0) {
if ((pins & mask) !=0){ *(pDigital->pValue) =0;
}else{ *(pDigital->pValue) = 1; }
*(pDigital->pValueNot) = !*(pDigital->pValue);
}
mask <<=1; }
portnum++;
offset=DATA_READ_OFFSET_1; }
}
static void
Device_DigitalOutWrite(void *arg, long period)
{
board_data_t *pboard = (board_data_t *)arg;
DigitalPinsParams *pDigital;
int i, j, portnum=0;
unsigned long pins, offset=DATA_WRITE_OFFSET_0,mask;
while (portnum<2)
{
pDigital = &pboard->port[portnum].io[0];
mask=1;
pins=0;
for(j = 0; j < 24; j++, pDigital++)
{
if ((pboard->port[portnum].mask & mask) !=0) {
if( (!*(pDigital->pValue) && !(pDigital->invert) ) ||
( *(pDigital->pValue) && (pDigital->invert) ))
{ pins |= mask; }
}
mask <<=1;
}
pDigital = &pboard->port[portnum].io[23]; for (i = 0;i < 2;i++)
{
mask=1<<(31-i);
pDigital++;
if ( *(pDigital->pValue) ==0 ) { pins |= mask; }
}
writel(pins,pboard->base + (offset));
portnum ++;
offset=DATA_WRITE_OFFSET_1; }
}