/* $Header: /usr/src/linux/kernel/chr_drv/lp.c,v 1.9 1992/01/06 16:11:19 james_r_wiegand Exp james_r_wiegand $ */ /* * Edited by Linus - cleaner interface etc. Still not using interrupts, so * it eats more resources than necessary, but it was easy to code this way... */ #include <linux/sched.h> #define __LP_C__ #include <linux/lp.h> static int lp_reset(int minor) { int testvalue; /* reset value */ outb(0, LP_B(minor)+2); for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++) ; outb(LP_PSELECP | LP_PINITP, LP_B(minor)+2); return LP_S(minor); } static int lp_char(char lpchar, int minor) { int retval = 0; unsigned long count = 0; outb(lpchar, LP_B(minor)); do { retval = LP_S(minor); schedule(); count ++; } while(!(retval & LP_PBUSY) && count < LP_TIMEOUT); if (count == LP_TIMEOUT) { printk("lp%d timeout\n\r", minor); return 0; } /* control port pr_table[0]+2 take strobe high */ outb(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_B( minor ) + 2 )); /* take strobe low */ outb(( LP_PSELECP | LP_PINITP ), ( LP_B( minor ) + 2 )); /* get something meaningful for return value */ return LP_S(minor); } static int lp_write(struct inode * inode, struct file * file, char * buf, int count) { int retval; unsigned int minor = MINOR(inode->i_rdev); char c, *temp = buf; temp = buf; while (count > 0) { c = get_fs_byte(temp++); retval = lp_char(c, minor); count--; if (retval & LP_POUTPA) { LP_F(minor) |= LP_NOPA; return temp-buf?temp-buf:-ENOSPC; } else LP_F(minor) &= ~LP_NOPA; if (!(retval & LP_PSELECD)) { LP_F(minor) &= ~LP_SELEC; return temp-buf?temp-buf:-EFAULT; } else LP_F(minor) &= ~LP_SELEC; /* not offline or out of paper. on fire? */ if (!(retval & LP_PERRORP)) { LP_F(minor) |= LP_ERR; return temp-buf?temp-buf:-EIO; } else LP_F(minor) &= ~LP_SELEC; } return temp-buf; } static int lp_read(struct inode * inode, struct file * file, char * buf, int count) { return -EINVAL; } static int lp_lseek(struct inode * inode, struct file * file, off_t offset, int origin) { return -EINVAL; } static int lp_open(struct inode * inode, struct file * file) { unsigned int minor = MINOR(inode->i_rdev); if (minor >= LP_NO) return -ENODEV; if ((LP_F(minor) & LP_EXIST) == 0) return -ENODEV; if (LP_F(minor) & LP_BUSY) return -EBUSY; LP_F(minor) |= LP_BUSY; return 0; } static void lp_release(struct inode * inode, struct file * file) { unsigned int minor = MINOR(inode->i_rdev); LP_F(minor) &= ~LP_BUSY; } static struct file_operations lp_fops = { lp_lseek, lp_read, lp_write, NULL, /* lp_readdir */ NULL, /* lp_select */ NULL, /* lp_ioctl */ lp_open, lp_release }; long lp_init(long kmem_start) { int offset = 0; unsigned int testvalue = 0; int count = 0; chrdev_fops[6] = &lp_fops; /* take on all known port values */ for (offset = 0; offset < LP_NO; offset++) { /* write to port & read back to check */ outb( LP_DUMMY, LP_B(offset)); for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++) ; testvalue = inb(LP_B(offset)); if (testvalue != 255) { LP_F(offset) |= LP_EXIST; lp_reset(offset); printk("lp_init: lp%d exists (%d)\n", offset, testvalue); count++; } } if (count == 0) printk("lp_init: no lp devices found\n"); return kmem_start; }