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

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