home *** CD-ROM | disk | FTP | other *** search
- /*
- * Amiga printer device by Michael Rausch (linux@uni-koblenz.de);
- * based upon work from
- *
- * Copyright (C) 1992 by Jim Weigand and Linus Torvalds
- * Copyright (C) 1992,1993 by Michael K. Johnson
- * - Thanks much to Gunter Windau for pointing out to me where the error
- * checking ought to be.
- * Copyright (C) 1993 by Nigel Gamble (added interrupt code)
- */
-
- #include <linux/errno.h>
- #include <linux/kernel.h>
- #include <linux/major.h>
- #include <linux/sched.h>
- #include <linux/lp.h>
- #include <linux/malloc.h>
- #include <linux/interrupt.h>
-
- #include <asm/segment.h>
- #include <asm/system.h>
-
- #include <linux/amigahw.h>
- #include <linux/amigaints.h>
-
- /*
- * why bother around with the pio driver when the interrupt works;
- * so, for "security" reasons only, it's configurable here.
- * saves some bytes, at least ...
- */
- #define FORCE_POLLING 0
- #define FORCE_INTERRUPT 1
- #define PREFER_INTERRUPT 2
-
- #define WHICH_DRIVER FORCE_INTERRUPT
-
- /*
- * All my debugging code assumes that you debug with only one printer at
- * a time. RWWH
- */
-
- #undef LP_DEBUG
-
- #ifdef LP_DEBUG
- static int lp_max_count = 1;
- #endif
-
-
- #if WHICH_DRIVER != FORCE_INTERRUPT
- static int lp_char_polled(char lpchar)
- {
- unsigned long count = 0;
-
- do {
- count ++;
- if(need_resched)
- schedule();
- } while(LP_IS_BUSY && count < LP_CHAR);
-
- if (count == LP_CHAR) {
- return 0;
- /* we timed out, and the character was /not/ printed */
- }
- #ifdef LP_DEBUG
- if (count > lp_max_count) {
- printk("lp success after %d counts.\n",count);
- lp_max_count=count;
- }
- #endif
- LP_OUT(lpchar); /* strobe is handled in hardware */
- return 1;
- }
- #endif
-
-
- #ifdef LP_DEBUG
- unsigned int lp_total_chars = 0;
- unsigned int lp_last_call = 0;
- #endif
-
-
- #if WHICH_DRIVER != FORCE_POLLING
- static int lp_char_interrupt(char lpchar)
- {
- if (!LP_IS_BUSY) {
- LP_OUT(lpchar);
- return 1;
- }
- return 0;
- }
-
- static int do_print=0;
- static unsigned long copy_size, bytes_written;
-
- static void lp_interrupt(void)
- {
- if(do_print)
- {
- if(copy_size)
- {
- cli();
- if (lp_char_interrupt(lp_table.lp_buffer[bytes_written])) {
- --copy_size;
- ++bytes_written;
- sti();
- }
- else
- {
- do_print=0;
- sti();
- wake_up(&lp_table.lp_wait_q);
- }
- }
- else
- {
- do_print=0;
- wake_up(&lp_table.lp_wait_q);
- }
-
- }
- }
-
- #if WHICH_DRIVER == FORCE_INTERRUPT
- static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
- #else
- static int lp_write_interrupt(struct inode * inode, struct file * file, char * buf, int count)
- #endif
- {
- static unsigned long total_bytes_written = 0;
-
- do {
- bytes_written = 0;
- copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
- memcpy_fromfs(lp_table.lp_buffer, buf, copy_size);
-
- do {
- cli();
- do_print=1;
-
- /* initial output, to trigger the interrupt */
- if (lp_char_interrupt(lp_table.lp_buffer[bytes_written])) {
- --copy_size;
- ++bytes_written;
- sti();
-
- /* ok, now wait for the interrupt routine to get ready */
- current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
- interruptible_sleep_on(&lp_table.lp_wait_q);
-
- /* just to be sure, if we timed out on this here */
- do_print=0;
- }
- else
- {
- do_print=0;
- sti();
- }
-
- if(copy_size) {
- int rc = total_bytes_written + bytes_written;
-
- if (LP_HAS_POUT) {
- printk("lp0: out of paper\n");
- if (!rc)
- rc = -ENOSPC;
- } else if (!LP_IS_ONLINE) {
- printk("lp0: off-line\n");
- if (!rc)
- rc = -EIO;
- } else if (LP_IS_BUSY) {
- printk("lp0: printer error\n");
- if (!rc)
- rc = -EIO;
- }
- if(LP_F & LP_ABORT)
- return rc;
-
- if (current->signal & ~current->blocked) {
- if (total_bytes_written + bytes_written)
- return total_bytes_written + bytes_written;
- else
- return -EINTR;
- }
-
- /* cli(); */
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + LP_TIMEOUT_POLLED;
- schedule();
- }
- } while(copy_size);
-
- total_bytes_written += bytes_written;
- buf += bytes_written;
- count -= bytes_written;
-
- } while (count > 0);
-
- return total_bytes_written;
- }
- #endif
-
-
- #if WHICH_DRIVER != FORCE_INTERRUPT
- #if WHICH_DRIVER == FORCE_POLLING
- static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
- #else
- static int lp_write_polled(struct inode * inode, struct file * file,
- char * buf, int count)
- #endif
- {
- char *temp = buf;
-
- #ifdef LP_DEBUG
- if (jiffies-lp_last_call > LP_TIME) {
- lp_total_chars = 0;
- lp_max_count = 1;
- }
- lp_last_call = jiffies;
- #endif
-
- temp = buf;
- while (count > 0) {
- if (lp_char_polled( get_fs_byte(temp) )) {
- /* only update counting vars if character was printed */
- count--; temp++;
- #ifdef LP_DEBUG
- lp_total_chars++;
- #endif
- } else { /* if printer timed out */
- if (LP_HAS_POUT) {
- printk("lp0: out of paper\n");
- if(LP_F & LP_ABORT)
- return temp-buf?temp-buf:-ENOSPC;
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + LP_TIMEOUT_POLLED;
- schedule();
- } else
- if (!LP_IS_ONLINE) {
- printk("lp0: off-line\n");
- if(LP_F & LP_ABORT)
- return temp-buf?temp-buf:-EIO;
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + LP_TIMEOUT_POLLED;
- schedule();
- } else
- /* not offline or out of paper. on fire? */
- if (LP_IS_BUSY) {
- printk("lp0: on fire\n");
- if(LP_F & LP_ABORT)
- return temp-buf?temp-buf:-EFAULT;
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + LP_TIMEOUT_POLLED;
- schedule();
- }
-
- /* check for signals before going to sleep */
- if (current->signal & ~current->blocked) {
- if (temp != buf)
- return temp-buf;
- else
- return -EINTR;
- }
- #ifdef LP_DEBUG
- printk("lp sleeping at %d characters for %d jiffies\n",
- lp_total_chars, LP_TIME);
- lp_total_chars=0;
- #endif
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + LP_TIME;
- schedule();
- }
- }
- return temp-buf;
- }
- #endif
-
-
- #if WHICH_DRIVER == PREFER_INTERRUPT
- static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
- {
- if (LP_IRQ)
- return lp_write_interrupt(inode, file, buf, count);
- else
- return lp_write_polled(inode, file, buf, count);
- }
- #endif
-
- static int lp_lseek(struct inode * inode, struct file * file,
- off_t offset, int origin)
- {
- return -ESPIPE;
- }
-
- static int lp_open(struct inode * inode, struct file * file)
- {
- if (MINOR(inode->i_rdev) >= LP_NO)
- return -ENODEV;
- if (!(LP_F & LP_EXIST))
- return -ENODEV;
- if (LP_F & LP_BUSY)
- return -EBUSY;
-
- LP_F |= LP_BUSY;
-
- ciaa.ddrb = 0xff; /* set the correct data directions */
- ciab.ddra &= 0xf8; /* maybe changed in some plip code */
-
- return 0;
- }
-
- static void lp_release(struct inode * inode, struct file * file)
- {
- LP_F &= ~LP_BUSY;
- }
-
-
- 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("lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
- #endif
- if (minor >= LP_NO)
- return -ENODEV;
- if (!(LP_F & LP_EXIST))
- return -ENODEV;
- switch ( cmd ) {
- case LPTIME:
- LP_TIME = arg;
- break;
- case LPCHAR:
- LP_CHAR = arg;
- break;
- case LPABORT:
- if (arg)
- LP_F |= LP_ABORT;
- else
- LP_F &= ~LP_ABORT;
- break;
- case LPWAIT: /* set/get interrupt is ignored, strobe handled by hardware */
- case LPSETIRQ:
- case LPGETIRQ:
- 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
- };
-
- long lp_init(long kmem_start)
- {
- if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
- printk("unable to get major %d for line printer\n", LP_MAJOR);
- return kmem_start;
- }
-
- #if WHICH_DRIVER == FORCE_POLLING
- lp_table.irq = 0;
- printk("lp_init: lp0 using polling driver\n");
- #else
-
- if((lp_table.irq = mach_add_isr(IRQ_AMIGA_CIAA_FLG, (isrfunc)lp_interrupt, 0, NULL)))
- {
- lp_table.lp_buffer = (char*)kmem_start;
- kmem_start += LP_BUFFER_SIZE;
- printk("lp_init: lp0 using cia interrupt\n");
- }
- else
-
- #if WHICH_DRIVER == PREFER_INTERRUPT
- printk("lp_init: lp0 using polling driver\n");
- #else
- {
- printk("cannot get cia interrupt for lp0, and polling driver not configured.\n");
- return kmem_start;
- }
- #endif
- #endif
-
- LP_F |= LP_EXIST;
- return kmem_start;
- }
-