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

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