// A simple program to set the 8 data pins of a parallel port to input ports. // If the first and only argument, the parallel port number, starting with 0, has a negativ sign, // this flag sets the port to write mode. // Compile with e. g. gcc -Wall -fbounds-check -O2 -o setpar_bppin setpar_bppin.c -lm // Dr. Rolf Freitag 2015. License: GPL version 3. #include // error codes #include // standard i/o #include // exit, atoi #include // strndup, #include // or #include // getuid(), usleep #include // INT_MAX, #include // and, or, ... #include // BIT0, BIT1, ... #include #include #include #include // ioctl(), #include // parallel port ioctls #include // parallel port modes // simple version (date) #define SOFTWARE_VERSION "2015-12-05" #define MAX_LINE_LENGTH 1024 // maximum line length, e. g. for a device name /* possible modes: IEEE1284_MODE_NIBBLE // similar to the SPP (Standard Parallel Port) IEEE1284_MODE_BYTE // bidirectional mode, mode bits (BIT5-BIT7 in the ECR) 001 (binary) IEEE1284_MODE_COMPAT // compatibility mode, unidirectional (outbound), SPP, mode bits 000 IEEE1284_MODE_BECP // Bounded ECP mode IEEE1284_MODE_ECP // ECP mode, mode bits 011 IEEE1284_MODE_ECPRLE // ECP mode with RLE (Run Length Encoding) IEEE1284_MODE_ECPSWE // Software-emulated IEEE1284_MODE_EPP // EPP mode, mode bits 100 IEEE1284_MODE_EPPSL // EPP 1.7 IEEE1284_MODE_EPPSWE // Software-emulated IEEE1284_DEVICEID // This is a flag IEEE1284_EXT_LINK // This flag causes the extensibility link to be requested, using bits 0-6. */ int main (int argc, char *argv[]) { long int li = 0; // local variable int i_dir = 0, i_ret = 0; // direction, return value int i_sign = 1; // sign of the first argument, default 1 for + sign int fd = 0; // file descriptor for the parallel port const int ci_mode = IEEE1284_MODE_BYTE; // bidirectional parallel port mode aka Byte mode aka PS/2 mode aka Tristate mode, should set 0x20 in the ECR via paused IO char ac_filename[MAX_LINE_LENGTH + 1] = { 0 }; // filename to the parallel port addresses char ac_tmpstring[MAX_LINE_LENGTH + 1] = { 0 }; // Array of Character, temporary string if ((1 >= argc) or (3 <= argc)) // less than two or more than two command line arguments { (void) fprintf (stderr, "Usage: %s \n", argv[0]); (void) fprintf (stderr, "This program sets the specified parallel port, e. g. parport0, into bidirectional parallel port mode\n"); (void) fprintf (stderr, "aka Byte mode aka PS/2 mode aka Tristate AND mode read mode, the 8 data pins as inputs.\n"); (void) fprintf (stderr, "If the first arguments has a negativ sign, this flag sets the port to write mode.\n"); (void) fprintf (stderr, "In this case you have to use an offset of 1, so you must write -1 for setting partport0 to write mode.\n"); exit (-1); } if (geteuid () != 0) { (void) fprintf (stderr, "\a\n\nError: $EUID==%d!=0 (you are not a superuser).\n\n", getuid ()); exit (-EPERM); } li = strtol (argv[1], (char **) NULL, 0); // convert the first program argument to long int, can be decimal, hex or octal if (li < 0) // < 0 because -0 is equal +0, so -0 is a crackpot input and treated like +0. { li = labs (li) - 1; // remove the sign and offset i_sign = -1; // store the sign flag } // get the device file name from the argument // print the parallel port number (0 or 1 or ...) snprintf (ac_tmpstring, sizeof (ac_tmpstring) - 1, "%ld", li); // Set the file name, e. g. "/dev/parport1" strncat (ac_filename, "/dev/parport", 100); strncat (ac_filename, ac_tmpstring, 100); // The extended mode direction (0 = write, read else), at most parallel ports BIT5 in the control port if (i_sign > 0) { i_dir = 0xff; // input mode for the data port } else { i_dir = 0; // output mode for the data port } fd = open (ac_filename, O_RDWR); // open read-write if (-1 == fd) // if error { (void) fprintf (stderr, "Could not open the device file %s, exiting!\n", ac_filename); exit (1); } (void) fprintf (stdout, "%s opened with file descriptor %d.\n", ac_filename, fd); // TODO: Read and display the current (old) mode (PPGETMODE) AND data port direction (PPRCONTROL, BIT5) /* // Exclusive mode: Usually does not work; the kernel complains with "parport0: cannot grant exclusive access for device ppdev0" i_ret = ioctl (fd, PPEXCL); // Register device exclusively (must be before PPCLAIM), if (-1 == i_ret) // if error { (void) fprintf (stderr, "ERROR: ioctl(fd(%s), PPEXCL) returned %d, errno: %d, %s\n", ac_filename, i_ret, errno, strerror (errno)); } */ i_ret = ioctl (fd, PPCLAIM); // Claim the port to start using it if (-1 == i_ret) // if error { (void) fprintf (stderr, "ERROR: ioctl(fd(%s), PPCLAIM) returned %d, errno: %d, %s\n", ac_filename, i_ret, errno, strerror (errno)); } //i_ret = ioctl (fd, PPNEGOT, &ci_mode); // Negotiate the particular IEEE 1284 mode i_ret = ioctl (fd, PPSETMODE, &ci_mode); // Set the particular IEEE 1284 mode if (-1 == i_ret) // if error { (void) fprintf (stderr, "ERROR: ioctl(fd(%s), PPNEGOT, ptr_ci_mode) returned %d, errno: %d, %s\n", ac_filename, i_ret, errno, strerror (errno)); } i_ret = ioctl (fd, PPCLRIRQ); // Clear (and return) interrupt count if (-1 == i_ret) // if error { (void) fprintf (stderr, "ERROR: ioctl(fd(%s), PPCLRIRQ) returned %d, errno: %d, %s\n", ac_filename, i_ret, errno, strerror (errno)); } i_ret = ioctl (fd, PPDATADIR, &i_dir); // set the data port direction: non-zero for input mode, zero for output if (-1 == i_ret) // if error { (void) fprintf (stderr, "ERROR: ioctl(fd(%s), PPDATADIR, ptr_i_dir) returned %d, errno: %d, %s\n", ac_filename, i_ret, errno, strerror (errno)); } i_ret = ioctl (fd, PPRELEASE); // Release the port if (-1 == i_ret) // if error { (void) fprintf (stderr, "ERROR: ioctl(fd(%s), PPRELEASE) returned %d, errno: %d, %s\n", ac_filename, i_ret, errno, strerror (errno)); } i_ret = close (fd); if (-1 == i_ret) // if error { (void) fprintf (stderr, "ERROR: close(fd(%s)) returned %d, errno: %d, %s\n", ac_filename, i_ret, errno, strerror (errno)); } (void) fprintf (stdout, "The data pins of the parallelport %s are now configured as %s.\n", ac_filename, (i_sign > 0) ? "inputs" : "outputs"); return (0); // exit with ok } // main