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 * Note: since only 1 printer is supported, minor dev is not used at present.
10 */
11
12 #include <minix/endpoint.h>
13 #include <minix/drivers.h>
14 #include <minix/chardriver.h>
15
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.
20 *
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
26 *
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).
32 */
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 */
37
38 /* Status bits (in port_base + 2).
39 *
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
45 */
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 */
51
52 #define MAX_ONLINE_RETRIES 120 /* about 60s: waits 0.5s after each retry */
53
54 /* Centronics interface timing that must be met by software (in microsec).
55 *
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).
60 *
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.
64 */
65
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 */
79
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);
90
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, int flags);
96 EXTERN void sef_cb_lu_state_dump(int state);
97
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
104 };
105
106 /*===========================================================================*
107 * printer_task *
108 *===========================================================================*/
main(void)109 int main(void)
110 {
111 /* Main routine of the printer task. */
112
113 /* SEF local startup. */
114 sef_local_startup();
115
116 chardriver_task(&printer_tab);
117 }
118
119 /*===========================================================================*
120 * sef_local_startup *
121 *===========================================================================*/
sef_local_startup()122 static void sef_local_startup()
123 {
124 /* Register init callbacks. */
125 sef_setcb_init_fresh(sef_cb_init_fresh);
126 sef_setcb_init_restart(sef_cb_init_fresh);
127
128 /* Register live update callbacks. */
129 sef_setcb_lu_prepare(sef_cb_lu_prepare);
130 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid);
131 sef_setcb_lu_state_dump(sef_cb_lu_state_dump);
132
133 /* Register signal callbacks. */
134 sef_setcb_signal_handler(sef_cb_signal_handler_term);
135
136 /* Let SEF perform startup. */
137 sef_startup();
138 }
139
140 /*===========================================================================*
141 * sef_cb_init_fresh *
142 *===========================================================================*/
sef_cb_init_fresh(int UNUSED (type),sef_init_info_t * UNUSED (info))143 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
144 {
145 /* Initialize the printer driver. */
146
147 /* If no printer is present, do not start. */
148 if (!do_probe())
149 return ENODEV; /* arbitrary error code */
150
151 /* Announce we are up! */
152 chardriver_announce();
153
154 return OK;
155 }
156
157 /*===========================================================================*
158 * do_write *
159 *===========================================================================*/
printer_write(devminor_t UNUSED (minor),u64_t UNUSED (position),endpoint_t endpt,cp_grant_id_t grant,size_t size,int flags,cdev_id_t id)160 static ssize_t printer_write(devminor_t UNUSED(minor), u64_t UNUSED(position),
161 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
162 cdev_id_t id)
163 {
164 /* The printer is used by sending write requests to it. Process one. */
165 int retries;
166 u32_t status;
167
168 /* Reject command if last write is not yet finished, the count is not
169 * positive, or we're asked not to block.
170 */
171 if (writing) return EIO;
172 if (size <= 0) return EINVAL;
173 if (flags & CDEV_NONBLOCK) return EAGAIN; /* not supported */
174
175 /* If no errors occurred, continue printing with the caller.
176 * First wait until the printer is online to prevent stupid errors.
177 */
178 caller = endpt;
179 user_left = size;
180 orig_count = size;
181 user_vir_d = 0; /* Offset. */
182 writing = TRUE;
183 grant_nr = grant;
184 write_id = id;
185
186 retries = MAX_ONLINE_RETRIES + 1;
187 while (--retries > 0) {
188 if (sys_inb(port_base + 1, &status) != OK) {
189 printf("printer: sys_inb of %x failed\n", port_base+1);
190 panic("sys_inb failed");
191 }
192 if (status & ON_LINE) { /* printer online! */
193 prepare_output();
194 printer_intr(0);
195 return EDONTREPLY; /* we may already have sent a reply */
196 }
197 micro_delay(500000); /* wait before retry */
198 }
199 /* If we reach this point, the printer was not online in time. */
200 done_status = status;
201 output_done();
202 return EDONTREPLY;
203 }
204
205 /*===========================================================================*
206 * output_done *
207 *===========================================================================*/
output_done()208 static void output_done()
209 {
210 /* Previous chunk of printing is finished. Continue if OK and more.
211 * Otherwise, reply to caller (FS).
212 */
213 int status;
214
215 if (!writing) return; /* probably leftover interrupt */
216 if (done_status != OK) { /* printer error occurred */
217 status = EIO;
218 if ((done_status & ON_LINE) == 0) {
219 printf("Printer is not on line\n");
220 } else if ((done_status & NO_PAPER)) {
221 printf("Printer is out of paper\n");
222 status = EAGAIN;
223 } else {
224 printf("Printer error, status is 0x%02X\n", done_status);
225 }
226 /* Some characters have been printed, tell how many. */
227 if (status == EAGAIN && user_left < orig_count) {
228 status = orig_count - user_left;
229 }
230 oleft = 0; /* cancel further output */
231 } else if (user_left != 0) { /* not yet done, continue! */
232 prepare_output();
233 return;
234 } else { /* done! report back to FS */
235 status = orig_count;
236 }
237
238 chardriver_reply_task(caller, write_id, status);
239
240 writing = FALSE; /* unmark event */
241 }
242
243 /*===========================================================================*
244 * printer_cancel *
245 *===========================================================================*/
printer_cancel(devminor_t UNUSED (minor),endpoint_t endpt,cdev_id_t id)246 static int printer_cancel(devminor_t UNUSED(minor), endpoint_t endpt,
247 cdev_id_t id)
248 {
249 /* Cancel a print request that has already started. Usually this means that
250 * the process doing the printing has been killed by a signal. It is not
251 * clear if there are race conditions. Try not to cancel the wrong process,
252 * but rely on VFS to handle the EINTR reply and de-suspension properly.
253 */
254
255 if (writing && caller == endpt && write_id == id) {
256 oleft = 0; /* cancel output by interrupt handler */
257 writing = FALSE;
258 return EINTR;
259 }
260 return EDONTREPLY;
261 }
262
263 /*===========================================================================*
264 * do_probe *
265 *===========================================================================*/
do_probe(void)266 static int do_probe(void)
267 {
268 /* See if there is a printer at all. */
269
270 /* Get the base port for first printer. */
271 if(sys_readbios(LPT1_IO_PORT_ADDR, &port_base, LPT1_IO_PORT_SIZE) != OK) {
272 panic("do_probe: sys_readbios failed");
273 }
274
275 /* If the port is zero, the parallel port is not available at all. */
276 return (port_base != 0);
277 }
278
279 /*===========================================================================*
280 * do_initialize *
281 *===========================================================================*/
do_initialize()282 static void do_initialize()
283 {
284 /* Set global variables and initialize the printer. */
285 if(sys_outb(port_base + 2, INIT_PRINTER) != OK) {
286 printf("printer: sys_outb of %x failed\n", port_base+2);
287 panic("do_initialize: sys_outb init failed");
288 }
289 micro_delay(1000000/20); /* easily satisfies Centronics minimum */
290 if(sys_outb(port_base + 2, PR_SELECT) != OK) {
291 printf("printer: sys_outb of %x failed\n", port_base+2);
292 panic("do_initialize: sys_outb select failed");
293 }
294 irq_hook_id = 0;
295 if(sys_irqsetpolicy(PRINTER_IRQ, 0, &irq_hook_id) != OK ||
296 sys_irqenable(&irq_hook_id) != OK) {
297 panic("do_initialize: irq enabling failed");
298 }
299 }
300
301 /*==========================================================================*
302 * printer_open *
303 *==========================================================================*/
printer_open(devminor_t UNUSED (minor),int UNUSED (access),endpoint_t UNUSED (user_endpt))304 static int printer_open(devminor_t UNUSED(minor), int UNUSED(access),
305 endpoint_t UNUSED(user_endpt))
306 {
307 /* Initialize on first open. */
308 static int initialized = FALSE;
309
310 if (!initialized) {
311 do_initialize();
312
313 initialized = TRUE;
314 }
315
316 return OK;
317 }
318
319 /*==========================================================================*
320 * prepare_output *
321 *==========================================================================*/
prepare_output()322 static void prepare_output()
323 {
324 /* Start next chunk of printer output. Fetch the data from user space. */
325 int s;
326 register int chunk;
327
328 if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf;
329
330 s=sys_safecopyfrom(caller, grant_nr, user_vir_d, (vir_bytes) obuf,
331 chunk);
332
333 if(s != OK) {
334 done_status = EFAULT;
335 output_done();
336 return;
337 }
338 optr = obuf;
339 oleft = chunk;
340 }
341
342 /*===========================================================================*
343 * printer_intr *
344 *===========================================================================*/
printer_intr(unsigned int UNUSED (mask))345 static void printer_intr(unsigned int UNUSED(mask))
346 {
347 /* This function does the actual output to the printer. This is called on an
348 * interrupt message sent from the generic interrupt handler that 'forwards'
349 * interrupts to this driver. The generic handler did not reenable the printer
350 * IRQ yet!
351 */
352
353 u32_t status;
354 pvb_pair_t char_out[3];
355
356 if (oleft == 0) {
357 /* Nothing more to print. Turn off printer interrupts in case they
358 * are level-sensitive as on the PS/2. This should be safe even
359 * when the printer is busy with a previous character, because the
360 * interrupt status does not affect the printer.
361 */
362 if(sys_outb(port_base + 2, PR_SELECT) != OK) {
363 printf("printer: sys_outb of %x failed\n", port_base+2);
364 panic("sys_outb failed");
365 }
366 if(sys_irqenable(&irq_hook_id) != OK) {
367 panic("sys_irqenable failed");
368 }
369 return;
370 }
371
372 do {
373 /* Loop to handle fast (buffered) printers. It is important that
374 * processor interrupts are not disabled here, just printer interrupts.
375 */
376 if(sys_inb(port_base + 1, &status) != OK) {
377 printf("printer: sys_inb of %x failed\n", port_base+1);
378 panic("sys_inb failed");
379 }
380 if ((status & STATUS_MASK) == BUSY_STATUS) {
381 /* Still busy with last output. This normally happens
382 * immediately after doing output to an unbuffered or slow
383 * printer. It may happen after a call from prepare_output or
384 * pr_restart, since they are not synchronized with printer
385 * interrupts. It may happen after a spurious interrupt.
386 */
387 if(sys_irqenable(&irq_hook_id) != OK) {
388 panic("sys_irqenable failed");
389 }
390 return;
391 }
392 if ((status & STATUS_MASK) == NORMAL_STATUS) {
393 /* Everything is all right. Output another character. */
394 pv_set(char_out[0], port_base, *optr);
395 optr++;
396 pv_set(char_out[1], port_base+2, ASSERT_STROBE);
397 pv_set(char_out[2], port_base+2, NEGATE_STROBE);
398 if(sys_voutb(char_out, 3) != OK) {
399 /* request series of port outb */
400 panic("sys_voutb failed");
401 }
402
403 user_vir_d++;
404 user_left--;
405 } else {
406 /* Error. This would be better ignored (treat as busy). */
407 done_status = status;
408 output_done();
409 if(sys_irqenable(&irq_hook_id) != OK) {
410 panic("sys_irqenable failed");
411 }
412 return;
413 }
414 }
415 while (--oleft != 0);
416
417 /* Finished printing chunk OK. */
418 done_status = OK;
419 output_done();
420 if(sys_irqenable(&irq_hook_id) != OK) {
421 panic("sys_irqenable failed");
422 }
423 }
424
425