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 | * The valid messages and their parameters are:
|
---|
10 | *
|
---|
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
|
---|
16 | *
|
---|
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 | * -------------------------------------------------------
|
---|
31 | *
|
---|
32 | * Note: since only 1 printer is supported, minor dev is not used at present.
|
---|
33 | */
|
---|
34 |
|
---|
35 | #include "../drivers.h"
|
---|
36 |
|
---|
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.
|
---|
41 | *
|
---|
42 | * +0x01 Pin 1 -Strobe
|
---|
43 | * +0x02 Pin 14 -Auto Feed
|
---|
44 | * -0x04 Pin 16 -Initialize Printer
|
---|
45 | * +0x08 Pin 17 -Select Printer
|
---|
46 | * +0x10 IRQ7 Enable
|
---|
47 | *
|
---|
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).
|
---|
53 | */
|
---|
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 */
|
---|
58 |
|
---|
59 | /* Status bits (in port_base + 2).
|
---|
60 | *
|
---|
61 | * -0x08 Pin 15 -Error
|
---|
62 | * +0x10 Pin 13 +Select Status
|
---|
63 | * +0x20 Pin 12 +Out of Paper
|
---|
64 | * -0x40 Pin 10 -Acknowledge
|
---|
65 | * -0x80 Pin 11 +Busy
|
---|
66 | */
|
---|
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 */
|
---|
72 |
|
---|
73 | #define MAX_ONLINE_RETRIES 120 /* about 60s: waits 0.5s after each retry */
|
---|
74 |
|
---|
75 | /* Centronics interface timing that must be met by software (in microsec).
|
---|
76 | *
|
---|
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).
|
---|
81 | *
|
---|
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.
|
---|
85 | */
|
---|
86 |
|
---|
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 int user_left; /* bytes of output left in user buf */
|
---|
98 | PRIVATE vir_bytes user_vir; /* address of remainder of user buf */
|
---|
99 | PRIVATE int writing; /* nonzero while write is in progress */
|
---|
100 | PRIVATE int irq_hook_id; /* id of irq hook at kernel */
|
---|
101 |
|
---|
102 | extern int errno; /* error number */
|
---|
103 |
|
---|
104 | FORWARD _PROTOTYPE( void do_cancel, (message *m_ptr) );
|
---|
105 | FORWARD _PROTOTYPE( void output_done, (void) );
|
---|
106 | FORWARD _PROTOTYPE( void do_write, (message *m_ptr) );
|
---|
107 | FORWARD _PROTOTYPE( void do_status, (message *m_ptr) );
|
---|
108 | FORWARD _PROTOTYPE( void prepare_output, (void) );
|
---|
109 | FORWARD _PROTOTYPE( void do_initialize, (void) );
|
---|
110 | FORWARD _PROTOTYPE( void reply, (int code,int replyee,int proc,int status));
|
---|
111 | FORWARD _PROTOTYPE( void do_printer_output, (void) );
|
---|
112 | FORWARD _PROTOTYPE( void do_signal, (message *m_ptr) );
|
---|
113 |
|
---|
114 |
|
---|
115 | /*===========================================================================*
|
---|
116 | * printer_task *
|
---|
117 | *===========================================================================*/
|
---|
118 | PUBLIC void main(void)
|
---|
119 | {
|
---|
120 | /* Main routine of the printer task. */
|
---|
121 | message pr_mess; /* buffer for all incoming messages */
|
---|
122 | struct sigaction sa;
|
---|
123 | int s;
|
---|
124 |
|
---|
125 | /* Install signal handlers. Ask PM to transform signal into message. */
|
---|
126 | sa.sa_handler = SIG_MESS;
|
---|
127 | sigemptyset(&sa.sa_mask);
|
---|
128 | sa.sa_flags = 0;
|
---|
129 | if (sigaction(SIGTERM,&sa,NULL)<0) panic("PRN","sigaction failed", errno);
|
---|
130 |
|
---|
131 | while (TRUE) {
|
---|
132 | receive(ANY, &pr_mess);
|
---|
133 | switch(pr_mess.m_type) {
|
---|
134 | case DEV_OPEN:
|
---|
135 | do_initialize(); /* initialize */
|
---|
136 | /* fall through */
|
---|
137 | case DEV_CLOSE:
|
---|
138 | reply(TASK_REPLY, pr_mess.m_source, pr_mess.IO_ENDPT, OK);
|
---|
139 | break;
|
---|
140 | case DEV_WRITE: do_write(&pr_mess); break;
|
---|
141 | case DEV_STATUS: do_status(&pr_mess); break;
|
---|
142 | case CANCEL: do_cancel(&pr_mess); break;
|
---|
143 | case HARD_INT: do_printer_output(); break;
|
---|
144 | case SYS_SIG: do_signal(&pr_mess); break;
|
---|
145 | case DEV_PING: notify(pr_mess.m_source); break;
|
---|
146 | case PROC_EVENT: break;
|
---|
147 | default:
|
---|
148 | reply(TASK_REPLY, pr_mess.m_source, pr_mess.IO_ENDPT, EINVAL);
|
---|
149 | }
|
---|
150 | }
|
---|
151 | }
|
---|
152 |
|
---|
153 |
|
---|
154 | /*===========================================================================*
|
---|
155 | * do_signal *
|
---|
156 | *===========================================================================*/
|
---|
157 | PRIVATE void do_signal(m_ptr)
|
---|
158 | message *m_ptr; /* signal message */
|
---|
159 | {
|
---|
160 | int sig;
|
---|
161 | sigset_t sigset = m_ptr->NOTIFY_ARG;
|
---|
162 |
|
---|
163 | /* Expect a SIGTERM signal when this server must shutdown. */
|
---|
164 | if (sigismember(&sigset, SIGTERM)) {
|
---|
165 | exit(0);
|
---|
166 | }
|
---|
167 | /* Ignore all other signals. */
|
---|
168 | }
|
---|
169 |
|
---|
170 | /*===========================================================================*
|
---|
171 | * do_write *
|
---|
172 | *===========================================================================*/
|
---|
173 | PRIVATE void do_write(m_ptr)
|
---|
174 | register message *m_ptr; /* pointer to the newly arrived message */
|
---|
175 | {
|
---|
176 | /* The printer is used by sending DEV_WRITE messages to it. Process one. */
|
---|
177 |
|
---|
178 | register int r = SUSPEND;
|
---|
179 | int retries;
|
---|
180 | unsigned long status;
|
---|
181 |
|
---|
182 | /* Reject command if last write is not yet finished, the count is not
|
---|
183 | * positive, or the user address is bad.
|
---|
184 | */
|
---|
185 | if (writing) r = EIO;
|
---|
186 | else if (m_ptr->COUNT <= 0) r = EINVAL;
|
---|
187 |
|
---|
188 | /* Reply to FS, no matter what happened, possible SUSPEND caller. */
|
---|
189 | reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, r);
|
---|
190 |
|
---|
191 | /* If no errors occurred, continue printing with SUSPENDED caller.
|
---|
192 | * First wait until the printer is online to prevent stupid errors.
|
---|
193 | */
|
---|
194 | if (SUSPEND == r) {
|
---|
195 | caller = m_ptr->m_source;
|
---|
196 | proc_nr = m_ptr->IO_ENDPT;
|
---|
197 | user_left = m_ptr->COUNT;
|
---|
198 | orig_count = m_ptr->COUNT;
|
---|
199 | user_vir = (vir_bytes) m_ptr->ADDRESS;
|
---|
200 | writing = TRUE;
|
---|
201 |
|
---|
202 | retries = MAX_ONLINE_RETRIES + 1;
|
---|
203 | while (--retries > 0) {
|
---|
204 | sys_inb(port_base + 1, &status);
|
---|
205 | if ((status & ON_LINE)) { /* printer online! */
|
---|
206 | prepare_output();
|
---|
207 | do_printer_output();
|
---|
208 | return;
|
---|
209 | }
|
---|
210 | tickdelay(30); /* wait before retry */
|
---|
211 | }
|
---|
212 | /* If we reach this point, the printer was not online in time. */
|
---|
213 | done_status = status;
|
---|
214 | output_done();
|
---|
215 | }
|
---|
216 | }
|
---|
217 |
|
---|
218 | /*===========================================================================*
|
---|
219 | * output_done *
|
---|
220 | *===========================================================================*/
|
---|
221 | PRIVATE void output_done()
|
---|
222 | {
|
---|
223 | /* Previous chunk of printing is finished. Continue if OK and more.
|
---|
224 | * Otherwise, reply to caller (FS).
|
---|
225 | */
|
---|
226 | register int status;
|
---|
227 |
|
---|
228 | if (!writing) return; /* probably leftover interrupt */
|
---|
229 | if (done_status != OK) { /* printer error occurred */
|
---|
230 | status = EIO;
|
---|
231 | if ((done_status & ON_LINE) == 0) {
|
---|
232 | printf("Printer is not on line\n");
|
---|
233 | } else if ((done_status & NO_PAPER)) {
|
---|
234 | printf("Printer is out of paper\n");
|
---|
235 | status = EAGAIN;
|
---|
236 | } else {
|
---|
237 | printf("Printer error, status is 0x%02X\n", done_status);
|
---|
238 | }
|
---|
239 | /* Some characters have been printed, tell how many. */
|
---|
240 | if (status == EAGAIN && user_left < orig_count) {
|
---|
241 | status = orig_count - user_left;
|
---|
242 | }
|
---|
243 | oleft = 0; /* cancel further output */
|
---|
244 | }
|
---|
245 | else if (user_left != 0) { /* not yet done, continue! */
|
---|
246 | prepare_output();
|
---|
247 | return;
|
---|
248 | }
|
---|
249 | else { /* done! report back to FS */
|
---|
250 | status = orig_count;
|
---|
251 | }
|
---|
252 | revive_pending = TRUE;
|
---|
253 | revive_status = status;
|
---|
254 | notify(caller);
|
---|
255 | }
|
---|
256 |
|
---|
257 | /*===========================================================================*
|
---|
258 | * do_status *
|
---|
259 | *===========================================================================*/
|
---|
260 | PRIVATE void do_status(m_ptr)
|
---|
261 | register message *m_ptr; /* pointer to the newly arrived message */
|
---|
262 | {
|
---|
263 | if (revive_pending) {
|
---|
264 | m_ptr->m_type = DEV_REVIVE; /* build message */
|
---|
265 | m_ptr->REP_ENDPT = proc_nr;
|
---|
266 | m_ptr->REP_STATUS = revive_status;
|
---|
267 |
|
---|
268 | writing = FALSE; /* unmark event */
|
---|
269 | revive_pending = FALSE; /* unmark event */
|
---|
270 | } else {
|
---|
271 | m_ptr->m_type = DEV_NO_STATUS;
|
---|
272 | }
|
---|
273 | send(m_ptr->m_source, m_ptr); /* send the message */
|
---|
274 | }
|
---|
275 |
|
---|
276 | /*===========================================================================*
|
---|
277 | * do_cancel *
|
---|
278 | *===========================================================================*/
|
---|
279 | PRIVATE void do_cancel(m_ptr)
|
---|
280 | register message *m_ptr; /* pointer to the newly arrived message */
|
---|
281 | {
|
---|
282 | /* Cancel a print request that has already started. Usually this means that
|
---|
283 | * the process doing the printing has been killed by a signal. It is not
|
---|
284 | * clear if there are race conditions. Try not to cancel the wrong process,
|
---|
285 | * but rely on FS to handle the EINTR reply and de-suspension properly.
|
---|
286 | */
|
---|
287 |
|
---|
288 | if (writing && m_ptr->IO_ENDPT == proc_nr) {
|
---|
289 | oleft = 0; /* cancel output by interrupt handler */
|
---|
290 | writing = FALSE;
|
---|
291 | revive_pending = FALSE;
|
---|
292 | }
|
---|
293 | reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, EINTR);
|
---|
294 | }
|
---|
295 |
|
---|
296 | /*===========================================================================*
|
---|
297 | * reply *
|
---|
298 | *===========================================================================*/
|
---|
299 | PRIVATE void reply(code, replyee, process, status)
|
---|
300 | int code; /* TASK_REPLY or REVIVE */
|
---|
301 | int replyee; /* destination for message (normally FS) */
|
---|
302 | int process; /* which user requested the printing */
|
---|
303 | int status; /* number of chars printed or error code */
|
---|
304 | {
|
---|
305 | /* Send a reply telling FS that printing has started or stopped. */
|
---|
306 |
|
---|
307 | message pr_mess;
|
---|
308 |
|
---|
309 | pr_mess.m_type = code; /* TASK_REPLY or REVIVE */
|
---|
310 | pr_mess.REP_STATUS = status; /* count or EIO */
|
---|
311 | pr_mess.REP_ENDPT = process; /* which user does this pertain to */
|
---|
312 | send(replyee, &pr_mess); /* send the message */
|
---|
313 | }
|
---|
314 |
|
---|
315 | /*===========================================================================*
|
---|
316 | * do_initialize *
|
---|
317 | *===========================================================================*/
|
---|
318 | PRIVATE void do_initialize()
|
---|
319 | {
|
---|
320 | /* Set global variables and initialize the printer. */
|
---|
321 | static int initialized = FALSE;
|
---|
322 | if (initialized) return;
|
---|
323 | initialized = TRUE;
|
---|
324 |
|
---|
325 | /* Get the base port for first printer. */
|
---|
326 | sys_vircopy(SELF, BIOS_SEG, LPT1_IO_PORT_ADDR,
|
---|
327 | SELF, D, (vir_bytes) &port_base, LPT1_IO_PORT_SIZE);
|
---|
328 | sys_outb(port_base + 2, INIT_PRINTER);
|
---|
329 | tickdelay(1); /* easily satisfies Centronics minimum */
|
---|
330 | /* was 2 millisecs; now is ~17 millisecs */
|
---|
331 | sys_outb(port_base + 2, PR_SELECT);
|
---|
332 | irq_hook_id = 0;
|
---|
333 | sys_irqsetpolicy(PRINTER_IRQ, 0, &irq_hook_id);
|
---|
334 | sys_irqenable(&irq_hook_id);
|
---|
335 |
|
---|
336 | }
|
---|
337 |
|
---|
338 | /*==========================================================================*
|
---|
339 | * prepare_output *
|
---|
340 | *==========================================================================*/
|
---|
341 | PRIVATE void prepare_output()
|
---|
342 | {
|
---|
343 | /* Start next chunk of printer output. Fetch the data from user space. */
|
---|
344 |
|
---|
345 | register int chunk;
|
---|
346 |
|
---|
347 | if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf;
|
---|
348 | if (OK!=sys_datacopy(proc_nr, user_vir, SELF, (vir_bytes) obuf, chunk)) {
|
---|
349 | done_status = EFAULT;
|
---|
350 | output_done();
|
---|
351 | return;
|
---|
352 | }
|
---|
353 | optr = obuf;
|
---|
354 | oleft = chunk;
|
---|
355 | }
|
---|
356 |
|
---|
357 | /*===========================================================================*
|
---|
358 | * do_printer_output *
|
---|
359 | *===========================================================================*/
|
---|
360 | PRIVATE void do_printer_output()
|
---|
361 | {
|
---|
362 | /* This function does the actual output to the printer. This is called on
|
---|
363 | * a HARD_INT message sent from the generic interrupt handler that 'forwards'
|
---|
364 | * interrupts to this driver. The generic handler did not reenable the
|
---|
365 | * printer IRQ yet!
|
---|
366 | */
|
---|
367 |
|
---|
368 | unsigned long status;
|
---|
369 | pvb_pair_t char_out[3];
|
---|
370 |
|
---|
371 | if (oleft == 0) {
|
---|
372 | /* Nothing more to print. Turn off printer interrupts in case they
|
---|
373 | * are level-sensitive as on the PS/2. This should be safe even
|
---|
374 | * when the printer is busy with a previous character, because the
|
---|
375 | * interrupt status does not affect the printer.
|
---|
376 | */
|
---|
377 | sys_outb(port_base + 2, PR_SELECT);
|
---|
378 | sys_irqenable(&irq_hook_id);
|
---|
379 | return;
|
---|
380 | }
|
---|
381 |
|
---|
382 | do {
|
---|
383 | /* Loop to handle fast (buffered) printers. It is important that
|
---|
384 | * processor interrupts are not disabled here, just printer interrupts.
|
---|
385 | */
|
---|
386 | (void) sys_inb(port_base + 1, &status);
|
---|
387 | if ((status & STATUS_MASK) == BUSY_STATUS) {
|
---|
388 | /* Still busy with last output. This normally happens
|
---|
389 | * immediately after doing output to an unbuffered or slow
|
---|
390 | * printer. It may happen after a call from prepare_output or
|
---|
391 | * pr_restart, since they are not synchronized with printer
|
---|
392 | * interrupts. It may happen after a spurious interrupt.
|
---|
393 | */
|
---|
394 | sys_irqenable(&irq_hook_id);
|
---|
395 | return;
|
---|
396 | }
|
---|
397 | if ((status & STATUS_MASK) == NORMAL_STATUS) {
|
---|
398 | /* Everything is all right. Output another character. */
|
---|
399 | pv_set(char_out[0], port_base, *optr++);
|
---|
400 | pv_set(char_out[1], port_base+2, ASSERT_STROBE);
|
---|
401 | pv_set(char_out[2], port_base+2, NEGATE_STROBE);
|
---|
402 | sys_voutb(char_out, 3); /* request series of port outb */
|
---|
403 |
|
---|
404 | user_vir++;
|
---|
405 | user_left--;
|
---|
406 | } else {
|
---|
407 | /* Error. This would be better ignored (treat as busy). */
|
---|
408 | done_status = status;
|
---|
409 | output_done();
|
---|
410 | sys_irqenable(&irq_hook_id);
|
---|
411 | return;
|
---|
412 | }
|
---|
413 | }
|
---|
414 | while (--oleft != 0);
|
---|
415 |
|
---|
416 | /* Finished printing chunk OK. */
|
---|
417 | done_status = OK;
|
---|
418 | output_done();
|
---|
419 | sys_irqenable(&irq_hook_id);
|
---|
420 | }
|
---|
421 |
|
---|