[9] | 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 |
|
---|
| 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 int user_left; /* bytes of output left in user buf */
|
---|
| 98 | PRIVATE vir_bytes user_vir; /* address of remainder of user buf */
|
---|
| 99 | PRIVATE int writing; /* nonzero while write is in progress */
|
---|
| 100 | PRIVATE int irq_hook_id; /* id of irq hook at kernel */
|
---|
| 101 |
|
---|
| 102 | extern int errno; /* error number */
|
---|
| 103 |
|
---|
| 104 | FORWARD _PROTOTYPE( void do_cancel, (message *m_ptr) );
|
---|
| 105 | FORWARD _PROTOTYPE( void output_done, (void) );
|
---|
| 106 | FORWARD _PROTOTYPE( void do_write, (message *m_ptr) );
|
---|
| 107 | FORWARD _PROTOTYPE( void do_status, (message *m_ptr) );
|
---|
| 108 | FORWARD _PROTOTYPE( void prepare_output, (void) );
|
---|
| 109 | FORWARD _PROTOTYPE( void do_initialize, (void) );
|
---|
| 110 | FORWARD _PROTOTYPE( void reply, (int code,int replyee,int proc,int status));
|
---|
| 111 | FORWARD _PROTOTYPE( void do_printer_output, (void) );
|
---|
| 112 | FORWARD _PROTOTYPE( void do_signal, (message *m_ptr) );
|
---|
| 113 |
|
---|
| 114 |
|
---|
| 115 | /*===========================================================================*
|
---|
| 116 | * printer_task *
|
---|
| 117 | *===========================================================================*/
|
---|
| 118 | PUBLIC 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 | *===========================================================================*/
|
---|
| 157 | PRIVATE void do_signal(m_ptr)
|
---|
| 158 | message *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 | *===========================================================================*/
|
---|
| 173 | PRIVATE void do_write(m_ptr)
|
---|
| 174 | register 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 | *===========================================================================*/
|
---|
| 221 | PRIVATE 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 | *===========================================================================*/
|
---|
| 260 | PRIVATE void do_status(m_ptr)
|
---|
| 261 | register 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 | *===========================================================================*/
|
---|
| 279 | PRIVATE void do_cancel(m_ptr)
|
---|
| 280 | register 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 | *===========================================================================*/
|
---|
| 299 | PRIVATE void reply(code, replyee, process, status)
|
---|
| 300 | int code; /* TASK_REPLY or REVIVE */
|
---|
| 301 | int replyee; /* destination for message (normally FS) */
|
---|
| 302 | int process; /* which user requested the printing */
|
---|
| 303 | int 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 | *===========================================================================*/
|
---|
| 318 | PRIVATE 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 | *==========================================================================*/
|
---|
| 341 | PRIVATE 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 | *===========================================================================*/
|
---|
| 360 | PRIVATE 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 |
|
---|