This is pp.c in view mode; [Download] [Up]
/*
* pp.c -- Do some special things to the printer (send some ioctls)
*
* $Header: /Users/marcel/src/pp/RCS/pp.c,v 1.5 96/01/24 19:01:27 marcel Exp $
*
* (c) 1996 Marcel Waldvogel, mwa@nice.ch
*
* History
* =======
* 960106 mwa Created
* 960122 mwa Fine-tuned, beautified,
* added fall-back for "interval" to "timeout",
* eliminated the second open() call for "interval"
* (removed possibility for another timeout to happen),
* prepared for a more public release
*/
#include <objc/objc.h>
#include <stdio.h>
#include <libc.h>
#include <errno.h>
/* Compile with -DDRIVER_PRIVATE, to get extended status info from pp_extern.h */
#include <architecture/ARCH_INCLUDE.h>
#include ARCH_INCLUDE(bsd/dev/, pp_extern.h)
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#define DEFDEV "/dev/pp0"
typedef struct{
int mask;
char *name;
} BITSTAT;
BITSTAT driverbits[] = {
PP_ST_INITIALIZED, "initialized",
PP_ST_BUSY, "busy",
PP_ST_NOPAPER, "paper out",
PP_ST_NOTSELECTED, "!selected",
PP_ST_TIMEOUT, "timeout",
PP_ST_ERROR, "error",
0, NULL
};
BITSTAT portbits[] = {
0x80, "!busy",
0x40, "!ack",
0x20, "paper out",
0x10, "!select",
0x08, "!error",
0x04, "rsvd2",
0x02, "rsvd1",
0x01, "rsvd0",
0, NULL
};
BITSTAT controlbits[] = {
0x80, "rsvd7",
0x40, "rsvd6",
0x20, "rsvd5",
0x10, "irq enable",
0x08, "select in",
0x04, "!reset",
0x02, "autofeed",
0x01, "strobe",
0, NULL
};
/** void usage(prog)
* Print usage information.
*/
void usage(const char *prog)
{
fprintf(stderr, "Usage: %s [-f device] <cmd> [<args> ...]\n", prog);
fprintf(stderr, "Parallel port setup/status, default device is %s.\n", DEFDEV);
fputs(
"<cmd> is one of: (can be abbreviated)\n"
" status show printer status\n"
" xstatus show extended status\n"
" reset reset printer\n"
" write write data from stdin and show status\n"
" timeout <secs> set timeout\n"
#if defined(PPIOCSRETRIES) && defined(PPIOCSRINTERVAL)
" interval <retry> <interval> set fine-grain timeout, <interval> fractional\n"
#else
" interval <retry> <interval> set timeout to <retry>*<interval>\n"
#endif
, stderr); // end of fputs()
exit(1);
}
/** void pperror(prog, func, device)
* Print decent error information about the current errno.
*/
void pperror(const char *prog, const char *func, const char *device)
{
if (func == NULL)
fprintf(stderr, "%s: %s: %s\n", prog, device, strerror(errno));
else
{
if (device == NULL)
fprintf(stderr, "%s: %s: %s\n", prog, func, strerror(errno));
else
fprintf(stderr, "%s: %s %s: %s\n", prog, func, device, strerror(errno));
}
exit(2);
}
/** void bitstat(stream, status, bs)
* Decode a flag bitmap to a human-readable format.
* If negate is != 0, then flags whose text start with that char
* are output if the bit is not set (useful for negated bits).
*/
void bitstat(FILE *stream, int status, const BITSTAT *bs, char negate)
{
int first = 1;
int foundbits = 0;
fputc('<', stream);
while (bs->mask != 0)
{
foundbits |= bs->mask;
// If you wanted alternate behavior (i.e. on multibit-stuff ALL bits
// needed to be set, change the "!= 0" to "== bs->mask")
if ((bs->mask & status) != 0 && (!negate || bs->name[0] != negate))
{
if (!first)
fputc(',', stream);
else
first = 0;
fprintf(stream, bs->name);
}
if ((bs->mask & status) == 0 && negate && bs->name[0] == negate)
{
if (!first)
fputc(',', stream);
else
first = 0;
fprintf(stream, bs->name + 1);
}
bs++;
}
// Did we output text for all bits?
if ((status & foundbits) != status)
{
if (!first)
fputc(',', stream);
fprintf(stream, "?");
}
fputc('>', stream);
}
/** void ppshowstatus(prog, device, fd)
* Prints status information for the given file descriptor
* (prog and device are used for error messages only)
*/
void ppshowstatus(const char *prog, const char *device, int fd)
{
int status;
// Official status
if (ioctl(fd, PPIOCSW, &status) < 0)
pperror(prog, "PPIOCSW", device);
printf("Driver status = 0x%x ", status);
bitstat(stdout, status, driverbits, '!');
printf("\n");
#ifdef PPIOCGSREG /* Defined, when compiled with -DDRIVER_PRIVATE */
if (ioctl(fd, PPIOCGSREG, &status) < 0)
pperror(prog, "PPIOCGSREG", device);
printf("Port status = 0x%x ", status);
bitstat(stdout, status, portbits, '!');
printf("\n");
#endif /* PPIOCGSREG */
#if defined(PPIOCGRINTERVAL) && defined(PPIOCGRETRIES)
/* Defined, when compiled with -DDRIVER_PRIVATE */
{
int count, interval;
if (ioctl(fd, PPIOCGRINTERVAL, &interval) < 0)
pperror(prog, "PPIOCGRINTERVAL", device);
if (ioctl(fd, PPIOCGRETRIES, &count) < 0)
pperror(prog, "PPIOCGRETRIES", device);
printf("Timeout = %ds (retries=%d, interval=%gs)\n",
(interval * count +500) / 1000, count, (float)interval/1000.0);
}
#endif /* PPIOCGRINTERVAL && PPIOCGRETRIES */
}
/** void ppshowextendedstatus(prog, device, fd)
* Print extended status information for the given file descriptor
* (prog and device are used for error messages only)
*/
void ppshowextendedstatus(const char *prog, const char *device, int fd)
{
int status;
// The "easy" part
#define GET(ctl, format) \
if (ioctl(fd, ctl, &status) < 0) \
pperror(prog, #ctl, device); \
printf(format, status);
// I would have liked to put the #ifdef into the macro,
// but this doesn't seem to work. Does anybody know how?
#ifdef PPIOCGIHDELAY
GET(PPIOCGIHDELAY, "Handler delay = %d usec\n");
#endif
#ifdef PPIOCGIOTDELAY
GET(PPIOCGIOTDELAY, "I/O task delay = %d usec\n");
#endif
#ifdef PPIOCGMINPHYS
GET(PPIOCGMINPHYS, "Max chunk size = %d\n");
#endif
#ifdef PPIOCGBSIZE
GET(PPIOCGBSIZE, "Block size = %d\n");
#endif
#ifdef PPIOCGCREG
GET(PPIOCGCREG, "Control register = 0x%2x ");
bitstat(stdout, status, controlbits, '!');
printf("\n");
#endif
#ifdef PPIOCGCREGDEF
GET(PPIOCGCREGDEF, "Ctl register def = 0x%2x ");
bitstat(stdout, status, controlbits, '!');
printf("\n");
#endif
#undef GET
}
/** void ppstatus(prog, device, extended)
* Opens the device and prints normal or extended status, depending
* on the flag.
*/
void ppstatus(const char *prog, const char *device, BOOL extended)
{
int fd;
fd = open(device, O_RDONLY | O_NDELAY); // O_NDELAY is (currently) ignored
if (fd < 0)
pperror(prog, "open", device);
ppshowstatus(prog, device, fd);
if (extended)
ppshowextendedstatus(prog, device, fd);
close(fd);
}
/** void ppreset(prog, device)
* Resets the printer
*/
void ppreset(const char *prog, const char *device)
{
int fd;
fd = open(device, O_RDONLY | O_NDELAY);
if (fd < 0)
pperror(prog, "open", device);
if (ioctl(fd, PPIOCINIT) < 0)
pperror(prog, "PPIOCINIT", device);
close(fd);
}
/** void ppset(prog, device, ctl, ioctlname, value)
* Does an ioctl on the device, setting the parameter to "value"
*/
void ppset(const char *prog, const char *device, int ctl, const char *text,
int value)
{
int fd;
fd = open(device, O_RDONLY | O_NDELAY);
if (fd < 0)
pperror(prog, "open", device);
if (ioctl(fd, ctl, &value) < 0)
pperror(prog, text, device);
close(fd);
}
/** void ppinterval(prog, device, retry, interval)
* Sets the timeout and retry intervals reasonably
* (tries to fall back to documented stuff if needed)
*/
void ppinterval(const char *prog, const char *device, int retry, int interval)
{
int fd, timeout;
fd = open(device, O_RDONLY | O_NDELAY);
if (fd < 0)
pperror(prog, "open", device);
// First try it the "conventional" way, so that we have a timeout at all
timeout = (retry * interval + 500) / 1000;
if (ioctl(fd, PPIOCSETTIMO, &retry) < 0)
pperror(prog, "PPIOCSETTIMO", device);
#if defined(PPIOCSRETRIES) && defined(PPIOCSRINTERVAL)
// Now try to set everything the way we really would like it
if (ioctl(fd, PPIOCSRETRIES, &retry) < 0)
pperror(prog, "PPIOCSRETRIES", device);
if (ioctl(fd, PPIOCSRINTERVAL, &interval) < 0)
pperror(prog, "PPIOCSRINTERVAL", device);
#endif
close(fd);
}
/** void ppwrite(prog, device)
* Reads character by character from stdin and sends it to the printer,
* giving full detail error messages in case anything should fail.
* Mainly used for debugging.
*/
void ppwrite(const char *prog, const char *device)
{
int fd;
char c;
fd = open(device, O_WRONLY);
if (fd < 0)
pperror(prog, "open", device);
// Read from stdin
while (read(STDIN_FILENO, &c, 1) == 1)
{
switch (write(fd, &c, 1))
{
case 0:
puts("EOF on write?");
ppshowstatus(prog, device, fd);
ppshowextendedstatus(prog, device, fd);
close(fd);
exit(3);
case 1:
break;
default:
perror("write");
ppshowstatus(prog, device, fd);
ppshowextendedstatus(prog, device, fd);
close(fd);
exit(3);
}
}
ppshowstatus(prog, device, fd);
ppshowextendedstatus(prog, device, fd);
close(fd);
}
/** void main(argc, argv)
* Handles all options and dispatches the commands
*/
void main(int argc, char *argv[])
{
const char *device = DEFDEV;
int argp = 1;
// At least two more arguments
if (argc > argp + 1 && strcmp(argv[argp], "-f") == 0)
{
argp++;
device = argv[argp++];
}
// No more arguments?
if (argc <= argp)
usage(argv[0]);
switch (argv[argp][0])
{
case 'r': // reset
ppreset(argv[0], device);
break;
case 's': // status
case 'x': // xstatus
// Show either normal or extended status
ppstatus(argv[0], device, argv[argp][0] == 'x');
break;
case 't': // timeout
argp++;
if (argc <= argp)
usage(argv[0]);
ppset(argv[0], device, PPIOCSETTIMO, "PPIOCSETTIMO", atoi(argv[argp]));
break;
case 'w': // write
ppwrite(argv[0], device);
break;
case 'i': // interval
argp += 2;
if (argc <= argp)
usage(argv[0]);
ppinterval(argv[0], device, atoi(argv[argp - 1]),
(int)(atof(argv[argp])*1000.0+0.5));
break;
default:
usage(argv[0]);
}
exit(0);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.