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
1.4.6