/ src / hal / drivers / hal_parport.c
hal_parport.c
  1  /********************************************************************
  2  * Description:  hal_parport.c
  3  *               This file, 'hal_parport.c', is a HAL component that 
  4  *               provides a driver for the standard PC parallel port.
  5  *
  6  * Author: John Kasunich
  7  * License: GPL Version 2
  8  *    
  9  * Copyright (c) 2003 All rights reserved.
 10  *
 11  * Last change: 
 12  ********************************************************************/
 13  
 14  /** This file, 'hal_parport.c', is a HAL component that provides a
 15      driver for the standard PC parallel port.
 16  
 17      It supports up to eight parallel ports, and if the port hardware
 18      is bidirectional, the eight data bits can be configured as inputs
 19      or outputs.
 20  
 21      The configuration is determined by command line arguments for the
 22      user space version of the driver, and by a config string passed
 23      to insmod for the realtime version.  The format is similar for
 24      both, and consists of a port address, followed by an optional
 25      direction, repeated for each port.  The direction is either "in"
 26      or "out" and determines the direction of the 8 bit data port.
 27      The default is out.  The 5 bits of the status port are always
 28      inputs, and the 4 bits of the control port are always outputs.
 29      Example command lines are as follows:
 30  
 31      user:        hal_parport 378 in 278
 32      realtime:    insmod hal_parport.o cfg="378 in 278"
 33  
 34      Both of these commands install the driver and configure parports
 35      at base addresses 0x0378 (using data port as input) and 0x0278
 36      (using data port as output).
 37  
 38      The driver creates HAL pins and parameters for each port pin
 39      as follows:
 40      Each physical output has a correspinding HAL pin, named
 41      'parport.<portnum>.pin-<pinnum>-out', and a HAL parameter
 42      'parport.<portnum>.pin-<pinnum>-out-invert'.
 43      Each physical input has two corresponding HAL pins, named
 44      'parport.<portnum>.pin-<pinnum>-in' and
 45      'parport.<portnum>.pin-<pinnum>-in-not'.
 46  
 47      <portnum> is the port number, starting from zero.  <pinnum> is
 48      the physical pin number on the DB-25 connector.
 49  
 50      The realtime version of the driver exports two HAL functions for
 51      each port, 'parport.<portnum>.read' and 'parport.<portnum>.write'.
 52      It also exports two additional functions, 'parport.read-all' and
 53      'parport.write-all'.  Any or all of these functions can be added
 54      to realtime HAL threads to update the port data periodically.
 55  
 56      The user space version of the driver cannot export functions,
 57      instead it exports parameters with the same names.  The main()
 58      function sits in a loop checking the parameters.  If they are
 59      zero, it does nothing.  If any parameter is greater than zero,
 60      the corresponding function runs once, then the parameter is
 61      reset to zero.  If any parameter is less than zero, the
 62      corresponding function runs on every pass through the loop.
 63      The driver will loop forever, until it receives either
 64      SIGINT (ctrl-C) or SIGTERM, at which point it cleans up and
 65      exits.
 66  
 67  */
 68  
 69  /** Copyright (C) 2003 John Kasunich
 70                         <jmkasunich AT users DOT sourceforge DOT net>
 71  */
 72  
 73  /** This program is free software; you can redistribute it and/or
 74      modify it under the terms of version 2 of the GNU General
 75      Public License as published by the Free Software Foundation.
 76      This library is distributed in the hope that it will be useful,
 77      but WITHOUT ANY WARRANTY; without even the implied warranty of
 78      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 79      GNU General Public License for more details.
 80  
 81      You should have received a copy of the GNU General Public
 82      License along with this library; if not, write to the Free Software
 83      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 84  
 85      THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR
 86      ANY HARM OR LOSS RESULTING FROM ITS USE.  IT IS _EXTREMELY_ UNWISE
 87      TO RELY ON SOFTWARE ALONE FOR SAFETY.  Any machinery capable of
 88      harming persons must have provisions for completely removing power
 89      from all motors, etc, before persons enter any danger area.  All
 90      machinery must be designed to comply with local and national safety
 91      codes, and the authors of this software can not, and do not, take
 92      any responsibility for such compliance.
 93  
 94      This code was written as part of the EMC HAL project.  For more
 95      information, go to www.linuxcnc.org.
 96  */
 97  
 98  #include "rtapi.h"		/* RTAPI realtime OS API */
 99  #include "rtapi_ctype.h"	/* isspace() */
