[Linux-parport] Parport irq handler patch

Eerin Rosenström eerin.rosenstrom at xylogas.fi
Sun Dec 6 07:26:19 EST 2009




> Frederick Barnes (frmb at mail.cern.ch)

> Sun, 26 Sep 1999 11:21:03 +0200 (CEST)

> 

> Hi,

>

> I've added two IOCTLs to ppdev to get and set the device timeout (as set



Hi, now ten years later I added three more IOCTLs to improve port usage in

time sensitive tasks.



Broblem:

 I have (turbine) axle that turns ~120rpm and I want measure it speed. 

 Simple circuit that feeds pulses to parport irq pin and linux handles

rest.



With userspace select() irq wait I ended to find only kernel internal

timings jitter.

Solution is quite simpe - move time recording to kernel irq handler. 

If use rdtsc instruction it make almost zero overheard. 

I used kernel getnstimeofday() for compatibility.



Some fast moving signals it is good that kernel also record status port.



It is quite easy add more irq handlers features. I used only three bits of

irqresponse vector.

 Feel free to modify that patch.



I wonder why that don't exist yet? 

I keep it 100% sure that I can do timings with linux parport.. 

It wasn't fun to find it unusable idea..

 But now it is here, enjoy



Main idea is to make free (GPL) software and simple parport circuit for 

genset monitoring, speed controlling and syncronized connection to mains.

Old PCs are junk and there are millions standalone gensets. 

 If interested, drop mail. I am not parport list subscriber.



ICTLs explanation



SIRQHDLR selects which irq handlers are active. Bit0 is old "write byte to

control" method.

Bit1 is time recording handler

Bit2 is status recorder. It may generate some (hardware) latency, but

people who use it can tolerate situation..



TSCONIRQ, returns struct timeval from last irq

RSTAONIRQ, returns status register from last irq



These boost timings accuracy ~two decades



Comments?



My C skills are quite limited (8 years ago one C prog course, no C coding

after it) It take whole day for me

to do and test such simpe patch..

 So feel free to make my patch better. 



I used 2.6.31 vanilla sources.



/usr/include/linux/ppdev.h may need patch too if system have it

standalone. 



--- include/linux/ppdev-orig.h  2009-12-04 15:41:20.000000000 +0200

+++ include/linux/ppdev.h       2009-12-04 17:30:08.000000000 +0200

@@ -88,6 +88,13 @@

 #define PPGETFLAGS     _IOR(PP_IOCTL, 0x9a, int)

 #define PPSETFLAGS     _IOW(PP_IOCTL, 0x9b, int)



+/* set irq handler behavior */

+#define SIRQHDLR       _IOW(PP_IOCTL, 0x9c, int)

+

+/* get time and statusreg on irq */

+#define TSCONIRQ       _IOR(PP_IOCTL, 0x9d, struct timeval)

+#define RSTAONIRQ      _IOR(PP_IOCTL, 0x9e, int)

+

 /* flags visible to the world */

 #define PP_FASTWRITE   (1<<2)

 #define PP_FASTREAD    (1<<3)



--- drivers/char/ppdev-orig.c   2009-12-04 15:04:57.000000000 +0200

+++ drivers/char/ppdev.c        2009-12-05 15:37:20.000000000 +0200

@@ -41,6 +41,9 @@

  *   GETPHASE   gets the current IEEE1284 phase

  *   GETFLAGS   gets current (user-visible) flags

  *   SETFLAGS   sets current (user-visible) flags

+ *   SIRQHDLR  select irq handler (bits 0, 1, 2 currently, 0=WCTLONIRQ,

1=TSCONIRQ, 2=RCTLONIRQ )

+ *   TSCONIRQ  rctsc on interrupt

+ *   RSTAONIRQ  status on interrupt

  * read/write  read or write in current IEEE 1284 protocol

  * select      wait for interrupt (in readfds)

  *

