ld.elf_so, grep, make: use mmap()
[minix3.git] / drivers / printer / printer.c
blob5ab2a9491c46c84a6bcafea41c6c80800d7f9d69
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 USER_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 <minix/endpoint.h>
36 #include <minix/drivers.h>
37 #include <minix/chardriver.h>
39 /* Control bits (in port_base + 2). "+" means positive logic and "-" means
40 * negative logic. Most of the signals are negative logic on the pins but
41 * many are converted to positive logic in the ports. Some manuals are
42 * misleading because they only document the pin logic.
44 * +0x01 Pin 1 -Strobe
45 * +0x02 Pin 14 -Auto Feed
46 * -0x04 Pin 16 -Initialize Printer
47 * +0x08 Pin 17 -Select Printer
48 * +0x10 IRQ7 Enable
50 * Auto Feed and Select Printer are always enabled. Strobe is enabled briefly
51 * when characters are output. Initialize Printer is enabled briefly when
52 * the task is started. IRQ7 is enabled when the first character is output
53 * and left enabled until output is completed (or later after certain
54 * abnormal completions).
56 #define ASSERT_STROBE 0x1D /* strobe a character to the interface */
57 #define NEGATE_STROBE 0x1C /* enable interrupt on interface */
58 #define PR_SELECT 0x0C /* select printer bit */
59 #define INIT_PRINTER 0x08 /* init printer bits */
61 /* Status bits (in port_base + 2).
63 * -0x08 Pin 15 -Error
64 * +0x10 Pin 13 +Select Status
65 * +0x20 Pin 12 +Out of Paper
66 * -0x40 Pin 10 -Acknowledge
67 * -0x80 Pin 11 +Busy
69 #define BUSY_STATUS 0x10 /* printer gives this status when busy */
70 #define NO_PAPER 0x20 /* status bit saying that paper is out */
71 #define NORMAL_STATUS 0x90 /* printer gives this status when idle */
72 #define ON_LINE 0x10 /* status bit saying that printer is online */
73 #define STATUS_MASK 0xB0 /* mask to filter out status bits */
75 #define MAX_ONLINE_RETRIES 120 /* about 60s: waits 0.5s after each retry */
77 /* Centronics interface timing that must be met by software (in microsec).
79 * Strobe length: 0.5u to 100u (not sure about the upper limit).
80 * Data set up: 0.5u before strobe.
81 * Data hold: 0.5u after strobe.
82 * Init pulse length: over 200u (not sure).
84 * The strobe length is about 50u with the code here and function calls for
85 * sys_outb() - not much to spare. The 0.5u minimums will not be violated
86 * with the sys_outb() messages exchanged.
89 static endpoint_t caller; /* process to tell when printing done (FS) */
90 static int revive_pending; /* set to true if revive is pending */
91 static int revive_status; /* revive status */
92 static int done_status; /* status of last output completion */
93 static int oleft; /* bytes of output left in obuf */
94 static unsigned char obuf[128]; /* output buffer */
95 static unsigned const char *optr; /* ptr to next char in obuf to print */
96 static int orig_count; /* original byte count */
97 static int port_base; /* I/O port for printer */
98 static endpoint_t proc_nr; /* user requesting the printing */
99 static cp_grant_id_t grant_nr; /* grant on which print happens */
100 static int user_left; /* bytes of output left in user buf */
101 static vir_bytes user_vir_d; /* offset in user buf */
102 int writing; /* nonzero while write is in progress */
103 static int irq_hook_id; /* id of irq hook at kernel */
105 static void do_cancel(message *m_ptr);
106 static void output_done(void);
107 static void do_write(message *m_ptr);
108 static void do_status(message *m_ptr);
109 static void prepare_output(void);
110 static int do_probe(void);
111 static void do_initialize(void);
112 static void reply(int code,int replyee,int proc,int status);
113 static void do_printer_output(void);
115 /* SEF functions and variables. */
116 static void sef_local_startup(void);
117 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
118 EXTERN int sef_cb_lu_prepare(int state);
119 EXTERN int sef_cb_lu_state_isvalid(int state);
120 EXTERN void sef_cb_lu_state_dump(int state);
121 int is_status_msg_expected = FALSE;
123 /*===========================================================================*
124 * printer_task *
125 *===========================================================================*/
126 int main(void)
128 /* Main routine of the printer task. */
129 message pr_mess; /* buffer for all incoming messages */
130 int ipc_status;
132 /* SEF local startup. */
133 sef_local_startup();
135 while (TRUE) {
136 if(driver_receive(ANY, &pr_mess, &ipc_status) != OK) {
137 panic("driver_receive failed");
140 if (is_ipc_notify(ipc_status)) {
141 switch (_ENDPOINT_P(pr_mess.m_source)) {
142 case HARDWARE:
143 do_printer_output();
144 break;
145 default:
146 reply(TASK_REPLY, pr_mess.m_source,
147 pr_mess.USER_ENDPT, EINVAL);
149 continue;
152 switch(pr_mess.m_type) {
153 case DEV_OPEN:
154 do_initialize(); /* initialize */
155 /* fall through */
156 case DEV_CLOSE:
157 reply(TASK_REPLY, pr_mess.m_source, pr_mess.USER_ENDPT, OK);
158 break;
159 case DEV_WRITE_S: do_write(&pr_mess); break;
160 case DEV_STATUS: do_status(&pr_mess); break;
161 case CANCEL: do_cancel(&pr_mess); break;
162 default:
163 reply(TASK_REPLY, pr_mess.m_source, pr_mess.USER_ENDPT,
164 EINVAL);
169 /*===========================================================================*
170 * sef_local_startup *
171 *===========================================================================*/
172 static void sef_local_startup()
174 /* Register init callbacks. */
175 sef_setcb_init_fresh(sef_cb_init_fresh);
176 sef_setcb_init_lu(sef_cb_init_fresh);
177 sef_setcb_init_restart(sef_cb_init_fresh);
179 /* Register live update callbacks. */
180 sef_setcb_lu_prepare(sef_cb_lu_prepare);
181 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
182 sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
184 /* Register signal callbacks. */
185 sef_setcb_signal_handler(sef_cb_signal_handler_term);
187 /* Let SEF perform startup. */
188 sef_startup();
191 /*===========================================================================*
192 * sef_cb_init_fresh *
193 *===========================================================================*/
194 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
196 /* Initialize the printer driver. */
198 /* If no printer is present, do not start. */
199 if (!do_probe())
200 return ENODEV; /* arbitrary error code */
202 /* Announce we are up! */
203 chardriver_announce();
205 return OK;
208 /*===========================================================================*
209 * do_write *
210 *===========================================================================*/
211 static void do_write(m_ptr)
212 register message *m_ptr; /* pointer to the newly arrived message */
214 /* The printer is used by sending DEV_WRITE messages to it. Process one. */
216 register int r = SUSPEND;
217 int retries;
218 u32_t status;
220 /* Reject command if last write is not yet finished, the count is not
221 * positive, or the user address is bad.
223 if (writing) r = EIO;
224 else if (m_ptr->COUNT <= 0) r = EINVAL;
226 /* Reply to FS, no matter what happened, possible SUSPEND caller. */
227 reply(TASK_REPLY, m_ptr->m_source, m_ptr->USER_ENDPT, r);
229 /* If no errors occurred, continue printing with SUSPENDED caller.
230 * First wait until the printer is online to prevent stupid errors.
232 if (SUSPEND == r) {
233 caller = m_ptr->m_source;
234 proc_nr = m_ptr->USER_ENDPT;
235 user_left = m_ptr->COUNT;
236 orig_count = m_ptr->COUNT;
237 user_vir_d = 0; /* Offset. */
238 writing = TRUE;
239 grant_nr = (cp_grant_id_t) m_ptr->IO_GRANT;
241 retries = MAX_ONLINE_RETRIES + 1;
242 while (--retries > 0) {
243 if(sys_inb(port_base + 1, &status) != OK) {
244 printf("printer: sys_inb of %x failed\n", port_base+1);
245 panic("sys_inb failed");
247 if ((status & ON_LINE)) { /* printer online! */
248 prepare_output();
249 do_printer_output();
250 return;
252 micro_delay(500000); /* wait before retry */
254 /* If we reach this point, the printer was not online in time. */
255 done_status = status;
256 output_done();
260 /*===========================================================================*
261 * output_done *
262 *===========================================================================*/
263 static void output_done()
265 /* Previous chunk of printing is finished. Continue if OK and more.
266 * Otherwise, reply to caller (FS).
268 register int status;
270 if (!writing) return; /* probably leftover interrupt */
271 if (done_status != OK) { /* printer error occurred */
272 status = EIO;
273 if ((done_status & ON_LINE) == 0) {
274 printf("Printer is not on line\n");
275 } else if ((done_status & NO_PAPER)) {
276 printf("Printer is out of paper\n");
277 status = EAGAIN;
278 } else {
279 printf("Printer error, status is 0x%02X\n", done_status);
281 /* Some characters have been printed, tell how many. */
282 if (status == EAGAIN && user_left < orig_count) {
283 status = orig_count - user_left;
285 oleft = 0; /* cancel further output */
287 else if (user_left != 0) { /* not yet done, continue! */
288 prepare_output();
289 return;
291 else { /* done! report back to FS */
292 status = orig_count;
294 is_status_msg_expected = TRUE;
295 revive_pending = TRUE;
296 revive_status = status;
297 notify(caller);
300 /*===========================================================================*
301 * do_status *
302 *===========================================================================*/
303 static void do_status(m_ptr)
304 register message *m_ptr; /* pointer to the newly arrived message */
306 if (revive_pending) {
307 m_ptr->m_type = DEV_REVIVE; /* build message */
308 m_ptr->REP_ENDPT = proc_nr;
309 m_ptr->REP_STATUS = revive_status;
310 m_ptr->REP_IO_GRANT = grant_nr;
312 writing = FALSE; /* unmark event */
313 revive_pending = FALSE; /* unmark event */
314 } else {
315 m_ptr->m_type = DEV_NO_STATUS;
317 is_status_msg_expected = FALSE;
319 send(m_ptr->m_source, m_ptr); /* send the message */
322 /*===========================================================================*
323 * do_cancel *
324 *===========================================================================*/
325 static void do_cancel(m_ptr)
326 register message *m_ptr; /* pointer to the newly arrived message */
328 /* Cancel a print request that has already started. Usually this means that
329 * the process doing the printing has been killed by a signal. It is not
330 * clear if there are race conditions. Try not to cancel the wrong process,
331 * but rely on FS to handle the EINTR reply and de-suspension properly.
334 if (writing && m_ptr->USER_ENDPT == proc_nr) {
335 oleft = 0; /* cancel output by interrupt handler */
336 writing = FALSE;
337 revive_pending = FALSE;
339 reply(TASK_REPLY, m_ptr->m_source, m_ptr->USER_ENDPT, EINTR);
342 /*===========================================================================*
343 * reply *
344 *===========================================================================*/
345 static void reply(code, replyee, process, status)
346 int code; /* TASK_REPLY or REVIVE */
347 int replyee; /* destination for message (normally FS) */
348 int process; /* which user requested the printing */
349 int status; /* number of chars printed or error code */
351 /* Send a reply telling FS that printing has started or stopped. */
353 message pr_mess;
355 pr_mess.m_type = code; /* TASK_REPLY or REVIVE */
356 pr_mess.REP_STATUS = status; /* count or EIO */
357 pr_mess.REP_ENDPT = process; /* which user does this pertain to */
358 send(replyee, &pr_mess); /* send the message */
361 /*===========================================================================*
362 * do_probe *
363 *===========================================================================*/
364 static int do_probe(void)
366 /* See if there is a printer at all. */
368 /* Get the base port for first printer. */
369 if(sys_readbios(LPT1_IO_PORT_ADDR, &port_base, LPT1_IO_PORT_SIZE) != OK) {
370 panic("do_initialize: sys_readbios failed");
373 /* If the port is zero, the parallel port is not available at all. */
374 return (port_base != 0);
377 /*===========================================================================*
378 * do_initialize *
379 *===========================================================================*/
380 static void do_initialize()
382 /* Set global variables and initialize the printer. */
383 static int initialized = FALSE;
384 if (initialized) return;
385 initialized = TRUE;
387 if(sys_outb(port_base + 2, INIT_PRINTER) != OK) {
388 printf("printer: sys_outb of %x failed\n", port_base+2);
389 panic("do_initialize: sys_outb init failed");
391 micro_delay(1000000/20); /* easily satisfies Centronics minimum */
392 if(sys_outb(port_base + 2, PR_SELECT) != OK) {
393 printf("printer: sys_outb of %x failed\n", port_base+2);
394 panic("do_initialize: sys_outb select failed");
396 irq_hook_id = 0;
397 if(sys_irqsetpolicy(PRINTER_IRQ, 0, &irq_hook_id) != OK ||
398 sys_irqenable(&irq_hook_id) != OK) {
399 panic("do_initialize: irq enabling failed");
403 /*==========================================================================*
404 * prepare_output *
405 *==========================================================================*/
406 static void prepare_output()
408 /* Start next chunk of printer output. Fetch the data from user space. */
409 int s;
410 register int chunk;
412 if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf;
414 s=sys_safecopyfrom(caller, grant_nr, user_vir_d, (vir_bytes) obuf,
415 chunk);
417 if(s != OK) {
418 done_status = EFAULT;
419 output_done();
420 return;
422 optr = obuf;
423 oleft = chunk;
426 /*===========================================================================*
427 * do_printer_output *
428 *===========================================================================*/
429 static void do_printer_output()
431 /* This function does the actual output to the printer. This is called on an
432 * interrupt message sent from the generic interrupt handler that 'forwards'
433 * interrupts to this driver. The generic handler did not reenable the printer
434 * IRQ yet!
437 u32_t status;
438 pvb_pair_t char_out[3];
440 if (oleft == 0) {
441 /* Nothing more to print. Turn off printer interrupts in case they
442 * are level-sensitive as on the PS/2. This should be safe even
443 * when the printer is busy with a previous character, because the
444 * interrupt status does not affect the printer.
446 if(sys_outb(port_base + 2, PR_SELECT) != OK) {
447 printf("printer: sys_outb of %x failed\n", port_base+2);
448 panic("sys_outb failed");
450 if(sys_irqenable(&irq_hook_id) != OK) {
451 panic("sys_irqenable failed");
453 return;
456 do {
457 /* Loop to handle fast (buffered) printers. It is important that
458 * processor interrupts are not disabled here, just printer interrupts.
460 if(sys_inb(port_base + 1, &status) != OK) {
461 printf("printer: sys_inb of %x failed\n", port_base+1);
462 panic("sys_inb failed");
464 if ((status & STATUS_MASK) == BUSY_STATUS) {
465 /* Still busy with last output. This normally happens
466 * immediately after doing output to an unbuffered or slow
467 * printer. It may happen after a call from prepare_output or
468 * pr_restart, since they are not synchronized with printer
469 * interrupts. It may happen after a spurious interrupt.
471 if(sys_irqenable(&irq_hook_id) != OK) {
472 panic("sys_irqenable failed");
474 return;
476 if ((status & STATUS_MASK) == NORMAL_STATUS) {
477 /* Everything is all right. Output another character. */
478 pv_set(char_out[0], port_base, *optr);
479 optr++;
480 pv_set(char_out[1], port_base+2, ASSERT_STROBE);
481 pv_set(char_out[2], port_base+2, NEGATE_STROBE);
482 if(sys_voutb(char_out, 3) != OK) {
483 /* request series of port outb */
484 panic("sys_voutb failed");
487 user_vir_d++;
488 user_left--;
489 } else {
490 /* Error. This would be better ignored (treat as busy). */
491 done_status = status;
492 output_done();
493 if(sys_irqenable(&irq_hook_id) != OK) {
494 panic("sys_irqenable failed");
496 return;
499 while (--oleft != 0);
501 /* Finished printing chunk OK. */
502 done_status = OK;
503 output_done();
504 if(sys_irqenable(&irq_hook_id) != OK) {
505 panic("sys_irqenable failed");