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 PROC_NR 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.PROC_NR, 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 default:
147 reply(TASK_REPLY, pr_mess.m_source, pr_mess.PROC_NR, EINVAL);
148 }
149 }
150 }
151
152
153 /*===========================================================================*
154 * do_signal *
155 *===========================================================================*/
156 PRIVATE void do_signal(m_ptr)
157 message *m_ptr; /* signal message */
158 {
159 int sig;
160 sigset_t sigset = m_ptr->NOTIFY_ARG;
161
162 /* Expect a SIGTERM signal when this server must shutdown. */
163 if (sigismember(&sigset, SIGTERM)) {
164 exit(0);
165 }
166 /* Ignore all other signals. */
167 }
168
169 /*===========================================================================*
170 * do_write *
171 *===========================================================================*/
172 PRIVATE void do_write(m_ptr)
173 register message *m_ptr; /* pointer to the newly arrived message */
174 {
175 /* The printer is used by sending DEV_WRITE messages to it. Process one. */
176
177 register int r = SUSPEND;
178 int retries;
179 int status;
180
181 /* Reject command if last write is not yet finished, the count is not
182 * positive, or the user address is bad.
183 */
184 if (writing) r = EIO;
185 else if (m_ptr->COUNT <= 0) r = EINVAL;
186
187 /* Reply to FS, no matter what happened, possible SUSPEND caller. */
188 reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r);
189
190 /* If no errors occurred, continue printing with SUSPENDED caller.
191 * First wait until the printer is online to prevent stupid errors.
192 */
193 if (SUSPEND == r) {
194 caller = m_ptr->m_source;
195 proc_nr = m_ptr->PROC_NR;
196 user_left = m_ptr->COUNT;
197 orig_count = m_ptr->COUNT;
198 user_vir = (vir_bytes) m_ptr->ADDRESS;
199 writing = TRUE;
200
201 retries = MAX_ONLINE_RETRIES + 1;
202 while (--retries > 0) {
203 sys_inb(port_base + 1, &status);
204 if ((status & ON_LINE)) { /* printer online! */
205 prepare_output();
206 do_printer_output();
207 return;
208 }
209 tickdelay(30); /* wait before retry */
210 }
211 /* If we reach this point, the printer was not online in time. */
212 done_status = status;
213 output_done();
214 }
215 }
216
217 /*===========================================================================*
218 * output_done *
219 *===========================================================================*/
220 PRIVATE void output_done()
221 {
222 /* Previous chunk of printing is finished. Continue if OK and more.
223 * Otherwise, reply to caller (FS).
224 */
225 register int status;
226
227 if (!writing) return; /* probably leftover interrupt */
228 if (done_status != OK) { /* printer error occurred */
229 status = EIO;
230 if ((done_status & ON_LINE) == 0) {
231 printf("Printer is not on line\n");
232 } else if ((done_status & NO_PAPER)) {
233 printf("Printer is out of paper\n");
234 status = EAGAIN;
235 } else {
236 printf("Printer error, status is 0x%02X\n", done_status);
237 }
238 /* Some characters have been printed, tell how many. */
239 if (status == EAGAIN && user_left < orig_count) {
240 status = orig_count - user_left;
241 }
242 oleft = 0; /* cancel further output */
243 }
244 else if (user_left != 0) { /* not yet done, continue! */
245 prepare_output();
246 return;
247 }
248 else { /* done! report back to FS */
249 status = orig_count;
250 }
251 revive_pending = TRUE;
252 revive_status = status;
253 notify(caller);
254 }
255
256 /*===========================================================================*
257 * do_status *
258 *===========================================================================*/
259 PRIVATE void do_status(m_ptr)
260 register message *m_ptr; /* pointer to the newly arrived message */
261 {
262 if (revive_pending) {
263 m_ptr->m_type = DEV_REVIVE; /* build message */
264 m_ptr->REP_PROC_NR = proc_nr;
265 m_ptr->REP_STATUS = revive_status;
266
267 writing = FALSE; /* unmark event */
268 revive_pending = FALSE; /* unmark event */
269 } else {
270 m_ptr->m_type = DEV_NO_STATUS;
271 }
272 send(m_ptr->m_source, m_ptr); /* send the message */
273 }
274
275 /*===========================================================================*
276 * do_cancel *
277 *===========================================================================*/
278 PRIVATE void do_cancel(m_ptr)
279 register message *m_ptr; /* pointer to the newly arrived message */
280 {
281 /* Cancel a print request that has already started. Usually this means that
282 * the process doing the printing has been killed by a signal. It is not
283 * clear if there are race conditions. Try not to cancel the wrong process,
284 * but rely on FS to handle the EINTR reply and de-suspension properly.
285 */
286
287 if (writing && m_ptr->PROC_NR == proc_nr) {
288 oleft = 0; /* cancel output by interrupt handler */
289 writing = FALSE;
290 revive_pending = FALSE;
291 }
292 reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR);
293 }
294
295 /*===========================================================================*
296 * reply *
297 *===========================================================================*/
298 PRIVATE void reply(code, replyee, process, status)
299 int code; /* TASK_REPLY or REVIVE */
300 int replyee; /* destination for message (normally FS) */
301 int process; /* which user requested the printing */
302 int status; /* number of chars printed or error code */
303 {
304 /* Send a reply telling FS that printing has started or stopped. */
305
306 message pr_mess;
307
308 pr_mess.m_type = code; /* TASK_REPLY or REVIVE */
309 pr_mess.REP_STATUS = status; /* count or EIO */
310 pr_mess.REP_PROC_NR = process; /* which user does this pertain to */
311 send(replyee, &pr_mess); /* send the message */
312 }
313
314 /*===========================================================================*
315 * do_initialize *
316 *===========================================================================*/
317 PRIVATE void do_initialize()
318 {
319 /* Set global variables and initialize the printer. */
320 static int initialized = FALSE;
321 if (initialized) return;
322 initialized = TRUE;
323
324 /* Get the base port for first printer. */
325 sys_vircopy(SELF, BIOS_SEG, LPT1_IO_PORT_ADDR,
326 SELF, D, (vir_bytes) &port_base, LPT1_IO_PORT_SIZE);
327 sys_outb(port_base + 2, INIT_PRINTER);
328 tickdelay(1); /* easily satisfies Centronics minimum */
329 /* was 2 millisecs; now is ~17 millisecs */
330 sys_outb(port_base + 2, PR_SELECT);
331 irq_hook_id = 0;
332 sys_irqsetpolicy(PRINTER_IRQ, 0, &irq_hook_id);
333 sys_irqenable(&irq_hook_id);
334
335 }
336
337 /*==========================================================================*
338 * prepare_output *
339 *==========================================================================*/
340 PRIVATE void prepare_output()
341 {
342 /* Start next chunk of printer output. Fetch the data from user space. */
343
344 register int chunk;
345
346 if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf;
347 if (OK!=sys_datacopy(proc_nr, user_vir, SELF, (vir_bytes) obuf, chunk)) {
348 done_status = EFAULT;
349 output_done();
350 return;
351 }
352 optr = obuf;
353 oleft = chunk;
354 }
355
356 /*===========================================================================*
357 * do_printer_output *
358 *===========================================================================*/
359 PRIVATE void do_printer_output()
360 {
361 /* This function does the actual output to the printer. This is called on
362 * a HARD_INT message sent from the generic interrupt handler that 'forwards'
363 * interrupts to this driver. The generic handler did not reenable the
364 * printer IRQ yet!
365 */
366
367 int status;
368 pvb_pair_t char_out[3];
369
370 if (oleft == 0) {
371 /* Nothing more to print. Turn off printer interrupts in case they
372 * are level-sensitive as on the PS/2. This should be safe even
373 * when the printer is busy with a previous character, because the
374 * interrupt status does not affect the printer.
375 */
376 sys_outb(port_base + 2, PR_SELECT);
377 sys_irqenable(&irq_hook_id);
378 return;
379 }
380
381 do {
382 /* Loop to handle fast (buffered) printers. It is important that
383 * processor interrupts are not disabled here, just printer interrupts.
384 */
385 (void) sys_inb(port_base + 1, &status);
386 if ((status & STATUS_MASK) == BUSY_STATUS) {
387 /* Still busy with last output. This normally happens
388 * immediately after doing output to an unbuffered or slow
389 * printer. It may happen after a call from prepare_output or
390 * pr_restart, since they are not synchronized with printer
391 * interrupts. It may happen after a spurious interrupt.
392 */
393 sys_irqenable(&irq_hook_id);
394 return;
395 }
396 if ((status & STATUS_MASK) == NORMAL_STATUS) {
397 /* Everything is all right. Output another character. */
398 pv_set(char_out[0], port_base, *optr++);
399 pv_set(char_out[1], port_base+2, ASSERT_STROBE);
400 pv_set(char_out[2], port_base+2, NEGATE_STROBE);
401 sys_voutb(char_out, 3); /* request series of port outb */
402
403 user_vir++;
404 user_left--;
405 } else {
406 /* Error. This would be better ignored (treat as busy). */
407 done_status = status;
408 output_done();
409 sys_irqenable(&irq_hook_id);
410 return;
411 }
412 }
413 while (--oleft != 0);
414
415 /* Finished printing chunk OK. */
416 done_status = OK;
417 output_done();
418 sys_irqenable(&irq_hook_id);
419 }
420
Cache object: e67d14d3b3ac4b62e490aa9c2afec5c0
|