100  #include "rtapi_app.h"		/* RTAPI realtime module decls */
101  
102  #include "hal.h"		/* HAL public API decls */
103  
104  /* If FASTIO is defined, uses outb() and inb() from <asm.io>,
105     instead of rtapi_outb() and rtapi_inb() - the <asm.io> ones
106     are inlined, and save a microsecond or two (on my 233MHz box)
107  */
108  #include <rtapi_io.h>
109  
110  #include "hal_parport.h"
111  
112  /* module information */
113  MODULE_AUTHOR("John Kasunich");
114  MODULE_DESCRIPTION("Parallel Port Driver for EMC HAL");
115  MODULE_LICENSE("GPL");
116  static char *cfg = "0x0278";	/* config string, default 1 output port at 278 */
117  RTAPI_MP_STRING(cfg, "config string");
118  
119  /***********************************************************************
120  *                STRUCTURES AND GLOBAL VARIABLES                       *
121  ************************************************************************/
122  
123  /* this structure contains the runtime data needed by the
124     parallel port driver for a single port
125  */
126  
127  typedef struct {
128      unsigned short base_addr;	/* base I/O address (0x378, etc.) */
129      unsigned char data_dir;	/* non-zero if pins 2-9 are input */
130      unsigned char use_control_in; /* non-zero if pins 1, 4, 16, 17 are input */ 
131      hal_bit_t *status_in[10];	/* ptrs for in pins 15, 13, 12, 10, 11 */
132      hal_bit_t *data_in[16];	/* ptrs for input pins 2 - 9 */
133      hal_bit_t *data_out[8];	/* ptrs for output pins 2 - 9 */
134      hal_bit_t data_inv[8];	/* polarity params for output pins 2 - 9 */
135      hal_bit_t data_reset[8];	/* reset flag for output pins 2 - 9 */
136      hal_bit_t *control_in[8];	/* ptrs for in pins 1, 14, 16, 17 */
137      hal_bit_t *control_out[4];	/* ptrs for out pins 1, 14, 16, 17 */
138      hal_bit_t control_inv[4];	/* pol. params for output pins 1, 14, 16, 17 */
139      hal_bit_t control_reset[4];	/* reset flag for output pins 1, 14, 16, 17 */
140      hal_u32_t reset_time;       /* min ns between write and reset */
141      hal_u32_t debug1, debug2;
142      long long write_time;
143      unsigned char outdata;
144      unsigned char reset_mask;       /* reset flag for pin 2..9 */
145      unsigned char reset_val;        /* reset values for pin 2..9 */
146      long long write_time_ctrl;
147      unsigned char outdata_ctrl;
148      unsigned char reset_mask_ctrl;  /* reset flag for pin 1, 14, 16, 17 */
149      unsigned char reset_val_ctrl;   /* reset values for pin 1, 14, 16, 17 */
150      struct hal_parport_t portdata;
151  } parport_t;
152  
153  /* pointer to array of parport_t structs in shared memory, 1 per port */
154  static parport_t *port_data_array;
155  
156  /* other globals */
157  static int comp_id;		/* component ID */
158  static int num_ports;		/* number of ports configured */
159  
160  static unsigned long ns2tsc_factor;
161  #define ns2tsc(x) (((x) * (unsigned long long)ns2tsc_factor) >> 12)
162  
163  /***********************************************************************
164  *                  LOCAL FUNCTION DECLARATIONS                         *
165  ************************************************************************/
166  
167  /* These are the functions that actually do the I/O
168     everything else is just init code
169  */
170  
171  static void read_port(void *arg, long period);
172  static void reset_port(void *arg, long period);
173  static void write_port(void *arg, long period);
174  static void read_all(void *arg, long period);
175  static void write_all(void *arg, long period);
176  
177  /* 'pins_and_params()' does most of the work involved in setting up
178     the driver.  It parses the command line (argv[]), then if the
179     command line is OK, it calls hal_init(), allocates shared memory
180     for the parport_t data structure(s), and exports pins and parameters
181     It does not set up functions, since that is handled differently in
182     realtime and user space.
183  */
184  static int pins_and_params(char *argv[]);
185  
186  static unsigned short parse_port_addr(char *cp);
187  static int export_port(int portnum, parport_t * addr);
188  static int export_input_pin(int portnum, int pin, hal_bit_t ** base, int n);
189  static int export_output_pin(int portnum, int pin, hal_bit_t ** dbase,
190      hal_bit_t * pbase, hal_bit_t * rbase, int n);
191  
192  /***********************************************************************
193  *                       INIT AND EXIT CODE                             *
194  ************************************************************************/
195  
196  #define MAX_PORTS 8
197  
198  #define MAX_TOK ((MAX_PORTS*2)+3)
199  
200  int rtapi_app_main(void)
201  {
202      char *cp;
203      char *argv[MAX_TOK];
204      char name[HAL_NAME_LEN + 1];
205      int n, retval;
206  
207  
208  #ifdef __KERNEL__
209      // this calculation fits in a 32-bit unsigned 
210      // as long as CPUs are under about 6GHz
211      ns2tsc_factor = (cpu_khz << 6) / 15625ul;
212  #else
213      ns2tsc_factor = 1ll<<12;
214  #endif
215  
216      /* test for config string */
217      if (cfg == 0) {
218  	rtapi_print_msg(RTAPI_MSG_ERR, "PARPORT: ERROR: no config string\n");
219  	return -1;
220      }
221  rtapi_print ( "config string '%s'\n", cfg );
222      /* as a RT module, we don't get a nice argc/argv command line, we only
223         get a single string... so we need to tokenize it ourselves */
224      /* in addition, it seems that insmod under kernel 2.6 will truncate 
225         a string parameter at the first whitespace.  So we allow '_' as
226         an alternate token separator. */
227      cp = cfg;
228      for (n = 0; n < MAX_TOK; n++) {
229  	/* strip leading whitespace */
230  	while ((*cp != '\0') && ( isspace(*cp) || ( *cp == '_') ))
231  	    cp++;
232  	/* mark beginning of token */
233  	argv[n] = cp;
234  	/* find end of token */
235  	while ((*cp != '\0') && !( isspace(*cp) || ( *cp == '_') ))
236  	    cp++;
237  	/* mark end of this token, prepare to search for next one */
238  	if (*cp != '\0') {
239  	    *cp = '\0';
240  	    cp++;
241  	}
242      }
243      for (n = 0; n < MAX_TOK; n++) {
244  	/* is token empty? */
245  	if (argv[n][0] == '\0') {
246  	    /* yes - make pointer NULL */
247  	    argv[n] = NULL;
248  	}
249      }
250      /* parse "command line", set up pins and parameters */
251      retval = pins_and_params(argv);
252      if (retval != 0) {
253  	return retval;
254      }
255      /* export functions for each port */
256      for (n = 0; n < num_ports; n++) {
257  	/* make read function name */
258  	rtapi_snprintf(name, sizeof(name), "parport.%d.read", n);
259  	/* export read function */
260  	retval = hal_export_funct(name, read_port, &(port_data_array[n]),
261  	    0, 0, comp_id);
262  	if (retval != 0) {
263  	    rtapi_print_msg(RTAPI_MSG_ERR,
264  		"PARPORT: ERROR: port %d read funct export failed\n", n);
265  	    hal_exit(comp_id);
266  	    return -1;
267  	}
268  	/* make write function name */
269  	rtapi_snprintf(name, sizeof(name), "parport.%d.write", n);
270  	/* export write function */
271  	retval = hal_export_funct(name, write_port, &(port_data_array[n]),
272  	    0, 0, comp_id);
273  	if (retval != 0) {
274  	    rtapi_print_msg(RTAPI_MSG_ERR,
275  		"PARPORT: ERROR: port %d write funct export failed\n", n);
276  	    hal_exit(comp_id);
277  	    return -1;
278  	}
279  	/* make reset function name */
280  	rtapi_snprintf(name, sizeof(name), "parport.%d.reset", n);
281  	/* export write function */
282  	retval = hal_export_funct(name, reset_port, &(port_data_array[n]),
283  	    0, 0, comp_id);
284  	if (retval != 0) {
285  	    rtapi_print_msg(RTAPI_MSG_ERR,
286  		"PARPORT: ERROR: port %d reset funct export failed\n", n);
287  	    hal_exit(comp_id);
288  	    return -1;
289  	}
290      }
291      /* export functions that read and write all ports */
292      retval = hal_export_funct("parport.read-all", read_all,
293  	port_data_array, 0, 0, comp_id);
294      if (retval != 0) {
295  	rtapi_print_msg(RTAPI_MSG_ERR,
296  	    "PARPORT: ERROR: read all funct export failed\n");
297  	hal_exit(comp_id);
298  	return -1;
299      }
300      retval = hal_export_funct("parport.write-all", write_all,
301  	port_data_array, 0, 0, comp_id);
302      if (retval != 0) {
303  	rtapi_print_msg(RTAPI_MSG_ERR,
304  	    "PARPORT: ERROR: write all funct export failed\n");
305  	hal_exit(comp_id);
306  	return -1;
307      }
308      rtapi_print_msg(RTAPI_MSG_INFO,
309  	"PARPORT: installed driver for %d ports\n", num_ports);
310      hal_ready(comp_id);
311      return 0;
312  }
313  
314  void rtapi_app_exit(void)
315  {
316      int n;
317      for (n = 0; n < num_ports; n++) {
318          hal_parport_release(&port_data_array[n].portdata);
319      }
320      hal_exit(comp_id);
321  }
322  
323  /***********************************************************************
324  *                  REALTIME PORT READ AND WRITE FUNCTIONS              *
325  ************************************************************************/
326  
327  static void read_port(void *arg, long period)
328  {
329      parport_t *port;
330      int b;
331      unsigned char indata, mask;
332  
333      port = arg;
334      /* read the status port */
335      indata = rtapi_inb(port->base_addr + 1);
336      /* invert bit 7 (pin 11) to compensate for hardware inverter */
337      indata ^= 0x80;
338      /* split the bits into 10 variables (5 regular, 5 inverted) */
339      mask = 0x08;
340      for (b = 0; b < 10; b += 2) {
341  	*(port->status_in[b]) = indata & mask;
342  	*(port->status_in[b + 1]) = !(indata & mask);
343  	mask <<= 1;
344      }
345      /* are we using the data port for input? */
346      if (port->data_dir != 0) {
347  	/* yes, read the data port */
348  	indata = rtapi_inb(port->base_addr);
349  	/* split the bits into 16 variables (8 regular, 8 inverted) */
350  	mask = 0x01;
351  	for (b = 0; b < 16; b += 2) {
352  	    *(port->data_in[b]) = indata & mask;
353  	    *(port->data_in[b + 1]) = !(indata & mask);
354  	    mask <<= 1;
355  	}
356      }
357      /* are we using the control port for input? */
358      if(port->use_control_in) {
359          mask = 0x01;
360          /* correct for hardware inverters on pins 1, 14, & 17 */
361          indata = rtapi_inb(port->base_addr + 2) ^ 0x0B;
362          for (b = 0; b < 8; b += 2) {
363              *(port->control_in[b]) = indata & mask;
364              *(port->control_in[b + 1]) = !(indata & mask);
365  	    mask <<= 1;
366          }
367      }
368  }
369  
370  static void reset_port(void *arg, long period) {
371      parport_t *port = arg;
372      long long deadline, reset_time_tsc;
373      unsigned char outdata = (port->outdata&~port->reset_mask) ^ port->reset_val;
374     
375      if(port->reset_time > period/4) port->reset_time = period/4;
376      reset_time_tsc = ns2tsc(port->reset_time);
377  
378      if(outdata != port->outdata) {
379          deadline = port->write_time + reset_time_tsc;
380          while(rtapi_get_clocks() < deadline) {}
381          rtapi_outb(outdata, port->base_addr);
382      }
383  
384      outdata = (port->outdata_ctrl&~port->reset_mask_ctrl)^port->reset_val_ctrl;
385  
386      if(outdata != port->outdata_ctrl) {
387  	/* correct for hardware inverters on pins 1, 14, & 17 */
388  	outdata ^= 0x0B;
389          deadline = port->write_time_ctrl + reset_time_tsc;
390          while(rtapi_get_clocks() < deadline) {}
391          rtapi_outb(outdata, port->base_addr + 2);
392      }
393  }
394  
395  static void write_port(void *arg, long period)
396  {
397      parport_t *port;
398      int b;
399      unsigned char outdata, mask;
400  
401      port = arg;
402      /* are we using the data port for output? */
403      if (port->data_dir == 0) {
404  	int reset_mask=0, reset_val=0;
405  	/* yes */
406  	outdata = 0x00;
407  	mask = 0x01;
408  	/* assemble output byte for data port from 8 source variables */
409  	for (b = 0; b < 8; b++) {
410  	    /* get the data, add to output byte */
411  	    if ((*(port->data_out[b])) && (!port->data_inv[b])) {
412  		outdata |= mask;
413  	    }
414  	    if ((!*(port->data_out[b])) && (port->data_inv[b])) {
415  		outdata |= mask;
416  	    }
417  	    if (port->data_reset[b]) {
418  		reset_mask |= mask;
419  		if(port->data_inv[b]) reset_val |= mask;
420  	    }
421  	    mask <<= 1;
422  	}
423  	/* write it to the hardware */
424  	rtapi_outb(outdata, port->base_addr);
425  	port->write_time = rtapi_get_clocks();
426  	port->reset_val = reset_val;
427  	port->reset_mask = reset_mask;
428  	port->outdata = outdata;
429  	/* prepare to build control port byte, with direction bit clear */
430  	outdata = 0x00;
431      } else {
432  	/* prepare to build control port byte, with direction bit set */
433  	outdata = 0x20;
434      }
435      /* are we using the control port for input? */
436      if (port->use_control_in) {
437  	/* yes, force those pins high */
438  	outdata |= 0x0F;
439      } else {
440  	int reset_mask=0, reset_val=0;
441  	/* no, assemble output byte from 4 source variables */
442  	mask = 0x01;
443  	for (b = 0; b < 4; b++) {
444  	    /* get the data, add to output byte */
445  	    if ((*(port->control_out[b])) && (!port->control_inv[b])) {
446  		outdata |= mask;
447  	    }
448  	    if ((!*(port->control_out[b])) && (port->control_inv[b])) {
449  		outdata |= mask;
450  	    }
451  	    if (port->control_reset[b]) {
452  		reset_mask |= mask;
453  		if(port->control_inv[b]) reset_val |= mask;
454  	    }
455  	    mask <<= 1;
456  	}
457          port->reset_mask_ctrl = reset_mask;
458          port->reset_val_ctrl = reset_val;
459  	port->outdata_ctrl = outdata;
460      }
461      /* correct for hardware inverters on pins 1, 14, & 17 */
462      outdata ^= 0x0B;
463      /* write it to the hardware */
464      rtapi_outb(outdata, port->base_addr + 2);
465      port->write_time_ctrl = rtapi_get_clocks();
466  }
467  
468  void read_all(void *arg, long period)
469  {
470      parport_t *port;
471      int n;
472      port = arg;
473      for (n = 0; n < num_ports; n++) {
474  	read_port(&(port[n]), period);
475      }
476  }
477  
478  void write_all(void *arg, long period)
479  {
480      parport_t *port;
481      int n;
482      port = arg;
483      for (n = 0; n < num_ports; n++) {
484  	write_port(&(port[n]), period);
485      }
486  }
487  
488  /***********************************************************************
489  *                   LOCAL FUNCTION DEFINITIONS                         *
490  ************************************************************************/
491  
492  static int pins_and_params(char *argv[])
493  {
494      long port_addr[MAX_PORTS];
495      int data_dir[MAX_PORTS];
496      int use_control_in[MAX_PORTS];
497      int force_epp[MAX_PORTS];
498      int n, retval;
499  
500      /* clear port_addr and data_dir arrays */
501      for (n = 0; n < MAX_PORTS; n++) {
502  	port_addr[n] = 0;
503  	data_dir[n] = 0;
504  	use_control_in[n] = 0;
505  	force_epp[n] = 0;
506      }
507      /* parse config string, results in port_addr[] and data_dir[] arrays */
508      num_ports = 0;
509      n = 0;
510      while ((num_ports < MAX_PORTS) && (argv[n] != 0)) {
511  	port_addr[num_ports] = parse_port_addr(argv[n]);
512  	if (port_addr[num_ports] < 0) {
513  	    rtapi_print_msg(RTAPI_MSG_ERR,
514  		"PARPORT: ERROR: bad port address '%s'\n", argv[n]);
515  	    return -1;
516  	}
517  	n++;
518  	if (argv[n] != 0) {
519  	    /* is the next token 'in' or 'out' ? */
520  	    if ((argv[n][0] == 'i') || (argv[n][0] == 'I')) {
521  		/* we aren't picky, anything starting with 'i' means 'in' ;-) 
522  		 */
523  		data_dir[num_ports] = 1;
524                  use_control_in[num_ports] = 0;
525  		n++;
526  	    } else if ((argv[n][0] == 'o') || (argv[n][0] == 'O')) {
527  		/* anything starting with 'o' means 'out' */
528  		data_dir[num_ports] = 0;
529                  use_control_in[num_ports] = 0;
530  		n++;
531  	    } else if ((argv[n][0] == 'e') || (argv[n][0] == 'E')) {
532  		/* anything starting with 'e' means 'epp', which is just
533                     like 'out' but with EPP mode requested, primarily for
534                     the G540 with its charge pump missing-pullup drive
535                     issue */
536                  data_dir[num_ports] = 0;
537                  use_control_in[num_ports] = 0;
538                  force_epp[num_ports] = 1;
539  		n++;
540  	    } else if ((argv[n][0] == 'x') || (argv[n][0] == 'X')) {
541                  /* experimental: some parports support a bidirectional
542                   * control port.  Enable this with pins 2-9 in output mode, 
543                   * which gives a very nice 8 outs and 9 ins. */
544                  data_dir[num_ports] = 0;
545                  use_control_in[num_ports] = 1;
546  		n++;
547              }
548  	}
549  	num_ports++;
550      }
551      /* OK, now we've parsed everything */
552      if (num_ports == 0) {
553  	rtapi_print_msg(RTAPI_MSG_ERR,
554  	    "PARPORT: ERROR: no ports configured\n");
555  	return -1;
556      }
557      /* have good config info, connect to the HAL */
558      comp_id = hal_init("hal_parport");
559      if (comp_id < 0) {
560  	rtapi_print_msg(RTAPI_MSG_ERR, "PARPORT: ERROR: hal_init() failed\n");
561  	return -1;
562      }
563      /* allocate shared memory for parport data */
564      port_data_array = hal_malloc(num_ports * sizeof(parport_t));
565      if (port_data_array == 0) {
566  	rtapi_print_msg(RTAPI_MSG_ERR,
567  	    "PARPORT: ERROR: hal_malloc() failed\n");
568  	hal_exit(comp_id);
569  	return -1;
570      }
571      /* export all the pins and params for each port */
572      for (n = 0; n < num_ports; n++) {
573          int modes = 0;
574  
575          if(use_control_in[n]) {
576              modes = PARPORT_MODE_TRISTATE;
577          } else if(force_epp[n]) {
578              modes = PARPORT_MODE_EPP;
579          }
580  
581          retval = hal_parport_get(comp_id, &port_data_array[n].portdata,
582                  port_addr[n], -1, modes);
583  
584          if(retval < 0) {
585              // failure message already printed by hal_parport_get
586  	    hal_exit(comp_id);
587              return retval;
588          }
589  
590  	/* config addr and direction */
591  	port_data_array[n].base_addr = port_data_array[n].portdata.base;
592  	port_data_array[n].data_dir = data_dir[n];
593  	port_data_array[n].use_control_in = use_control_in[n];
594  
595          if(force_epp[n] && port_data_array[n].portdata.base_hi) {
596              /* select EPP mode in ECR */
597              outb(0x94, port_data_array[n].portdata.base_hi + 2);
598          }
599  
600  	/* set data port (pins 2-9) direction to "in" if needed */
601  	if (data_dir[n]) {
602  	    rtapi_outb(rtapi_inb(port_data_array[n].base_addr+2) | 0x20, port_data_array[n].base_addr+2);
603  	}
604  
605  	/* export all vars */
606  	retval = export_port(n, &(port_data_array[n]));
607  	if (retval != 0) {
608  	    rtapi_print_msg(RTAPI_MSG_ERR,
609  		"PARPORT: ERROR: port %d var export failed\n", n);
610  	    hal_exit(comp_id);
611  	    return retval;
612  	}
613      }
614      return 0;
615  }
616  
617  static unsigned short parse_port_addr(char *cp)
618  {
619      unsigned short result;
620  
621      /* initial value */
622      result = 0;
623      /* test for leading '0x' */
624      if (cp[0] == '0') {
625  	if ((cp[1] == 'X') || (cp[1] == 'x')) {
626  	    /* leading '0x', skip it */
627  	    cp += 2;
628  	}
629      }
630      /* ok, now parse digits */
631      while (*cp != '\0') {
632  	/* if char is a hex digit, add it to result */
633  	if ((*cp >= '0') && (*cp <= '9')) {
634  	    result <<= 4;
635  	    result += *cp - '0';
636  	} else if ((*cp >= 'A') && (*cp <= 'F')) {
637  	    result <<= 4;
638  	    result += (*cp - 'A') + 10;
639  	} else if ((*cp >= 'a') && (*cp <= 'f')) {
640  	    result <<= 4;
641  	    result += (*cp - 'a') + 10;
642  	} else {
643  	    /* not a valid hex digit */
644  	    return -1;
645  	}
646  	/* next char */
647  	cp++;
648      }
649  
650      return result;
651  }
652  
653  static int export_port(int portnum, parport_t * port)
654  {
655      int retval, msg;
656  
657      /* This function exports a lot of stuff, which results in a lot of
658         logging if msg_level is at INFO or ALL. So we save the current value
659         of msg_level and restore it later.  If you actually need to log this
660         function's actions, change the second line below */
661      msg = rtapi_get_msg_level();
662      rtapi_set_msg_level(RTAPI_MSG_WARN);
663  
664      retval = 0;
665      /* declare input pins (status port) */
666      retval += export_input_pin(portnum, 15, port->status_in, 0);
667      retval += export_input_pin(portnum, 13, port->status_in, 1);
668      retval += export_input_pin(portnum, 12, port->status_in, 2);
669      retval += export_input_pin(portnum, 10, port->status_in, 3);
670      retval += export_input_pin(portnum, 11, port->status_in, 4);
671      if (port->data_dir != 0) {
672  	/* declare input pins (data port) */
673  	retval += export_input_pin(portnum, 2, port->data_in, 0);
674  	retval += export_input_pin(portnum, 3, port->data_in, 1);
675  	retval += export_input_pin(portnum, 4, port->data_in, 2);
676  	retval += export_input_pin(portnum, 5, port->data_in, 3);
677  	retval += export_input_pin(portnum, 6, port->data_in, 4);
678  	retval += export_input_pin(portnum, 7, port->data_in, 5);
679  	retval += export_input_pin(portnum, 8, port->data_in, 6);
680  	retval += export_input_pin(portnum, 9, port->data_in, 7);
681      } else {
682  	/* declare output pins (data port) */
683  	retval += export_output_pin(portnum, 2,
684  	    port->data_out, port->data_inv, port->data_reset, 0);
685  	retval += export_output_pin(portnum, 3,
686  	    port->data_out, port->data_inv, port->data_reset, 1);
687  	retval += export_output_pin(portnum, 4,
688  	    port->data_out, port->data_inv, port->data_reset, 2);
689  	retval += export_output_pin(portnum, 5,
690  	    port->data_out, port->data_inv, port->data_reset, 3);
691  	retval += export_output_pin(portnum, 6,
692  	    port->data_out, port->data_inv, port->data_reset, 4);
693  	retval += export_output_pin(portnum, 7,
694  	    port->data_out, port->data_inv, port->data_reset, 5);
695  	retval += export_output_pin(portnum, 8,
696  	    port->data_out, port->data_inv, port->data_reset, 6);
697  	retval += export_output_pin(portnum, 9,
698  	    port->data_out, port->data_inv, port->data_reset, 7);
699  	retval += hal_param_u32_newf(HAL_RW, &port->reset_time, comp_id, 
700  			"parport.%d.reset-time", portnum);
701  	retval += hal_param_u32_newf(HAL_RW, &port->debug1, comp_id, 
702  			"parport.%d.debug1", portnum);
703  	retval += hal_param_u32_newf(HAL_RW, &port->debug2, comp_id, 
704  			"parport.%d.debug2", portnum);
705  	port->write_time = 0;
706      }
707      if(port->use_control_in == 0) {
708  	/* declare output variables (control port) */
709  	retval += export_output_pin(portnum, 1,
710  	    port->control_out, port->control_inv, port->control_reset, 0);
711  	retval += export_output_pin(portnum, 14,
712  	    port->control_out, port->control_inv, port->control_reset, 1);
713  	retval += export_output_pin(portnum, 16,
714  	    port->control_out, port->control_inv, port->control_reset, 2);
715  	retval += export_output_pin(portnum, 17,
716  	    port->control_out, port->control_inv, port->control_reset, 3);
717      } else {
718  	/* declare input variables (control port) */
719          retval += export_input_pin(portnum, 1, port->control_in, 0);
720          retval += export_input_pin(portnum, 14, port->control_in, 1);
721          retval += export_input_pin(portnum, 16, port->control_in, 2);
722          retval += export_input_pin(portnum, 17, port->control_in, 3);
723      }
724  
725      /* restore saved message level */
726      rtapi_set_msg_level(msg);
727      return retval;
728  }
729  
730  static int export_input_pin(int portnum, int pin, hal_bit_t ** base, int n)
731  {
732      int retval;
733  
734      /* export write only HAL pin for the input bit */
735      retval = hal_pin_bit_newf(HAL_OUT, base + (2 * n), comp_id,
736              "parport.%d.pin-%02d-in", portnum, pin);
737      if (retval != 0) {
738  	return retval;
739      }
740      /* export another write only HAL pin for the same bit inverted */
741      retval = hal_pin_bit_newf(HAL_OUT, base + (2 * n) + 1, comp_id,
742              "parport.%d.pin-%02d-in-not", portnum, pin);
743      return retval;
744  }
745  
746  static int export_output_pin(int portnum, int pin, hal_bit_t ** dbase,
747      hal_bit_t * pbase, hal_bit_t * rbase, int n)
748  {
749      int retval;
750  
751      /* export read only HAL pin for output data */
752      retval = hal_pin_bit_newf(HAL_IN, dbase + n, comp_id,
753              "parport.%d.pin-%02d-out", portnum, pin);
754      if (retval != 0) {
755  	return retval;
756      }
757      /* export parameter for polarity */
758      retval = hal_param_bit_newf(HAL_RW, pbase + n, comp_id,
759              "parport.%d.pin-%02d-out-invert", portnum, pin);
760      if (retval != 0) {
761  	return retval;
762      }
763      /* export parameter for reset */
764      if (rbase)
765  	retval = hal_param_bit_newf(HAL_RW, rbase + n, comp_id,
766  		"parport.%d.pin-%02d-out-reset", portnum, pin);
767      return retval;
768  }