LCDproc development and user support list

Text archives Help


[Lcdproc] ppdev support


Chronological Thread 
  • From: mail AT andrestrapanotto.com.ar (Andrés Trapanotto)
  • Subject: [Lcdproc] ppdev support
  • Date: Wed Mar 9 12:11:02 2005

Hi Joris! Thanks for your answer. Here we go.


Joris Robijn escribió:

On 7 Mar 2005 at 11:31, Andrés Trapanotto wrote:

The present implementation is in port.h and I've added port.c; then I've
modified port.h to add ppdev compilation option.
I've add a way to those drivers that aren't writted specifically to use
ppdev can work with ppdev anyway :)


Excelent. I had wanted that feature for a long time, but never got to write it.

I've defined struct pp_driver {....} because I think that is an easy way to pass the descriptor and the arguments to ppdev based functions. Each driver must have one of this to refer the parallel port.
You'll see that there is one more section on port.h that declares pp_* functions that are ppdev based functions, these functions are written in port.c
There are port_* functions too, but are just calls to pp_port () function. <- Because this, you can just define HAVE_PPDEV, recompile every drivers and use them thru ppdev without touching a thing :)

I think that every drivers can be re-writted to use ppdev based functions and then eliminate pp_port () compatibility function.

There is an issue with the DOS version: if you write your driver using ppdev based functions, you'll can't compile it to work under DOS. I'll have to write some way to allow ppdev based functions to be replaced by DOS working code....

Well, It is working for me right now and I want add it to the project.
I don't know what to do with:
* how to manage the copyright thing


This is not difficult. Just add your name. Remember all LCDproc code requires to be GPL, so you should add a header like in other files.

I'd something like that. Check it please.


* how to fully integrate the new piece of source code in makefile system
(how to check that the kernel has ppdev support)


Yes that's tricky probably. There is a way to check if a piece of code is compilable. It needs only be a small typical ppdev pieve of code. If there is no ppdev support the compilation will fail and so the compiler knows pppdev is not supported. But I'm not really an expert in this, so I don't know how to do this now.

I know, It should check if the ppdev.h is in the kernel headers tree. I know that It isn't hard but I didn't yet. Perhaps somebody will want to do it... :)
At now, I'm adding '-DHAVE_PPDEV' to the server/drivers/Makefile

* how to send the new files... where... who?


Oh well just send it to the list first. This way people can check if they see points for improvements and it's archived in the mail archives. Some developer can check the code in then.

Ok, there is the port.h modified and the port.c, I think that you can put them in your lcdproc/server/drivers, review the HAVE_PPDEV issue and start testing it :)
Remember that your kernel must be compiled with ppdev support (ppdev.o module) and you need permissions to use /dev/parportX, but LCDd don't need to be root anymore :)

Best regards,



--
Técnico Andrés Trapanotto
INSTITUTO NACIONAL DE TECNOLOGÍA INDUSTRIAL
Centro de Investigación Telecomunicaciones, Electrónica e Informática
Teléfono (54 11) 4724 6300 Interno 6362
mail AT andrestrapanotto.com.ar
___________________________________________
0800 444 4004 | www.inti.gov.ar

/* $id$
 * Low level I/O functions taken from led-stat.txt
 * Jan 22 95 copyright damianf AT wpi.edu
 *
 * DOS part ( tested only with DOS version of GCC, DJGPP)
 * by M.Prinke <m.prinke AT trashcan.mcnet.de> 3/97
 *
 * FreeBSD support by Guillaume Filion and Philip Pokorny, copyright 05/2001
 *
 * NetBSD & OpenBSD port by Guillaume Filion, copyright 12/2001 and 02/2002
 *
 * ( Better) FreeBSD port by Guillaume Filion, copyright 05/2002
 *
 * Improved support for old Linux ( glibc1 and libc5) by Guillaume Filion, 12/2002
 *
 * Access to ports over 0x3FF by Joris Robijn, 04/2003
 *
 * ppdev support by Andrés Trapanotto 03/2005
 *
 */

/*
This file defines 6 static inline functions for port I/O:

static inline int port_in ( unsigned short port);
        Read a byte from port
        Returns the content of the byte.

static inline void port_out ( unsigned short port, unsigned char val);
        Write a char( byte) 'val' to port.
        Returns nothing.

static inline int port_access ( unsigned short port);
        Get access to a specific port
        Returns 0 if successful, -1 if failed

static inline int port_deny ( unsigned short port);
        Close access to a specific port
        Returns 0 if successful, -1 if failed

static inline int port_access_multiple ( unsigned short port, unsigned short count)
        Get access multiple to ports at once.
        Returns 0 if successful, -1 if failed

static inline int port_deny_multiple ( unsigned short port, unsigned short count)
        Close access to multiple ports at once.
        Returns 0 if successful, -1 if failed

If you make modifications to this file: References to the LPT port should
not be in this file but in lpt-port.h ...
*/

