📄 lp.c
字号:
we may still want to open it to perform ioctl()s. Therefore we have commandeered O_NONBLOCK, even though it is being used in a non-standard manner. This is strictly a Linux hack, and should most likely only ever be used by the tunelp application. */ if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) { int status = LP_S(minor); if (status & LP_POUTPA) { printk(KERN_INFO "lp%d out of paper\n", minor); MOD_DEC_USE_COUNT; return -ENOSPC; } else if (!(status & LP_PSELECD)) { printk(KERN_INFO "lp%d off-line\n", minor); MOD_DEC_USE_COUNT; return -EIO; } else if (!(status & LP_PERRORP)) { printk(KERN_ERR "lp%d printer error\n", minor); MOD_DEC_USE_COUNT; return -EIO; } } if ((irq = LP_IRQ(minor)) != NO_IRQ) { lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL); if (!lp_table[minor].lp_buffer) { MOD_DEC_USE_COUNT; return -ENOMEM; } ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer", NULL); if (ret) { kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE); lp_table[minor].lp_buffer = NULL; printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret); MOD_DEC_USE_COUNT; return ret; } } LP_F(minor) |= LP_BUSY; return 0;}static void lp_release(struct inode * inode, struct file * file){ unsigned int minor = MINOR(inode->i_rdev); unsigned int irq; if ((irq = LP_IRQ(minor)) != NO_IRQ) { free_irq(irq, NULL); kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE); lp_table[minor].lp_buffer = NULL; } LP_F(minor) &= ~LP_BUSY; MOD_DEC_USE_COUNT;}static int lp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ unsigned int minor = MINOR(inode->i_rdev); int retval = 0;#ifdef LP_DEBUG printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);#endif if (minor >= LP_NO) return -ENODEV; if ((LP_F(minor) & LP_EXIST) == 0) return -ENODEV; switch ( cmd ) { case LPTIME: LP_TIME(minor) = arg * HZ/100; break; case LPCHAR: LP_CHAR(minor) = arg; break; case LPABORT: if (arg) LP_F(minor) |= LP_ABORT; else LP_F(minor) &= ~LP_ABORT; break; case LPABORTOPEN: if (arg) LP_F(minor) |= LP_ABORTOPEN; else LP_F(minor) &= ~LP_ABORTOPEN; break; case LPCAREFUL: if (arg) LP_F(minor) |= LP_CAREFUL; else LP_F(minor) &= ~LP_CAREFUL; break; case LPWAIT: LP_WAIT(minor) = arg; break; case LPSETIRQ: { int oldirq; int newirq = arg; struct lp_struct *lp = &lp_table[minor]; if (!suser()) return -EPERM; oldirq = LP_IRQ(minor); /* Allocate buffer now if we are going to need it */ if (oldirq == NO_IRQ && newirq != NO_IRQ) { lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL); if (!lp->lp_buffer) return -ENOMEM; } if (oldirq != NO_IRQ) { free_irq(oldirq, NULL); } if (newirq != NO_IRQ) { /* Install new irq */ if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer", NULL))) { if (oldirq != NO_IRQ) { /* restore old irq */ request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer", NULL); } else { /* We don't need the buffer */ kfree_s(lp->lp_buffer, LP_BUFFER_SIZE); lp->lp_buffer = NULL; } return retval; } } if (oldirq != NO_IRQ && newirq == NO_IRQ) { /* We don't need the buffer */ kfree_s(lp->lp_buffer, LP_BUFFER_SIZE); lp->lp_buffer = NULL; } LP_IRQ(minor) = newirq; lp_reset(minor); break; } case LPGETIRQ: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); if (retval) return retval; memcpy_tofs((int *) arg, &LP_IRQ(minor), sizeof(int)); break; case LPGETSTATUS: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); if (retval) return retval; else { int status = LP_S(minor); memcpy_tofs((int *) arg, &status, sizeof(int)); } break; case LPRESET: lp_reset(minor); break; case LPGETSTATS: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct lp_stats)); if (retval) return retval; else { memcpy_tofs((int *) arg, &LP_STAT(minor), sizeof(struct lp_stats)); if (suser()) memset(&LP_STAT(minor), 0, sizeof(struct lp_stats)); } break; case LPGETFLAGS: retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); if (retval) return retval; else { int status = LP_F(minor); memcpy_tofs((int *) arg, &status, sizeof(int)); } break; default: retval = -EINVAL; } return retval;}static struct file_operations lp_fops = { lp_lseek, NULL, /* lp_read */ lp_write, NULL, /* lp_readdir */ NULL, /* lp_select */ lp_ioctl, NULL, /* lp_mmap */ lp_open, lp_release};static int lp_probe(int offset){ int base, size; unsigned int testvalue; base = LP_B(offset); if (base == 0) return -1; /* de-configured by command line */ if (LP_IRQ(offset) != NO_IRQ && LP_IRQ(offset) > 15) return -1; /* bogus interrupt value */ size = (base == 0x3bc)? 3 : 8; if (check_region(base, size) < 0) return -1; /* write to port & read back to check */ outb_p(LP_DUMMY, base); udelay(LP_DELAY); testvalue = inb_p(base); if (testvalue == LP_DUMMY) { LP_F(offset) |= LP_EXIST; lp_reset(offset); printk(KERN_INFO "lp%d at 0x%04x, ", offset, base); request_region(base, size, "lp"); if (LP_IRQ(offset) != NO_IRQ) printk("(irq = %d)\n", LP_IRQ(offset)); else printk("(polling)\n"); return 1; } else return 0;}/* Command line parameters: When the lp driver is built in to the kernel, you may use the LILO/LOADLIN command line to set the port addresses and interrupts that the driver will use. Syntax: lp=port0[,irq0[,port1[,irq1[,port2[,irq2]]]]] For example: lp=0x378,0 or lp=0x278,5,0x378,7 Note that if this feature is used, you must specify *all* the ports you want considered, there are no defaults. You can disable a built-in driver with lp=0 .*/void lp_setup(char *str, int *ints){ LP_B(0) = ((ints[0] > 0) ? ints[1] : 0 ); LP_IRQ(0) = ((ints[0] > 1) ? ints[2] : NO_IRQ ); LP_B(1) = ((ints[0] > 2) ? ints[3] : 0 ); LP_IRQ(1) = ((ints[0] > 3) ? ints[4] : NO_IRQ ); LP_B(2) = ((ints[0] > 4) ? ints[5] : 0 ); LP_IRQ(2) = ((ints[0] > 5) ? ints[6] : NO_IRQ );}#ifdef MODULEstatic int io[] = {0, 0, 0};static int irq[] = {NO_IRQ, NO_IRQ, NO_IRQ};#define lp_init init_module#endifint lp_init(void){ int offset = 0; int count = 0;#ifdef MODULE int failed = 0;#endif if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) { printk("lp: unable to get major %d\n", LP_MAJOR); return -EIO; }#ifdef MODULE /* When user feeds parameters, use them */ for (offset=0; offset < LP_NO; offset++) { int specified=0; if (io[offset] != 0) { LP_B(offset) = io[offset]; specified++; } if (irq[offset] != NO_IRQ) { LP_IRQ(offset) = irq[offset]; specified++; } if (specified) { if (lp_probe(offset) <= 0) { printk(KERN_INFO "lp%d: Not found\n", offset); failed++; } else count++; } } /* Successful specified devices increase count * Unsuccessful specified devices increase failed */ if (count) return 0; if (failed) { printk(KERN_INFO "lp: No override devices found.\n"); unregister_chrdev(LP_MAJOR,"lp"); return -EIO; } /* Only get here if there were no specified devices. To continue * would be silly since the above code has scribbled all over the * probe list. */#endif /* take on all known port values */ for (offset = 0; offset < LP_NO; offset++) { int ret = lp_probe(offset); if (ret < 0) continue; count += ret; } if (count == 0) printk("lp: Driver configured but no interfaces found.\n"); return 0;}#ifdef MODULEvoid cleanup_module(void){ int offset; unregister_chrdev(LP_MAJOR,"lp"); for (offset = 0; offset < LP_NO; offset++) { int base, size; base = LP_B(offset); size = (base == 0x3bc)? 3 : 8; if (LP_F(offset) & LP_EXIST) release_region(LP_B(offset),size); }}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -