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

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

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