#ifndef PORT_H
#define PORT_H

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>

struct pp_driver
{
#ifdef __linux__
  int fd,mode;
#else
  int base_addr;
#endif
  unsigned char data,control,status;
} ;

/*  ------------------------------------------------------------- */
/*  Use ppdev <linux/ppdev.h> ( Linux)                             */
#if defined HAVE_PPDEV
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/parport.h>
#include <linux/ppdev.h>

/* Libc5 */
# ifdef HAVE_UNISTD_H
#  include <unistd.h>
# endif

extern int pp_read_data     ( struct pp_driver * );
extern int pp_read_status   ( struct pp_driver * );
extern int pp_read_control  ( struct pp_driver * );
extern int pp_write_data    ( struct pp_driver * );
extern int pp_write_status  ( struct pp_driver * ); /* Obsolete */
extern int pp_write_control ( struct pp_driver * );
extern int pp_init  ( struct pp_driver * , char * );
extern int pp_bye   ( struct pp_driver * );
extern int pp_port  ( unsigned short , unsigned char , unsigned char );

/* The following 6 functions are used by the drivers that don't use ppdev natively */
static inline int  port_in ( unsigned short port )
{
  return pp_port ( port, 0, 0);
}

static inline void port_out    ( unsigned short port, unsigned char val)
{
 pp_port ( port, 1, val);
 return;
}

static inline int  port_access ( unsigned short port )
{
 return pp_port ( port, 2, 0);
}

static inline int  port_deny ( unsigned short port )
{
 return pp_port ( port, 3, 0);
}

static inline int  port_access_multiple ( unsigned short port, int val)
{
 return pp_port ( port, 2, 0);
}

static inline int  port_deny_multiple   ( unsigned short port, int val)
{
 return pp_port ( port, 3, 0);
}

/*  ------------------------------------------------------------- */
/*  Use ioperm, inb and outb in <sys/io.h> ( Linux) */
/*  And iopl for higher addresses of PCI LPT cards */
#elif defined HAVE_IOPERM

/* Glibc2 and Glibc1 */
# ifdef HAVE_SYS_IO_H 
#  include <sys/io.h>
# endif

/* Libc5 */
# ifdef HAVE_UNISTD_H
#  include <unistd.h>
# endif

/*  Read a byte from port */
static inline int port_in ( unsigned short port) {
        return inb( port);
}

/*  Write a byte 'val' to port */
static inline void port_out ( unsigned short port, unsigned char val) {
        outb( val, port);
}

/*  Get access to a specific port */
static inline int port_access ( unsigned short port) {
        if ( port <= 0x3FF) {
                return ioperm( port, 1, 255);
        } else {
#ifdef HAVE_IOPL
                /* Is there a better way to do this ? */
                static short int iopl_done = 0;
                if ( iopl_done) return 0;
                iopl_done = 1;
                return iopl( 1);
#else
                return -1; /* Error, can't access the requested port */
#endif
        }
}

/*  Close access to a specific port */
static inline int port_deny ( unsigned short port) {
        if ( port <= 0x3FF) {
                return ioperm( port, 1, 0);
        }
        /* We can't simply close access acquired with iopl */
        return 0;
}

/*  Get access to multiple ports at once */
static inline int port_access_multiple ( unsigned short port, int count) {
        if ( port+count-1 <= 0x3FF) {
                return ioperm( port, count, 255);
        } else {
#ifdef HAVE_IOPL
                return port_access( port+count);
                /* to use the iopl part there... */
#else
                return -1;
#endif
        }
        return 0;
}

/*  Close access to multiple ports at once */
static inline int port_deny_multiple ( unsigned short port, int count) {
        if ( port+count-1 <= 0x3FF) {
                return ioperm( port, count, 0);
        }
        /* We can't simply close access acquired with iopl */
        return 0;
}

/*  ------------------------------------------------------------- */
/*  Use i386_get_ioperm, i386_set_ioperm, inb and outb from <machine/pio.h> ( NetBSD&OpenBSD) */
#elif defined HAVE_I386_IOPERM_NETBSD && defined HAVE_MACHINE_PIO_H && defined HAVE_MACHINE_SYSARCH_H && defined HAVE_SYS_TYPES_H
#include <sys/types.h>
#include <machine/pio.h>
#include <machine/sysarch.h>

