1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1991-2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #ifndef lint
30 static char sccsid[] = "%Z%%M% %I% %E%";
31 #endif
32 
33 extern char *postbegin;
34 
35 #include <stdio.h>
36 #include <errno.h>
37 extern char *_sys_errlist[];
38 #include <string.h>
39 #include <stdarg.h>
40 #include <signal.h>
41 #include <unistd.h>
42 #include <sys/types.h>
43 #include <sys/ioccom.h>
44 #include <sys/ioctl.h>
45 
46 #include <sys/bpp_io.h>
47 #include <sys/ecppsys.h>
48 #include <sys/prnio.h>
49 
50 #define PRINTER_IO_ERROR	129
51 
52 /*
53  * the parameter structure for the parallel port
54  */
55 struct ppc_params_t {
56 	int		flags;		/* same as above */
57 	int		state;		/* status of the printer interface */
58 	int		strobe_w;	/* strobe width, in uS */
59 	int		data_setup;	/* data setup time, in uS */
60 	int		ack_timeout;	/* ACK timeout, in secs */
61 	int		error_timeout;	/* PAPER OUT, etc... timeout, in secs */
62 	int		busy_timeout;	/* BUSY timeout, in seconds */
63 };
64 
65 
66 
67 extern char *block;
68 extern int head, tail;
69 extern int readblock(int);
70 extern FILE *fp_log;
71 static void printer_info(char *fmt, ...);
72 
73 /*	These are the routines avaliable to others for use 	*/
74 int is_a_parallel_bpp(int);
75 int bpp_state(int);
76 int parallel_comm(int, int());
77 int get_ecpp_status(int);
78 int is_a_prnio(int);
79 int prnio_state(int);
80 
81 #define PRINTER_ERROR_PAPER_OUT		1
82 #define PRINTER_ERROR_OFFLINE		2
83 #define PRINTER_ERROR_BUSY		3
84 #define PRINTER_ERROR_ERROR		4
85 #define PRINTER_ERROR_CABLE_POWER	5
86 #define PRINTER_ERROR_UNKNOWN		6
87 #define PRINTER_ERROR_TIMEOUT		7
88 
89 /****************************************************************************/
90 
91 /**
92  *	for BPP PARALLEL interfaces
93  **/
94 
95 int is_a_parallel_bpp(int fd)
96 {
97 	if (ioctl(fd, BPPIOC_TESTIO) == 0 || errno == EIO)
98 		return(1);
99 	return(0);
100 }
101 
102 
103 #if defined(DEBUG) && defined(NOTDEF)
104 char *BppState(int state)
105 {
106 	static char buf[BUFSIZ];
107 
108 	memset(buf, 0, sizeof(buf));
109 	sprintf(buf, "State (0x%.4x) - (%s%s%s%s)\n", state,
110 		((state & BPP_SLCT_ERR) ?  "offline " : ""),
111 		((state & BPP_BUSY_ERR) ?  "busy " : ""),
112 		((state & BPP_PE_ERR) ?  "paper " : ""),
113 		((state & BPP_ERR_ERR) ?  "error " : ""));
114 
115 	return(buf);
116 }
117 #endif
118 
119 int bpp_state(int fd)
120 {
121 	if (ioctl(fd, BPPIOC_TESTIO)) {
122 		struct bpp_error_status  bpp_stat;
123 		int state;
124 
125 		if (ioctl(fd, BPPIOC_GETERR, &bpp_stat) < 0)
126 			exit(PRINTER_IO_ERROR);
127 		state = bpp_stat.pin_status;
128 
129 #if defined(DEBUG) && defined(NOTDEF)
130 		logit("%s", BppState(state));
131 #endif
132 
133 		if (state == (BPP_PE_ERR | BPP_ERR_ERR | BPP_SLCT_ERR)) {
134 			/* paper is out */
135 			return(PRINTER_ERROR_PAPER_OUT);
136 		} else if (state & BPP_BUSY_ERR) {
137 			/* printer is busy */
138 			return(PRINTER_ERROR_BUSY);
139 		} else if (state & BPP_SLCT_ERR) {
140 			/* printer is offline */
141 			return(PRINTER_ERROR_OFFLINE);
142 		} else if (state & BPP_ERR_ERR) {
143 			/* printer is errored */
144 			return(PRINTER_ERROR_ERROR);
145 		} else if (state == BPP_PE_ERR) {
146 			/* printer is off/unplugged */
147 			return(PRINTER_ERROR_CABLE_POWER);
148 		} else if (state) {
149 			return(PRINTER_ERROR_UNKNOWN);
150 		} else
151 			return(0);
152 	}
153 	return(0);
154 }
155 
156 int
157 get_ecpp_status(int fd)
158 {
159 	int state;
160 	struct ecpp_transfer_parms transfer_parms;
161 
162 
163 	if (ioctl(fd, ECPPIOC_GETPARMS, &transfer_parms) == -1) {
164 		return(-1);
165 	}
166 
167 	state = transfer_parms.mode;
168 	/*
169 	 * We don't know what all printers will return in
170 	 * nibble mode, therefore if we support nibble mode we will
171 	 * force the printer to be in CENTRONICS mode.
172 	 */
173 
174 	if (state != ECPP_CENTRONICS) {
175 		transfer_parms.mode = ECPP_CENTRONICS;
176 		if (ioctl(fd, ECPPIOC_SETPARMS, &transfer_parms) == -1) {
177 			return(-1);
178 		} else {
179 			state = ECPP_CENTRONICS;
180 		}
181 	}
182 
183 	return(state);
184 }
185 
186 /**
187  * For prnio(7I) - generic printer interface
188  **/
189 int is_a_prnio(int fd)
190 {
191 	uint_t	cap;
192 
193 	/* check if device supports prnio */
194 	if (ioctl(fd, PRNIOC_GET_IFCAP, &cap) == -1) {
195 		return (0);
196 	}
197 	/* we will use 1284 status if available */
198 	if ((cap & PRN_1284_STATUS) == 0) {
199 		/* some devices may only support 1284 status in unidir. mode */
200 		if (cap & PRN_BIDI) {
201 			cap &= ~PRN_BIDI;
202 			(void) ioctl(fd, PRNIOC_SET_IFCAP, &cap);
203 		}
204 	}
205 	return (1);
206 }
207 
208 int prnio_state(int fd)
209 {
210 	uint_t	status;
211 	uchar_t	pins;
212 
213 	if ((ioctl(fd, PRNIOC_GET_STATUS, &status) == 0) &&
214 	    (status & PRN_READY)) {
215 		return(0);
216 	}
217 
218 	if (ioctl(fd, PRNIOC_GET_1284_STATUS, &pins) != 0) {
219 		return(PRINTER_ERROR_UNKNOWN);
220 	}
221 
222 	if ((pins & ~PRN_1284_BUSY) == PRN_1284_PE) {
223 		/* paper is out */
224 		return(PRINTER_ERROR_PAPER_OUT);
225 	} else if (pins == (PRN_1284_PE | PRN_1284_SELECT |
226 				PRN_1284_NOFAULT | PRN_1284_BUSY)) {
227 		/* printer is off/unplugged */
228 		return(PRINTER_ERROR_CABLE_POWER);
229 	} else if ((pins & PRN_1284_SELECT) == 0) {
230 		/* printer is offline */
231 		return(PRINTER_ERROR_OFFLINE);
232 	} else if ((pins & PRN_1284_NOFAULT) == 0) {
233 		/* printer is errored */
234 		return(PRINTER_ERROR_ERROR);
235 	} else if (pins & PRN_1284_PE) {
236 		/* paper is out */
237 		return(PRINTER_ERROR_PAPER_OUT);
238 	} else if (pins ^ (PRN_1284_SELECT | PRN_1284_NOFAULT)) {
239 		return(PRINTER_ERROR_UNKNOWN);
240 	}
241 	return(0);
242 }
243 
244 /**
245  *	Common routines
246  **/
247 
248 /*ARGSUSED0*/
249 static void
250 ByeByeParallel(int sig)
251 {
252 	/* try to shove out the EOT */
253 	(void) write(1, "\004", 1);
254 	exit(0);
255 }
256 
257 
258 /*ARGSUSED0*/
259 static void
260 printer_info(char *fmt, ...)
261 {
262 	char mesg[BUFSIZ];
263 	va_list ap;
264 
265 	va_start(ap, fmt);
266 	vsprintf(mesg, fmt, ap);
267 	va_end(ap);
268 
269 	fprintf(stderr,
270 		"%%%%[ PrinterError: %s; source: parallel ]%%%%\n",
271 		mesg);
272 	fflush(stderr);
273 	fsync(2);
274 
275 	if (fp_log != stderr) {
276 		fprintf(fp_log,
277 		   "%%%%[ PrinterError: %s; source: parallel ]%%%%\n",
278 		   mesg);
279 		fflush(fp_log);
280 	}
281 }
282 
283 static void
284 printer_error(int error)
285 {
286 	switch (error) {
287 		case -1:
288 			printer_info("ioctl(): %s", _sys_errlist[errno]);
289 			break;
290 		case PRINTER_ERROR_PAPER_OUT:
291 			printer_info("out of paper");
292 			break;
293 		case PRINTER_ERROR_OFFLINE:
294 			printer_info("offline");
295 			break;
296 		case PRINTER_ERROR_BUSY:
297 			printer_info("busy");
298 			break;
299 		case PRINTER_ERROR_ERROR:
300 			printer_info("printer error");
301 			break;
302 		case PRINTER_ERROR_CABLE_POWER:
303 			printer_info("printer powered off or disconnected");
304 			break;
305 		case PRINTER_ERROR_UNKNOWN:
306 			printer_info("unknown error");
307 			break;
308 		case PRINTER_ERROR_TIMEOUT:
309 			printer_info("communications timeout");
310 			break;
311 		default:
312 			printer_info("get_status() failed");
313 	}
314 }
315 
316 
317 static void
318 wait_state(int fd, int get_state())
319 {
320 	int state;
321 	int was_faulted = 0;
322 
323 	while (state = get_state(fd)) {
324 		was_faulted=1;
325 		printer_error(state);
326 		sleep(15);
327 	}
328 
329 	if (was_faulted) {
330 		fprintf(stderr, "%%%%[ status: idle ]%%%%\n");
331 		fflush(stderr);
332 		fsync(2);
333 		if (fp_log != stderr) {
334 			fprintf(fp_log, "%%%%[ status: idle ]%%%%\n");
335 			fflush(fp_log);
336 		}
337 	}
338 }
339 
340 
341 int
342 parallel_comm(int fd, int get_state())
343 {
344 	int  actual;		/* number of bytes successfully written */
345 	int count = 0;
346 
347 	(void) signal(SIGTERM, ByeByeParallel);
348 	(void) signal(SIGQUIT, ByeByeParallel);
349 	(void) signal(SIGHUP, ByeByeParallel);
350 	(void) signal(SIGINT, ByeByeParallel);
351 	(void) signal(SIGALRM, SIG_IGN);
352 
353 	/* is the device ready? */
354 
355 	/* bracket job with EOT */
356 	wait_state(fd, get_state);
357 	(void) write(fd, "\004", 1);
358 
359 /* 	write(fd, postbegin, strlen(postbegin)); */
360 
361 	while (readblock(fileno(stdin)) > 0) {
362 		wait_state(fd, get_state);
363 		alarm(120);
364 		if ((actual = write(fd, block + head, tail - head)) == -1) {
365 			alarm(0);
366 		  	if (errno == EINTR) {
367 				printer_error(PRINTER_ERROR_TIMEOUT);
368 				sleep(30);
369 				continue;
370 			} else {
371 				printer_info("I/O Error during write(): %s",
372 					strerror(errno));
373 				exit(2);
374 			}
375 		}
376 		alarm(0);
377 		if (actual >= 0)
378 			head += actual;
379 
380 #if defined(DEBUG) && defined(NOTDEF)
381 		logit("Writing (%d) at 0x%x actual: %d, %s\n", count++, head,
382 			actual, (actual < 1 ? _sys_errlist[errno] : ""));
383 #endif
384 	}
385 
386 	/* write the final EOT */
387 	wait_state(fd, get_state);
388 	(void) write(fd, "\004", 1);
389 
390 	return (0);
391 }
392