Skip to content

Instantly share code, notes, and snippets.

@pavly-gerges
Created May 31, 2024 09:09
Show Gist options
  • Save pavly-gerges/352ca80aa6f7ce6c7ce156e55bccb3cc to your computer and use it in GitHub Desktop.
Save pavly-gerges/352ca80aa6f7ce6c7ce156e55bccb3cc to your computer and use it in GitHub Desktop.
Linux `parport.h` IOCTL Magic Macros
/*
* Any part of this program may be used in documents licensed under
* the GNU Free Documentation License, Version 1.1 or any later version
* published by the Free Software Foundation.
*/
#ifndef _PARPORT_H_
#define _PARPORT_H_
/* Start off with user-visible constants */
/* Maximum of 16 ports per machine */
#define PARPORT_MAX 16
/* Magic numbers */
#define PARPORT_IRQ_NONE -1
#define PARPORT_DMA_NONE -1
#define PARPORT_IRQ_AUTO -2
#define PARPORT_DMA_AUTO -2
#define PARPORT_DMA_NOFIFO -3
#define PARPORT_DISABLE -2
#define PARPORT_IRQ_PROBEONLY -3
#define PARPORT_IOHI_AUTO -1
#define PARPORT_CONTROL_STROBE 0x1
#define PARPORT_CONTROL_AUTOFD 0x2
#define PARPORT_CONTROL_INIT 0x4
#define PARPORT_CONTROL_SELECT 0x8
#define PARPORT_STATUS_ERROR 0x8
#define PARPORT_STATUS_SELECT 0x10
#define PARPORT_STATUS_PAPEROUT 0x20
#define PARPORT_STATUS_ACK 0x40
#define PARPORT_STATUS_BUSY 0x80
/* Type classes for Plug-and-Play probe. */
typedef enum {
PARPORT_CLASS_LEGACY = 0, /* Non-IEEE1284 device */
PARPORT_CLASS_PRINTER,
PARPORT_CLASS_MODEM,
PARPORT_CLASS_NET,
PARPORT_CLASS_HDC, /* Hard disk controller */
PARPORT_CLASS_PCMCIA,
PARPORT_CLASS_MEDIA, /* Multimedia device */
PARPORT_CLASS_FDC, /* Floppy disk controller */
PARPORT_CLASS_PORTS,
PARPORT_CLASS_SCANNER,
PARPORT_CLASS_DIGCAM,
PARPORT_CLASS_OTHER, /* Anything else */
PARPORT_CLASS_UNSPEC, /* No CLS field in ID */
PARPORT_CLASS_SCSIADAPTER
} parport_device_class;
/* The "modes" entry in parport is a bit field representing the
capabilities of the hardware. */
#define PARPORT_MODE_PCSPP (1<<0) /* IBM PC registers available. */
#define PARPORT_MODE_TRISTATE (1<<1) /* Can tristate. */
#define PARPORT_MODE_EPP (1<<2) /* Hardware EPP. */
#define PARPORT_MODE_ECP (1<<3) /* Hardware ECP. */
#define PARPORT_MODE_COMPAT (1<<4) /* Hardware 'printer protocol'. */
#define PARPORT_MODE_DMA (1<<5) /* Hardware can DMA. */
#define PARPORT_MODE_SAFEININT (1<<6) /* SPP registers accessible in IRQ. */
/* IEEE1284 modes:
Nibble mode, byte mode, ECP, ECPRLE and EPP are their own
'extensibility request' values. Others are special.
'Real' ECP modes must have the IEEE1284_MODE_ECP bit set. */
#define IEEE1284_MODE_NIBBLE 0
#define IEEE1284_MODE_BYTE (1<<0)
#define IEEE1284_MODE_COMPAT (1<<8)
#define IEEE1284_MODE_BECP (1<<9) /* Bounded ECP mode */
#define IEEE1284_MODE_ECP (1<<4)
#define IEEE1284_MODE_ECPRLE (IEEE1284_MODE_ECP | (1<<5))
#define IEEE1284_MODE_ECPSWE (1<<10) /* Software-emulated */
#define IEEE1284_MODE_EPP (1<<6)
#define IEEE1284_MODE_EPPSL (1<<11) /* EPP 1.7 */
#define IEEE1284_MODE_EPPSWE (1<<12) /* Software-emulated */
#define IEEE1284_DEVICEID (1<<2) /* This is a flag */
#define IEEE1284_EXT_LINK (1<<14) /* This flag causes the
* extensibility link to
* be requested, using
* bits 0-6. */
/* For the benefit of parport_read/write, you can use these with
* parport_negotiate to use address operations. They have no effect
* other than to make parport_read/write use address transfers. */
#define IEEE1284_ADDR (1<<13) /* This is a flag */
#define IEEE1284_DATA 0 /* So is this */
/* Flags for block transfer operations. */
#define PARPORT_EPP_FAST (1<<0) /* Unreliable counts. */
#define PARPORT_W91284PIC (1<<1) /* have a Warp9 w91284pic in the device */
/* The rest is for the kernel only */
#endif /* _PARPORT_H_ */
@pavly-gerges
Copy link
Author

Use:

gedit /usr/include/linux/parport.h 

@pavly-gerges
Copy link
Author

Responses from Gemini:
Here's an example program (C code) demonstrating basic operations on the parallel port using ioctl and the ppdev driver:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/parport.h>

#define PARPORT_DEVICE "/dev/parport0"