/*  Read a byte from port */
static inline int port_in ( unsigned short port) {
        return inb( port);
}

/*  Write a byte 'val' to port */
static inline void port_out ( unsigned short port, unsigned char val) {
        outb( port, val);
}

static inline void setaccess( u_long * map, u_int bit, int allow) {
        u_int           word;
        u_int           shift;
        u_long          mask;

        word = bit / 32;
        shift = bit - ( word * 32);

        mask = 0x000000001 << shift;
        if ( allow)
                map[word] &= ~mask;
        else
                map[word] |= mask;
}

/*  Get access to a specific port */
static inline int port_access ( unsigned short port) {
        u_long          iomap[32];

        if ( i386_get_ioperm( iomap) == -1) return -1;

        setaccess( iomap, port  , 1);

        if ( i386_set_ioperm( iomap) == -1) return -1;

        return 0;
}

/*  Close access to a specific port */
static inline int port_deny ( unsigned short port) {
        u_long          iomap[32];

        if ( i386_get_ioperm( iomap) == -1) return -1;

        setaccess( iomap, port, 0);

        if ( i386_set_ioperm( iomap) == -1) return -1;

        return 0;
}

/*  Get access to multiple ports at once */
static inline int port_access_multiple ( unsigned short port, unsigned short count) {
        u_long          iomap[32];
        unsigned short  i;

        if ( i386_get_ioperm( iomap) == -1) return -1;

        for ( i=0; i<count; i++) {
                setaccess( iomap, port + i, 1);
        }

        if ( i386_set_ioperm( iomap) == -1) return -1;

        return 0;
}

/*  Close access to multiple ports at once */
static inline int port_deny_multiple ( unsigned short port, unsigned short count) {
        u_long          iomap[32];
        unsigned short  i;

        if ( i386_get_ioperm( iomap) == -1) return -1;

        for ( i=0; i<count; i++) {
                setaccess( iomap, port + i, 0);
        }

        if ( i386_set_ioperm( iomap) == -1) return -1;

        return 0;
}

/*#endif // defined HAVE_I386_IOPERM_NETBSD && defined HAVE_MACHINE_PIO_H && defined HAVE_MACHINE_SYSARCH_H
-------------------------------------------------------------
Use i386_get_ioperm, i386_set_ioperm from <machine/sysarch.h> and inb and outb from <machine/cpufunc.h> ( FreeBSD) */
#elif defined HAVE_I386_IOPERM_FREEBSD && defined HAVE_MACHINE_CPUFUNC_H && defined HAVE_MACHINE_SYSARCH_H && defined HAVE_SYS_TYPES_H
#include <sys/types.h>
#include <machine/cpufunc.h>
#include <machine/sysarch.h>
        
/* Read a byte from port */
static inline int port_in ( unsigned short port) {
        return inb( port);
}
        
/* Write a byte 'val' to port */
static inline void port_out ( unsigned short port, unsigned char val) {
        outb( port,val);
}
        
/* Get access to a specific port */
static inline int port_access ( unsigned short port) {
        return i386_set_ioperm( port, 1, 1);
}

/* Get access to multiple ports at once */
static inline int port_access_multiple ( unsigned short port, unsigned short count) {
        return i386_set_ioperm( port, count, 1);
}

/* Close access to a specific port */
static inline int port_deny ( unsigned short port) {
        return i386_set_ioperm( port, 1, 0);
}

/* Close access to multiple ports at once */
static inline int port_deny_multiple ( unsigned short port, unsigned short count) {
        return i386_set_ioperm( port, count, 0);
}

#else
/*  ------------------------------------------------------------- */
/*  Last chance! Use /dev/io and i386 ASM code ( BSD4.3 ?) */

/*  Read a byte from port */
static inline int port_in ( unsigned short port) {
        unsigned char value;
        __asm__ volatile ( "inb %1,%0":"=a" ( value)
                                                        :"d" ( ( unsigned short) port));
        return value;
}

/*  Write a byte 'val' to port */
static inline void port_out ( unsigned short port, unsigned char val) {
        __asm__ volatile ( "outb %0,%1\n"::"a" ( val), "d" ( port)
                 );
}

/*  Get access to a specific port */
static inline int port_access ( unsigned short port) {
        static FILE *  port_access_handle = NULL ;

        if( port_access_handle
            || ( port_access_handle = fopen( "/dev/io", "rw")) != NULL ) {
                return( 0 );  /*  Success */
        } else {
                return( -1 );  /*  Failure */
        };

        return -1;
}

/*  Close access to a specific port */
static inline int port_deny ( unsigned short port) {
        /* Can't close /dev/io... */
        return 0;
}

