/* lpif.c - Raw Parallel Port ppdev Interface Version 0.02 Copyright (c) 2004-2005 Joseph Battaglia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. IBM Parallel Port Specifications: Register Name Value Pin Inverted -------- ---- ----- --- -------- DATA DATA 0 0x01 2 NO DATA DATA 1 0x02 3 NO DATA DATA 2 0x04 4 NO DATA DATA 3 0x08 5 NO DATA DATA 4 0x10 6 NO DATA DATA 5 0x20 7 NO DATA DATA 6 0x40 8 NO DATA DATA 7 0x80 9 NO STATUS ERROR 0x08 15 NO STATUS SELECTED 0x10 13 NO STATUS NO PAPER 0x20 12 NO STATUS ACK 0x40 10 NO STATUS BUSY 0x80 11 YES CONTROL STROBE 0x01 1 YES CONTROL AUTOFEED 0x02 14 YES CONTROL INITIALIZE 0x04 16 NO CONTROL SELECT 0x08 17 YES The DATA and CONTROL registers are output, STATUS is input. Pins 18-25 are ground. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /********** defaults **********/ #define DEV_PARPORT "/dev/parports/0" #define VERSION "0.02" /********** end defaults **********/ /* the only reason this is global is so that parport_cleanup() can be called in the case of an error in one of the wrapper functions */ int ppdev_fd; /* ppdev device file descriptor */ /********** cleanup functions **********/ /* release parallel port */ void parport_cleanup(void) { if (ioctl(ppdev_fd, PPRELEASE) == -1) { fprintf(stderr, "ioctl(%d, PPRELEASE): %s\n", ppdev_fd, strerror(errno)); } if (close(ppdev_fd) == -1) { fprintf(stderr, "close(%d): %s\n", ppdev_fd, strerror(errno)); } } /********** end cleanup functions **********/ /********** function wrappers **********/ /* allocate memory with out of memory checking [size] allocate size bytes returns pointer to allocated memory */ void *xmalloc(size_t size) { void *ptr; ptr = malloc(size); if (ptr == NULL) { fprintf(stderr, "malloc(%d): %s\n", size, strerror(errno)); parport_cleanup(); exit(EXIT_FAILURE); } return ptr; } /* reallocate memory with out of memory checking [ptr] memory to reallocate [size] allocate size bytes returns pointer to reallocated memory */ void *xrealloc(void *ptr, size_t size) { void *nptr; nptr = realloc(ptr, size); if (nptr == NULL) { fprintf(stderr, "realloc(%p, %d): %s\n", ptr, size, strerror(errno)); parport_cleanup(); exit(EXIT_FAILURE); } return nptr; } /* copy a string with out of memory checking [string] string to copy returns newly allocated copy of string */ char *xstrdup(char *string) { char *ptr; ptr = xmalloc(strlen(string) + 1); strcpy(ptr, string); return ptr; } /* open with error checking [pathname] pathname to open [flags] flags returns file descriptor */ int xopen(const char *pathname, int flags) { int retval; retval = open(pathname, flags); if (retval == -1) { fprintf(stderr, "open(%s, %d): %s\n", pathname, flags, strerror(errno)); parport_cleanup(); exit(EXIT_FAILURE); } return retval; } /* read with error checking [fd] file descriptor to read from [buf] buffer [count] bytes to read returns bytes read */ ssize_t xread(int fd, void *buf, size_t count) { int retval; retval = read(fd, buf, count); if (retval == -1) { fprintf(stderr, "read(%d, %p, %d): %s\n", fd, buf, count, strerror(errno)); parport_cleanup(); exit(EXIT_FAILURE); } return retval; } /********** end function wrappers **********/ /********** version functions **********/ /* prints version [stream] output stream */ void print_version(FILE *stream) { fprintf(stream, "lpif - Raw Parallel Port ppdev Interface\n" "Version %s\n" "Copyright (C) 2004-2005 Joseph Battaglia \n", VERSION); } /* prints version and help [stream] output stream [exec] string containing the name of the program executable */ void print_help(FILE *stream, char *exec) { print_version(stream); fprintf(stream, "\nUsage: %s [OPTIONS]\n\n" "Informative output:\n" " -h, --help Print help information\n" " -v, --version Print version information\n\n", exec); fprintf(stream, "Device configuration:\n" " -d, --device Set ppdev device\n" " Default: %s\n" " -n --no-inv Disable inversion compensation\n" " Default: Compensate for hardware inversion\n\n", DEV_PARPORT); fprintf(stream, "Output functions:\n" " -p, --print-states Print parallel port pin states in table form\n" " Exits with the decimal value of STATUS\n" " -r, --raw-reg Print raw parallel port register values in hex\n" " The registers are output in the following order:\n" " STATUS CONTROL DATA\n" " Exits with the decimal value of STATUS\n\n"); fprintf(stream, "Input functions:\n" " -c, --set-control Sets the control register to the supplied value\n" " -s, --set-data Sets the data register to the supplied value\n\n"); fprintf(stream, "When using the --set-xxx options, the value can be specified in hex,\n" "decimal, or octal. For example: 0xFF (hex), 255 (dec), or 0377 (oct)\n" "\n"); fprintf(stream, "Keep in mind that the three least significant values of the STATUS port\n" "are almost always reported as high (0x07). The five most significant\n" "bits are the actual hardware pins.\n\n"); fprintf(stream, "When using the --set-control option, although the CONTROL pin states\n" "are being set, they tend not to retain their setting when PPRELEASE is\n" "called (i.e., as lpif exits).\n"); } /********** end version functions **********/ /********** i/o functions **********/ /* initialize parallel port [pathname] path to ppdev device returns file descriptor for [pathname] */ int parport_init(const char *pathname) { int fd; fd = xopen(pathname, O_RDWR); if (ioctl(fd, PPCLAIM) == -1) { fprintf(stderr, "ioctl(%d, PPCLAIM): %s\n", fd, strerror(errno)); parport_cleanup(); exit(EXIT_FAILURE); } return fd; } /* read parallel port register [fd] file descriptor of ppdev device [line] register to read (PPRDATA, PPRCONTROL, or PPRSTATUS) [inv_comp] hardware inversion compensation if true returns byte of data read */ unsigned char parport_read(int fd, int line, int inv_comp) { unsigned char byte; if (ioctl(fd, line, &byte)) { fprintf(stderr, "ioctl(%d, %d, %p): %s\n", fd, line, &byte, strerror(errno)); parport_cleanup(); exit(EXIT_FAILURE); } if (inv_comp) { if (line == (int)PPRCONTROL) /* compensate for STROBE, AUTOFEED, and SELECT inversion */ byte = (byte ^ 0x0b); if (line == (int)PPRSTATUS) /* compensate for BUSY inversion */ byte = (byte ^ 0x80); } return byte; } /* write parallel port register [pathname] file descriptor of ppdev device [line] register to write (PPWDATA, or PPWCONTROL) [inv_comp] hardware inversion compensation if true [byte] byte of data to write returns [byte] */ unsigned char parport_write(int fd, int line, int inv_comp, unsigned char byte) { if (inv_comp) if (line == (int)PPWCONTROL) /* compensate for STROBE, AUTOFEED, and SELECT inversion */ byte = (byte ^ 0x0b); if (ioctl(fd, line, &byte)) { fprintf(stderr, "ioctl(%d, %d, %p): %s\n", fd, line, &byte, strerror(errno)); parport_cleanup(); exit(EXIT_FAILURE); } return byte; } /* print pin states in table form [fd] file descriptor of ppdev device [inv_comp] hardware inversion compensation if true */ void parport_print_state(int fd, int inv_comp) { unsigned char cbyte, dbyte, sbyte; cbyte = parport_read(fd, PPRCONTROL, inv_comp); dbyte = parport_read(fd, PPRDATA, inv_comp); sbyte = parport_read(fd, PPRSTATUS, inv_comp); /* what a mess; if i didn't split up the printf() statement, the string would have exceeded 509 characters. this mess should be rewritten, eventually, but it's late now. */ printf( "Pin DATA Name S Pin CONTROL Name S Pin STATUS Name S\n" "--- ----------- - --- ------------ - --- ----------- -\n" " 2 DATA 0 %d 1 STROBE %d 15 ERROR %d\n" " 3 DATA 1 %d 14 AUTOFEED %d 13 SELECTED %d\n" " 4 DATA 2 %d 16 INITIALIZE %d 12 NO PAPER %d\n" " 5 DATA 3 %d 17 SELECT %d 10 ACK %d\n" " 6 DATA 4 %d 11 BUSY %d\n", (dbyte >> 0) & 0x01, (cbyte & PARPORT_CONTROL_STROBE) ? 1 : 0, (sbyte & PARPORT_STATUS_ERROR) ? 1 : 0, (dbyte >> 1) & 0x01, (cbyte & PARPORT_CONTROL_AUTOFD) ? 1 : 0, (sbyte & PARPORT_STATUS_SELECT) ? 1 : 0, (dbyte >> 2) & 0x01, (cbyte & PARPORT_CONTROL_INIT) ? 1 : 0, (sbyte & PARPORT_STATUS_PAPEROUT) ? 1 : 0, (dbyte >> 3) & 0x01, (cbyte & PARPORT_CONTROL_SELECT) ? 1 : 0, (sbyte & PARPORT_STATUS_ACK) ? 1 : 0, (dbyte >> 4) & 0x01, (sbyte & PARPORT_STATUS_BUSY) ? 1 : 0); printf( " 7 DATA 5 %d \n" " 8 DATA 6 %d \n" " 9 DATA 7 %d \n", (dbyte >> 5) & 0x01, (dbyte >> 6) & 0x01, (dbyte >> 7) & 0x01); } /********** end i/o functions **********/ /* main */ int main(int argc, char *argv[]) { unsigned char cbyte, dbyte, sbyte; long int tmp; /* configuration variables */ char *device = NULL; int inv_comp = 1; int raw_states = 0, print_states = 0, cwrite = 0, dwrite = 0; /* getopt variables */ int ch, option_index; static struct option long_options[] = { {"set-control", 1, 0, 'c'}, {"device", 1, 0, 'd'}, {"help", 0, 0, 'h'}, {"no-inv", 0, 0, 'n'}, {"print-states", 0, 0, 'p'}, {"raw-states", 0, 0, 'r'}, {"set-data", 1, 0, 's'}, {"version", 0, 0, 'v'}, { 0, 0, 0, 0 } }; /* process command line arguments */ while (1) { ch = getopt_long(argc, argv, "c:d:hnrps:v", long_options, &option_index); if (ch == -1) break; switch (ch) { /* set-control */ case 'c': cwrite = 1; tmp = strtol(optarg, NULL, 0); if (tmp > 0xFF || tmp < 0x00) { fprintf(stderr, "set-control: Invalid value\n"); exit(EXIT_FAILURE); } cbyte = (unsigned char)tmp; break; /* device */ case 'd': device = xstrdup(optarg); break; /* help */ case 'h': print_help(stdout, argv[0]); exit(EXIT_SUCCESS); break; /* no-inv */ case 'n': inv_comp = 0; break; /* show-states */ case 'p': print_states = 1; break; /* raw-states */ case 'r': raw_states = 1; break; /* set-data */ case 's': dwrite = 1; tmp = strtol(optarg, NULL, 0); if (tmp > 0xFF || tmp < 0x00) { fprintf(stderr, "set-data: Invalid value\n"); exit(EXIT_FAILURE); } dbyte = (unsigned char)tmp; break; /* version */ case 'v': print_version(stdout); exit(EXIT_SUCCESS); break; /* default */ default: print_help(stderr, argv[0]); exit(EXIT_FAILURE); break; } } if (!raw_states && !print_states && !dwrite && !cwrite) { print_help(stderr, argv[0]); exit(EXIT_FAILURE); } /* set default device */ if (device == NULL) device = xstrdup(DEV_PARPORT); /* initialize parallel port */ ppdev_fd = parport_init(device); if (raw_states) { /* print raw values of each register */ cbyte = parport_read(ppdev_fd, PPRCONTROL, inv_comp); dbyte = parport_read(ppdev_fd, PPRDATA, inv_comp); sbyte = parport_read(ppdev_fd, PPRSTATUS, inv_comp); printf("0x%.2X 0x%.2X 0x%.2X\n", sbyte, cbyte, dbyte); /* exit with the decimal value of the status register */ parport_cleanup(); exit(sbyte); } if (print_states) { /* print state of each pin in table form */ parport_print_state(ppdev_fd, inv_comp); /* exit with the decimal value of the status register */ parport_cleanup(); exit(sbyte); } if (cwrite) { /* don't set pins that don't exist */ cbyte = (cbyte & 0x0F); printf("Setting CONTROL register to 0x%.2X\n", cbyte); parport_write(ppdev_fd, PPWCONTROL, inv_comp, cbyte); } if (dwrite) { printf("Setting DATA register to 0x%.2X\n", dbyte); parport_write(ppdev_fd, PPWDATA, inv_comp, dbyte); } parport_cleanup(); return 0; }