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); 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_lu(sef_cb_init_fresh); 127 sef_setcb_init_restart(sef_cb_init_fresh); 128 129 /* Register live update callbacks. */ 130 sef_setcb_lu_prepare(sef_cb_lu_prepare); 131 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid); 132 sef_setcb_lu_state_dump(sef_cb_lu_state_dump); 133 134 /* Register signal callbacks. */ 135 sef_setcb_signal_handler(sef_cb_signal_handler_term); 136 137 /* Let SEF perform startup. */ 138 sef_startup(); 139 } 140 141 /*===========================================================================* 142 * sef_cb_init_fresh * 143 *===========================================================================*/ 144 static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info)) 145 { 146 /* Initialize the printer driver. */ 147 148 /* If no printer is present, do not start. */ 149 if (!do_probe()) 150 return ENODEV; /* arbitrary error code */ 151 152 /* Announce we are up! */ 153 chardriver_announce(); 154 155 return OK; 156 } 157 158 /*===========================================================================* 159 * do_write * 160 *===========================================================================*/ 161 static ssize_t printer_write(devminor_t UNUSED(minor), u64_t UNUSED(position), 162 endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, 163 cdev_id_t id) 164 { 165 /* The printer is used by sending write requests to it. Process one. */ 166 int retries; 167 u32_t status; 168 169 /* Reject command if last write is not yet finished, the count is not 170 * positive, or we're asked not to block. 171 */ 172 if (writing) return EIO; 173 if (size <= 0) return EINVAL; 174 if (flags & CDEV_NONBLOCK) return EAGAIN; /* not supported */ 175 176 /* If no errors occurred, continue printing with the caller. 177 * First wait until the printer is online to prevent stupid errors. 178 */ 179 caller = endpt; 180 user_left = size; 181 orig_count = size; 182 user_vir_d = 0; /* Offset. */ 183 writing = TRUE; 184 grant_nr = grant; 185 write_id = id; 186 187 retries = MAX_ONLINE_RETRIES + 1; 188 while (--retries > 0) { 189 if (sys_inb(port_base + 1, &status) != OK) { 190 printf("printer: sys_inb of %x failed\n", port_base+1); 191 panic("sys_inb failed"); 192 } 193 if (status & ON_LINE) { /* printer online! */ 194 prepare_output(); 195 printer_intr(0); 196 return EDONTREPLY; /* we may already have sent a reply */ 197 } 198 micro_delay(500000); /* wait before retry */ 199 } 200 /* If we reach this point, the printer was not online in time. */ 201 done_status = status; 202 output_done(); 203 return EDONTREPLY; 204 } 205 206 /*===========================================================================* 207 * output_done * 208 *===========================================================================*/ 209 static void output_done() 210 { 211 /* Previous chunk of printing is finished. Continue if OK and more. 212 * Otherwise, reply to caller (FS). 213 */ 214 int status; 215 216 if (!writing) return; /* probably leftover interrupt */ 217 if (done_status != OK) { /* printer error occurred */ 218 status = EIO; 219 if ((done_status & ON_LINE) == 0) { 220 printf("Printer is not on line\n"); 221 } else if ((done_status & NO_PAPER)) { 222 printf("Printer is out of paper\n"); 223 status = EAGAIN; 224 } else { 225 printf("Printer error, status is 0x%02X\n", done_status); 226 } 227 /* Some characters have been printed, tell how many. */ 228 if (status == EAGAIN && user_left < orig_count) { 229 status = orig_count - user_left; 230 } 231 oleft = 0; /* cancel further output */ 232 } else if (user_left != 0) { /* not yet done, continue! */ 233 prepare_output(); 234 return; 235 } else { /* done! report back to FS */ 236 status = orig_count; 237 } 238 239 chardriver_reply_task(caller, write_id, status); 240 241 writing = FALSE; /* unmark event */ 242 } 243 244 /*===========================================================================* 245 * printer_cancel * 246 *===========================================================================*/ 247 static int printer_cancel(devminor_t UNUSED(minor), endpoint_t endpt, 248 cdev_id_t id) 249 { 250 /* Cancel a print request that has already started. Usually this means that 251 * the process doing the printing has been killed by a signal. It is not 252 * clear if there are race conditions. Try not to cancel the wrong process, 253 * but rely on VFS to handle the EINTR reply and de-suspension properly. 254 */ 255 256 if (writing && caller == endpt && write_id == id) { 257 oleft = 0; /* cancel output by interrupt handler */ 258 writing = FALSE; 259 return EINTR; 260 } 261 return EDONTREPLY; 262 } 263 264 /*===========================================================================* 265 * do_probe * 266 *===========================================================================*/ 267 static int do_probe(void) 268 { 269 /* See if there is a printer at all. */ 270 271 /* Get the base port for first printer. */ 272 if(sys_readbios(LPT1_IO_PORT_ADDR, &port_base, LPT1_IO_PORT_SIZE) != OK) { 273 panic("do_probe: sys_readbios failed"); 274 } 275 276 /* If the port is zero, the parallel port is not available at all. */ 277 return (port_base != 0); 278 } 279 280 /*===========================================================================* 281 * do_initialize * 282 *===========================================================================*/ 283 static void do_initialize() 284 { 285 /* Set global variables and initialize the printer. */ 286 if(sys_outb(port_base + 2, INIT_PRINTER) != OK) { 287 printf("printer: sys_outb of %x failed\n", port_base+2); 288 panic("do_initialize: sys_outb init failed"); 289 } 290 micro_delay(1000000/20); /* easily satisfies Centronics minimum */ 291 if(sys_outb(port_base + 2, PR_SELECT) != OK) { 292 printf("printer: sys_outb of %x failed\n", port_base+2); 293 panic("do_initialize: sys_outb select failed"); 294 } 295 irq_hook_id = 0; 296 if(sys_irqsetpolicy(PRINTER_IRQ, 0, &irq_hook_id) != OK || 297 sys_irqenable(&irq_hook_id) != OK) { 298 panic("do_initialize: irq enabling failed"); 299 } 300 } 301 302 /*==========================================================================* 303 * printer_open * 304 *==========================================================================*/ 305 static int printer_open(devminor_t UNUSED(minor), int UNUSED(access), 306 endpoint_t UNUSED(user_endpt)) 307 { 308 /* Initialize on first open. */ 309 static int initialized = FALSE; 310 311 if (!initialized) { 312 do_initialize(); 313 314 initialized = TRUE; 315 } 316 317 return OK; 318 } 319 320 /*==========================================================================* 321 * prepare_output * 322 *==========================================================================*/ 323 static void prepare_output() 324 { 325 /* Start next chunk of printer output. Fetch the data from user space. */ 326 int s; 327 register int chunk; 328 329 if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf; 330 331 s=sys_safecopyfrom(caller, grant_nr, user_vir_d, (vir_bytes) obuf, 332 chunk); 333 334 if(s != OK) { 335 done_status = EFAULT; 336 output_done(); 337 return; 338 } 339 optr = obuf; 340 oleft = chunk; 341 } 342 343 /*===========================================================================* 344 * printer_intr * 345 *===========================================================================*/ 346 static void printer_intr(unsigned int UNUSED(mask)) 347 { 348 /* This function does the actual output to the printer. This is called on an 349 * interrupt message sent from the generic interrupt handler that 'forwards' 350 * interrupts to this driver. The generic handler did not reenable the printer 351 * IRQ yet! 352 */ 353 354 u32_t status; 355 pvb_pair_t char_out[3]; 356 357 if (oleft == 0) { 358 /* Nothing more to print. Turn off printer interrupts in case they 359 * are level-sensitive as on the PS/2. This should be safe even 360 * when the printer is busy with a previous character, because the 361 * interrupt status does not affect the printer. 362 */ 363 if(sys_outb(port_base + 2, PR_SELECT) != OK) { 364 printf("printer: sys_outb of %x failed\n", port_base+2); 365 panic("sys_outb failed"); 366 } 367 if(sys_irqenable(&irq_hook_id) != OK) { 368 panic("sys_irqenable failed"); 369 } 370 return; 371 } 372 373 do { 374 /* Loop to handle fast (buffered) printers. It is important that 375 * processor interrupts are not disabled here, just printer interrupts. 376 */ 377 if(sys_inb(port_base + 1, &status) != OK) { 378 printf("printer: sys_inb of %x failed\n", port_base+1); 379 panic("sys_inb failed"); 380 } 381 if ((status & STATUS_MASK) == BUSY_STATUS) { 382 /* Still busy with last output. This normally happens 383 * immediately after doing output to an unbuffered or slow 384 * printer. It may happen after a call from prepare_output or 385 * pr_restart, since they are not synchronized with printer 386 * interrupts. It may happen after a spurious interrupt. 387 */ 388 if(sys_irqenable(&irq_hook_id) != OK) { 389 panic("sys_irqenable failed"); 390 } 391 return; 392 } 393 if ((status & STATUS_MASK) == NORMAL_STATUS) { 394 /* Everything is all right. Output another character. */ 395 pv_set(char_out[0], port_base, *optr); 396 optr++; 397 pv_set(char_out[1], port_base+2, ASSERT_STROBE); 398 pv_set(char_out[2], port_base+2, NEGATE_STROBE); 399 if(sys_voutb(char_out, 3) != OK) { 400 /* request series of port outb */ 401 panic("sys_voutb failed"); 402 } 403 404 user_vir_d++; 405 user_left--; 406 } else { 407 /* Error. This would be better ignored (treat as busy). */ 408 done_status = status; 409 output_done(); 410 if(sys_irqenable(&irq_hook_id) != OK) { 411 panic("sys_irqenable failed"); 412 } 413 return; 414 } 415 } 416 while (--oleft != 0); 417 418 /* Finished printing chunk OK. */ 419 done_status = OK; 420 output_done(); 421 if(sys_irqenable(&irq_hook_id) != OK) { 422 panic("sys_irqenable failed"); 423 } 424 } 425 426