/*  Get access to multiple ports at once */
static inline int port_access_multiple ( unsigned short port, unsigned short count) {
        return port_access ( port); /* /dev/io gives you access to all ports. */
}

/*  Close access to multiple ports at once */
static inline int port_deny_multiple ( unsigned short port, unsigned short count) {
        /*  Can't close /dev/io... */
        return 0;
}

#endif

/*
#else
#include <pc.h>

static inline int
port_in ( int port)
{
        unsigned char value;
        value = inportb ( ( unsigned short) port);
        return ( int) value;
}

static inline void
port_out ( unsigned int port, unsigned char val)
{
        outportb ( ( unsigned short) port, val);
}
#endif
*/

#endif /*  PORT_H */

/* $id$
 * Low level ppdev based I/O functions
 *
 * This file is released under the GPL. See file COPYING in this
 * package for further details.
 *
 * ppdev support by Andrés Trapanotto 03/2005
 */

/*
This file defines 8 functions for port I/O (2 obsolete ones):

int pp_read_data ( struct pp_driver * driver );         -- NOT IMPLEMENTED --
        Read a byte from data port
        Returns 0 if successful, -1 if failed

int pp_read_status ( struct pp_driver * driver );
        Read a byte from status port
        Returns 0 if successful, -1 if failed

int pp_read_control ( struct pp_driver * driver );
        Read a byte from control port
        Returns 0 if successful, -1 if failed

int pp_write_data ( struct pp_driver * driver );
        Write a byte to data port
        Returns 0 if successful, -1 if failed

int pp_write_status ( struct pp_driver * driver );      -- Obsolete --
        Write a byte to status port
        Returns 0 if successful, -1 if failed

int pp_write_control ( struct pp_driver * driver );
        Write a byte to control port
        Returns 0 if successful, -1 if failed

int  pp_init ( struct pp_driver * driver, char * device )
        Get access to a specific port (/dev/parport0,1,2)
        Returns 0 if successful, -1 if failed

int  pp_bye  ( struct pp_driver * driver)
        Close access to a specific port
        Returns 0 if successful, -1 if failed

ppdev support:
- If the drivers is writted to work with ppdev, it has to have a pp_driver where the ppdev data and descriptors will be
  stored. I can be part of private_data :)
  Then, the driver have to use pp_* functions to open/close/write/read the parallel port

- If the driver isn't writted to work woth ppdev, you can compile the project using USE_PPDEV and it will use ppdev
  anyway :)

- The idea is that drivers can be modified to use ppdev, and then port_* functions can be removed.
*/

#include <port.h>

#if defined HAVE_PPDEV
#include <string.h>

/*
// I think that it is useless by now
int  pp_read_data ( struct pp_driver * driver)
{
  return (-1);
//  return ioctl (driver->fd, PPRDATA, &(driver->data));
}
*/

int  pp_read_status ( struct pp_driver * driver)
{
  return ioctl (driver->fd, PPRSTATUS, &(driver->status));
}

int  pp_read_control ( struct pp_driver * driver)
{
  return ioctl (driver->fd, PPRCONTROL, &(driver->control));
}

int pp_write_data ( struct pp_driver * driver)
{
  return ioctl (driver->fd, PPWDATA, &(driver->data));
}

/*
int pp_write_status ( struct pp_driver * driver)                -- Obsolete --
  return (-1);
//  return ioctl (driver->fd, PPWSTATUS, driver->status);
}
*/

int pp_write_control ( struct pp_driver * driver)
{
  return ioctl (driver->fd, PPWCONTROL, &(driver->control));
}

