source: trunk/minix/drivers/printer/printer.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 14.9 KB
Line 
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
87PRIVATE int caller; /* process to tell when printing done (FS) */
88PRIVATE int revive_pending; /* set to true if revive is pending */
89PRIVATE int revive_status; /* revive status */
90PRIVATE int done_status; /* status of last output completion */
91PRIVATE int oleft; /* bytes of output left in obuf */
92PRIVATE char obuf[128]; /* output buffer */
93PRIVATE char *optr; /* ptr to next char in obuf to print */
94PRIVATE int orig_count; /* original byte count */
95PRIVATE int port_base; /* I/O port for printer */
96PRIVATE int proc_nr; /* user requesting the printing */
97PRIVATE int user_left; /* bytes of output left in user buf */
98PRIVATE vir_bytes user_vir; /* address of remainder of user buf */
99PRIVATE int writing; /* nonzero while write is in progress */
100PRIVATE int irq_hook_id; /* id of irq hook at kernel */
101
102extern int errno; /* error number */
103
104FORWARD _PROTOTYPE( void do_cancel, (message *m_ptr) );
105FORWARD _PROTOTYPE( void output_done, (void) );
106FORWARD _PROTOTYPE( void do_write, (message *m_ptr) );
107FORWARD _PROTOTYPE( void do_status, (message *m_ptr) );
108FORWARD _PROTOTYPE( void prepare_output, (void) );
109FORWARD _PROTOTYPE( void do_initialize, (void) );
110FORWARD _PROTOTYPE( void reply, (int code,int replyee,int proc,int status));
111FORWARD _PROTOTYPE( void do_printer_output, (void) );
112FORWARD _PROTOTYPE( void do_signal, (message *m_ptr) );
113
114
115/*===========================================================================*
116 * printer_task *
117 *===========================================================================*/
118PUBLIC 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 *===========================================================================*/
157PRIVATE void do_signal(m_ptr)
158message *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 *===========================================================================*/
173PRIVATE void do_write(m_ptr)
174register 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 *===========================================================================*/
221PRIVATE 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 *===========================================================================*/
260PRIVATE void do_status(m_ptr)
261register 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 *===========================================================================*/
279PRIVATE void do_cancel(m_ptr)
280register 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 *===========================================================================*/
299PRIVATE void reply(code, replyee, process, status)
300int code; /* TASK_REPLY or REVIVE */
301int replyee; /* destination for message (normally FS) */
302int process; /* which user requested the printing */
303int 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 *===========================================================================*/
318PRIVATE 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 *==========================================================================*/
341PRIVATE 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 *===========================================================================*/
360PRIVATE 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
Note: See TracBrowser for help on using the repository browser.