xref: /minix/minix/drivers/printer/printer/printer.c (revision 7f5f010b)
1 /* This file contains the printer driver. It is a fairly simple driver,
2  * supporting only one printer.  Characters that are written to the driver
3  * are written to the printer without any changes at all.
4  *
5  * Changes:
6  *	May 07, 2004	fix: wait until printer is ready  (Jorrit N. Herder)
7  *	May 06, 2004	printer driver moved to user-space  (Jorrit N. Herder)
8  *
9  * Note: since only 1 printer is supported, minor dev is not used at present.
10  */
11 
12 #include <minix/endpoint.h>
13 #include <minix/drivers.h>
14 #include <minix/chardriver.h>
15 
16 /* Control bits (in port_base + 2).  "+" means positive logic and "-" means
17  * negative logic.  Most of the signals are negative logic on the pins but
18  * many are converted to positive logic in the ports.  Some manuals are
19  * misleading because they only document the pin logic.
20  *
21  *	+0x01	Pin 1	-Strobe
22  *	+0x02	Pin 14	-Auto Feed
23  *	-0x04	Pin 16	-Initialize Printer
24  *	+0x08	Pin 17	-Select Printer
25  *	+0x10	IRQ7 Enable
26  *
27  * Auto Feed and Select Printer are always enabled. Strobe is enabled briefly
28  * when characters are output.  Initialize Printer is enabled briefly when
29  * the task is started.  IRQ7 is enabled when the first character is output
30  * and left enabled until output is completed (or later after certain
31  * abnormal completions).
32  */
33 #define ASSERT_STROBE   0x1D	/* strobe a character to the interface */
34 #define NEGATE_STROBE   0x1C	/* enable interrupt on interface */
35 #define PR_SELECT          0x0C	/* select printer bit */
36 #define INIT_PRINTER    0x08	/* init printer bits */
37 
38 /* Status bits (in port_base + 2).
39  *
40  *	-0x08	Pin 15	-Error
41  *	+0x10	Pin 13	+Select Status
42  *	+0x20	Pin 12	+Out of Paper
43  *	-0x40	Pin 10	-Acknowledge
44  *	-0x80	Pin 11	+Busy
45  */
46 #define BUSY_STATUS     0x10	/* printer gives this status when busy */
47 #define NO_PAPER        0x20	/* status bit saying that paper is out */
48 #define NORMAL_STATUS   0x90	/* printer gives this status when idle */
49 #define ON_LINE         0x10	/* status bit saying that printer is online */
50 #define STATUS_MASK	0xB0	/* mask to filter out status bits */
51 
52 #define MAX_ONLINE_RETRIES 120  /* about 60s: waits 0.5s after each retry */
53 
54 /* Centronics interface timing that must be met by software (in microsec).
55  *
56  * Strobe length:	0.5u to 100u (not sure about the upper limit).
57  * Data set up:		0.5u before strobe.
58  * Data hold:		0.5u after strobe.
59  * Init pulse length:	over 200u (not sure).
60  *
61  * The strobe length is about 50u with the code here and function calls for
62  * sys_outb() - not much to spare.  The 0.5u minimums will not be violated
63  * with the sys_outb() messages exchanged.
64  */
65 
66 static endpoint_t caller;	/* process to tell when printing done (FS) */
67 static int done_status;	/* status of last output completion */
68 static int oleft;		/* bytes of output left in obuf */
69 static unsigned char obuf[128];	/* output buffer */
70 static unsigned const char *optr;	/* ptr to next char in obuf to print */
71 static int orig_count;		/* original byte count */
72 static int port_base;		/* I/O port for printer */
73 static cp_grant_id_t grant_nr;	/* grant on which print happens */
74 static int user_left;		/* bytes of output left in user buf */
75 static vir_bytes user_vir_d;	/* offset in user buf */
76 int writing;		/* nonzero while write is in progress */
77 static cdev_id_t write_id;	/* request ID of ongoing write */
78 static int irq_hook_id;	/* id of irq hook at kernel */
79 
80 static void output_done(void);
81 static void prepare_output(void);
82 static int do_probe(void);
83 static void do_initialize(void);
84 static int printer_open(devminor_t minor, int access, endpoint_t user_endpt);
85 static ssize_t printer_write(devminor_t minor, u64_t position,
86 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
87 	cdev_id_t id);
88 static int printer_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
89 static void printer_intr(unsigned int mask);
90 
91 /* SEF functions and variables. */
92 static void sef_local_startup(void);
93 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
94 EXTERN int sef_cb_lu_prepare(int state);
95 EXTERN int sef_cb_lu_state_isvalid(int state);
96 EXTERN void sef_cb_lu_state_dump(int state);
97 
98 /* Entry points to this driver. */
99 static struct chardriver printer_tab = {
100   .cdr_open	= printer_open,
101   .cdr_write	= printer_write,
102   .cdr_cancel	= printer_cancel,
103   .cdr_intr	= printer_intr
104 };
105 
106 /*===========================================================================*
107  *				printer_task				     *
108  *===========================================================================*/
109 int main(void)
110 {
111 /* Main routine of the printer task. */
112 
113   /* SEF local startup. */
114   sef_local_startup();
115 
116   chardriver_task(&printer_tab);
117 }
118 
119 /*===========================================================================*
120  *			       sef_local_startup			     *
121  *===========================================================================*/
122 static void sef_local_startup()
123 {
124   /* Register init callbacks. */
125   sef_setcb_init_fresh(sef_cb_init_fresh);
126   sef_setcb_init_lu(sef_cb_init_fresh);
127   sef_setcb_init_restart(sef_cb_init_fresh);
128 
129   /* Register live update callbacks. */
130   sef_setcb_lu_prepare(sef_cb_lu_prepare);
131   sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
132   sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
133 
134   /* Register signal callbacks. */
135   sef_setcb_signal_handler(sef_cb_signal_handler_term);
136 
137   /* Let SEF perform startup. */
138   sef_startup();
139 }
140 
141 /*===========================================================================*
142  *		            sef_cb_init_fresh                                *
143  *===========================================================================*/
144 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
145 {
146 /* Initialize the printer driver. */
147 
148   /* If no printer is present, do not start. */
149   if (!do_probe())
150 	return ENODEV;	/* arbitrary error code */
151 
152   /* Announce we are up! */
153   chardriver_announce();
154 
155   return OK;
156 }
157 
158 /*===========================================================================*
159  *				do_write				     *
160  *===========================================================================*/
161 static ssize_t printer_write(devminor_t UNUSED(minor), u64_t UNUSED(position),
162 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
163 	cdev_id_t id)
164 {
165 /* The printer is used by sending write requests to it. Process one. */
166   int retries;
167   u32_t status;
168 
169   /* Reject command if last write is not yet finished, the count is not
170    * positive, or we're asked not to block.
171    */
172   if (writing)			return EIO;
173   if (size <= 0)		return EINVAL;
174   if (flags & CDEV_NONBLOCK)	return EAGAIN;	/* not supported */
175 
176   /* If no errors occurred, continue printing with the caller.
177    * First wait until the printer is online to prevent stupid errors.
178    */
179   caller = endpt;
180   user_left = size;
181   orig_count = size;
182   user_vir_d = 0;	/* Offset. */
183   writing = TRUE;
184   grant_nr = grant;
185   write_id = id;
186 
187   retries = MAX_ONLINE_RETRIES + 1;
188   while (--retries > 0) {
189         if (sys_inb(port_base + 1, &status) != OK) {
190 		printf("printer: sys_inb of %x failed\n", port_base+1);
191 		panic("sys_inb failed");
192 	}
193 	if (status & ON_LINE) {		/* printer online! */
194 		prepare_output();
195 		printer_intr(0);
196 		return EDONTREPLY;	/* we may already have sent a reply */
197 	}
198 	micro_delay(500000);		/* wait before retry */
199   }
200   /* If we reach this point, the printer was not online in time. */
201   done_status = status;
202   output_done();
203   return EDONTREPLY;
204 }
205 
206 /*===========================================================================*
207  *				output_done				     *
208  *===========================================================================*/
209 static void output_done()
210 {
211 /* Previous chunk of printing is finished.  Continue if OK and more.
212  * Otherwise, reply to caller (FS).
213  */
214   int status;
215 
216   if (!writing) return;	  	/* probably leftover interrupt */
217   if (done_status != OK) {      	/* printer error occurred */
218 	status = EIO;
219 	if ((done_status & ON_LINE) == 0) {
220 	    printf("Printer is not on line\n");
221 	} else if ((done_status & NO_PAPER)) {
222 	    printf("Printer is out of paper\n");
223 	    status = EAGAIN;
224 	} else {
225 	    printf("Printer error, status is 0x%02X\n", done_status);
226 	}
227 	/* Some characters have been printed, tell how many. */
228 	if (status == EAGAIN && user_left < orig_count) {
229 		status = orig_count - user_left;
230 	}
231 	oleft = 0;			/* cancel further output */
232   } else if (user_left != 0) {		/* not yet done, continue! */
233 	prepare_output();
234 	return;
235   } else {				/* done! report back to FS */
236 	status = orig_count;
237   }
238 
239   chardriver_reply_task(caller, write_id, status);
240 
241   writing = FALSE;			/* unmark event */
242 }
243 
244 /*===========================================================================*
245  *				printer_cancel				     *
246  *===========================================================================*/
247 static int printer_cancel(devminor_t UNUSED(minor), endpoint_t endpt,
248 	cdev_id_t id)
249 {
250 /* Cancel a print request that has already started.  Usually this means that
251  * the process doing the printing has been killed by a signal.  It is not
252  * clear if there are race conditions.  Try not to cancel the wrong process,
253  * but rely on VFS to handle the EINTR reply and de-suspension properly.
254  */
255 
256   if (writing && caller == endpt && write_id == id) {
257 	oleft = 0;		/* cancel output by interrupt handler */
258 	writing = FALSE;
259 	return EINTR;
260   }
261   return EDONTREPLY;
262 }
263 
264 /*===========================================================================*
265  *				do_probe				     *
266  *===========================================================================*/
267 static int do_probe(void)
268 {
269 /* See if there is a printer at all. */
270 
271   /* Get the base port for first printer. */
272   if(sys_readbios(LPT1_IO_PORT_ADDR, &port_base, LPT1_IO_PORT_SIZE) != OK) {
273 	panic("do_probe: sys_readbios failed");
274   }
275 
276   /* If the port is zero, the parallel port is not available at all. */
277   return (port_base != 0);
278 }
279 
280 /*===========================================================================*
281  *				do_initialize				     *
282  *===========================================================================*/
283 static void do_initialize()
284 {
285 /* Set global variables and initialize the printer. */
286   if(sys_outb(port_base + 2, INIT_PRINTER) != OK) {
287 	printf("printer: sys_outb of %x failed\n", port_base+2);
288 	panic("do_initialize: sys_outb init failed");
289   }
290   micro_delay(1000000/20);	/* easily satisfies Centronics minimum */
291   if(sys_outb(port_base + 2, PR_SELECT) != OK) {
292 	printf("printer: sys_outb of %x failed\n", port_base+2);
293 	panic("do_initialize: sys_outb select failed");
294   }
295   irq_hook_id = 0;
296   if(sys_irqsetpolicy(PRINTER_IRQ, 0, &irq_hook_id) != OK ||
297      sys_irqenable(&irq_hook_id) != OK) {
298 	panic("do_initialize: irq enabling failed");
299   }
300 }
301 
302 /*==========================================================================*
303  *		    	      printer_open				    *
304  *==========================================================================*/
305 static int printer_open(devminor_t UNUSED(minor), int UNUSED(access),
306 	endpoint_t UNUSED(user_endpt))
307 {
308 /* Initialize on first open. */
309   static int initialized = FALSE;
310 
311   if (!initialized) {
312 	do_initialize();
313 
314 	initialized = TRUE;
315   }
316 
317   return OK;
318 }
319 
320 /*==========================================================================*
321  *		    	      prepare_output				    *
322  *==========================================================================*/
323 static void prepare_output()
324 {
325 /* Start next chunk of printer output. Fetch the data from user space. */
326   int s;
327   register int chunk;
328 
329   if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf;
330 
331   s=sys_safecopyfrom(caller, grant_nr, user_vir_d, (vir_bytes) obuf,
332 	chunk);
333 
334   if(s != OK) {
335   	done_status = EFAULT;
336   	output_done();
337   	return;
338   }
339   optr = obuf;
340   oleft = chunk;
341 }
342 
343 /*===========================================================================*
344  *				printer_intr				     *
345  *===========================================================================*/
346 static void printer_intr(unsigned int UNUSED(mask))
347 {
348 /* This function does the actual output to the printer. This is called on an
349  * interrupt message sent from the generic interrupt handler that 'forwards'
350  * interrupts to this driver. The generic handler did not reenable the printer
351  * IRQ yet!
352  */
353 
354   u32_t status;
355   pvb_pair_t char_out[3];
356 
357   if (oleft == 0) {
358 	/* Nothing more to print.  Turn off printer interrupts in case they
359 	 * are level-sensitive as on the PS/2.  This should be safe even
360 	 * when the printer is busy with a previous character, because the
361 	 * interrupt status does not affect the printer.
362 	 */
363 	if(sys_outb(port_base + 2, PR_SELECT) != OK) {
364 		printf("printer: sys_outb of %x failed\n", port_base+2);
365 		panic("sys_outb failed");
366 	}
367 	if(sys_irqenable(&irq_hook_id) != OK) {
368 		panic("sys_irqenable failed");
369 	}
370 	return;
371   }
372 
373   do {
374 	/* Loop to handle fast (buffered) printers.  It is important that
375 	 * processor interrupts are not disabled here, just printer interrupts.
376 	 */
377 	if(sys_inb(port_base + 1, &status) != OK) {
378 		printf("printer: sys_inb of %x failed\n", port_base+1);
379 		panic("sys_inb failed");
380 	}
381 	if ((status & STATUS_MASK) == BUSY_STATUS) {
382 		/* Still busy with last output.  This normally happens
383 		 * immediately after doing output to an unbuffered or slow
384 		 * printer.  It may happen after a call from prepare_output or
385 		 * pr_restart, since they are not synchronized with printer
386 		 * interrupts.  It may happen after a spurious interrupt.
387 		 */
388 		if(sys_irqenable(&irq_hook_id) != OK) {
389 			panic("sys_irqenable failed");
390 		}
391 		return;
392 	}
393 	if ((status & STATUS_MASK) == NORMAL_STATUS) {
394 		/* Everything is all right.  Output another character. */
395 		pv_set(char_out[0], port_base, *optr);
396 		optr++;
397 		pv_set(char_out[1], port_base+2, ASSERT_STROBE);
398 		pv_set(char_out[2], port_base+2, NEGATE_STROBE);
399 		if(sys_voutb(char_out, 3) != OK) {
400 			/* request series of port outb */
401 			panic("sys_voutb failed");
402 		}
403 
404 		user_vir_d++;
405 		user_left--;
406 	} else {
407 		/* Error.  This would be better ignored (treat as busy). */
408 		done_status = status;
409 		output_done();
410 		if(sys_irqenable(&irq_hook_id) != OK) {
411 			panic("sys_irqenable failed");
412 		}
413 		return;
414 	}
415   }
416   while (--oleft != 0);
417 
418   /* Finished printing chunk OK. */
419   done_status = OK;
420   output_done();
421   if(sys_irqenable(&irq_hook_id) != OK) {
422 	panic("sys_irqenable failed");
423   }
424 }
425 
426