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

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