int  pp_init ( struct pp_driver * driver, char * device )
{
   int retval=0,modes,dir;

   driver->fd=open (device,O_RDWR);
   if (driver->fd<0) {
      fprintf (stderr, "Error opening %s",device);
      exit (-1);
      return -1;
   }

   retval=ioctl (driver->fd, PPCLAIM);
   if (retval<0) {
      fprintf (stderr, "I can't get %s",device);
      return retval;
   }

   retval=ioctl (driver->fd, PPGETMODES, &modes);
   if (retval<0) {
      fprintf (stderr,"\nI can't read %s available modes.\n",device);
      return retval;
   } else {
      fprintf (stderr,"\n");
      if (!(modes & PARPORT_MODE_EPP) && !(modes & PARPORT_MODE_ECP))
         fprintf (stderr,"/dev/parport0  does not have EPP or ECP, giving up ...\n");
    else {
         fprintf (stderr,"%s available modes:\n",device);
         if (modes & PARPORT_MODE_PCSPP)    fprintf (stderr,"\t\tPARPORT_MODE_PCSPP\n");
         if (modes & PARPORT_MODE_TRISTATE) fprintf (stderr,"\t\tPARPORT_MODE_TRISTATE\n");
         if (modes & PARPORT_MODE_EPP)      fprintf (stderr,"\t\tPARPORT_MODE_EPP\n");
         if (modes & PARPORT_MODE_ECP)      fprintf (stderr,"\t\tPARPORT_MODE_ECP\n");
         if (modes & PARPORT_MODE_COMPAT)   fprintf (stderr,"\t\tPARPORT_MODE_COMPAT\n");
         if (modes & PARPORT_MODE_DMA)      fprintf (stderr,"\t\tPARPORT_MODE_DMA\n");
    }
 }

/* Is mandatory to set the data direction  */
 dir=0;  /* Data Forward */
 retval=ioctl (driver->fd, PPDATADIR, &dir);
 if (retval<0) {
    fprintf (stderr,"\nError setting the data direction on %s ",device);
    close (driver->fd);
    return (-1);
    }

return 0;
}

int  pp_bye  ( struct pp_driver * driver)
{
 int retval;

 retval=ioctl (driver->fd, PPRELEASE);
 if (retval<0)
    fprintf (stderr,"\nI can't free the parallel port.");

 close (driver->fd);

 return retval;
}

/* The following function is used by the drivers that don't use ppdev natively */
int  pp_port (unsigned short port, unsigned char arg, unsigned char val )
{
  static struct pp_driver driver;
  static int init=-1;
  int retval=0;
  char device[200];

  switch (arg) {
     case 0:   // Read Port
     case 1:   // Write Port
     case 3:   // Close Parallel Port
       if (!init){
          fprintf (stderr, "You must initialize the parallel port before using it!\n");
          exit (-1);
       }
       break;  // Doesn't matters
     case 2:
       if (init>0) return 0;  //Already initializated
       break;  // Doesn't matters
     default:
       fprintf (stderr,"HEY! Programmer! ¿What are you doing?\n");
       exit (-1);
  }

  switch (arg) {
     case 0:   // Read Parallel Port
       switch (port&0x03) {
          case 0x00:
             return (-1);
/*             retval=pp_read_data (&driver);                                   -- NOT IMPLEMENTED --
             if (retval<0) fprintf (stderr, "ERROR: Reading data bus.\n");
             retval=driver.data;
*/
             break;

          case 0x01:
             retval=pp_read_status (&driver);
             if (retval<0) fprintf (stderr, "ERROR: Reading status lines.\n");
             retval=driver.status;
             break;

          case 0x02:
             retval=pp_read_control (&driver);
             if (retval<0) fprintf (stderr, "ERROR: Reading control lines.\n");
             retval=driver.control;
             break;
          default:
             fprintf (stderr,"HEY! Programmer! ¿What are you doing?\n");
             exit (-1);
       }
       break;

     case 1:   // Write Parallel Port
       switch (port&0x03) {
          case 0x00:
             driver.data=val;
             retval=pp_write_data (&driver);
             if (retval<0) fprintf (stderr, "ERROR: Writing data bus.\n");
             break;

          case 0x01:
             return (-1);
/*             driver.status=val;                                               -- Obsolete --
             retval=pp_write_status (&driver);
             if (retval<0) fprintf (stderr, "ERROR: Writing status lines.\n");
*/
             break;

          case 0x02:
             driver.control=val;
             retval=pp_write_control (&driver);
             if (retval<0) fprintf (stderr, "ERROR: Writing control lines.\n");
             break;
          default:
             fprintf (stderr,"HEY! Programmer! ¿What are you doing?\n");
             exit (-1);
       }
       break;
       
     case 2:   // Init Parallel Port
               // It's possible to add a selection of which parallel port you want
               // to use but is easier to implement use of pp_* in your driver and
               // select the parallel port there. :)

/* Sick code...
       switch (port) {
          case 0x378:
             strcpy (device, "/dev/parport0");
             break;
          case 0x278:
             strcpy (device, "/dev/parport1");
             break;
          case 0x3BC:
             strcpy (device, "/dev/parport2");
             break;
          default:
             strcpy (device, "/dev/parport0");
       }
*/
       strcpy (device, "/dev/parport0");
       retval=pp_init (&driver, device);
       if (retval==0) init=1;
       break;
       
     case 3:   // Close Parallel Port
       retval=pp_bye (&driver);
       if (retval==0) init=-1;
       break;
  }

 return retval;
}

#endif /*  HAVE_PPDEV */




Archive powered by MHonArc 2.6.18.

Top of page