URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [char/] [lp_m68k.c] - Rev 1765
Compare with Previous | Blame | View Log
/* * split in two parts for better support of different hardware * by Joerg Dorchain (dorchain@mpi-sb.mpg.de) * * Amiga printer device by Michael Rausch (linux@uni-koblenz.de); * Atari support added by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.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) */ /* 01/17/95: Matthias Welwarsky (dg8y@rs11.hrz.th-darmstadt.de) * lp_write(): rewritten from scratch * lp_interrupt(): fixed cli()/sti()-bug * * 95/05/28: Andreas Schwab (schwab@issan.informatik.uni-dortmund.de) * lp_write() fixed to make it work again. * 95/08/18: Andreas Schwab * lp_write_interrupt: fix race condition * * * CAUTION, please do check! * * * on 68000-based machines sti() must NEVER appear in interrupt driven * code. The 68k-CPU has a priority-based interrupt scheme. while an interrupt * with a certain priority is executed, all requests with lower or same * priority get locked out. executing the sti()-macro allows ANY interrupt * to be served. this really causes BIG trouble! * to protect an interrupt driven routine against being interrupted * (if absolutely needed!) one should use save_flags();cli()/restore_flags()! * */ #include <linux/config.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/major.h> #include <linux/sched.h> #include <asm/irq.h> #ifdef CONFIG_AMIGA #include <asm/amigaints.h> #ifdef CONFIG_MULTIFACE_III_LP #include <linux/lp_mfc.h> #endif #endif #ifdef CONFIG_ATARI #include <asm/atarihw.h> #include <asm/atariints.h> #endif #include <linux/lp_m68k.h> #include <linux/lp_intern.h> #include <linux/malloc.h> #include <linux/interrupt.h> #include <asm/segment.h> #include <asm/system.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 #define MAX_LP 3 /* the maximum number of devices */ struct lp_struct lp_table[MAX_LP] = {{0,},}; static int max_lp; /* the real number of devices */ /* * All my debugging code assumes that you debug with only one printer at * a time. RWWH */ #define LP_DEBUG #undef LP_DEBUG #if WHICH_DRIVER != FORCE_INTERRUPT #ifdef LP_DEBUG static int lp_max_count = 1; #endif static int lp_char_polled(char lpchar, int dev) { unsigned long count = 0; do { count ++; if(need_resched) schedule(); } while (lp_table[dev].lp_is_busy(dev) && count < lp_table[dev].chars); if (count == lp_table[dev].chars) { 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_table[dev].lp_out(lpchar, dev); 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 __inline__ int lp_char_interrupt(char lpchar, int dev) { if (!lp_table[dev].lp_is_busy(dev)) { lp_table[dev].lp_out(lpchar,dev); return 1; } return 0; } static int lp_error; static void lp_interrupt(int irq, struct pt_regs *fp, void *dummy) { unsigned long flags; int dev; for (dev = 0; dev < max_lp; dev++) { if (lp_table[dev].lp_my_interrupt(dev) != 0) if (lp_table[dev].do_print) { if (lp_table[dev].copy_size) { save_flags(flags); cli(); if (lp_char_interrupt(lp_table[dev].lp_buffer[lp_table[dev].bytes_written], dev)) { --lp_table[dev].copy_size; ++lp_table[dev].bytes_written; restore_flags(flags); } else { lp_table[dev].do_print = 0; restore_flags(flags); lp_error = 1; wake_up_interruptible(&lp_table[dev].lp_wait_q); } } else { lp_table[dev].do_print = 0; lp_error = 0; wake_up_interruptible(&lp_table[dev].lp_wait_q); } } } } #if WHICH_DRIVER == FORCE_INTERRUPT static int lp_write(struct inode *inode, struct file *file, const char *buf, int count) #else static int lp_write_interrupt(struct inode *inode, struct file *file, const char *buf, int count) #endif { unsigned long total_bytes_written = 0; unsigned int flags; int rc; int dev = MINOR(inode->i_rdev); do { lp_table[dev].do_print = 0; /* disable lp_interrupt() */ lp_table[dev].bytes_written = 0; /* init buffer read-pointer */ lp_error = 0; lp_table[dev].copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE); memcpy_fromfs(lp_table[dev].lp_buffer, buf, lp_table[dev].copy_size); while (lp_table[dev].copy_size) { save_flags(flags); cli(); /* no interrupts now */ lp_table[dev].do_print = 1; /* enable lp_interrupt() */ if (lp_char_interrupt(lp_table[dev].lp_buffer[lp_table[dev].bytes_written], dev)) { ++lp_table[dev].bytes_written; --lp_table[dev].copy_size; lp_error = 0; } else { /* something went wrong */ lp_table[dev].do_print = 0; /* disable lp_interrupt() */ lp_error = 1; /* printer caused error */ } if (lp_error) { /* something blocked printing, so we don't want to sleep too long, in case we have to rekick the interrupt */ current->timeout = jiffies + LP_TIMEOUT_POLLED; } else { current->timeout = jiffies + LP_TIMEOUT_INTERRUPT; } interruptible_sleep_on(&lp_table[dev].lp_wait_q); restore_flags(flags); /* we're up again and running. we first disable lp_interrupt(), then check what happened meanwhile */ lp_table[dev].do_print = 0; rc = total_bytes_written + lp_table[dev].bytes_written; if (current->signal & ~current->blocked) { if (rc) return rc; else return -EINTR; } if (lp_error) { /* an error has occurred, maybe in lp_interrupt(). figure out the type of error, exit on request or if nothing has been printed at all. */ if (lp_table[dev].lp_has_pout(dev)) { printk(KERN_NOTICE "lp%d: paper-out\n",dev); if (!rc) rc = -ENOSPC; } else if (!lp_table[dev].lp_is_online(dev)) { printk(KERN_NOTICE "lp%d: off-line\n",dev); if (!rc) rc = -EIO; } else if (lp_table[dev].lp_is_busy(dev)) { printk(KERN_NOTICE "lp%d: on fire\n",dev); if (!rc) rc = -EIO; } if (lp_table[dev].flags & LP_ABORT) return rc; } /* check if our buffer was completely printed, if not, most likely an unsolved error blocks the printer. As we can`t do anything against, we start all over again. Else we set the read-pointer of the buffer and count the printed characters */ if (!lp_table[dev].copy_size) { total_bytes_written += lp_table[dev].bytes_written; buf += lp_table[dev].bytes_written; count -= lp_table[dev].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, const char *buf, int count) #else static int lp_write_polled(struct inode *inode, struct file *file, const char *buf, int count) #endif { char *temp = buf; int dev = MINOR(inode->i_rdev); #ifdef LP_DEBUG if (jiffies-lp_last_call > lp_table[dev].time) { lp_total_chars = 0; lp_max_count = 1; } lp_last_call = jiffies; #endif temp = buf; while (count > 0) { if (lp_char_polled(get_user(temp), dev)) { /* only update counting vars if character was printed */ count--; temp++; #ifdef LP_DEBUG lp_total_chars++; #endif } else { /* if printer timed out */ if (lp_table[dev].lp_has_pout(dev)) { printk(KERN_NOTICE "lp%d: out of paper\n",dev); if (lp_table[dev].flags & LP_ABORT) return temp - buf ? temp-buf : -ENOSPC; current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + LP_TIMEOUT_POLLED; schedule(); } else if (!lp_table[dev].lp_is_online(dev)) { printk(KERN_NOTICE "lp%d: off-line\n",dev); if (lp_table[dev].flags & 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_table[dev].lp_is_busy(dev)) { printk(KERN_NOTICE "lp%d: on fire\n",dev); if (lp_table[dev].flags & 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_table[dev].time); lp_total_chars = 0; #endif current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + lp_table[dev].time; schedule(); } } return temp - buf; } #endif static unsigned int lp_irq = 0; #if WHICH_DRIVER == PREFER_INTERRUPT static int lp_write(struct inode *inode, struct file *file, const 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) { int dev = MINOR(inode->i_rdev); if (dev >= max_lp) return -ENODEV; if (!(lp_table[dev].flags & LP_EXIST)) return -ENODEV; if (lp_table[dev].flags & LP_BUSY) return -EBUSY; lp_table[dev].flags |= LP_BUSY; return 0; } static void lp_release(struct inode *inode, struct file *file) { lp_table[MINOR(inode->i_rdev)].flags &= ~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 >= max_lp) return -ENODEV; if (!(lp_table[minor].flags & LP_EXIST)) return -ENODEV; switch (cmd) { case LPTIME: lp_table[minor].time = arg; break; case LPCHAR: lp_table[minor].chars = arg; break; case LPABORT: if (arg) lp_table[minor].flags |= LP_ABORT; else lp_table[minor].flags &= ~LP_ABORT; break; case LPWAIT: lp_table[minor].wait = arg; break; case LPSETIRQ: case LPGETIRQ: retval = lp_irq; 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 }; int lp_init(void) { extern char m68k_debug_device[]; if (!strcmp( m68k_debug_device, "par" )) return -EBUSY; if (register_chrdev(LP_MAJOR,"lp", &lp_fops)) { printk("unable to get major %d for line printer\n", LP_MAJOR); return -EBUSY; } #if WHICH_DRIVER == FORCE_POLLING lp_irq = 0; printk(KERN_INFO "lp_init: lp using polling driver\n"); #else #ifdef CONFIG_AMIGA if (MACH_IS_AMIGA && AMIGAHW_PRESENT(AMI_PARALLEL)) lp_irq = add_isr(IRQ_AMIGA_CIAA_FLG, lp_interrupt, 0, NULL, "printer"); #endif #ifdef CONFIG_ATARI if (MACH_IS_ATARI) lp_irq = add_isr(IRQ_MFP_BUSY, lp_interrupt, IRQ_TYPE_SLOW, NULL, "printer"); #endif if (lp_irq) printk(KERN_INFO "lp_init: lp using interrupt\n"); else #if WHICH_DRIVER == PREFER_INTERRUPT printk(KERN_INFO "lp_init: lp using polling driver\n"); #else printk(KERN_WARNING "lp_init: can't get interrupt, and polling driver not configured\n"); #endif #endif max_lp = 0; max_lp += lp_internal_init(lp_table, max_lp, MAX_LP, WHICH_DRIVER); #ifdef CONFIG_MULTIFACE_III_LP max_lp += lp_mfc_init(lp_table, max_lp, MAX_LP, WHICH_DRIVER); #if WHICH_DRIVER != FORCE_POLLING add_isr(IRQ_AMIGA_PORTS, lp_interrupt, 0, NULL, "Multiface III printer"); #endif #endif return 0; } /* * Currently we do not accept any lp-parameters, but that may change. */ void lp_setup(char *str, int *ints) { }