int main(int argc, char *argv[]) {
  int fd, ret;
  unsigned char data;

  // Open the parallel port device file
  fd = open(PARPORT_DEVICE, O_RDWR);
  if (fd == -1) {
    perror("Failed to open parallel port");
    return 1;
  }

  // Set data pin 7 (D7) to high (output 1)
  data = 0x80; // 0b10000000 (only bit 7 set)
  ret = ioctl(fd, PPWDATA, &data);
  if (ret == -1) {
    perror("Failed to write to data pin");
    close(fd);
    return 1;
  }
  printf("Set data pin 7 (D7) to HIGH\n");

  // Read the state of control pins (e.g., strobe)
  data = 0;
  ret = ioctl(fd, PPRCONTROL, &data);
  if (ret == -1) {
    perror("Failed to read control pins");
    close(fd);
    return 1;
  }
  printf("Control pin state: 0x%02X\n", data);

  // Toggle data pin 0 (D0) (set to low then high)
  data = 0x01; // 0b00000001 (only bit 0 set)
  ret = ioctl(fd, PPWDATA, &data);
  if (ret == -1) {
    perror("Failed to write to data pin");
    close(fd);
    return 1;
  }
  printf("Set data pin 0 (D0) to LOW\n");

  usleep(100000); // Delay for 0.1 seconds

  data = 0xFE; // Set all data pins except D0 to high
  ret = ioctl(fd, PPWDATA, &data);
  if (ret == -1) {
    perror("Failed to write to data pin");
    close(fd);
    return 1;
  }
  printf("Set all data pins except D0 to HIGH\n");

  // Close the device file
  close(fd);

  return 0;
}

Explanation:

  1. The program includes necessary header files for IOCTL operations and parallel port control.
  2. It defines the device file path (PARPORT_DEVICE) for the parallel port (usually /dev/parport0).
  3. It opens the device file with read/write access (O_RDWR).
  4. It sets data pin 7 (D7) to high by writing 0x80 (binary: 10000000) to the data port using the PPWDATA ioctl.
  5. It reads the state of the control pins using PPRCONTROL and prints the value.
  6. It toggles data pin 0 (D0) by writing 0x01 (binary: 00000001) and then 0xFE (all data pins high except D0).
  7. It closes the device file.

Compiling and Running:

  1. Save the code as a .c file (e.g., parallel_test.c).
  2. You might need root privileges to access the parallel port. Use sudo when compiling:
    sudo gcc parallel_test.c -o parallel_test -lppdev
    This links the program with the libppdev library (if available).
  3. Run the program with sudo ./parallel_test.

Important Notes:

  • This is a basic example. Refer to the linux/parport.h header for a complete list of ioctls and their arguments.
  • Remember, parallel port access requires root privileges.
  • Modern systems might not have libppdev pre-installed. You might need to install the development package for the ppdev driver.
  • Consider error handling and safety measures.

@pavly-gerges
Copy link
Author

While directly controlling all 25 pins of a parallel port might not be practical for most uses due to limitations of the data pins (8) and special purpose pins, here's an enhanced example demonstrating operations on various parallel port pins:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/parport.h>

#define PARPORT_DEVICE "/dev/parport0"

int main(int argc, char *argv[]) {
  int fd, ret;
  unsigned char data;

  // Open the parallel port device file
  fd = open(PARPORT_DEVICE, O_RDWR);
  if (fd == -1) {
    perror("Failed to open parallel port");
    return 1;
  }

  // Data pins (D0-D7) - Output specific values
  printf("Setting data pins:\n");
  for (int i = 0; i < 8; i++) {
    data = 1 << i;  // Set each bit individually for specific data pattern
    ret = ioctl(fd, PPWDATA, &data);
    if (ret == -1) {
      perror("Failed to write to data pin");
      close(fd);
      return 1;
    }
    printf("  - D%d: 0x%02X\n", i, data);
  }

  // Control pins (e.g., Strobe, Select, AutoFeed) - Set and Read
  printf("Control pins:\n");
  // Set Strobe pin (pin 1) high
  data = PARPORT_CONTROL_STROBE;  // Use defined constants from parport.h
  ret = ioctl(fd, PPWCONTROL, &data);
  if (ret == -1) {
    perror("Failed to write to control pin");
    close(fd);
    return 1;
  }
  printf("  - Strobe (pin 1): HIGH\n");

  // Read the state of all control pins
  data = 0;
  ret = ioctl(fd, PPRCONTROL, &data);
  if (ret == -1) {
    perror("Failed to read control pins");
    close(fd);
    return 1;
  }
  printf("  - Current state: 0x%02X\n", data);

  // Status pins (e.g., Busy, PaperOut) - Read only (example)
  printf("Status pins (read-only):\n");
  data = 0;
  ret = ioctl(fd, PPRSTATUS, &data);  // Might not work on all systems
  if (ret == -1) {
    perror("Failed to read status pin");
  } else {
    printf("  - Status register: 0x%02X\n", data);
  }

  // Close the device file
  close(fd);

  return 0;
}

Explanation:

  1. The code includes necessary header files and defines the device file path.
  2. It opens the device file with read/write access.
  3. It iterates through each data pin (D0-D7) and sets it high (1) individually using bit manipulation (1 << i).
  4. It sets the Strobe control pin (pin 1) high using the PARPORT_CONTROL_STROBE constant from linux/parport.h.
  5. It reads the current state of all control pins using PPRCONTROL.
  6. It attempts to read the status register using PPRSTATUS (might not work on all systems as these pins are often read-only).

Important Notes:

  • This example demonstrates basic operations. Refer to linux/parport.h for a complete list of ioctls, constants, and pin definitions.
  • Remember, parallel port access requires root privileges.
  • Modern systems might not have libppdev pre-installed. You might need to install the development package for the ppdev driver.
  • Consider error handling and safety measures in real-world applications.

Additional Considerations:

  • Data pins (D0-D7) can be used for bi-directional communication, but some external hardware might require specific configurations (e.g., setting data direction using control pins).
  • Not all control and status pins might be available on all parallel port implementations. Consult your system's documentation for specific pin functionalities.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment