#include "rtapi.h"
#include "rtapi_ctype.h"
#include "rtapi_app.h"
#include "hal.h"
#include <rtapi_io.h>
#include "hal_parport.h"
MODULE_AUTHOR("John Kasunich");
MODULE_DESCRIPTION("Parallel Port Driver for EMC HAL");
MODULE_LICENSE("GPL");
static char *cfg = "0x0278";
RTAPI_MP_STRING(cfg, "config string");
typedef struct {
unsigned short base_addr;
unsigned char data_dir;
unsigned char use_control_in;
hal_bit_t *status_in[10];
hal_bit_t *data_in[16];
hal_bit_t *data_out[8];
hal_bit_t data_inv[8];
hal_bit_t data_reset[8];
hal_bit_t *control_in[8];
hal_bit_t *control_out[4];
hal_bit_t control_inv[4];
hal_bit_t control_reset[4];
hal_u32_t reset_time;
hal_u32_t debug1, debug2;
long long write_time;
unsigned char outdata;
unsigned char reset_mask;
unsigned char reset_val;
long long write_time_ctrl;
unsigned char outdata_ctrl;
unsigned char reset_mask_ctrl;
unsigned char reset_val_ctrl;
struct hal_parport_t portdata;
} parport_t;
static parport_t *port_data_array;
static int comp_id;
static int num_ports;
static unsigned long ns2tsc_factor;
#define ns2tsc(x) (((x) * (unsigned long long)ns2tsc_factor) >> 12)
static void read_port(void *arg, long period);
static void reset_port(void *arg, long period);
static void write_port(void *arg, long period);
static void read_all(void *arg, long period);
static void write_all(void *arg, long period);
static int pins_and_params(char *argv[]);
static unsigned short parse_port_addr(char *cp);
static int export_port(int portnum, parport_t * addr);
static int export_input_pin(int portnum, int pin, hal_bit_t ** base, int n);
static int export_output_pin(int portnum, int pin, hal_bit_t ** dbase,
hal_bit_t * pbase, hal_bit_t * rbase, int n);
#define MAX_PORTS 8
#define MAX_TOK ((MAX_PORTS*2)+3)
int rtapi_app_main(void)
{
char *cp;
char *argv[MAX_TOK];
char name[HAL_NAME_LEN + 1];
int n, retval;
#ifdef __KERNEL__
ns2tsc_factor = (cpu_khz << 6) / 15625ul;
#else
ns2tsc_factor = 1ll<<12;
#endif
if (cfg == 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "PARPORT: ERROR: no config string\n");
return -1;
}
rtapi_print ( "config string '%s'\n", cfg );
cp = cfg;
for (n = 0; n < MAX_TOK; n++) {
while ((*cp != '\0') && ( isspace(*cp) || ( *cp == '_') ))
cp++;
argv[n] = cp;
while ((*cp != '\0') && !( isspace(*cp) || ( *cp == '_') ))
cp++;
if (*cp != '\0') {
*cp = '\0';
cp++;
}
}
for (n = 0; n < MAX_TOK; n++) {
if (argv[n][0] == '\0') {
argv[n] = NULL;
}
}
retval = pins_and_params(argv);
if (retval != 0) {
return retval;
}
for (n = 0; n < num_ports; n++) {
rtapi_snprintf(name, sizeof(name), "parport.%d.read", n);
retval = hal_export_funct(name, read_port, &(port_data_array[n]),
0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PARPORT: ERROR: port %d read funct export failed\n", n);
hal_exit(comp_id);
return -1;
}
rtapi_snprintf(name, sizeof(name), "parport.%d.write", n);
retval = hal_export_funct(name, write_port, &(port_data_array[n]),
0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PARPORT: ERROR: port %d write funct export failed\n", n);
hal_exit(comp_id);
return -1;
}
rtapi_snprintf(name, sizeof(name), "parport.%d.reset", n);
retval = hal_export_funct(name, reset_port, &(port_data_array[n]),
0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PARPORT: ERROR: port %d reset funct export failed\n", n);
hal_exit(comp_id);
return -1;
}
}
retval = hal_export_funct("parport.read-all", read_all,
port_data_array, 0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PARPORT: ERROR: read all funct export failed\n");
hal_exit(comp_id);
return -1;
}
retval = hal_export_funct("parport.write-all", write_all,
port_data_array, 0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PARPORT: ERROR: write all funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO,
"PARPORT: installed driver for %d ports\n", num_ports);
hal_ready(comp_id);
return 0;
}
void rtapi_app_exit(void)
{
int n;
for (n = 0; n < num_ports; n++) {
hal_parport_release(&port_data_array[n].portdata);
}
hal_exit(comp_id);
}
static void read_port(void *arg, long period)
{
parport_t *port;
int b;
unsigned char indata, mask;
port = arg;
indata = rtapi_inb(port->base_addr + 1);
indata ^= 0x80;
mask = 0x08;
for (b = 0; b < 10; b += 2) {
*(port->status_in[b]) = indata & mask;
*(port->status_in[b + 1]) = !(indata & mask);
mask <<= 1;
}
if (port->data_dir != 0) {
indata = rtapi_inb(port->base_addr);
mask = 0x01;
for (b = 0; b < 16; b += 2) {
*(port->data_in[b]) = indata & mask;
*(port->data_in[b + 1]) = !(indata & mask);
mask <<= 1;
}
}
if(port->use_control_in) {
mask = 0x01;
indata = rtapi_inb(port->base_addr + 2) ^ 0x0B;
for (b = 0; b < 8; b += 2) {
*(port->control_in[b]) = indata & mask;
*(port->control_in[b + 1]) = !(indata & mask);
mask <<= 1;
}
}
}
static void reset_port(void *arg, long period) {
parport_t *port = arg;
long long deadline, reset_time_tsc;
unsigned char outdata = (port->outdata&~port->reset_mask) ^ port->reset_val;
if(port->reset_time > period/4) port->reset_time = period/4;
reset_time_tsc = ns2tsc(port->reset_time);
if(outdata != port->outdata) {
deadline = port->write_time + reset_time_tsc;
while(rtapi_get_clocks() < deadline) {}
rtapi_outb(outdata, port->base_addr);
}
outdata = (port->outdata_ctrl&~port->reset_mask_ctrl)^port->reset_val_ctrl;
if(outdata != port->outdata_ctrl) {
outdata ^= 0x0B;
deadline = port->write_time_ctrl + reset_time_tsc;
while(rtapi_get_clocks() < deadline) {}
rtapi_outb(outdata, port->base_addr + 2);
}
}
static void write_port(void *arg, long period)
{
parport_t *port;
int b;
unsigned char outdata, mask;
port = arg;
if (port->data_dir == 0) {
int reset_mask=0, reset_val=0;
outdata = 0x00;
mask = 0x01;
for (b = 0; b < 8; b++) {
if ((*(port->data_out[b])) && (!port->data_inv[b])) {
outdata |= mask;
}
if ((!*(port->data_out[b])) && (port->data_inv[b])) {
outdata |= mask;
}
if (port->data_reset[b]) {
reset_mask |= mask;
if(port->data_inv[b]) reset_val |= mask;
}
mask <<= 1;
}
rtapi_outb(outdata, port->base_addr);
port->write_time = rtapi_get_clocks();
port->reset_val = reset_val;
port->reset_mask = reset_mask;
port->outdata = outdata;
outdata = 0x00;
} else {
outdata = 0x20;
}
if (port->use_control_in) {
outdata |= 0x0F;
} else {
int reset_mask=0, reset_val=0;
mask = 0x01;
for (b = 0; b < 4; b++) {
if ((*(port->control_out[b])) && (!port->control_inv[b])) {
outdata |= mask;
}
if ((!*(port->control_out[b])) && (port->control_inv[b])) {
outdata |= mask;
}
if (port->control_reset[b]) {
reset_mask |= mask;
if(port->control_inv[b]) reset_val |= mask;
}
mask <<= 1;
}
port->reset_mask_ctrl = reset_mask;
port->reset_val_ctrl = reset_val;
port->outdata_ctrl = outdata;
}
outdata ^= 0x0B;
rtapi_outb(outdata, port->base_addr + 2);
port->write_time_ctrl = rtapi_get_clocks();
}
void read_all(void *arg, long period)
{
parport_t *port;
int n;
port = arg;
for (n = 0; n < num_ports; n++) {
read_port(&(port[n]), period);
}
}
void write_all(void *arg, long period)
{
parport_t *port;
int n;
port = arg;
for (n = 0; n < num_ports; n++) {
write_port(&(port[n]), period);
}
}
static int pins_and_params(char *argv[])
{
long port_addr[MAX_PORTS];
int data_dir[MAX_PORTS];
int use_control_in[MAX_PORTS];
int force_epp[MAX_PORTS];
int n, retval;
for (n = 0; n < MAX_PORTS; n++) {
port_addr[n] = 0;
data_dir[n] = 0;
use_control_in[n] = 0;
force_epp[n] = 0;
}
num_ports = 0;
n = 0;
while ((num_ports < MAX_PORTS) && (argv[n] != 0)) {
port_addr[num_ports] = parse_port_addr(argv[n]);
if (port_addr[num_ports] < 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PARPORT: ERROR: bad port address '%s'\n", argv[n]);
return -1;
}
n++;
if (argv[n] != 0) {
if ((argv[n][0] == 'i') || (argv[n][0] == 'I')) {
data_dir[num_ports] = 1;
use_control_in[num_ports] = 0;
n++;
} else if ((argv[n][0] == 'o') || (argv[n][0] == 'O')) {
data_dir[num_ports] = 0;
use_control_in[num_ports] = 0;
n++;
} else if ((argv[n][0] == 'e') || (argv[n][0] == 'E')) {
data_dir[num_ports] = 0;
use_control_in[num_ports] = 0;
force_epp[num_ports] = 1;
n++;
} else if ((argv[n][0] == 'x') || (argv[n][0] == 'X')) {
data_dir[num_ports] = 0;
use_control_in[num_ports] = 1;
n++;
}
}
num_ports++;
}
if (num_ports == 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PARPORT: ERROR: no ports configured\n");
return -1;
}
comp_id = hal_init("hal_parport");
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "PARPORT: ERROR: hal_init() failed\n");
return -1;
}
port_data_array = hal_malloc(num_ports * sizeof(parport_t));
if (port_data_array == 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PARPORT: ERROR: hal_malloc() failed\n");
hal_exit(comp_id);
return -1;
}
for (n = 0; n < num_ports; n++) {
int modes = 0;
if(use_control_in[n]) {
modes = PARPORT_MODE_TRISTATE;
} else if(force_epp[n]) {
modes = PARPORT_MODE_EPP;
}
retval = hal_parport_get(comp_id, &port_data_array[n].portdata,
port_addr[n], -1, modes);
if(retval < 0) {
hal_exit(comp_id);
return retval;
}
port_data_array[n].base_addr = port_data_array[n].portdata.base;
port_data_array[n].data_dir = data_dir[n];
port_data_array[n].use_control_in = use_control_in[n];
if(force_epp[n] && port_data_array[n].portdata.base_hi) {
outb(0x94, port_data_array[n].portdata.base_hi + 2);
}
if (data_dir[n]) {
rtapi_outb(rtapi_inb(port_data_array[n].base_addr+2) | 0x20, port_data_array[n].base_addr+2);
}
retval = export_port(n, &(port_data_array[n]));
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"PARPORT: ERROR: port %d var export failed\n", n);
hal_exit(comp_id);
return retval;
}
}
return 0;
}
static unsigned short parse_port_addr(char *cp)
{
unsigned short result;
result = 0;
if (cp[0] == '0') {
if ((cp[1] == 'X') || (cp[1] == 'x')) {
cp += 2;
}
}
while (*cp != '\0') {
if ((*cp >= '0') && (*cp <= '9')) {
result <<= 4;
result += *cp - '0';
} else if ((*cp >= 'A') && (*cp <= 'F')) {
result <<= 4;
result += (*cp - 'A') + 10;
} else if ((*cp >= 'a') && (*cp <= 'f')) {
result <<= 4;
result += (*cp - 'a') + 10;
} else {
return -1;
}
cp++;
}
return result;
}
static int export_port(int portnum, parport_t * port)
{
int retval, msg;
msg = rtapi_get_msg_level();
rtapi_set_msg_level(RTAPI_MSG_WARN);
retval = 0;
retval += export_input_pin(portnum, 15, port->status_in, 0);
retval += export_input_pin(portnum, 13, port->status_in, 1);
retval += export_input_pin(portnum, 12, port->status_in, 2);
retval += export_input_pin(portnum, 10, port->status_in, 3);
retval += export_input_pin(portnum, 11, port->status_in, 4);
if (port->data_dir != 0) {
retval += export_input_pin(portnum, 2, port->data_in, 0);
retval += export_input_pin(portnum, 3, port->data_in, 1);
retval += export_input_pin(portnum, 4, port->data_in, 2);
retval += export_input_pin(portnum, 5, port->data_in, 3);
retval += export_input_pin(portnum, 6, port->data_in, 4);
retval += export_input_pin(portnum, 7, port->data_in, 5);
retval += export_input_pin(portnum, 8, port->data_in, 6);
retval += export_input_pin(portnum, 9, port->data_in, 7);
} else {
retval += export_output_pin(portnum, 2,
port->data_out, port->data_inv, port->data_reset, 0);
retval += export_output_pin(portnum, 3,
port->data_out, port->data_inv, port->data_reset, 1);
retval += export_output_pin(portnum, 4,
port->data_out, port->data_inv, port->data_reset, 2);
retval += export_output_pin(portnum, 5,
port->data_out, port->data_inv, port->data_reset, 3);
retval += export_output_pin(portnum, 6,
port->data_out, port->data_inv, port->data_reset, 4);
retval += export_output_pin(portnum, 7,
port->data_out, port->data_inv, port->data_reset, 5);
retval += export_output_pin(portnum, 8,
port->data_out, port->data_inv, port->data_reset, 6);
retval += export_output_pin(portnum, 9,
port->data_out, port->data_inv, port->data_reset, 7);
retval += hal_param_u32_newf(HAL_RW, &port->reset_time, comp_id,
"parport.%d.reset-time", portnum);
retval += hal_param_u32_newf(HAL_RW, &port->debug1, comp_id,
"parport.%d.debug1", portnum);
retval += hal_param_u32_newf(HAL_RW, &port->debug2, comp_id,
"parport.%d.debug2", portnum);
port->write_time = 0;
}
if(port->use_control_in == 0) {
retval += export_output_pin(portnum, 1,
port->control_out, port->control_inv, port->control_reset, 0);
retval += export_output_pin(portnum, 14,
port->control_out, port->control_inv, port->control_reset, 1);
retval += export_output_pin(portnum, 16,
port->control_out, port->control_inv, port->control_reset, 2);
retval += export_output_pin(portnum, 17,
port->control_out, port->control_inv, port->control_reset, 3);
} else {
retval += export_input_pin(portnum, 1, port->control_in, 0);
retval += export_input_pin(portnum, 14, port->control_in, 1);
retval += export_input_pin(portnum, 16, port->control_in, 2);
retval += export_input_pin(portnum, 17, port->control_in, 3);
}
rtapi_set_msg_level(msg);
return retval;
}
static int export_input_pin(int portnum, int pin, hal_bit_t ** base, int n)
{
int retval;
retval = hal_pin_bit_newf(HAL_OUT, base + (2 * n), comp_id,
"parport.%d.pin-%02d-in", portnum, pin);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_OUT, base + (2 * n) + 1, comp_id,
"parport.%d.pin-%02d-in-not", portnum, pin);
return retval;
}
static int export_output_pin(int portnum, int pin, hal_bit_t ** dbase,
hal_bit_t * pbase, hal_bit_t * rbase, int n)
{
int retval;
retval = hal_pin_bit_newf(HAL_IN, dbase + n, comp_id,
"parport.%d.pin-%02d-out", portnum, pin);
if (retval != 0) {
return retval;
}
retval = hal_param_bit_newf(HAL_RW, pbase + n, comp_id,
"parport.%d.pin-%02d-out-invert", portnum, pin);
if (retval != 0) {
return retval;
}
if (rbase)
retval = hal_param_bit_newf(HAL_RW, rbase + n, comp_id,
"parport.%d.pin-%02d-out-reset", portnum, pin);
return retval;
}