patch-1.3.94 linux/drivers/char/lp_m68k.c

Next file: linux/drivers/char/riscom8.c
Previous file: linux/drivers/char/lp_intern.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.93/linux/drivers/char/lp_m68k.c linux/drivers/char/lp_m68k.c
@@ -0,0 +1,492 @@
+/*
+ * 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 beeing 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 occured, 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("lp%d: paper-out\n",dev);
+	  if (!rc) rc = -ENOSPC;
+	} else if (!lp_table[dev].lp_is_online(dev)) {
+	  printk("lp%d: off-line\n",dev);
+	  if (!rc) rc = -EIO;
+	} else if (lp_table[dev].lp_is_busy(dev)) {
+	  printk("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("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("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("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("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("lp_init: lp using interrupt\n");
+	else
+
+#if WHICH_DRIVER == PREFER_INTERRUPT
+		printk("lp_init: lp using polling driver\n");
+#else
+		printk("lp_init: cant 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)
+{	
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this