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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *==========================================================================*/ 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 *==========================================================================*/ 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 *===========================================================================*/ 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