root/drivers/char/lp.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lp_reset
  2. lp_char_polled
  3. lp_char_interrupt
  4. lp_interrupt
  5. lp_write_interrupt
  6. lp_write_polled
  7. lp_write
  8. lp_lseek
  9. lp_open
  10. lp_release
  11. lp_ioctl
  12. init_module
  13. cleanup_module

   1 /*
   2  * Copyright (C) 1992 by Jim Weigand and Linus Torvalds
   3  * Copyright (C) 1992,1993 by Michael K. Johnson
   4  * - Thanks much to Gunter Windau for pointing out to me where the error
   5  *   checking ought to be.
   6  * Copyright (C) 1993 by Nigel Gamble (added interrupt code)
   7  * Copyright (C) 1994 by Alan Cox (Modularised it)
   8  * LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
   9  */
  10 
  11 #ifdef MODULE
  12 #include <linux/module.h>
  13 #include <linux/version.h>
  14 #else
  15 #define MOD_INC_USE_COUNT
  16 #define MOD_DEC_USE_COUNT
  17 #endif
  18 
  19 #include <linux/errno.h>
  20 #include <linux/kernel.h>
  21 #include <linux/major.h>
  22 #include <linux/sched.h>
  23 #include <linux/lp.h>
  24 #include <linux/malloc.h>
  25 #include <linux/ioport.h>
  26 #include <linux/fcntl.h>
  27 
  28 #include <asm/io.h>
  29 #include <asm/segment.h>
  30 #include <asm/system.h>
  31 
  32 /* the BIOS manuals say there can be up to 4 lpt devices
  33  * but I have not seen a board where the 4th address is listed
  34  * if you have different hardware change the table below 
  35  * please let me know if you have different equipment
  36  * if you have more than 3 printers, remember to increase LP_NO
  37  */
  38 struct lp_struct lp_table[] = {
  39         { 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
  40         { 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
  41         { 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
  42 }; 
  43 #define LP_NO 3
  44 
  45 /* Test if printer is ready (and optionally has no error conditions) */
  46 #define LP_READY(minor, status) \
  47   ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY))
  48 #define LP_CAREFUL_READY(minor, status) \
  49   ((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : 1)
  50 #define _LP_CAREFUL_READY(status) \
  51    (status & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
  52       (LP_PBUSY|LP_PSELECD|LP_PERRORP) 
  53 
  54 /* 
  55  * All my debugging code assumes that you debug with only one printer at
  56  * a time. RWWH
  57  */
  58 
  59 #undef LP_DEBUG
  60 
  61 static int lp_reset(int minor)
     /* [previous][next][first][last][top][bottom][index][help] */
  62 {
  63         int testvalue;
  64         unsigned char command;
  65 
  66         command = LP_PSELECP | LP_PINITP;
  67 
  68         /* reset value */
  69         outb_p(0, LP_C(minor));
  70         for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
  71                 ;
  72         outb_p(command, LP_C(minor));
  73         return LP_S(minor);
  74 }
  75 
  76 #ifdef LP_DEBUG
  77 static int lp_max_count = 1;
  78 #endif
  79 
  80 static int lp_char_polled(char lpchar, int minor)
     /* [previous][next][first][last][top][bottom][index][help] */
  81 {
  82         int status = 0, wait = 0;
  83         unsigned long count  = 0; 
  84 
  85         do {
  86                 status = LP_S(minor);
  87                 count ++;
  88                 if(need_resched)
  89                         schedule();
  90         } while(!LP_READY(minor,status) && count < LP_CHAR(minor));
  91 
  92         if (count == LP_CHAR(minor)) {
  93                 return 0;
  94                 /* we timed out, and the character was /not/ printed */
  95         }
  96 #ifdef LP_DEBUG
  97         if (count > lp_max_count) {
  98                 printk("lp success after %d counts.\n",count);
  99                 lp_max_count=count;
 100         }
 101 #endif
 102         outb_p(lpchar, LP_B(minor));
 103         /* must wait before taking strobe high, and after taking strobe
 104            low, according spec.  Some printers need it, others don't. */
 105         while(wait != LP_WAIT(minor)) wait++;
 106         /* control port takes strobe high */
 107         outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
 108         while(wait) wait--;
 109         /* take strobe low */
 110         outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
 111 
 112         return 1;
 113 }
 114 
 115 static int lp_char_interrupt(char lpchar, int minor)
     /* [previous][next][first][last][top][bottom][index][help] */
 116 {
 117         int wait = 0;
 118         unsigned char status;
 119 
 120 
 121         if (!((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
 122         || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
 123         || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) {
 124 
 125                 if (!LP_CAREFUL_READY(minor, status))
 126                         return 0;
 127                 outb_p(lpchar, LP_B(minor));
 128                 /* must wait before taking strobe high, and after taking strobe
 129                    low, according spec.  Some printers need it, others don't. */
 130                 while(wait != LP_WAIT(minor)) wait++;
 131                 /* control port takes strobe high */
 132                 outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
 133                 while(wait) wait--;
 134                 /* take strobe low */
 135                 outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
 136                 return 1;
 137         }
 138 
 139         return 0;
 140 }
 141 
 142 #ifdef LP_DEBUG
 143         unsigned int lp_total_chars = 0;
 144         unsigned int lp_last_call = 0;
 145 #endif
 146 
 147 static void lp_interrupt(int irq, struct pt_regs *regs)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 {
 149         struct lp_struct *lp = &lp_table[0];
 150         struct lp_struct *lp_end = &lp_table[LP_NO];
 151 
 152         while (irq != lp->irq) {
 153                 if (++lp >= lp_end)
 154                         return;
 155         }
 156 
 157         wake_up(&lp->lp_wait_q);
 158 }
 159 
 160 static int lp_write_interrupt(struct inode * inode, struct file * file, const char * buf, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 161 {
 162         unsigned int minor = MINOR(inode->i_rdev);
 163         unsigned long copy_size;
 164         unsigned long total_bytes_written = 0;
 165         unsigned long bytes_written;
 166         struct lp_struct *lp = &lp_table[minor];
 167         unsigned char status;
 168 
 169         do {
 170                 bytes_written = 0;
 171                 copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
 172                 memcpy_fromfs(lp->lp_buffer, buf, copy_size);
 173 
 174                 while (copy_size) {
 175                         if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
 176                                 --copy_size;
 177                                 ++bytes_written;
 178                         } else {
 179                                 int rc = total_bytes_written + bytes_written;
 180                                 status = LP_S(minor);
 181                                 if ((status & LP_POUTPA)) {
 182                                         printk(KERN_INFO "lp%d out of paper\n", minor);
 183                                         if (LP_F(minor) & LP_ABORT)
 184                                                 return rc?rc:-ENOSPC;
 185                                 } else if (!(status & LP_PSELECD)) {
 186                                         printk(KERN_INFO "lp%d off-line\n", minor);
 187                                         if (LP_F(minor) & LP_ABORT)
 188                                                 return rc?rc:-EIO;
 189                                 } else if (!(status & LP_PERRORP)) {
 190                                         printk(KERN_ERR "lp%d printer error\n", minor);
 191                                         if (LP_F(minor) & LP_ABORT)
 192                                                 return rc?rc:-EIO;
 193                                 }
 194                                 cli();
 195                                 outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
 196                                 status = LP_S(minor);
 197                                 if ((!(status & LP_PACK) || (status & LP_PBUSY))
 198                                   && LP_CAREFUL_READY(minor, status)) {
 199                                         outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
 200                                         sti();
 201                                         continue;
 202                                 }
 203                                 current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
 204                                 interruptible_sleep_on(&lp->lp_wait_q);
 205                                 outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
 206                                 sti();
 207                                 if (current->signal & ~current->blocked) {
 208                                         if (total_bytes_written + bytes_written)
 209                                                 return total_bytes_written + bytes_written;
 210                                         else
 211                                                 return -EINTR;
 212                                 }
 213                         }
 214                 }
 215 
 216                 total_bytes_written += bytes_written;
 217                 buf += bytes_written;
 218                 count -= bytes_written;
 219 
 220         } while (count > 0);
 221 
 222         return total_bytes_written;
 223 }
 224 
 225 static int lp_write_polled(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 226                            const char * buf, int count)
 227 {
 228         int  retval;
 229         unsigned int minor = MINOR(inode->i_rdev);
 230         char c;
 231         const char *temp = buf;
 232 
 233 #ifdef LP_DEBUG
 234         if (jiffies-lp_last_call > LP_TIME(minor)) {
 235                 lp_total_chars = 0;
 236                 lp_max_count = 1;
 237         }
 238         lp_last_call = jiffies;
 239 #endif
 240 
 241         temp = buf;
 242         while (count > 0) {
 243                 c = get_user(temp);
 244                 retval = lp_char_polled(c, minor);
 245                 /* only update counting vars if character was printed */
 246                 if (retval) { count--; temp++;
 247 #ifdef LP_DEBUG
 248                         lp_total_chars++;
 249 #endif
 250                 }
 251                 if (!retval) { /* if printer timed out */
 252                         int status = LP_S(minor);
 253 
 254                         if (status & LP_POUTPA) {
 255                                 printk(KERN_INFO "lp%d out of paper\n", minor);
 256                                 if(LP_F(minor) & LP_ABORT)
 257                                         return temp-buf?temp-buf:-ENOSPC;
 258                                 current->state = TASK_INTERRUPTIBLE;
 259                                 current->timeout = jiffies + LP_TIMEOUT_POLLED;
 260                                 schedule();
 261                         } else
 262                         if (!(status & LP_PSELECD)) {
 263                                 printk(KERN_INFO "lp%d off-line\n", minor);
 264                                 if(LP_F(minor) & LP_ABORT)
 265                                         return temp-buf?temp-buf:-EIO;
 266                                 current->state = TASK_INTERRUPTIBLE;
 267                                 current->timeout = jiffies + LP_TIMEOUT_POLLED;
 268                                 schedule();
 269                         } else
 270                         /* not offline or out of paper. on fire? */
 271                         if (!(status & LP_PERRORP)) {
 272                                 printk(KERN_ERR "lp%d reported invalid error status (on fire, eh?)\n", minor);
 273                                 if(LP_F(minor) & LP_ABORT)
 274                                         return temp-buf?temp-buf:-EIO;
 275                                 current->state = TASK_INTERRUPTIBLE;
 276                                 current->timeout = jiffies + LP_TIMEOUT_POLLED;
 277                                 schedule();
 278                         }
 279 
 280                         /* check for signals before going to sleep */
 281                         if (current->signal & ~current->blocked) {
 282                                 if (temp != buf)
 283                                         return temp-buf;
 284                                 else
 285                                         return -EINTR;
 286                         }
 287 #ifdef LP_DEBUG
 288                         printk("lp sleeping at %d characters for %d jiffies\n",
 289                                 lp_total_chars, LP_TIME(minor));
 290                         lp_total_chars=0;
 291 #endif
 292                         current->state = TASK_INTERRUPTIBLE;
 293                         current->timeout = jiffies + LP_TIME(minor);
 294                         schedule();
 295                 }
 296         }
 297         return temp-buf;
 298 }
 299 
 300 static int lp_write(struct inode * inode, struct file * file, const char * buf, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 301 {
 302         if (LP_IRQ(MINOR(inode->i_rdev)))
 303                 return lp_write_interrupt(inode, file, buf, count);
 304         else
 305                 return lp_write_polled(inode, file, buf, count);
 306 }
 307 
 308 static int lp_lseek(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 309                     off_t offset, int origin)
 310 {
 311         return -ESPIPE;
 312 }
 313 
 314 static int lp_open(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 315 {
 316         unsigned int minor = MINOR(inode->i_rdev);
 317         int ret;
 318         unsigned int irq;
 319 
 320         if (minor >= LP_NO)
 321                 return -ENODEV;
 322         if ((LP_F(minor) & LP_EXIST) == 0)
 323                 return -ENODEV;
 324         if (LP_F(minor) & LP_BUSY)
 325                 return -EBUSY;
 326 
 327         MOD_INC_USE_COUNT;
 328 
 329         /* If ABORTOPEN is set and the printer is offline or out of paper,
 330            we may still want to open it to perform ioctl()s.  Therefore we
 331            have commandeered O_NONBLOCK, even though it is being used in
 332            a non-standard manner.  This is strictly a Linux hack, and
 333            should most likely only ever be used by the tunelp application. */
 334         if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
 335                 int status = LP_S(minor);
 336                 if (status & LP_POUTPA) {
 337                         printk(KERN_INFO "lp%d out of paper\n", minor);
 338                         MOD_DEC_USE_COUNT;
 339                         return -ENOSPC;
 340                 } else if (!(status & LP_PSELECD)) {
 341                         printk(KERN_INFO "lp%d off-line\n", minor);
 342                         MOD_DEC_USE_COUNT;
 343                         return -EIO;
 344                 } else if (!(status & LP_PERRORP)) {
 345                         printk(KERN_ERR "lp%d printer error\n", minor);
 346                         MOD_DEC_USE_COUNT;
 347                         return -EIO;
 348                 }
 349         }
 350 
 351         if ((irq = LP_IRQ(minor))) {
 352                 lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
 353                 if (!lp_table[minor].lp_buffer) {
 354                         MOD_DEC_USE_COUNT;
 355                         return -ENOMEM;
 356                 }
 357 
 358                 ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer");
 359                 if (ret) {
 360                         kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
 361                         lp_table[minor].lp_buffer = NULL;
 362                         printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
 363                         MOD_DEC_USE_COUNT;
 364                         return ret;
 365                 }
 366         }
 367 
 368         LP_F(minor) |= LP_BUSY;
 369         return 0;
 370 }
 371 
 372 static void lp_release(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 373 {
 374         unsigned int minor = MINOR(inode->i_rdev);
 375         unsigned int irq;
 376 
 377         if ((irq = LP_IRQ(minor))) {
 378                 free_irq(irq);
 379                 kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
 380                 lp_table[minor].lp_buffer = NULL;
 381         }
 382 
 383         LP_F(minor) &= ~LP_BUSY;
 384         MOD_DEC_USE_COUNT;
 385 }
 386 
 387 
 388 static int lp_ioctl(struct inode *inode, struct file *file,
     /* [previous][next][first][last][top][bottom][index][help] */
 389                     unsigned int cmd, unsigned long arg)
 390 {
 391         unsigned int minor = MINOR(inode->i_rdev);
 392         int retval = 0;
 393 
 394 #ifdef LP_DEBUG
 395         printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
 396 #endif
 397         if (minor >= LP_NO)
 398                 return -ENODEV;
 399         if ((LP_F(minor) & LP_EXIST) == 0)
 400                 return -ENODEV;
 401         switch ( cmd ) {
 402                 case LPTIME:
 403                         LP_TIME(minor) = arg;
 404                         break;
 405                 case LPCHAR:
 406                         LP_CHAR(minor) = arg;
 407                         break;
 408                 case LPABORT:
 409                         if (arg)
 410                                 LP_F(minor) |= LP_ABORT;
 411                         else
 412                                 LP_F(minor) &= ~LP_ABORT;
 413                         break;
 414                 case LPABORTOPEN:
 415                         if (arg)
 416                                 LP_F(minor) |= LP_ABORTOPEN;
 417                         else
 418                                 LP_F(minor) &= ~LP_ABORTOPEN;
 419                         break;
 420                 case LPCAREFUL:
 421                         if (arg)
 422                                 LP_F(minor) |= LP_CAREFUL;
 423                         else
 424                                 LP_F(minor) &= ~LP_CAREFUL;
 425                         break;
 426                 case LPWAIT:
 427                         LP_WAIT(minor) = arg;
 428                         break;
 429                 case LPSETIRQ: {
 430                         int oldirq;
 431                         int newirq = arg;
 432                         struct lp_struct *lp = &lp_table[minor];
 433 
 434                         if (!suser())
 435                                 return -EPERM;
 436 
 437                         oldirq = LP_IRQ(minor);
 438 
 439                         /* Allocate buffer now if we are going to need it */
 440                         if (!oldirq && newirq) {
 441                                 lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
 442                                 if (!lp->lp_buffer)
 443                                         return -ENOMEM;
 444                         }
 445 
 446                         if (oldirq) {
 447                                 free_irq(oldirq);
 448                         }
 449                         if (newirq) {
 450                                 /* Install new irq */
 451                                 if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer"))) {
 452                                         if (oldirq) {
 453                                                 /* restore old irq */
 454                                                 request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer");
 455                                         } else {
 456                                                 /* We don't need the buffer */
 457                                                 kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
 458                                                 lp->lp_buffer = NULL;
 459                                         }
 460                                         return retval;
 461                                 }
 462                         }
 463                         if (oldirq && !newirq) {
 464                                 /* We don't need the buffer */
 465                                 kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
 466                                 lp->lp_buffer = NULL;
 467                         }
 468                         LP_IRQ(minor) = newirq;
 469                         lp_reset(minor);
 470                         break;
 471                 }
 472                 case LPGETIRQ:
 473                         retval = verify_area(VERIFY_WRITE, (void *) arg,
 474                             sizeof(int));
 475                         if (retval)
 476                                 return retval;
 477                         memcpy_tofs((int *) arg, &LP_IRQ(minor), sizeof(int));
 478                         break;
 479                 case LPGETSTATUS:
 480                         retval = verify_area(VERIFY_WRITE, (void *) arg,
 481                             sizeof(int));
 482                         if (retval)
 483                                 return retval;
 484                         else {
 485                                 int status = LP_S(minor);
 486                                 memcpy_tofs((int *) arg, &status, sizeof(int));
 487                         }
 488                         break;
 489                 case LPRESET:
 490                         lp_reset(minor);
 491                         break;
 492                 default:
 493                         retval = -EINVAL;
 494         }
 495         return retval;
 496 }
 497 
 498 
 499 static struct file_operations lp_fops = {
 500         lp_lseek,
 501         NULL,           /* lp_read */
 502         lp_write,
 503         NULL,           /* lp_readdir */
 504         NULL,           /* lp_select */
 505         lp_ioctl,
 506         NULL,           /* lp_mmap */
 507         lp_open,
 508         lp_release
 509 };
 510 
 511 #ifdef MODULE
 512 char kernel_version[]=UTS_RELEASE;
 513 
 514 int init_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 515 #else
 516 long lp_init(long kmem_start)
 517 #endif
 518 {
 519         int offset = 0;
 520         unsigned int testvalue = 0;
 521         int count = 0;
 522 
 523         if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
 524                 printk("lp: unable to get major %d\n", LP_MAJOR);
 525 #ifdef MODULE
 526                 return -EIO;
 527 #else           
 528                 return kmem_start;
 529 #endif
 530         }
 531         /* take on all known port values */
 532         for (offset = 0; offset < LP_NO; offset++) {
 533                 if (check_region(LP_B(offset), 3))
 534                         continue;
 535                 /* write to port & read back to check */
 536                 outb_p( LP_DUMMY, LP_B(offset));
 537                 for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
 538                         ;
 539                 testvalue = inb_p(LP_B(offset));
 540                 if (testvalue == LP_DUMMY) {
 541                         LP_F(offset) |= LP_EXIST;
 542                         lp_reset(offset);
 543                         printk("lp%d at 0x%04x, ", offset,LP_B(offset));
 544                         request_region(LP_B(offset), 3, "lp");
 545                         if (LP_IRQ(offset))
 546                                 printk("(irq = %d)\n", LP_IRQ(offset));
 547                         else
 548                                 printk("(polling)\n");
 549                         count++;
 550                 }
 551         }
 552         if (count == 0)
 553                 printk("lp: Driver configured but no interfaces found.\n");
 554 
 555 
 556 #ifdef MODULE
 557         return 0;
 558 #else   
 559         return kmem_start;
 560 #endif
 561 }
 562 
 563 #ifdef MODULE
 564 void cleanup_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 565 {
 566         int offset;
 567         if(MOD_IN_USE)
 568                printk("lp: busy - remove delayed\n");
 569         else {
 570                unregister_chrdev(LP_MAJOR,"lp");
 571                for (offset = 0; offset < LP_NO; offset++) 
 572                         if (LP_F(offset) & LP_EXIST) 
 573                                 release_region(LP_B(offset),3);
 574         }
 575 }
 576 
 577 #endif

/* [previous][next][first][last][top][bottom][index][help] */