@@ -54,6 +57,14 @@

  *

  * Added GETMODES/GETMODE/GETPHASE ioctls, Fred Barnes <frmb2 at ukc.ac.uk>,

03/01/2001.

  * Added GETFLAGS/SETFLAGS ioctls, Fred Barnes, 04/2001

+ *

+ * Added ioctls to boost port timings usage, Eerin Rosenström

<eros at iki.fi>, 4.12.2009

+ *   SIRQHDLR  - select irq handler

+ *   TSCONIRQ  - rctsc on interrupt

+ *   RSTAONIRQ  - control on interrupt

+ * #define SIRQHDLR        _IOW(PP_IOCTL, 0x9c, int)

+ * #define TSCONIRQ        _IOR(PP_IOCTL, 0x9d, struct timeval)

+ * #define RSTAONIRQ       _IOR(PP_IOCTL, 0x9e, int)

  */



 #include <linux/module.h>

@@ -77,11 +88,13 @@

        wait_queue_head_t irq_wait;

        atomic_t irqc;

        unsigned int flags;

-       int irqresponse;

+       unsigned int irqresponse;

        unsigned char irqctl;

        struct ieee1284_info state;

        struct ieee1284_info saved_state;

        long default_inactivity;

+       struct timespec irqtv;

+       unsigned char irqsta;

 };



 /* pp_struct.flags bitfields */

@@ -270,9 +283,17 @@

 {

        struct pp_struct *pp = private;



-       if (pp->irqresponse) {

-               parport_write_control (pp->pdev->port, pp->irqctl);

-               pp->irqresponse = 0;

+       if ((pp->irqresponse) & (1 << 0)) {                             //

0bit=1

+               parport_write_control (pp->pdev->port, pp->irqctl);     //

write to control

+               pp->irqresponse &= ~(1 << 0);                           //

0bit=0

+       }

+

+       if ((pp->irqresponse) & (1 << 1)) {                             //

1bit=1, save time

+               getnstimeofday(&pp->irqtv);

+       }

+

+       if ((pp->irqresponse) & (1 << 2)) {                             //

2bit=1, read status

+               pp->irqsta = parport_read_status (pp->pdev->port);

        }



        atomic_inc (&pp->irqc);

@@ -585,7 +606,7 @@

                /* Remember what to set the control lines to, for next

                 * time we get an interrupt. */

                pp->irqctl = reg;

-               pp->irqresponse = 1;

+               pp->irqresponse |= (1 << 0);                            //

set 0bit=1 to trigger ctrl write

                return 0;



        case PPCLRIRQ:

@@ -619,6 +640,25 @@

                        return -EFAULT;

                return 0;



+       case SIRQHDLR:

+               if (copy_from_user (&reg, argp, sizeof (reg)))

+                       return -EFAULT;

+               pp->irqresponse =  reg;

+               return 0;

+

+       case TSCONIRQ:

+               par_timeout.tv_sec = pp->irqtv.tv_sec;                    

    // recycled par_timeout, thanks Barnes

+               par_timeout.tv_usec = ((pp->irqtv.tv_nsec) / 1000) ;

+               if (copy_to_user (argp, &par_timeout, sizeof(struct

timeval)))

+                       return -EFAULT;

+               return 0;

+

+       case RSTAONIRQ:

+               reg = pp->irqsta;

+               if (copy_to_user (argp, &reg, sizeof (reg)))

+                       return -EFAULT;

+               return 0;

+

        default:

                pr_debug(CHRDEV "%x: What? (cmd=0x%x)\n", minor, cmd);

                return -EINVAL;

@@ -653,7 +693,7 @@

        pp->state.mode = IEEE1284_MODE_COMPAT;

        pp->state.phase = init_phase (pp->state.mode);

        pp->flags = 0;

-       pp->irqresponse = 0;

+       pp->irqresponse = 0x00;                                 // turn

irq handlers off

        atomic_set (&pp->irqc, 0);

        init_waitqueue_head (&pp->irq_wait);







More information about the Linux-parport mailing list