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.
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)
9 * The valid messages and their parameters are:
11 * DEV_OPEN: initializes the printer
12 * DEV_CLOSE: does nothing
13 * HARD_INT: interrupt handler has finished current chunk of output
14 * DEV_WRITE: a process wants to write on a terminal
15 * CANCEL: terminate a previous incomplete system call immediately
17 * m_type TTY_LINE USER_ENDPT COUNT ADDRESS
18 * |-------------+---------+---------+---------+---------|
19 * | DEV_OPEN | | | | |
20 * |-------------+---------+---------+---------+---------|
21 * | DEV_CLOSE | | proc nr | | |
22 * -------------------------------------------------------
23 * | HARD_INT | | | | |
24 * |-------------+---------+---------+---------+---------|
25 * | SYS_EVENT | | | | |
26 * |-------------+---------+---------+---------+---------|
27 * | DEV_WRITE |minor dev| proc nr | count | buf ptr |
28 * |-------------+---------+---------+---------+---------|
29 * | CANCEL |minor dev| proc nr | | |
30 * -------------------------------------------------------
32 * Note: since only 1 printer is supported, minor dev is not used at present.
35 #include <minix/endpoint.h>
36 #include <minix/drivers.h>
37 #include <minix/chardriver.h>
39 /* Control bits (in port_base + 2). "+" means positive logic and "-" means
40 * negative logic. Most of the signals are negative logic on the pins but
41 * many are converted to positive logic in the ports. Some manuals are
42 * misleading because they only document the pin logic.
45 * +0x02 Pin 14 -Auto Feed
46 * -0x04 Pin 16 -Initialize Printer
47 * +0x08 Pin 17 -Select Printer
50 * Auto Feed and Select Printer are always enabled. Strobe is enabled briefly
51 * when characters are output. Initialize Printer is enabled briefly when
52 * the task is started. IRQ7 is enabled when the first character is output
53 * and left enabled until output is completed (or later after certain
54 * abnormal completions).
56 #define ASSERT_STROBE 0x1D /* strobe a character to the interface */
57 #define NEGATE_STROBE 0x1C /* enable interrupt on interface */
58 #define PR_SELECT 0x0C /* select printer bit */
59 #define INIT_PRINTER 0x08 /* init printer bits */
61 /* Status bits (in port_base + 2).
64 * +0x10 Pin 13 +Select Status
65 * +0x20 Pin 12 +Out of Paper
66 * -0x40 Pin 10 -Acknowledge
69 #define BUSY_STATUS 0x10 /* printer gives this status when busy */
70 #define NO_PAPER 0x20 /* status bit saying that paper is out */
71 #define NORMAL_STATUS 0x90 /* printer gives this status when idle */
72 #define ON_LINE 0x10 /* status bit saying that printer is online */
73 #define STATUS_MASK 0xB0 /* mask to filter out status bits */
75 #define MAX_ONLINE_RETRIES 120 /* about 60s: waits 0.5s after each retry */
77 /* Centronics interface timing that must be met by software (in microsec).
79 * Strobe length: 0.5u to 100u (not sure about the upper limit).
80 * Data set up: 0.5u before strobe.
81 * Data hold: 0.5u after strobe.
82 * Init pulse length: over 200u (not sure).
84 * The strobe length is about 50u with the code here and function calls for
85 * sys_outb() - not much to spare. The 0.5u minimums will not be violated
86 * with the sys_outb() messages exchanged.
89 static endpoint_t caller
; /* process to tell when printing done (FS) */
90 static int revive_pending
; /* set to true if revive is pending */
91 static int revive_status
; /* revive status */
92 static int done_status
; /* status of last output completion */
93 static int oleft
; /* bytes of output left in obuf */
94 static unsigned char obuf
[128]; /* output buffer */
95 static unsigned const char *optr
; /* ptr to next char in obuf to print */
96 static int orig_count
; /* original byte count */
97 static int port_base
; /* I/O port for printer */
98 static endpoint_t proc_nr
; /* user requesting the printing */
99 static cp_grant_id_t grant_nr
; /* grant on which print happens */
100 static int user_left
; /* bytes of output left in user buf */
101 static vir_bytes user_vir_d
; /* offset in user buf */
102 int writing
; /* nonzero while write is in progress */
103 static int irq_hook_id
; /* id of irq hook at kernel */
105 static void do_cancel(message
*m_ptr
);
106 static void output_done(void);
107 static void do_write(message
*m_ptr
);
108 static void do_status(message
*m_ptr
);
109 static void prepare_output(void);
110 static int do_probe(void);
111 static void do_initialize(void);
112 static void reply(int code
,int replyee
,int proc
,int status
);
113 static void do_printer_output(void);
115 /* SEF functions and variables. */
116 static void sef_local_startup(void);
117 static int sef_cb_init_fresh(int type
, sef_init_info_t
*info
);
118 EXTERN
int sef_cb_lu_prepare(int state
);
119 EXTERN
int sef_cb_lu_state_isvalid(int state
);
120 EXTERN
void sef_cb_lu_state_dump(int state
);
121 int is_status_msg_expected
= FALSE
;
123 /*===========================================================================*
125 *===========================================================================*/
128 /* Main routine of the printer task. */
129 message pr_mess
; /* buffer for all incoming messages */
132 /* SEF local startup. */
136 if(driver_receive(ANY
, &pr_mess
, &ipc_status
) != OK
) {
137 panic("driver_receive failed");
140 if (is_ipc_notify(ipc_status
)) {
141 switch (_ENDPOINT_P(pr_mess
.m_source
)) {
146 reply(TASK_REPLY
, pr_mess
.m_source
,
147 pr_mess
.USER_ENDPT
, EINVAL
);
152 switch(pr_mess
.m_type
) {
154 do_initialize(); /* initialize */
157 reply(TASK_REPLY
, pr_mess
.m_source
, pr_mess
.USER_ENDPT
, OK
);
159 case DEV_WRITE_S
: do_write(&pr_mess
); break;
160 case DEV_STATUS
: do_status(&pr_mess
); break;
161 case CANCEL
: do_cancel(&pr_mess
); break;
163 reply(TASK_REPLY
, pr_mess
.m_source
, pr_mess
.USER_ENDPT
,
169 /*===========================================================================*
170 * sef_local_startup *
171 *===========================================================================*/
172 static void sef_local_startup()
174 /* Register init callbacks. */
175 sef_setcb_init_fresh(sef_cb_init_fresh
);
176 sef_setcb_init_lu(sef_cb_init_fresh
);
177 sef_setcb_init_restart(sef_cb_init_fresh
);
179 /* Register live update callbacks. */
180 sef_setcb_lu_prepare(sef_cb_lu_prepare
);
181 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid
);
182 sef_setcb_lu_state_dump(sef_cb_lu_state_dump
);
184 /* Register signal callbacks. */
185 sef_setcb_signal_handler(sef_cb_signal_handler_term
);
187 /* Let SEF perform startup. */
191 /*===========================================================================*
192 * sef_cb_init_fresh *
193 *===========================================================================*/
194 static int sef_cb_init_fresh(int UNUSED(type
), sef_init_info_t
*UNUSED(info
))
196 /* Initialize the printer driver. */
198 /* If no printer is present, do not start. */
200 return ENODEV
; /* arbitrary error code */
202 /* Announce we are up! */
203 chardriver_announce();
208 /*===========================================================================*
210 *===========================================================================*/
211 static void do_write(m_ptr
)
212 register message
*m_ptr
; /* pointer to the newly arrived message */
214 /* The printer is used by sending DEV_WRITE messages to it. Process one. */
216 register int r
= SUSPEND
;
220 /* Reject command if last write is not yet finished, the count is not
221 * positive, or the user address is bad.
223 if (writing
) r
= EIO
;
224 else if (m_ptr
->COUNT
<= 0) r
= EINVAL
;
226 /* Reply to FS, no matter what happened, possible SUSPEND caller. */
227 reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->USER_ENDPT
, r
);
229 /* If no errors occurred, continue printing with SUSPENDED caller.
230 * First wait until the printer is online to prevent stupid errors.
233 caller
= m_ptr
->m_source
;
234 proc_nr
= m_ptr
->USER_ENDPT
;
235 user_left
= m_ptr
->COUNT
;
236 orig_count
= m_ptr
->COUNT
;
237 user_vir_d
= 0; /* Offset. */
239 grant_nr
= (cp_grant_id_t
) m_ptr
->IO_GRANT
;
241 retries
= MAX_ONLINE_RETRIES
+ 1;
242 while (--retries
> 0) {
243 if(sys_inb(port_base
+ 1, &status
) != OK
) {
244 printf("printer: sys_inb of %x failed\n", port_base
+1);
245 panic("sys_inb failed");
247 if ((status
& ON_LINE
)) { /* printer online! */
252 micro_delay(500000); /* wait before retry */
254 /* If we reach this point, the printer was not online in time. */
255 done_status
= status
;
260 /*===========================================================================*
262 *===========================================================================*/
263 static void output_done()
265 /* Previous chunk of printing is finished. Continue if OK and more.
266 * Otherwise, reply to caller (FS).
270 if (!writing
) return; /* probably leftover interrupt */
271 if (done_status
!= OK
) { /* printer error occurred */
273 if ((done_status
& ON_LINE
) == 0) {
274 printf("Printer is not on line\n");
275 } else if ((done_status
& NO_PAPER
)) {
276 printf("Printer is out of paper\n");
279 printf("Printer error, status is 0x%02X\n", done_status
);
281 /* Some characters have been printed, tell how many. */
282 if (status
== EAGAIN
&& user_left
< orig_count
) {
283 status
= orig_count
- user_left
;
285 oleft
= 0; /* cancel further output */
287 else if (user_left
!= 0) { /* not yet done, continue! */
291 else { /* done! report back to FS */
294 is_status_msg_expected
= TRUE
;
295 revive_pending
= TRUE
;
296 revive_status
= status
;
300 /*===========================================================================*
302 *===========================================================================*/
303 static void do_status(m_ptr
)
304 register message
*m_ptr
; /* pointer to the newly arrived message */
306 if (revive_pending
) {
307 m_ptr
->m_type
= DEV_REVIVE
; /* build message */
308 m_ptr
->REP_ENDPT
= proc_nr
;
309 m_ptr
->REP_STATUS
= revive_status
;
310 m_ptr
->REP_IO_GRANT
= grant_nr
;
312 writing
= FALSE
; /* unmark event */
313 revive_pending
= FALSE
; /* unmark event */
315 m_ptr
->m_type
= DEV_NO_STATUS
;
317 is_status_msg_expected
= FALSE
;
319 send(m_ptr
->m_source
, m_ptr
); /* send the message */
322 /*===========================================================================*
324 *===========================================================================*/
325 static void do_cancel(m_ptr
)
326 register message
*m_ptr
; /* pointer to the newly arrived message */
328 /* Cancel a print request that has already started. Usually this means that
329 * the process doing the printing has been killed by a signal. It is not
330 * clear if there are race conditions. Try not to cancel the wrong process,
331 * but rely on FS to handle the EINTR reply and de-suspension properly.
334 if (writing
&& m_ptr
->USER_ENDPT
== proc_nr
) {
335 oleft
= 0; /* cancel output by interrupt handler */
337 revive_pending
= FALSE
;
339 reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->USER_ENDPT
, EINTR
);
342 /*===========================================================================*
344 *===========================================================================*/
345 static void reply(code
, replyee
, process
, status
)
346 int code
; /* TASK_REPLY or REVIVE */
347 int replyee
; /* destination for message (normally FS) */
348 int process
; /* which user requested the printing */
349 int status
; /* number of chars printed or error code */
351 /* Send a reply telling FS that printing has started or stopped. */
355 pr_mess
.m_type
= code
; /* TASK_REPLY or REVIVE */
356 pr_mess
.REP_STATUS
= status
; /* count or EIO */
357 pr_mess
.REP_ENDPT
= process
; /* which user does this pertain to */
358 send(replyee
, &pr_mess
); /* send the message */
361 /*===========================================================================*
363 *===========================================================================*/
364 static int do_probe(void)
366 /* See if there is a printer at all. */
368 /* Get the base port for first printer. */
369 if(sys_readbios(LPT1_IO_PORT_ADDR
, &port_base
, LPT1_IO_PORT_SIZE
) != OK
) {
370 panic("do_initialize: sys_readbios failed");
373 /* If the port is zero, the parallel port is not available at all. */
374 return (port_base
!= 0);
377 /*===========================================================================*
379 *===========================================================================*/
380 static void do_initialize()
382 /* Set global variables and initialize the printer. */
383 static int initialized
= FALSE
;
384 if (initialized
) return;
387 if(sys_outb(port_base
+ 2, INIT_PRINTER
) != OK
) {
388 printf("printer: sys_outb of %x failed\n", port_base
+2);
389 panic("do_initialize: sys_outb init failed");
391 micro_delay(1000000/20); /* easily satisfies Centronics minimum */
392 if(sys_outb(port_base
+ 2, PR_SELECT
) != OK
) {
393 printf("printer: sys_outb of %x failed\n", port_base
+2);
394 panic("do_initialize: sys_outb select failed");
397 if(sys_irqsetpolicy(PRINTER_IRQ
, 0, &irq_hook_id
) != OK
||
398 sys_irqenable(&irq_hook_id
) != OK
) {
399 panic("do_initialize: irq enabling failed");
403 /*==========================================================================*
405 *==========================================================================*/
406 static void prepare_output()
408 /* Start next chunk of printer output. Fetch the data from user space. */
412 if ( (chunk
= user_left
) > sizeof obuf
) chunk
= sizeof obuf
;
414 s
=sys_safecopyfrom(caller
, grant_nr
, user_vir_d
, (vir_bytes
) obuf
,
418 done_status
= EFAULT
;
426 /*===========================================================================*
427 * do_printer_output *
428 *===========================================================================*/
429 static void do_printer_output()
431 /* This function does the actual output to the printer. This is called on an
432 * interrupt message sent from the generic interrupt handler that 'forwards'
433 * interrupts to this driver. The generic handler did not reenable the printer
438 pvb_pair_t char_out
[3];
441 /* Nothing more to print. Turn off printer interrupts in case they
442 * are level-sensitive as on the PS/2. This should be safe even
443 * when the printer is busy with a previous character, because the
444 * interrupt status does not affect the printer.
446 if(sys_outb(port_base
+ 2, PR_SELECT
) != OK
) {
447 printf("printer: sys_outb of %x failed\n", port_base
+2);
448 panic("sys_outb failed");
450 if(sys_irqenable(&irq_hook_id
) != OK
) {
451 panic("sys_irqenable failed");
457 /* Loop to handle fast (buffered) printers. It is important that
458 * processor interrupts are not disabled here, just printer interrupts.
460 if(sys_inb(port_base
+ 1, &status
) != OK
) {
461 printf("printer: sys_inb of %x failed\n", port_base
+1);
462 panic("sys_inb failed");
464 if ((status
& STATUS_MASK
) == BUSY_STATUS
) {
465 /* Still busy with last output. This normally happens
466 * immediately after doing output to an unbuffered or slow
467 * printer. It may happen after a call from prepare_output or
468 * pr_restart, since they are not synchronized with printer
469 * interrupts. It may happen after a spurious interrupt.
471 if(sys_irqenable(&irq_hook_id
) != OK
) {
472 panic("sys_irqenable failed");
476 if ((status
& STATUS_MASK
) == NORMAL_STATUS
) {
477 /* Everything is all right. Output another character. */
478 pv_set(char_out
[0], port_base
, *optr
);
480 pv_set(char_out
[1], port_base
+2, ASSERT_STROBE
);
481 pv_set(char_out
[2], port_base
+2, NEGATE_STROBE
);
482 if(sys_voutb(char_out
, 3) != OK
) {
483 /* request series of port outb */
484 panic("sys_voutb failed");
490 /* Error. This would be better ignored (treat as busy). */
491 done_status
= status
;
493 if(sys_irqenable(&irq_hook_id
) != OK
) {
494 panic("sys_irqenable failed");
499 while (--oleft
!= 0);
501 /* Finished printing chunk OK. */
504 if(sys_irqenable(&irq_hook_id
) != OK
) {
505 panic("sys_irqenable failed");