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 IO_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 "../drivers.h"
37 /* Control bits (in port_base + 2). "+" means positive logic and "-" means
38 * negative logic. Most of the signals are negative logic on the pins but
39 * many are converted to positive logic in the ports. Some manuals are
40 * misleading because they only document the pin logic.
43 * +0x02 Pin 14 -Auto Feed
44 * -0x04 Pin 16 -Initialize Printer
45 * +0x08 Pin 17 -Select Printer
48 * Auto Feed and Select Printer are always enabled. Strobe is enabled briefly
49 * when characters are output. Initialize Printer is enabled briefly when
50 * the task is started. IRQ7 is enabled when the first character is output
51 * and left enabled until output is completed (or later after certain
52 * abnormal completions).
54 #define ASSERT_STROBE 0x1D /* strobe a character to the interface */
55 #define NEGATE_STROBE 0x1C /* enable interrupt on interface */
56 #define PR_SELECT 0x0C /* select printer bit */
57 #define INIT_PRINTER 0x08 /* init printer bits */
59 /* Status bits (in port_base + 2).
62 * +0x10 Pin 13 +Select Status
63 * +0x20 Pin 12 +Out of Paper
64 * -0x40 Pin 10 -Acknowledge
67 #define BUSY_STATUS 0x10 /* printer gives this status when busy */
68 #define NO_PAPER 0x20 /* status bit saying that paper is out */
69 #define NORMAL_STATUS 0x90 /* printer gives this status when idle */
70 #define ON_LINE 0x10 /* status bit saying that printer is online */
71 #define STATUS_MASK 0xB0 /* mask to filter out status bits */
73 #define MAX_ONLINE_RETRIES 120 /* about 60s: waits 0.5s after each retry */
75 /* Centronics interface timing that must be met by software (in microsec).
77 * Strobe length: 0.5u to 100u (not sure about the upper limit).
78 * Data set up: 0.5u before strobe.
79 * Data hold: 0.5u after strobe.
80 * Init pulse length: over 200u (not sure).
82 * The strobe length is about 50u with the code here and function calls for
83 * sys_outb() - not much to spare. The 0.5u minimums will not be violated
84 * with the sys_outb() messages exchanged.
87 PRIVATE
int caller
; /* process to tell when printing done (FS) */
88 PRIVATE
int revive_pending
; /* set to true if revive is pending */
89 PRIVATE
int revive_status
; /* revive status */
90 PRIVATE
int done_status
; /* status of last output completion */
91 PRIVATE
int oleft
; /* bytes of output left in obuf */
92 PRIVATE
char obuf
[128]; /* output buffer */
93 PRIVATE
char *optr
; /* ptr to next char in obuf to print */
94 PRIVATE
int orig_count
; /* original byte count */
95 PRIVATE
int port_base
; /* I/O port for printer */
96 PRIVATE
int proc_nr
; /* user requesting the printing */
97 PRIVATE cp_grant_id_t grant_nr
; /* grant on which print happens */
98 PRIVATE
int user_left
; /* bytes of output left in user buf */
99 PRIVATE vir_bytes user_vir_g
; /* start of user buf (address or grant) */
100 PRIVATE vir_bytes user_vir_d
; /* offset in user buf */
101 PRIVATE
int user_safe
; /* address or grant? */
102 PRIVATE
int writing
; /* nonzero while write is in progress */
103 PRIVATE
int irq_hook_id
; /* id of irq hook at kernel */
105 extern int errno
; /* error number */
107 FORWARD
_PROTOTYPE( void do_cancel
, (message
*m_ptr
) );
108 FORWARD
_PROTOTYPE( void output_done
, (void) );
109 FORWARD
_PROTOTYPE( void do_write
, (message
*m_ptr
, int safe
) );
110 FORWARD
_PROTOTYPE( void do_status
, (message
*m_ptr
) );
111 FORWARD
_PROTOTYPE( void prepare_output
, (void) );
112 FORWARD
_PROTOTYPE( void do_initialize
, (void) );
113 FORWARD
_PROTOTYPE( void reply
, (int code
,int replyee
,int proc
,int status
));
114 FORWARD
_PROTOTYPE( void do_printer_output
, (void) );
115 FORWARD
_PROTOTYPE( void do_signal
, (message
*m_ptr
) );
118 /*===========================================================================*
120 *===========================================================================*/
121 PUBLIC
void main(void)
123 /* Main routine of the printer task. */
124 message pr_mess
; /* buffer for all incoming messages */
128 /* Install signal handlers. Ask PM to transform signal into message. */
129 sa
.sa_handler
= SIG_MESS
;
130 sigemptyset(&sa
.sa_mask
);
132 if (sigaction(SIGTERM
,&sa
,NULL
)<0) panic("PRN","sigaction failed", errno
);
135 receive(ANY
, &pr_mess
);
136 switch(pr_mess
.m_type
) {
138 do_initialize(); /* initialize */
141 reply(TASK_REPLY
, pr_mess
.m_source
, pr_mess
.IO_ENDPT
, OK
);
143 case DEV_WRITE_S
: do_write(&pr_mess
, 1); break;
144 case DEV_STATUS
: do_status(&pr_mess
); break;
145 case CANCEL
: do_cancel(&pr_mess
); break;
146 case HARD_INT
: do_printer_output(); break;
147 case SYS_SIG
: do_signal(&pr_mess
); break;
148 case DEV_PING
: notify(pr_mess
.m_source
); break;
149 case PROC_EVENT
: break;
151 reply(TASK_REPLY
, pr_mess
.m_source
, pr_mess
.IO_ENDPT
, EINVAL
);
157 /*===========================================================================*
159 *===========================================================================*/
160 PRIVATE
void do_signal(m_ptr
)
161 message
*m_ptr
; /* signal message */
164 sigset_t sigset
= m_ptr
->NOTIFY_ARG
;
166 /* Expect a SIGTERM signal when this server must shutdown. */
167 if (sigismember(&sigset
, SIGTERM
)) {
170 /* Ignore all other signals. */
173 /*===========================================================================*
175 *===========================================================================*/
176 PRIVATE
void do_write(m_ptr
, safe
)
177 register message
*m_ptr
; /* pointer to the newly arrived message */
178 int safe
; /* use virtual addresses or grant id's? */
180 /* The printer is used by sending DEV_WRITE messages to it. Process one. */
182 register int r
= SUSPEND
;
184 unsigned long status
;
186 /* Reject command if last write is not yet finished, the count is not
187 * positive, or the user address is bad.
189 if (writing
) r
= EIO
;
190 else if (m_ptr
->COUNT
<= 0) r
= EINVAL
;
192 /* Reply to FS, no matter what happened, possible SUSPEND caller. */
193 reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, r
);
195 /* If no errors occurred, continue printing with SUSPENDED caller.
196 * First wait until the printer is online to prevent stupid errors.
199 caller
= m_ptr
->m_source
;
200 proc_nr
= m_ptr
->IO_ENDPT
;
201 user_left
= m_ptr
->COUNT
;
202 orig_count
= m_ptr
->COUNT
;
203 user_vir_g
= (vir_bytes
) m_ptr
->ADDRESS
; /* Address or grant id. */
204 user_vir_d
= 0; /* Offset. */
205 user_safe
= safe
; /* Address or grant? */
207 grant_nr
= safe
? (cp_grant_id_t
) m_ptr
->ADDRESS
: GRANT_INVALID
;
209 retries
= MAX_ONLINE_RETRIES
+ 1;
210 while (--retries
> 0) {
211 sys_inb(port_base
+ 1, &status
);
212 if ((status
& ON_LINE
)) { /* printer online! */
217 tickdelay(30); /* wait before retry */
219 /* If we reach this point, the printer was not online in time. */
220 done_status
= status
;
225 /*===========================================================================*
227 *===========================================================================*/
228 PRIVATE
void output_done()
230 /* Previous chunk of printing is finished. Continue if OK and more.
231 * Otherwise, reply to caller (FS).
235 if (!writing
) return; /* probably leftover interrupt */
236 if (done_status
!= OK
) { /* printer error occurred */
238 if ((done_status
& ON_LINE
) == 0) {
239 printf("Printer is not on line\n");
240 } else if ((done_status
& NO_PAPER
)) {
241 printf("Printer is out of paper\n");
244 printf("Printer error, status is 0x%02X\n", done_status
);
246 /* Some characters have been printed, tell how many. */
247 if (status
== EAGAIN
&& user_left
< orig_count
) {
248 status
= orig_count
- user_left
;
250 oleft
= 0; /* cancel further output */
252 else if (user_left
!= 0) { /* not yet done, continue! */
256 else { /* done! report back to FS */
259 revive_pending
= TRUE
;
260 revive_status
= status
;
264 /*===========================================================================*
266 *===========================================================================*/
267 PRIVATE
void do_status(m_ptr
)
268 register message
*m_ptr
; /* pointer to the newly arrived message */
270 if (revive_pending
) {
271 m_ptr
->m_type
= DEV_REVIVE
; /* build message */
272 m_ptr
->REP_ENDPT
= proc_nr
;
273 m_ptr
->REP_STATUS
= revive_status
;
274 m_ptr
->REP_IO_GRANT
= grant_nr
;
276 writing
= FALSE
; /* unmark event */
277 revive_pending
= FALSE
; /* unmark event */
279 m_ptr
->m_type
= DEV_NO_STATUS
;
281 send(m_ptr
->m_source
, m_ptr
); /* send the message */
284 /*===========================================================================*
286 *===========================================================================*/
287 PRIVATE
void do_cancel(m_ptr
)
288 register message
*m_ptr
; /* pointer to the newly arrived message */
290 /* Cancel a print request that has already started. Usually this means that
291 * the process doing the printing has been killed by a signal. It is not
292 * clear if there are race conditions. Try not to cancel the wrong process,
293 * but rely on FS to handle the EINTR reply and de-suspension properly.
296 if (writing
&& m_ptr
->IO_ENDPT
== proc_nr
) {
297 oleft
= 0; /* cancel output by interrupt handler */
299 revive_pending
= FALSE
;
301 reply(TASK_REPLY
, m_ptr
->m_source
, m_ptr
->IO_ENDPT
, EINTR
);
304 /*===========================================================================*
306 *===========================================================================*/
307 PRIVATE
void reply(code
, replyee
, process
, status
)
308 int code
; /* TASK_REPLY or REVIVE */
309 int replyee
; /* destination for message (normally FS) */
310 int process
; /* which user requested the printing */
311 int status
; /* number of chars printed or error code */
313 /* Send a reply telling FS that printing has started or stopped. */
317 pr_mess
.m_type
= code
; /* TASK_REPLY or REVIVE */
318 pr_mess
.REP_STATUS
= status
; /* count or EIO */
319 pr_mess
.REP_ENDPT
= process
; /* which user does this pertain to */
320 send(replyee
, &pr_mess
); /* send the message */
323 /*===========================================================================*
325 *===========================================================================*/
326 PRIVATE
void do_initialize()
328 /* Set global variables and initialize the printer. */
329 static int initialized
= FALSE
;
330 if (initialized
) return;
333 /* Get the base port for first printer. */
334 sys_vircopy(SELF
, BIOS_SEG
, LPT1_IO_PORT_ADDR
,
335 SELF
, D
, (vir_bytes
) &port_base
, LPT1_IO_PORT_SIZE
);
336 sys_outb(port_base
+ 2, INIT_PRINTER
);
337 tickdelay(1); /* easily satisfies Centronics minimum */
338 /* was 2 millisecs; now is ~17 millisecs */
339 sys_outb(port_base
+ 2, PR_SELECT
);
341 sys_irqsetpolicy(PRINTER_IRQ
, 0, &irq_hook_id
);
342 sys_irqenable(&irq_hook_id
);
346 /*==========================================================================*
348 *==========================================================================*/
349 PRIVATE
void prepare_output()
351 /* Start next chunk of printer output. Fetch the data from user space. */
355 if ( (chunk
= user_left
) > sizeof obuf
) chunk
= sizeof obuf
;
357 s
=sys_safecopyfrom(proc_nr
, user_vir_g
, user_vir_d
,
358 (vir_bytes
) obuf
, chunk
, D
);
360 s
=sys_datacopy(proc_nr
, user_vir_g
, SELF
, (vir_bytes
) obuf
, chunk
);
364 done_status
= EFAULT
;
372 /*===========================================================================*
373 * do_printer_output *
374 *===========================================================================*/
375 PRIVATE
void do_printer_output()
377 /* This function does the actual output to the printer. This is called on
378 * a HARD_INT message sent from the generic interrupt handler that 'forwards'
379 * interrupts to this driver. The generic handler did not reenable the
383 unsigned long status
;
384 pvb_pair_t char_out
[3];
387 /* Nothing more to print. Turn off printer interrupts in case they
388 * are level-sensitive as on the PS/2. This should be safe even
389 * when the printer is busy with a previous character, because the
390 * interrupt status does not affect the printer.
392 sys_outb(port_base
+ 2, PR_SELECT
);
393 sys_irqenable(&irq_hook_id
);
398 /* Loop to handle fast (buffered) printers. It is important that
399 * processor interrupts are not disabled here, just printer interrupts.
401 (void) sys_inb(port_base
+ 1, &status
);
402 if ((status
& STATUS_MASK
) == BUSY_STATUS
) {
403 /* Still busy with last output. This normally happens
404 * immediately after doing output to an unbuffered or slow
405 * printer. It may happen after a call from prepare_output or
406 * pr_restart, since they are not synchronized with printer
407 * interrupts. It may happen after a spurious interrupt.
409 sys_irqenable(&irq_hook_id
);
412 if ((status
& STATUS_MASK
) == NORMAL_STATUS
) {
413 /* Everything is all right. Output another character. */
414 pv_set(char_out
[0], port_base
, *optr
++);
415 pv_set(char_out
[1], port_base
+2, ASSERT_STROBE
);
416 pv_set(char_out
[2], port_base
+2, NEGATE_STROBE
);
417 sys_voutb(char_out
, 3); /* request series of port outb */
422 /* Error. This would be better ignored (treat as busy). */
423 done_status
= status
;
425 sys_irqenable(&irq_hook_id
);
429 while (--oleft
!= 0);
431 /* Finished printing chunk OK. */
434 sys_irqenable(&irq_hook_id
);