Fixes for /usr/xbin binaries bootstrap dir.
[minix3.git] / drivers / printer / printer.c
blob61ab831fb1f9d36eb859181aacddc02ca9286044
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.
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)
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.
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
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).
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
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 /*===========================================================================*
119 * printer_task *
120 *===========================================================================*/
121 PUBLIC void main(void)
123 /* Main routine of the printer task. */
124 message pr_mess; /* buffer for all incoming messages */
125 struct sigaction sa;
126 int s;
128 /* Install signal handlers. Ask PM to transform signal into message. */
129 sa.sa_handler = SIG_MESS;
130 sigemptyset(&sa.sa_mask);
131 sa.sa_flags = 0;
132 if (sigaction(SIGTERM,&sa,NULL)<0) panic("PRN","sigaction failed", errno);
134 while (TRUE) {
135 receive(ANY, &pr_mess);
136 switch(pr_mess.m_type) {
137 case DEV_OPEN:
138 do_initialize(); /* initialize */
139 /* fall through */
140 case DEV_CLOSE:
141 reply(TASK_REPLY, pr_mess.m_source, pr_mess.IO_ENDPT, OK);
142 break;
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;
150 default:
151 reply(TASK_REPLY, pr_mess.m_source, pr_mess.IO_ENDPT, EINVAL);
157 /*===========================================================================*
158 * do_signal *
159 *===========================================================================*/
160 PRIVATE void do_signal(m_ptr)
161 message *m_ptr; /* signal message */
163 int sig;
164 sigset_t sigset = m_ptr->NOTIFY_ARG;
166 /* Expect a SIGTERM signal when this server must shutdown. */
167 if (sigismember(&sigset, SIGTERM)) {
168 exit(0);
170 /* Ignore all other signals. */
173 /*===========================================================================*
174 * do_write *
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;
183 int retries;
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.
198 if (SUSPEND == r) {
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? */
206 writing = TRUE;
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! */
213 prepare_output();
214 do_printer_output();
215 return;
217 tickdelay(30); /* wait before retry */
219 /* If we reach this point, the printer was not online in time. */
220 done_status = status;
221 output_done();
225 /*===========================================================================*
226 * output_done *
227 *===========================================================================*/
228 PRIVATE void output_done()
230 /* Previous chunk of printing is finished. Continue if OK and more.
231 * Otherwise, reply to caller (FS).
233 register int status;
235 if (!writing) return; /* probably leftover interrupt */
236 if (done_status != OK) { /* printer error occurred */
237 status = EIO;
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");
242 status = EAGAIN;
243 } else {
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! */
253 prepare_output();
254 return;
256 else { /* done! report back to FS */
257 status = orig_count;
259 revive_pending = TRUE;
260 revive_status = status;
261 notify(caller);
264 /*===========================================================================*
265 * do_status *
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 */
278 } else {
279 m_ptr->m_type = DEV_NO_STATUS;
281 send(m_ptr->m_source, m_ptr); /* send the message */
284 /*===========================================================================*
285 * do_cancel *
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 */
298 writing = FALSE;
299 revive_pending = FALSE;
301 reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, EINTR);
304 /*===========================================================================*
305 * reply *
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. */
315 message pr_mess;
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 /*===========================================================================*
324 * do_initialize *
325 *===========================================================================*/
326 PRIVATE void do_initialize()
328 /* Set global variables and initialize the printer. */
329 static int initialized = FALSE;
330 if (initialized) return;
331 initialized = TRUE;
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);
340 irq_hook_id = 0;
341 sys_irqsetpolicy(PRINTER_IRQ, 0, &irq_hook_id);
342 sys_irqenable(&irq_hook_id);
346 /*==========================================================================*
347 * prepare_output *
348 *==========================================================================*/
349 PRIVATE void prepare_output()
351 /* Start next chunk of printer output. Fetch the data from user space. */
352 int s;
353 register int chunk;
355 if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf;
356 if(user_safe) {
357 s=sys_safecopyfrom(proc_nr, user_vir_g, user_vir_d,
358 (vir_bytes) obuf, chunk, D);
359 } else {
360 s=sys_datacopy(proc_nr, user_vir_g, SELF, (vir_bytes) obuf, chunk);
363 if(s != OK) {
364 done_status = EFAULT;
365 output_done();
366 return;
368 optr = obuf;
369 oleft = chunk;
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
380 * printer IRQ yet!
383 unsigned long status;
384 pvb_pair_t char_out[3];
386 if (oleft == 0) {
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);
394 return;
397 do {
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);
410 return;
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 */
419 user_vir_d++;
420 user_left--;
421 } else {
422 /* Error. This would be better ignored (treat as busy). */
423 done_status = status;
424 output_done();
425 sys_irqenable(&irq_hook_id);
426 return;
429 while (--oleft != 0);
431 /* Finished printing chunk OK. */
432 done_status = OK;
433 output_done();
434 sys_irqenable(&irq_hook_id);