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 
 286         if (minor >= LP_NO)
 287                 return -ENODEV;
 288         if ((LP_F(minor) & LP_EXIST) == 0)
 289                 return -ENODEV;
 290         if (LP_F(minor) & LP_BUSY)
 291                 return -EBUSY;
 292 
 293         if ((irq = LP_IRQ(minor))) {
 294                 lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
 295                 if (!lp_table[minor].lp_buffer)
 296                         return -ENOMEM;
 297 
 298                 ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer");
 299                 if (ret) {
 300                         kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
 301                         lp_table[minor].lp_buffer = NULL;
 302                         printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
 303                         return ret;
 304                 }
 305         }
 306 
 307         LP_F(minor) |= LP_BUSY;
 308 
 309         return 0;
 310 }
 311 
 312 static void lp_release(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 313 {
 314         unsigned int minor = MINOR(inode->i_rdev);
 315         unsigned int irq;
 316 
 317         if ((irq = LP_IRQ(minor))) {
 318                 free_irq(irq);
 319                 kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
 320                 lp_table[minor].lp_buffer = NULL;
 321         }
 322 
 323         LP_F(minor) &= ~LP_BUSY;
 324 }
 325 
 326 
 327 static int lp_ioctl(struct inode *inode, struct file *file,
     /* [previous][next][first][last][top][bottom][index][help] */
 328                     unsigned int cmd, unsigned long arg)
 329 {
 330         unsigned int minor = MINOR(inode->i_rdev);
 331         int retval = 0;
 332 
 333 #ifdef LP_DEBUG
 334         printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
 335 #endif
 336         if (minor >= LP_NO)
 337                 return -ENODEV;
 338         if ((LP_F(minor) & LP_EXIST) == 0)
 339                 return -ENODEV;
 340         switch ( cmd ) {
 341                 case LPTIME:
 342                         LP_TIME(minor) = arg;
 343                         break;
 344                 case LPCHAR:
 345                         LP_CHAR(minor) = arg;
 346                         break;
 347                 case LPABORT:
 348                         if (arg)
 349                                 LP_F(minor) |= LP_ABORT;
 350                         else
 351                                 LP_F(minor) &= ~LP_ABORT;
 352                         break;
 353                 case LPWAIT:
 354                         LP_WAIT(minor) = arg;
 355                         break;
 356                 case LPSETIRQ: {
 357                         int oldirq;
 358                         int newirq = arg;
 359                         struct lp_struct *lp = &lp_table[minor];
 360 
 361                         if (!suser())
 362                                 return -EPERM;
 363 
 364                         oldirq = LP_IRQ(minor);
 365 
 366                         /* Allocate buffer now if we are going to need it */
 367                         if (!oldirq && newirq) {
 368                                 lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
 369                                 if (!lp->lp_buffer)
 370                                         return -ENOMEM;
 371                         }
 372 
 373                         if (oldirq) {
 374                                 free_irq(oldirq);
 375                         }
 376                         if (newirq) {
 377                                 /* Install new irq */
 378                                 if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer"))) {
 379                                         if (oldirq) {
 380                                                 /* restore old irq */
 381                                                 request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer");
 382                                         } else {
 383                                                 /* We don't need the buffer */
 384                                                 kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
 385                                                 lp->lp_buffer = NULL;
 386                                         }
 387                                         return retval;
 388                                 }
 389                         }
 390                         if (oldirq && !newirq) {
 391                                 /* We don't need the buffer */
 392                                 kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
 393                                 lp->lp_buffer = NULL;
 394                         }
 395                         LP_IRQ(minor) = newirq;
 396                         lp_reset(minor);
 397                         break;
 398                 }
 399                 case LPGETIRQ:
 400                         retval = LP_IRQ(minor);
 401                         break;
 402                 default:
 403                         retval = -EINVAL;
 404         }
 405         return retval;
 406 }
 407 
 408 
 409 static struct file_operations lp_fops = {
 410         lp_lseek,
 411         NULL,           /* lp_read */
 412         lp_write,
 413         NULL,           /* lp_readdir */
 414         NULL,           /* lp_select */
 415         lp_ioctl,
 416         NULL,           /* lp_mmap */
 417         lp_open,
 418         lp_release
 419 };
 420 
 421 long lp_init(long kmem_start)
     /* [previous][next][first][last][top][bottom][index][help] */
 422 {
 423         int offset = 0;
 424         unsigned int testvalue = 0;
 425         int count = 0;
 426 
 427         if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
 428                 printk("unable to get major %d for line printer\n", LP_MAJOR);
 429                 return kmem_start;
 430         }
 431         /* take on all known port values */
 432         for (offset = 0; offset < LP_NO; offset++) {
 433                 /* write to port & read back to check */
 434                 outb_p( LP_DUMMY, LP_B(offset));
 435                 for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
 436                         ;
 437                 testvalue = inb_p(LP_B(offset));
 438                 if (testvalue == LP_DUMMY) {
 439                         LP_F(offset) |= LP_EXIST;
 440                         lp_reset(offset);
 441                         printk("lp_init: lp%d exists, ", offset);
 442                         if (LP_IRQ(offset))
 443                                 printk("using IRQ%d\n", LP_IRQ(offset));
 444                         else
 445                                 printk("using polling driver\n");
 446                         count++;
 447                 }
 448         }
 449         if (count == 0)
 450                 printk("lp_init: no lp devices found\n");
 451         return kmem_start;
 452 }

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