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                                 if (current->signal & ~current->blocked) {
 174                                         if (total_bytes_written + bytes_written)
 175                                                 return total_bytes_written + bytes_written;
 176                                         else
 177                                                 return -EINTR;
 178                                 }
 179                         }
 180                 }
 181 
 182                 total_bytes_written += bytes_written;
 183                 buf += bytes_written;
 184                 count -= bytes_written;
 185 
 186         } while (count > 0);
 187 
 188         return total_bytes_written;
 189 }
 190 
 191 static int lp_write_polled(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 192                            char * buf, int count)
 193 {
 194         int  retval;
 195         unsigned int minor = MINOR(inode->i_rdev);
 196         char c, *temp = buf;
 197 
 198 #ifdef LP_DEBUG
 199         if (jiffies-lp_last_call > LP_TIME(minor)) {
 200                 lp_total_chars = 0;
 201                 lp_max_count = 1;
 202         }
 203         lp_last_call = jiffies;
 204 #endif
 205 
 206         temp = buf;
 207         while (count > 0) {
 208                 c = get_fs_byte(temp);
 209                 retval = lp_char_polled(c, minor);
 210                 /* only update counting vars if character was printed */
 211                 if (retval) { count--; temp++;
 212 #ifdef LP_DEBUG
 213                         lp_total_chars++;
 214 #endif
 215                 }
 216                 if (!retval) { /* if printer timed out */
 217                         int status = LP_S(minor);
 218 
 219                         if (status & LP_POUTPA) {
 220                                 printk("lp%d out of paper\n", minor);
 221                                 if(LP_F(minor) & LP_ABORT)
 222                                         return temp-buf?temp-buf:-ENOSPC;
 223                                 current->state = TASK_INTERRUPTIBLE;
 224                                 current->timeout = jiffies + LP_TIMEOUT_POLLED;
 225                                 schedule();
 226                         } else
 227                         if (!(status & LP_PSELECD)) {
 228                                 printk("lp%d off-line\n", minor);
 229                                 if(LP_F(minor) & LP_ABORT)
 230                                         return temp-buf?temp-buf:-EIO;
 231                                 current->state = TASK_INTERRUPTIBLE;
 232                                 current->timeout = jiffies + LP_TIMEOUT_POLLED;
 233                                 schedule();
 234                         } else
 235                         /* not offline or out of paper. on fire? */
 236                         if (!(status & LP_PERRORP)) {
 237                                 printk("lp%d on fire\n", minor);
 238                                 if(LP_F(minor) & LP_ABORT)
 239                                         return temp-buf?temp-buf:-EFAULT;
 240                                 current->state = TASK_INTERRUPTIBLE;
 241                                 current->timeout = jiffies + LP_TIMEOUT_POLLED;
 242                                 schedule();
 243                         }
 244 
 245                         /* check for signals before going to sleep */
 246                         if (current->signal & ~current->blocked) {
 247                                 if (temp != buf)
 248                                         return temp-buf;
 249                                 else
 250                                         return -EINTR;
 251                         }
 252 #ifdef LP_DEBUG
 253                         printk("lp sleeping at %d characters for %d jiffies\n",
 254                                 lp_total_chars, LP_TIME(minor));
 255                         lp_total_chars=0;
 256 #endif
 257                         current->state = TASK_INTERRUPTIBLE;
 258                         current->timeout = jiffies + LP_TIME(minor);
 259                         schedule();
 260                 }
 261         }
 262         return temp-buf;
 263 }
 264 
 265 static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 266 {
 267         if (LP_IRQ(MINOR(inode->i_rdev)))
 268                 return lp_write_interrupt(inode, file, buf, count);
 269         else
 270                 return lp_write_polled(inode, file, buf, count);
 271 }
 272 
 273 static int lp_lseek(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 274                     off_t offset, int origin)
 275 {
 276         return -ESPIPE;
 277 }
 278 
 279 static int lp_open(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 280 {
 281         unsigned int minor = MINOR(inode->i_rdev);
 282         int ret;
 283         unsigned int irq;
 284         struct sigaction sa;
 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                 sa.sa_handler = lp_interrupt;
 299                 sa.sa_flags = SA_INTERRUPT;
 300                 sa.sa_mask = 0;
 301                 sa.sa_restorer = NULL;
 302                 ret = irqaction(irq, &sa);
 303                 if (ret) {
 304                         kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
 305                         lp_table[minor].lp_buffer = NULL;
 306                         printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
 307                         return ret;
 308                 }
 309         }
 310 
 311         LP_F(minor) |= LP_BUSY;
 312 
 313         return 0;
 314 }
 315 
 316 static void lp_release(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 317 {
 318         unsigned int minor = MINOR(inode->i_rdev);
 319         unsigned int irq;
 320 
 321         if ((irq = LP_IRQ(minor))) {
 322                 free_irq(irq);
 323                 kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
 324                 lp_table[minor].lp_buffer = NULL;
 325         }
 326 
 327         LP_F(minor) &= ~LP_BUSY;
 328 }
 329 
 330 
 331 static int lp_ioctl(struct inode *inode, struct file *file,
     /* [previous][next][first][last][top][bottom][index][help] */
 332                     unsigned int cmd, unsigned long arg)
 333 {
 334         unsigned int minor = MINOR(inode->i_rdev);
 335         int retval = 0;
 336 
 337 #ifdef LP_DEBUG
 338         printk("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
 339 #endif
 340         if (minor >= LP_NO)
 341                 return -ENODEV;
 342         if ((LP_F(minor) & LP_EXIST) == 0)
 343                 return -ENODEV;
 344         switch ( cmd ) {
 345                 case LPTIME:
 346                         LP_TIME(minor) = arg;
 347                         break;
 348                 case LPCHAR:
 349                         LP_CHAR(minor) = arg;
 350                         break;
 351                 case LPABORT:
 352                         if (arg)
 353                                 LP_F(minor) |= LP_ABORT;
 354                         else
 355                                 LP_F(minor) &= ~LP_ABORT;
 356                         break;
 357                 case LPWAIT:
 358                         LP_WAIT(minor) = arg;
 359                         break;
 360                 case LPSETIRQ: {
 361                         int oldirq;
 362                         int newirq = arg;
 363                         struct lp_struct *lp = &lp_table[minor];
 364                         struct sigaction sa;
 365 
 366                         if (!suser())
 367                                 return -EPERM;
 368 
 369                         oldirq = LP_IRQ(minor);
 370 
 371                         /* Allocate buffer now if we are going to need it */
 372                         if (!oldirq && newirq) {
 373                                 lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
 374                                 if (!lp->lp_buffer)
 375                                         return -ENOMEM;
 376                         }
 377 
 378                         if (oldirq) {
 379                                 free_irq(oldirq);
 380                         }
 381                         if (newirq) {
 382                                 /* Install new irq */
 383                                 sa.sa_handler = lp_interrupt;
 384                                 sa.sa_flags = SA_INTERRUPT;
 385                                 sa.sa_mask = 0;
 386                                 sa.sa_restorer = NULL;
 387                                 if ((retval = irqaction(newirq, &sa))) {
 388                                         if (oldirq) {
 389                                                 /* restore old irq */
 390                                                 irqaction(oldirq, &sa);
 391                                         } else {
 392                                                 /* We don't need the buffer */
 393                                                 kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
 394                                                 lp->lp_buffer = NULL;
 395                                         }
 396                                         return retval;
 397                                 }
 398                         }
 399                         if (oldirq && !newirq) {
 400                                 /* We don't need the buffer */
 401                                 kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
 402                                 lp->lp_buffer = NULL;
 403                         }
 404                         LP_IRQ(minor) = newirq;
 405                         lp_reset(minor);
 406                         break;
 407                 }
 408                 case LPGETIRQ:
 409                         retval = LP_IRQ(minor);
 410                         break;
 411                 default:
 412                         retval = -EINVAL;
 413         }
 414         return retval;
 415 }
 416 
 417 
 418 static struct file_operations lp_fops = {
 419         lp_lseek,
 420         NULL,           /* lp_read */
 421         lp_write,
 422         NULL,           /* lp_readdir */
 423         NULL,           /* lp_select */
 424         lp_ioctl,
 425         NULL,           /* lp_mmap */
 426         lp_open,
 427         lp_release
 428 };
 429 
 430 long lp_init(long kmem_start)
     /* [previous][next][first][last][top][bottom][index][help] */
 431 {
 432         int offset = 0;
 433         unsigned int testvalue = 0;
 434         int count = 0;
 435 
 436         if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
 437                 printk("unable to get major %d for line printer\n", LP_MAJOR);
 438                 return kmem_start;
 439         }
 440         /* take on all known port values */
 441         for (offset = 0; offset < LP_NO; offset++) {
 442                 /* write to port & read back to check */
 443                 outb_p( LP_DUMMY, LP_B(offset));
 444                 for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
 445                         ;
 446                 testvalue = inb_p(LP_B(offset));
 447                 if (testvalue != 255) {
 448                         LP_F(offset) |= LP_EXIST;
 449                         lp_reset(offset);
 450                         printk("lp_init: lp%d exists (%d), ", offset, testvalue);
 451                         if (LP_IRQ(offset))
 452                                 printk("using IRQ%d\n", LP_IRQ(offset));
 453                         else
 454                                 printk("using polling driver\n");
 455                         count++;
 456                 }
 457         }
 458         if (count == 0)
 459                 printk("lp_init: no lp devices found\n");
 460         return kmem_start;
 461 }

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