<sys/socket.h>: turn off MSG_NOSIGNAL
[minix3.git] / drivers / printer / printer.c
blob47abec2387bc4591236bb97cfcdbd32231a15886
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)
8 *
9 * Note: since only 1 printer is supported, minor dev is not used at present.
12 #include <minix/endpoint.h>
13 #include <minix/drivers.h>
14 #include <minix/chardriver.h>
16 /* Control bits (in port_base + 2). "+" means positive logic and "-" means
17 * negative logic. Most of the signals are negative logic on the pins but
18 * many are converted to positive logic in the ports. Some manuals are
19 * misleading because they only document the pin logic.
21 * +0x01 Pin 1 -Strobe
22 * +0x02 Pin 14 -Auto Feed
23 * -0x04 Pin 16 -Initialize Printer
24 * +0x08 Pin 17 -Select Printer
25 * +0x10 IRQ7 Enable
27 * Auto Feed and Select Printer are always enabled. Strobe is enabled briefly
28 * when characters are output. Initialize Printer is enabled briefly when
29 * the task is started. IRQ7 is enabled when the first character is output
30 * and left enabled until output is completed (or later after certain
31 * abnormal completions).
33 #define ASSERT_STROBE 0x1D /* strobe a character to the interface */
34 #define NEGATE_STROBE 0x1C /* enable interrupt on interface */
35 #define PR_SELECT 0x0C /* select printer bit */
36 #define INIT_PRINTER 0x08 /* init printer bits */
38 /* Status bits (in port_base + 2).
40 * -0x08 Pin 15 -Error
41 * +0x10 Pin 13 +Select Status
42 * +0x20 Pin 12 +Out of Paper
43 * -0x40 Pin 10 -Acknowledge
44 * -0x80 Pin 11 +Busy
46 #define BUSY_STATUS 0x10 /* printer gives this status when busy */
47 #define NO_PAPER 0x20 /* status bit saying that paper is out */
48 #define NORMAL_STATUS 0x90 /* printer gives this status when idle */
49 #define ON_LINE 0x10 /* status bit saying that printer is online */
50 #define STATUS_MASK 0xB0 /* mask to filter out status bits */
52 #define MAX_ONLINE_RETRIES 120 /* about 60s: waits 0.5s after each retry */
54 /* Centronics interface timing that must be met by software (in microsec).
56 * Strobe length: 0.5u to 100u (not sure about the upper limit).
57 * Data set up: 0.5u before strobe.
58 * Data hold: 0.5u after strobe.
59 * Init pulse length: over 200u (not sure).
61 * The strobe length is about 50u with the code here and function calls for
62 * sys_outb() - not much to spare. The 0.5u minimums will not be violated
63 * with the sys_outb() messages exchanged.
66 static endpoint_t caller; /* process to tell when printing done (FS) */
67 static int done_status; /* status of last output completion */
68 static int oleft; /* bytes of output left in obuf */
69 static unsigned char obuf[128]; /* output buffer */
70 static unsigned const char *optr; /* ptr to next char in obuf to print */
71 static int orig_count; /* original byte count */
72 static int port_base; /* I/O port for printer */
73 static cp_grant_id_t grant_nr; /* grant on which print happens */
74 static int user_left; /* bytes of output left in user buf */
75 static vir_bytes user_vir_d; /* offset in user buf */
76 int writing; /* nonzero while write is in progress */
77 static cdev_id_t write_id; /* request ID of ongoing write */
78 static int irq_hook_id; /* id of irq hook at kernel */
80 static void output_done(void);
81 static void prepare_output(void);
82 static int do_probe(void);
83 static void do_initialize(void);
84 static int printer_open(devminor_t minor, int access, endpoint_t user_endpt);
85 static ssize_t printer_write(devminor_t minor, u64_t position,
86 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
87 cdev_id_t id);
88 static int printer_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
89 static void printer_intr(unsigned int mask);
91 /* SEF functions and variables. */
92 static void sef_local_startup(void);
93 static int sef_cb_init_fresh(int type, sef_init_info_t *info);
94 EXTERN int sef_cb_lu_prepare(int state);
95 EXTERN int sef_cb_lu_state_isvalid(int state);
96 EXTERN void sef_cb_lu_state_dump(int state);
98 /* Entry points to this driver. */
99 static struct chardriver printer_tab = {
100 .cdr_open = printer_open,
101 .cdr_write = printer_write,
102 .cdr_cancel = printer_cancel,
103 .cdr_intr = printer_intr
106 /*===========================================================================*
107 * printer_task *
108 *===========================================================================*/
109 int main(void)
111 /* Main routine of the printer task. */
113 /* SEF local startup. */
114 sef_local_startup();
116 chardriver_task(&printer_tab);
119 /*===========================================================================*
120 * sef_local_startup *
121 *===========================================================================*/
122 static void sef_local_startup()
124 /* Register init callbacks. */
125 sef_setcb_init_fresh(sef_cb_init_fresh);
126 sef_setcb_init_lu(sef_cb_init_fresh);
127 sef_setcb_init_restart(sef_cb_init_fresh);
129 /* Register live update callbacks. */
130 sef_setcb_lu_prepare(sef_cb_lu_prepare);
131 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
132 sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
134 /* Register signal callbacks. */
135 sef_setcb_signal_handler(sef_cb_signal_handler_term);
137 /* Let SEF perform startup. */
138 sef_startup();
141 /*===========================================================================*
142 * sef_cb_init_fresh *
143 *===========================================================================*/
144 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
146 /* Initialize the printer driver. */
148 /* If no printer is present, do not start. */
149 if (!do_probe())
150 return ENODEV; /* arbitrary error code */
152 /* Announce we are up! */
153 chardriver_announce();
155 return OK;
158 /*===========================================================================*
159 * do_write *
160 *===========================================================================*/
161 static ssize_t printer_write(devminor_t UNUSED(minor), u64_t UNUSED(position),
162 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
163 cdev_id_t id)
165 /* The printer is used by sending write requests to it. Process one. */
166 int retries;
167 u32_t status;
169 /* Reject command if last write is not yet finished, the count is not
170 * positive, or we're asked not to block.
172 if (writing) return EIO;
173 if (size <= 0) return EINVAL;
174 if (flags & CDEV_NONBLOCK) return EAGAIN; /* not supported */
176 /* If no errors occurred, continue printing with the caller.
177 * First wait until the printer is online to prevent stupid errors.
179 caller = endpt;
180 user_left = size;
181 orig_count = size;
182 user_vir_d = 0; /* Offset. */
183 writing = TRUE;
184 grant_nr = grant;
185 write_id = id;
187 retries = MAX_ONLINE_RETRIES + 1;
188 while (--retries > 0) {
189 if (sys_inb(port_base + 1, &status) != OK) {
190 printf("printer: sys_inb of %x failed\n", port_base+1);
191 panic("sys_inb failed");
193 if (status & ON_LINE) { /* printer online! */
194 prepare_output();
195 printer_intr(0);
196 return EDONTREPLY; /* we may already have sent a reply */
198 micro_delay(500000); /* wait before retry */
200 /* If we reach this point, the printer was not online in time. */
201 done_status = status;
202 output_done();
203 return EDONTREPLY;
206 /*===========================================================================*
207 * output_done *
208 *===========================================================================*/
209 static void output_done()
211 /* Previous chunk of printing is finished. Continue if OK and more.
212 * Otherwise, reply to caller (FS).
214 int status;
216 if (!writing) return; /* probably leftover interrupt */
217 if (done_status != OK) { /* printer error occurred */
218 status = EIO;
219 if ((done_status & ON_LINE) == 0) {
220 printf("Printer is not on line\n");
221 } else if ((done_status & NO_PAPER)) {
222 printf("Printer is out of paper\n");
223 status = EAGAIN;
224 } else {
225 printf("Printer error, status is 0x%02X\n", done_status);
227 /* Some characters have been printed, tell how many. */
228 if (status == EAGAIN && user_left < orig_count) {
229 status = orig_count - user_left;
231 oleft = 0; /* cancel further output */
232 } else if (user_left != 0) { /* not yet done, continue! */
233 prepare_output();
234 return;
235 } else { /* done! report back to FS */
236 status = orig_count;
239 chardriver_reply_task(caller, write_id, status);
241 writing = FALSE; /* unmark event */
244 /*===========================================================================*
245 * printer_cancel *
246 *===========================================================================*/
247 static int printer_cancel(devminor_t UNUSED(minor), endpoint_t endpt,
248 cdev_id_t id)
250 /* Cancel a print request that has already started. Usually this means that
251 * the process doing the printing has been killed by a signal. It is not
252 * clear if there are race conditions. Try not to cancel the wrong process,
253 * but rely on VFS to handle the EINTR reply and de-suspension properly.
256 if (writing && caller == endpt && write_id == id) {
257 oleft = 0; /* cancel output by interrupt handler */
258 writing = FALSE;
259 return EINTR;
261 return EDONTREPLY;
264 /*===========================================================================*
265 * do_probe *
266 *===========================================================================*/
267 static int do_probe(void)
269 /* See if there is a printer at all. */
271 /* Get the base port for first printer. */
272 if(sys_readbios(LPT1_IO_PORT_ADDR, &port_base, LPT1_IO_PORT_SIZE) != OK) {
273 panic("do_probe: sys_readbios failed");
276 /* If the port is zero, the parallel port is not available at all. */
277 return (port_base != 0);
280 /*===========================================================================*
281 * do_initialize *
282 *===========================================================================*/
283 static void do_initialize()
285 /* Set global variables and initialize the printer. */
286 if(sys_outb(port_base + 2, INIT_PRINTER) != OK) {
287 printf("printer: sys_outb of %x failed\n", port_base+2);
288 panic("do_initialize: sys_outb init failed");
290 micro_delay(1000000/20); /* easily satisfies Centronics minimum */
291 if(sys_outb(port_base + 2, PR_SELECT) != OK) {
292 printf("printer: sys_outb of %x failed\n", port_base+2);
293 panic("do_initialize: sys_outb select failed");
295 irq_hook_id = 0;
296 if(sys_irqsetpolicy(PRINTER_IRQ, 0, &irq_hook_id) != OK ||
297 sys_irqenable(&irq_hook_id) != OK) {
298 panic("do_initialize: irq enabling failed");
302 /*==========================================================================*
303 * printer_open *
304 *==========================================================================*/
305 static int printer_open(devminor_t UNUSED(minor), int UNUSED(access),
306 endpoint_t UNUSED(user_endpt))
308 /* Initialize on first open. */
309 static int initialized = FALSE;
311 if (!initialized) {
312 do_initialize();
314 initialized = TRUE;
317 return OK;
320 /*==========================================================================*
321 * prepare_output *
322 *==========================================================================*/
323 static void prepare_output()
325 /* Start next chunk of printer output. Fetch the data from user space. */
326 int s;
327 register int chunk;
329 if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf;
331 s=sys_safecopyfrom(caller, grant_nr, user_vir_d, (vir_bytes) obuf,
332 chunk);
334 if(s != OK) {
335 done_status = EFAULT;
336 output_done();
337 return;
339 optr = obuf;
340 oleft = chunk;
343 /*===========================================================================*
344 * printer_intr *
345 *===========================================================================*/
346 static void printer_intr(unsigned int UNUSED(mask))
348 /* This function does the actual output to the printer. This is called on an
349 * interrupt message sent from the generic interrupt handler that 'forwards'
350 * interrupts to this driver. The generic handler did not reenable the printer
351 * IRQ yet!
354 u32_t status;
355 pvb_pair_t char_out[3];
357 if (oleft == 0) {
358 /* Nothing more to print. Turn off printer interrupts in case they
359 * are level-sensitive as on the PS/2. This should be safe even
360 * when the printer is busy with a previous character, because the
361 * interrupt status does not affect the printer.
363 if(sys_outb(port_base + 2, PR_SELECT) != OK) {
364 printf("printer: sys_outb of %x failed\n", port_base+2);
365 panic("sys_outb failed");
367 if(sys_irqenable(&irq_hook_id) != OK) {
368 panic("sys_irqenable failed");
370 return;
373 do {
374 /* Loop to handle fast (buffered) printers. It is important that
375 * processor interrupts are not disabled here, just printer interrupts.
377 if(sys_inb(port_base + 1, &status) != OK) {
378 printf("printer: sys_inb of %x failed\n", port_base+1);
379 panic("sys_inb failed");
381 if ((status & STATUS_MASK) == BUSY_STATUS) {
382 /* Still busy with last output. This normally happens
383 * immediately after doing output to an unbuffered or slow
384 * printer. It may happen after a call from prepare_output or
385 * pr_restart, since they are not synchronized with printer
386 * interrupts. It may happen after a spurious interrupt.
388 if(sys_irqenable(&irq_hook_id) != OK) {
389 panic("sys_irqenable failed");
391 return;
393 if ((status & STATUS_MASK) == NORMAL_STATUS) {
394 /* Everything is all right. Output another character. */
395 pv_set(char_out[0], port_base, *optr);
396 optr++;
397 pv_set(char_out[1], port_base+2, ASSERT_STROBE);
398 pv_set(char_out[2], port_base+2, NEGATE_STROBE);
399 if(sys_voutb(char_out, 3) != OK) {
400 /* request series of port outb */
401 panic("sys_voutb failed");
404 user_vir_d++;
405 user_left--;
406 } else {
407 /* Error. This would be better ignored (treat as busy). */
408 done_status = status;
409 output_done();
410 if(sys_irqenable(&irq_hook_id) != OK) {
411 panic("sys_irqenable failed");
413 return;
416 while (--oleft != 0);
418 /* Finished printing chunk OK. */
419 done_status = OK;
420 output_done();
421 if(sys_irqenable(&irq_hook_id) != OK) {
422 panic("sys_irqenable failed");