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