The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/drivers/printer/printer.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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   PROC_NR    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.PROC_NR, 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             default:
  147                 reply(TASK_REPLY, pr_mess.m_source, pr_mess.PROC_NR, EINVAL);
  148         }
  149   }
  150 }
  151 
  152 
  153 /*===========================================================================*
  154  *                               do_signal                                   *
  155  *===========================================================================*/
  156 PRIVATE void do_signal(m_ptr)
  157 message *m_ptr;                                 /* signal message */
  158 {
  159   int sig;
  160   sigset_t sigset = m_ptr->NOTIFY_ARG;
  161   
  162   /* Expect a SIGTERM signal when this server must shutdown. */
  163   if (sigismember(&sigset, SIGTERM)) {
  164         exit(0);
  165   } 
  166   /* Ignore all other signals. */
  167 }
  168 
  169 /*===========================================================================*
  170  *                              do_write                                     *
  171  *===========================================================================*/
  172 PRIVATE void do_write(m_ptr)
  173 register message *m_ptr;        /* pointer to the newly arrived message */
  174 {
  175 /* The printer is used by sending DEV_WRITE messages to it. Process one. */
  176 
  177     register int r = SUSPEND;
  178     int retries;
  179     int status;
  180 
  181     /* Reject command if last write is not yet finished, the count is not
  182      * positive, or the user address is bad.
  183      */
  184     if (writing)                        r = EIO;
  185     else if (m_ptr->COUNT <= 0)         r = EINVAL;
  186 
  187     /* Reply to FS, no matter what happened, possible SUSPEND caller. */
  188     reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r);
  189 
  190     /* If no errors occurred, continue printing with SUSPENDED caller.
  191      * First wait until the printer is online to prevent stupid errors.
  192      */
  193     if (SUSPEND == r) {         
  194         caller = m_ptr->m_source;
  195         proc_nr = m_ptr->PROC_NR;
  196         user_left = m_ptr->COUNT;
  197         orig_count = m_ptr->COUNT;
  198         user_vir = (vir_bytes) m_ptr->ADDRESS;
  199         writing = TRUE;
  200 
  201         retries = MAX_ONLINE_RETRIES + 1;  
  202         while (--retries > 0) {
  203             sys_inb(port_base + 1, &status);
  204             if ((status & ON_LINE)) {           /* printer online! */
  205                 prepare_output();
  206                 do_printer_output();
  207                 return;
  208             }
  209             tickdelay(30);              /* wait before retry */
  210         }
  211         /* If we reach this point, the printer was not online in time. */
  212         done_status = status;
  213         output_done();
  214     }
  215 }
  216 
  217 /*===========================================================================*
  218  *                              output_done                                  *
  219  *===========================================================================*/
  220 PRIVATE void output_done()
  221 {
  222 /* Previous chunk of printing is finished.  Continue if OK and more.
  223  * Otherwise, reply to caller (FS).
  224  */
  225     register int status;
  226 
  227     if (!writing) return;               /* probably leftover interrupt */
  228     if (done_status != OK) {            /* printer error occurred */
  229         status = EIO;
  230         if ((done_status & ON_LINE) == 0) { 
  231             printf("Printer is not on line\n");
  232         } else if ((done_status & NO_PAPER)) { 
  233             printf("Printer is out of paper\n");
  234             status = EAGAIN;    
  235         } else {
  236             printf("Printer error, status is 0x%02X\n", done_status);
  237         }
  238         /* Some characters have been printed, tell how many. */
  239         if (status == EAGAIN && user_left < orig_count) {
  240                 status = orig_count - user_left;
  241         }
  242         oleft = 0;                      /* cancel further output */
  243     } 
  244     else if (user_left != 0) {          /* not yet done, continue! */
  245         prepare_output();
  246         return;
  247     } 
  248     else {                              /* done! report back to FS */
  249         status = orig_count;
  250     }
  251     revive_pending = TRUE;
  252     revive_status = status;
  253     notify(caller);
  254 }
  255 
  256 /*===========================================================================*
  257  *                              do_status                                    *
  258  *===========================================================================*/
  259 PRIVATE void do_status(m_ptr)
  260 register message *m_ptr;        /* pointer to the newly arrived message */
  261 {
  262   if (revive_pending) {
  263         m_ptr->m_type = DEV_REVIVE;             /* build message */
  264         m_ptr->REP_PROC_NR = proc_nr;
  265         m_ptr->REP_STATUS = revive_status;
  266 
  267         writing = FALSE;                        /* unmark event */
  268         revive_pending = FALSE;                 /* unmark event */
  269   } else {
  270         m_ptr->m_type = DEV_NO_STATUS;
  271   }
  272   send(m_ptr->m_source, m_ptr);                 /* send the message */
  273 }
  274 
  275 /*===========================================================================*
  276  *                              do_cancel                                    *
  277  *===========================================================================*/
  278 PRIVATE void do_cancel(m_ptr)
  279 register message *m_ptr;        /* pointer to the newly arrived message */
  280 {
  281 /* Cancel a print request that has already started.  Usually this means that
  282  * the process doing the printing has been killed by a signal.  It is not
  283  * clear if there are race conditions.  Try not to cancel the wrong process,
  284  * but rely on FS to handle the EINTR reply and de-suspension properly.
  285  */
  286 
  287   if (writing && m_ptr->PROC_NR == proc_nr) {
  288         oleft = 0;              /* cancel output by interrupt handler */
  289         writing = FALSE;
  290         revive_pending = FALSE;
  291   }
  292   reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR);
  293 }
  294 
  295 /*===========================================================================*
  296  *                              reply                                        *
  297  *===========================================================================*/
  298 PRIVATE void reply(code, replyee, process, status)
  299 int code;                       /* TASK_REPLY or REVIVE */
  300 int replyee;                    /* destination for message (normally FS) */
  301 int process;                    /* which user requested the printing */
  302 int status;                     /* number of  chars printed or error code */
  303 {
  304 /* Send a reply telling FS that printing has started or stopped. */
  305 
  306   message pr_mess;
  307 
  308   pr_mess.m_type = code;                /* TASK_REPLY or REVIVE */
  309   pr_mess.REP_STATUS = status;          /* count or EIO */
  310   pr_mess.REP_PROC_NR = process;        /* which user does this pertain to */
  311   send(replyee, &pr_mess);              /* send the message */
  312 }
  313 
  314 /*===========================================================================*
  315  *                              do_initialize                                *
  316  *===========================================================================*/
  317 PRIVATE void do_initialize()
  318 {
  319 /* Set global variables and initialize the printer. */
  320   static int initialized = FALSE;
  321   if (initialized) return;
  322   initialized = TRUE;
  323   
  324   /* Get the base port for first printer.  */
  325   sys_vircopy(SELF, BIOS_SEG, LPT1_IO_PORT_ADDR, 
  326         SELF, D, (vir_bytes) &port_base, LPT1_IO_PORT_SIZE);
  327   sys_outb(port_base + 2, INIT_PRINTER);
  328   tickdelay(1);                 /* easily satisfies Centronics minimum */
  329                                 /* was 2 millisecs; now is ~17 millisecs */
  330   sys_outb(port_base + 2, PR_SELECT);
  331   irq_hook_id = 0;
  332   sys_irqsetpolicy(PRINTER_IRQ, 0, &irq_hook_id);
  333   sys_irqenable(&irq_hook_id);
  334 
  335 }
  336 
  337 /*==========================================================================*
  338  *                            prepare_output                                *
  339  *==========================================================================*/
  340 PRIVATE void prepare_output()
  341 {
  342 /* Start next chunk of printer output. Fetch the data from user space. */
  343 
  344   register int chunk;
  345 
  346   if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf;
  347   if (OK!=sys_datacopy(proc_nr, user_vir, SELF, (vir_bytes) obuf, chunk)) {
  348         done_status = EFAULT;
  349         output_done();
  350         return;
  351   }
  352   optr = obuf;
  353   oleft = chunk;
  354 }
  355 
  356 /*===========================================================================*
  357  *                              do_printer_output                                    *
  358  *===========================================================================*/
  359 PRIVATE void do_printer_output()
  360 {
  361 /* This function does the actual output to the printer. This is called on
  362  * a HARD_INT message sent from the generic interrupt handler that 'forwards'
  363  * interrupts to this driver. The generic handler did not reenable the 
  364  * printer IRQ yet! 
  365  */
  366 
  367   int status;
  368   pvb_pair_t char_out[3];
  369 
  370   if (oleft == 0) {
  371         /* Nothing more to print.  Turn off printer interrupts in case they
  372          * are level-sensitive as on the PS/2.  This should be safe even
  373          * when the printer is busy with a previous character, because the
  374          * interrupt status does not affect the printer.
  375          */
  376         sys_outb(port_base + 2, PR_SELECT);
  377         sys_irqenable(&irq_hook_id);
  378         return;
  379   }
  380 
  381   do {
  382         /* Loop to handle fast (buffered) printers.  It is important that
  383          * processor interrupts are not disabled here, just printer interrupts.
  384          */
  385         (void) sys_inb(port_base + 1, &status);
  386         if ((status & STATUS_MASK) == BUSY_STATUS) {
  387                 /* Still busy with last output.  This normally happens
  388                  * immediately after doing output to an unbuffered or slow
  389                  * printer.  It may happen after a call from prepare_output or
  390                  * pr_restart, since they are not synchronized with printer
  391                  * interrupts.  It may happen after a spurious interrupt.
  392                  */
  393                 sys_irqenable(&irq_hook_id);
  394                 return;
  395         }
  396         if ((status & STATUS_MASK) == NORMAL_STATUS) {
  397                 /* Everything is all right.  Output another character. */
  398                 pv_set(char_out[0], port_base, *optr++);        
  399                 pv_set(char_out[1], port_base+2, ASSERT_STROBE);
  400                 pv_set(char_out[2], port_base+2, NEGATE_STROBE);
  401                 sys_voutb(char_out, 3); /* request series of port outb */
  402 
  403                 user_vir++;
  404                 user_left--;
  405         } else {
  406                 /* Error.  This would be better ignored (treat as busy). */
  407                 done_status = status;
  408                 output_done();
  409                 sys_irqenable(&irq_hook_id);
  410                 return;
  411         }
  412   }
  413   while (--oleft != 0);
  414 
  415   /* Finished printing chunk OK. */
  416   done_status = OK;
  417   output_done();
  418   sys_irqenable(&irq_hook_id);
  419 }
  420 

Cache object: e67d14d3b3ac4b62e490aa9c2afec5c0


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.