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

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