printer.c

Go to the documentation of this file.
00001 /* This file contains the printer driver. It is a fairly simple driver,
00002  * supporting only one printer.  Characters that are written to the driver
00003  * are written to the printer without any changes at all.
00004  *
00005  * Changes:
00006  *      May 07, 2004    fix: wait until printer is ready  (Jorrit N. Herder)
00007  *      May 06, 2004    printer driver moved to user-space  (Jorrit N. Herder) 
00008  *
00009  * The valid messages and their parameters are:
00010  *
00011  *   DEV_OPEN:  initializes the printer
00012  *   DEV_CLOSE: does nothing
00013  *   HARD_INT:  interrupt handler has finished current chunk of output
00014  *   DEV_WRITE: a process wants to write on a terminal
00015  *   CANCEL:    terminate a previous incomplete system call immediately
00016  *
00017  *    m_type      TTY_LINE   IO_ENDPT    COUNT    ADDRESS
00018  * |-------------+---------+---------+---------+---------|
00019  * | DEV_OPEN    |         |         |         |         |
00020  * |-------------+---------+---------+---------+---------|
00021  * | DEV_CLOSE   |         | proc nr |         |         |
00022  * -------------------------------------------------------
00023  * | HARD_INT    |         |         |         |         |
00024  * |-------------+---------+---------+---------+---------|
00025  * | SYS_EVENT   |         |         |         |         |
00026  * |-------------+---------+---------+---------+---------|
00027  * | DEV_WRITE   |minor dev| proc nr |  count  | buf ptr |
00028  * |-------------+---------+---------+---------+---------|
00029  * | CANCEL      |minor dev| proc nr |         |         |
00030  * -------------------------------------------------------
00031  * 
00032  * Note: since only 1 printer is supported, minor dev is not used at present.
00033  */
00034 
00035 #include "../drivers.h"
00036 
00037 /* Control bits (in port_base + 2).  "+" means positive logic and "-" means
00038  * negative logic.  Most of the signals are negative logic on the pins but
00039  * many are converted to positive logic in the ports.  Some manuals are
00040  * misleading because they only document the pin logic.
00041  *
00042  *      +0x01   Pin 1   -Strobe
00043  *      +0x02   Pin 14  -Auto Feed
00044  *      -0x04   Pin 16  -Initialize Printer
00045  *      +0x08   Pin 17  -Select Printer
00046  *      +0x10   IRQ7 Enable
00047  *
00048  * Auto Feed and Select Printer are always enabled. Strobe is enabled briefly
00049  * when characters are output.  Initialize Printer is enabled briefly when
00050  * the task is started.  IRQ7 is enabled when the first character is output
00051  * and left enabled until output is completed (or later after certain
00052  * abnormal completions).
00053  */
00054 #define ASSERT_STROBE   0x1D    /* strobe a character to the interface */
00055 #define NEGATE_STROBE   0x1C    /* enable interrupt on interface */
00056 #define PR_SELECT          0x0C /* select printer bit */
00057 #define INIT_PRINTER    0x08    /* init printer bits */
00058 
00059 /* Status bits (in port_base + 2).
00060  *
00061  *      -0x08   Pin 15  -Error
00062  *      +0x10   Pin 13  +Select Status
00063  *      +0x20   Pin 12  +Out of Paper
00064  *      -0x40   Pin 10  -Acknowledge
00065  *      -0x80   Pin 11  +Busy
00066  */
00067 #define BUSY_STATUS     0x10    /* printer gives this status when busy */
00068 #define NO_PAPER        0x20    /* status bit saying that paper is out */
00069 #define NORMAL_STATUS   0x90    /* printer gives this status when idle */
00070 #define ON_LINE         0x10    /* status bit saying that printer is online */
00071 #define STATUS_MASK     0xB0    /* mask to filter out status bits */ 
00072 
00073 #define MAX_ONLINE_RETRIES 120  /* about 60s: waits 0.5s after each retry */
00074 
00075 /* Centronics interface timing that must be met by software (in microsec).
00076  *
00077  * Strobe length:       0.5u to 100u (not sure about the upper limit).
00078  * Data set up:         0.5u before strobe.
00079  * Data hold:           0.5u after strobe.
00080  * Init pulse length:   over 200u (not sure).
00081  *
00082  * The strobe length is about 50u with the code here and function calls for
00083  * sys_outb() - not much to spare.  The 0.5u minimums will not be violated 
00084  * with the sys_outb() messages exchanged.
00085  */
00086 
00087 PRIVATE int caller;             /* process to tell when printing done (FS) */
00088 PRIVATE int revive_pending;     /* set to true if revive is pending */
00089 PRIVATE int revive_status;      /* revive status */
00090 PRIVATE int done_status;        /* status of last output completion */
00091 PRIVATE int oleft;              /* bytes of output left in obuf */
00092 PRIVATE char obuf[128];         /* output buffer */
00093 PRIVATE char *optr;             /* ptr to next char in obuf to print */
00094 PRIVATE int orig_count;         /* original byte count */
00095 PRIVATE int port_base;          /* I/O port for printer */
00096 PRIVATE int proc_nr;            /* user requesting the printing */
00097 PRIVATE int user_left;          /* bytes of output left in user buf */
00098 PRIVATE vir_bytes user_vir;     /* address of remainder of user buf */
00099 PRIVATE int writing;            /* nonzero while write is in progress */
00100 PRIVATE int irq_hook_id;        /* id of irq hook at kernel */
00101 
00102 extern int errno;               /* error number */
00103 
00104 FORWARD _PROTOTYPE( void do_cancel, (message *m_ptr) );
00105 FORWARD _PROTOTYPE( void output_done, (void) );
00106 FORWARD _PROTOTYPE( void do_write, (message *m_ptr) );
00107 FORWARD _PROTOTYPE( void do_status, (message *m_ptr) );
00108 FORWARD _PROTOTYPE( void prepare_output, (void) );
00109 FORWARD _PROTOTYPE( void do_initialize, (void) );
00110 FORWARD _PROTOTYPE( void reply, (int code,int replyee,int proc,int status));
00111 FORWARD _PROTOTYPE( void do_printer_output, (void) );
00112 FORWARD _PROTOTYPE( void do_signal, (message *m_ptr) );
00113 
00114 
00115 /*===========================================================================*
00116  *                              printer_task                                 *
00117  *===========================================================================*/
00118 PUBLIC void main(void)
00119 {
00120 /* Main routine of the printer task. */
00121   message pr_mess;              /* buffer for all incoming messages */
00122   struct sigaction sa;
00123   int s;
00124 
00125   /* Install signal handlers. Ask PM to transform signal into message. */
00126   sa.sa_handler = SIG_MESS;
00127   sigemptyset(&sa.sa_mask);
00128   sa.sa_flags = 0;
00129   if (sigaction(SIGTERM,&sa,NULL)<0) panic("PRN","sigaction failed", errno);
00130   
00131   while (TRUE) {
00132         receive(ANY, &pr_mess);
00133         switch(pr_mess.m_type) {
00134             case DEV_OPEN:
00135                  do_initialize();               /* initialize */
00136                 /* fall through */
00137             case DEV_CLOSE:
00138                 reply(TASK_REPLY, pr_mess.m_source, pr_mess.IO_ENDPT, OK);
00139                 break;
00140             case DEV_WRITE:     do_write(&pr_mess);     break;
00141             case DEV_STATUS:    do_status(&pr_mess);    break;
00142             case CANCEL:        do_cancel(&pr_mess);    break;
00143             case HARD_INT:      do_printer_output();    break;
00144             case SYS_SIG:       do_signal(&pr_mess);    break;
00145             case DEV_PING:      notify(pr_mess.m_source);       break;
00146             case PROC_EVENT:    break;
00147             default:
00148                 reply(TASK_REPLY, pr_mess.m_source, pr_mess.IO_ENDPT, EINVAL);
00149         }
00150   }
00151 }
00152 
00153 
00154 /*===========================================================================*
00155  *                               do_signal                                   *
00156  *===========================================================================*/
00157 PRIVATE void do_signal(m_ptr)
00158 message *m_ptr;                                 /* signal message */
00159 {
00160   int sig;
00161   sigset_t sigset = m_ptr->NOTIFY_ARG;
00162   
00163   /* Expect a SIGTERM signal when this server must shutdown. */
00164   if (sigismember(&sigset, SIGTERM)) {
00165         exit(0);
00166   } 
00167   /* Ignore all other signals. */
00168 }
00169 
00170 /*===========================================================================*
00171  *                              do_write                                     *
00172  *===========================================================================*/
00173 PRIVATE void do_write(m_ptr)
00174 register message *m_ptr;        /* pointer to the newly arrived message */
00175 {
00176 /* The printer is used by sending DEV_WRITE messages to it. Process one. */
00177 
00178     register int r = SUSPEND;
00179     int retries;
00180     unsigned long status;
00181 
00182     /* Reject command if last write is not yet finished, the count is not
00183      * positive, or the user address is bad.
00184      */
00185     if (writing)                        r = EIO;
00186     else if (m_ptr->COUNT <= 0)         r = EINVAL;
00187 
00188     /* Reply to FS, no matter what happened, possible SUSPEND caller. */
00189     reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, r);
00190 
00191     /* If no errors occurred, continue printing with SUSPENDED caller.
00192      * First wait until the printer is online to prevent stupid errors.
00193      */
00194     if (SUSPEND == r) {         
00195         caller = m_ptr->m_source;
00196         proc_nr = m_ptr->IO_ENDPT;
00197         user_left = m_ptr->COUNT;
00198         orig_count = m_ptr->COUNT;
00199         user_vir = (vir_bytes) m_ptr->ADDRESS;
00200         writing = TRUE;
00201 
00202         retries = MAX_ONLINE_RETRIES + 1;  
00203         while (--retries > 0) {
00204             sys_inb(port_base + 1, &status);
00205             if ((status & ON_LINE)) {           /* printer online! */
00206                 prepare_output();
00207                 do_printer_output();
00208                 return;
00209             }
00210             tickdelay(30);              /* wait before retry */
00211         }
00212         /* If we reach this point, the printer was not online in time. */
00213         done_status = status;
00214         output_done();
00215     }
00216 }
00217 
00218 /*===========================================================================*
00219  *                              output_done                                  *
00220  *===========================================================================*/
00221 PRIVATE void output_done()
00222 {
00223 /* Previous chunk of printing is finished.  Continue if OK and more.
00224  * Otherwise, reply to caller (FS).
00225  */
00226     register int status;
00227 
00228     if (!writing) return;               /* probably leftover interrupt */
00229     if (done_status != OK) {            /* printer error occurred */
00230         status = EIO;
00231         if ((done_status & ON_LINE) == 0) { 
00232             printf("Printer is not on line\n");
00233         } else if ((done_status & NO_PAPER)) { 
00234             printf("Printer is out of paper\n");
00235             status = EAGAIN;    
00236         } else {
00237             printf("Printer error, status is 0x%02X\n", done_status);
00238         }
00239         /* Some characters have been printed, tell how many. */
00240         if (status == EAGAIN && user_left < orig_count) {
00241                 status = orig_count - user_left;
00242         }
00243         oleft = 0;                      /* cancel further output */
00244     } 
00245     else if (user_left != 0) {          /* not yet done, continue! */
00246         prepare_output();
00247         return;
00248     } 
00249     else {                              /* done! report back to FS */
00250         status = orig_count;
00251     }
00252     revive_pending = TRUE;
00253     revive_status = status;
00254     notify(caller);
00255 }
00256 
00257 /*===========================================================================*
00258  *                              do_status                                    *
00259  *===========================================================================*/
00260 PRIVATE void do_status(m_ptr)
00261 register message *m_ptr;        /* pointer to the newly arrived message */
00262 {
00263   if (revive_pending) {
00264         m_ptr->m_type = DEV_REVIVE;             /* build message */
00265         m_ptr->REP_ENDPT = proc_nr;
00266         m_ptr->REP_STATUS = revive_status;
00267 
00268         writing = FALSE;                        /* unmark event */
00269         revive_pending = FALSE;                 /* unmark event */
00270   } else {
00271         m_ptr->m_type = DEV_NO_STATUS;
00272   }
00273   send(m_ptr->m_source, m_ptr);                 /* send the message */
00274 }
00275 
00276 /*===========================================================================*
00277  *                              do_cancel                                    *
00278  *===========================================================================*/
00279 PRIVATE void do_cancel(m_ptr)
00280 register message *m_ptr;        /* pointer to the newly arrived message */
00281 {
00282 /* Cancel a print request that has already started.  Usually this means that
00283  * the process doing the printing has been killed by a signal.  It is not
00284  * clear if there are race conditions.  Try not to cancel the wrong process,
00285  * but rely on FS to handle the EINTR reply and de-suspension properly.
00286  */
00287 
00288   if (writing && m_ptr->IO_ENDPT == proc_nr) {
00289         oleft = 0;              /* cancel output by interrupt handler */
00290         writing = FALSE;
00291         revive_pending = FALSE;
00292   }
00293   reply(TASK_REPLY, m_ptr->m_source, m_ptr->IO_ENDPT, EINTR);
00294 }
00295 
00296 /*===========================================================================*
00297  *                              reply                                        *
00298  *===========================================================================*/
00299 PRIVATE void reply(code, replyee, process, status)
00300 int code;                       /* TASK_REPLY or REVIVE */
00301 int replyee;                    /* destination for message (normally FS) */
00302 int process;                    /* which user requested the printing */
00303 int status;                     /* number of  chars printed or error code */
00304 {
00305 /* Send a reply telling FS that printing has started or stopped. */
00306 
00307   message pr_mess;
00308 
00309   pr_mess.m_type = code;                /* TASK_REPLY or REVIVE */
00310   pr_mess.REP_STATUS = status;          /* count or EIO */
00311   pr_mess.REP_ENDPT = process;  /* which user does this pertain to */
00312   send(replyee, &pr_mess);              /* send the message */
00313 }
00314 
00315 /*===========================================================================*
00316  *                              do_initialize                                *
00317  *===========================================================================*/
00318 PRIVATE void do_initialize()
00319 {
00320 /* Set global variables and initialize the printer. */
00321   static int initialized = FALSE;
00322   if (initialized) return;
00323   initialized = TRUE;
00324   
00325   /* Get the base port for first printer.  */
00326   sys_vircopy(SELF, BIOS_SEG, LPT1_IO_PORT_ADDR, 
00327         SELF, D, (vir_bytes) &port_base, LPT1_IO_PORT_SIZE);
00328   sys_outb(port_base + 2, INIT_PRINTER);
00329   tickdelay(1);                 /* easily satisfies Centronics minimum */
00330                                 /* was 2 millisecs; now is ~17 millisecs */
00331   sys_outb(port_base + 2, PR_SELECT);
00332   irq_hook_id = 0;
00333   sys_irqsetpolicy(PRINTER_IRQ, 0, &irq_hook_id);
00334   sys_irqenable(&irq_hook_id);
00335 
00336 }
00337 
00338 /*==========================================================================*
00339  *                            prepare_output                                *
00340  *==========================================================================*/
00341 PRIVATE void prepare_output()
00342 {
00343 /* Start next chunk of printer output. Fetch the data from user space. */
00344 
00345   register int chunk;
00346 
00347   if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf;
00348   if (OK!=sys_datacopy(proc_nr, user_vir, SELF, (vir_bytes) obuf, chunk)) {
00349         done_status = EFAULT;
00350         output_done();
00351         return;
00352   }
00353   optr = obuf;
00354   oleft = chunk;
00355 }
00356 
00357 /*===========================================================================*
00358  *                              do_printer_output                                    *
00359  *===========================================================================*/
00360 PRIVATE void do_printer_output()
00361 {
00362 /* This function does the actual output to the printer. This is called on
00363  * a HARD_INT message sent from the generic interrupt handler that 'forwards'
00364  * interrupts to this driver. The generic handler did not reenable the 
00365  * printer IRQ yet! 
00366  */
00367 
00368   unsigned long status;
00369   pvb_pair_t char_out[3];
00370 
00371   if (oleft == 0) {
00372         /* Nothing more to print.  Turn off printer interrupts in case they
00373          * are level-sensitive as on the PS/2.  This should be safe even
00374          * when the printer is busy with a previous character, because the
00375          * interrupt status does not affect the printer.
00376          */
00377         sys_outb(port_base + 2, PR_SELECT);
00378         sys_irqenable(&irq_hook_id);
00379         return;
00380   }
00381 
00382   do {
00383         /* Loop to handle fast (buffered) printers.  It is important that
00384          * processor interrupts are not disabled here, just printer interrupts.
00385          */
00386         (void) sys_inb(port_base + 1, &status);
00387         if ((status & STATUS_MASK) == BUSY_STATUS) {
00388                 /* Still busy with last output.  This normally happens
00389                  * immediately after doing output to an unbuffered or slow
00390                  * printer.  It may happen after a call from prepare_output or
00391                  * pr_restart, since they are not synchronized with printer
00392                  * interrupts.  It may happen after a spurious interrupt.
00393                  */
00394                 sys_irqenable(&irq_hook_id);
00395                 return;
00396         }
00397         if ((status & STATUS_MASK) == NORMAL_STATUS) {
00398                 /* Everything is all right.  Output another character. */
00399                 pv_set(char_out[0], port_base, *optr++);        
00400                 pv_set(char_out[1], port_base+2, ASSERT_STROBE);
00401                 pv_set(char_out[2], port_base+2, NEGATE_STROBE);
00402                 sys_voutb(char_out, 3); /* request series of port outb */
00403 
00404                 user_vir++;
00405                 user_left--;
00406         } else {
00407                 /* Error.  This would be better ignored (treat as busy). */
00408                 done_status = status;
00409                 output_done();
00410                 sys_irqenable(&irq_hook_id);
00411                 return;
00412         }
00413   }
00414   while (--oleft != 0);
00415 
00416   /* Finished printing chunk OK. */
00417   done_status = OK;
00418   output_done();
00419   sys_irqenable(&irq_hook_id);
00420 }
00421 

Generated on Fri Apr 14 22:57:17 2006 for minix by  doxygen 1.4.6