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

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