patch-2.2.5 linux/drivers/char/stallion.c

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

diff -u --recursive --new-file v2.2.4/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
@@ -3,7 +3,7 @@
 /*
  *	stallion.c  -- stallion multiport serial driver.
  *
- *	Copyright (C) 1996-1998  Stallion Technologies (support@stallion.oz.au).
+ *	Copyright (C) 1996-1999  Stallion Technologies (support@stallion.oz.au).
  *	Copyright (C) 1994-1996  Greg Ungerer (gerg@stallion.oz.au).
  *
  *	This code is loosely based on the Linux serial driver, written by
@@ -27,28 +27,18 @@
 /*****************************************************************************/
 
 #include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/wait.h>
+#include <linux/malloc.h>
 #include <linux/interrupt.h>
-#include <linux/termios.h>
-#include <linux/fcntl.h>
-#include <linux/tty_driver.h>
-#include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
 #include <linux/cd1400.h>
 #include <linux/sc26198.h>
 #include <linux/comstats.h>
 #include <linux/stallion.h>
-#include <linux/string.h>
-#include <linux/malloc.h>
 #include <linux/ioport.h>
-#include <linux/config.h>
 #include <linux/init.h>
 #include <linux/smp_lock.h>
 
-#include <asm/system.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
 
@@ -78,7 +68,7 @@
  *	stl_brdconf[] array is a board. Each line contains io/irq/memory
  *	ranges for that board (as well as what type of board it is).
  *	Some examples:
- *		{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 }
+ *		{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },
  *	This line would configure an EasyIO board (4 or 8, no difference),
  *	at io address 2a0 and irq 10.
  *	Another example:
@@ -105,7 +95,7 @@
 } stlconf_t;
 
 static stlconf_t	stl_brdconf[] = {
-	{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },
+	/*{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },*/
 };
 
 static int	stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t);
@@ -144,7 +134,7 @@
  */
 static char	*stl_drvtitle = "Stallion Multiport Serial Driver";
 static char	*stl_drvname = "stallion";
-static char	*stl_drvversion = "5.4.7";
+static char	*stl_drvversion = "5.5.1";
 static char	*stl_serialname = "ttyE";
 static char	*stl_calloutname = "cue";
 
@@ -259,6 +249,85 @@
 
 /*****************************************************************************/
 
+#ifdef MODULE
+/*
+ *	Define some string labels for arguments passed from the module
+ *	load line. These allow for easy board definitions, and easy
+ *	modification of the io, memory and irq resoucres.
+ */
+
+static char	*board0[4];
+static char	*board1[4];
+static char	*board2[4];
+static char	*board3[4];
+
+static char	**stl_brdsp[] = {
+	(char **) &board0,
+	(char **) &board1,
+	(char **) &board2,
+	(char **) &board3
+};
+
+/*
+ *	Define a set of common board names, and types. This is used to
+ *	parse any module arguments.
+ */
+
+typedef struct stlbrdtype {
+	char	*name;
+	int	type;
+} stlbrdtype_t;
+
+static stlbrdtype_t	stl_brdstr[] = {
+	{ "easyio", BRD_EASYIO },
+	{ "eio", BRD_EASYIO },
+	{ "20", BRD_EASYIO },
+	{ "ec8/32", BRD_ECH },
+	{ "ec8/32-at", BRD_ECH },
+	{ "ec8/32-isa", BRD_ECH },
+	{ "ech", BRD_ECH },
+	{ "echat", BRD_ECH },
+	{ "21", BRD_ECH },
+	{ "ec8/32-mc", BRD_ECHMC },
+	{ "ec8/32-mca", BRD_ECHMC },
+	{ "echmc", BRD_ECHMC },
+	{ "echmca", BRD_ECHMC },
+	{ "22", BRD_ECHMC },
+	{ "ec8/32-pc", BRD_ECHPCI },
+	{ "ec8/32-pci", BRD_ECHPCI },
+	{ "26", BRD_ECHPCI },
+	{ "ec8/64-pc", BRD_ECH64PCI },
+	{ "ec8/64-pci", BRD_ECH64PCI },
+	{ "ech-pci", BRD_ECH64PCI },
+	{ "echpci", BRD_ECH64PCI },
+	{ "echpc", BRD_ECH64PCI },
+	{ "27", BRD_ECH64PCI },
+	{ "easyio-pc", BRD_EASYIOPCI },
+	{ "easyio-pci", BRD_EASYIOPCI },
+	{ "eio-pci", BRD_EASYIOPCI },
+	{ "eiopci", BRD_EASYIOPCI },
+	{ "28", BRD_EASYIOPCI },
+};
+
+/*
+ *	Define the module agruments.
+ */
+MODULE_AUTHOR("Greg Ungerer");
+MODULE_DESCRIPTION("Stallion Multiport Serial Driver");
+
+MODULE_PARM(board0, "1-4s");
+MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,ioaddr2][,irq]]");
+MODULE_PARM(board1, "1-4s");
+MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,ioaddr2][,irq]]");
+MODULE_PARM(board2, "1-4s");
+MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,ioaddr2][,irq]]");
+MODULE_PARM(board3, "1-4s");
+MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,ioaddr2][,irq]]");
+
+#endif
+
+/*****************************************************************************/
+
 /*
  *	Hardware ID bits for the EasyIO and ECH boards. These defines apply
  *	to the directly accessible io ports of these boards (not the uarts -
@@ -399,9 +468,11 @@
 /*
  *	Define some handy local macros...
  */
-#ifndef	MIN
-#define	MIN(a,b)		(((a) <= (b)) ? (a) : (b))
-#endif
+#undef	MIN
+#define	MIN(a,b)	(((a) <= (b)) ? (a) : (b))
+
+#undef	TOLOWER
+#define	TOLOWER(x)	((((x) >= 'A') && ((x) <= 'Z')) ? ((x) + 0x20) : (x))
 
 /*****************************************************************************/
 
@@ -412,6 +483,10 @@
 #ifdef MODULE
 int		init_module(void);
 void		cleanup_module(void);
+static void	stl_argbrds(void);
+static int	stl_parsebrd(stlconf_t *confp, char **argp);
+
+static unsigned long stl_atol(char *str);
 #endif
 
 int		stl_init(void);
@@ -459,15 +534,17 @@
 static void	stl_echpci64intr(stlbrd_t *brdp);
 static void	stl_offintr(void *private);
 static void	*stl_memalloc(int len);
+static stlbrd_t *stl_allocbrd(void);
 static stlport_t *stl_getport(int brdnr, int panelnr, int portnr);
 
 static inline int	stl_initbrds(void);
 static inline int	stl_initeio(stlbrd_t *brdp);
 static inline int	stl_initech(stlbrd_t *brdp);
+static inline int	stl_getbrdnr(void);
 
 #ifdef	CONFIG_PCI
 static inline int	stl_findpcibrds(void);
-static inline int	stl_initpcibrd(int brdtype, struct pci_dev *dev);
+static inline int	stl_initpcibrd(int brdtype, struct pci_dev *devp);
 #endif
 
 /*
@@ -747,7 +824,8 @@
 		kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE);
 
 	for (i = 0; (i < stl_nrbrds); i++) {
-		brdp = stl_brds[i];
+		if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL)
+			continue;
 		for (j = 0; (j < STL_MAXPANELS); j++) {
 			panelp = brdp->panels[j];
 			if (panelp == (stlpanel_t *) NULL)
@@ -779,6 +857,124 @@
 	restore_flags(flags);
 }
 
+/*****************************************************************************/
+
+/*
+ *	Check for any arguments passed in on the module load command line.
+ */
+
+static void stl_argbrds()
+{
+	stlconf_t	conf;
+	stlbrd_t	*brdp;
+	int		nrargs, i;
+
+#if DEBUG
+	printk("stl_argbrds()\n");
+#endif
+
+	nrargs = sizeof(stl_brdsp) / sizeof(char **);
+
+	for (i = stl_nrbrds; (i < nrargs); i++) {
+		memset(&conf, 0, sizeof(conf));
+		if (stl_parsebrd(&conf, stl_brdsp[i]) == 0)
+			continue;
+		if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL)
+			continue;
+		stl_nrbrds = i + 1;
+		brdp->brdnr = i;
+		brdp->brdtype = conf.brdtype;
+		brdp->ioaddr1 = conf.ioaddr1;
+		brdp->ioaddr2 = conf.ioaddr2;
+		brdp->irq = conf.irq;
+		brdp->irqtype = conf.irqtype;
+		stl_brdinit(brdp);
+	}
+}
+
+/*****************************************************************************/
+
+/*
+ *	Convert an ascii string number into an unsigned long.
+ */
+
+static unsigned long stl_atol(char *str)
+{
+	unsigned long	val;
+	int		base, c;
+	char		*sp;
+
+	val = 0;
+	sp = str;
+	if ((*sp == '0') && (*(sp+1) == 'x')) {
+		base = 16;
+		sp += 2;
+	} else if (*sp == '0') {
+		base = 8;
+		sp++;
+	} else {
+		base = 10;
+	}
+
+	for (; (*sp != 0); sp++) {
+		c = (*sp > '9') ? (TOLOWER(*sp) - 'a' + 10) : (*sp - '0');
+		if ((c < 0) || (c >= base)) {
+			printk("STALLION: invalid argument %s\n", str);
+			val = 0;
+			break;
+		}
+		val = (val * base) + c;
+	}
+	return(val);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Parse the supplied argument string, into the board conf struct.
+ */
+
+static int stl_parsebrd(stlconf_t *confp, char **argp)
+{
+	char	*sp;
+	int	nrbrdnames, i;
+
+#if DEBUG
+	printk("stl_parsebrd(confp=%x,argp=%x)\n", (int) confp, (int) argp);
+#endif
+
+	if ((argp[0] == (char *) NULL) || (*argp[0] == 0))
+		return(0);
+
+	for (sp = argp[0], i = 0; ((*sp != 0) && (i < 25)); sp++, i++)
+		*sp = TOLOWER(*sp);
+
+	nrbrdnames = sizeof(stl_brdstr) / sizeof(stlbrdtype_t);
+	for (i = 0; (i < nrbrdnames); i++) {
+		if (strcmp(stl_brdstr[i].name, argp[0]) == 0)
+			break;
+	}
+	if (i >= nrbrdnames) {
+		printk("STALLION: unknown board name, %s?\n", argp[0]);
+		return(0);
+	}
+
+	confp->brdtype = stl_brdstr[i].type;
+
+	i = 1;
+	if ((argp[i] != (char *) NULL) && (*argp[i] != 0))
+		confp->ioaddr1 = stl_atol(argp[i]);
+	i++;
+	if (confp->brdtype == BRD_ECH) {
+		if ((argp[i] != (char *) NULL) && (*argp[i] != 0))
+			confp->ioaddr2 = stl_atol(argp[i]);
+		i++;
+	}
+	if ((argp[i] != (char *) NULL) && (*argp[i] != 0))
+		confp->irq = stl_atol(argp[i]);
+	return(1);
+}
+
 #endif
 
 /*****************************************************************************/
@@ -794,6 +990,28 @@
 
 /*****************************************************************************/
 
+/*
+ *	Allocate a new board structure. Fill out the basic info in it.
+ */
+
+static stlbrd_t *stl_allocbrd()
+{
+	stlbrd_t	*brdp;
+
+	brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+	if (brdp == (stlbrd_t *) NULL) {
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			sizeof(stlbrd_t));
+		return((stlbrd_t *) NULL);
+	}
+
+	memset(brdp, 0, sizeof(stlbrd_t));
+	brdp->magic = STL_BOARDMAGIC;
+	return(brdp);
+}
+
+/*****************************************************************************/
+
 static int stl_open(struct tty_struct *tty, struct file *filp)
 {
 	stlport_t	*portp;
@@ -1121,7 +1339,6 @@
 		
 		down(&stl_tmpwritesem);
 		copy_from_user(stl_tmpwritebuf, chbuf, count);
-		up(&stl_tmpwritesem);
 		chbuf = &stl_tmpwritebuf[0];
 	}
 
@@ -1154,6 +1371,9 @@
 	clear_bit(ASYI_TXLOW, &portp->istate);
 	stl_startrxtx(portp, -1, 1);
 
+	if (from_user)
+		up(&stl_tmpwritesem);
+
 	return(count);
 }
 
@@ -2185,12 +2405,12 @@
 	}
 
 	if (check_region(brdp->ioaddr1, brdp->iosize1)) {
-		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+		printk("STALLION: Warning, board %d I/O address %x conflicts "
 			"with another device\n", brdp->brdnr, brdp->ioaddr1);
 	}
 	if (brdp->iosize2 > 0) {
 		if (check_region(brdp->ioaddr2, brdp->iosize2)) {
-			printk("STALLION: Warning, unit %d I/O address %x "
+			printk("STALLION: Warning, board %d I/O address %x "
 				"conflicts with another device\n",
 				brdp->brdnr, brdp->ioaddr2);
 		}
@@ -2376,7 +2596,7 @@
 		conflict = check_region(brdp->ioaddr2, brdp->iosize2) ?
 			brdp->ioaddr2 : 0;
 	if (conflict) {
-		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+		printk("STALLION: Warning, board %d I/O address %x conflicts "
 			"with another device\n", brdp->brdnr, conflict);
 	}
 
@@ -2499,14 +2719,14 @@
 		stl_initech(brdp);
 		break;
 	default:
-		printk("STALLION: unit=%d is unknown board type=%d\n",
+		printk("STALLION: board=%d is unknown board type=%d\n",
 			brdp->brdnr, brdp->brdtype);
 		return(ENODEV);
 	}
 
 	stl_brds[brdp->brdnr] = brdp;
 	if ((brdp->state & BRD_FOUND) == 0) {
-		printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n",
+		printk("STALLION: %s board not found, board=%d io=%x irq=%d\n",
 			stl_brdnames[brdp->brdtype], brdp->brdnr,
 			brdp->ioaddr1, brdp->irq);
 		return(ENODEV);
@@ -2516,7 +2736,7 @@
 		if (brdp->panels[i] != (stlpanel_t *) NULL)
 			stl_initports(brdp, brdp->panels[i]);
 
-	printk("STALLION: %s found, unit=%d io=%x irq=%d "
+	printk("STALLION: %s found, board=%d io=%x irq=%d "
 		"nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype],
 		brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels,
 		brdp->nrports);
@@ -2525,6 +2745,26 @@
 
 /*****************************************************************************/
 
+/*
+ *	Find the next available board number that is free.
+ */
+
+static inline int stl_getbrdnr()
+{
+	int	i;
+
+	for (i = 0; (i < STL_MAXBRDS); i++) {
+		if (stl_brds[i] == (stlbrd_t *) NULL) {
+			if (i >= stl_nrbrds)
+				stl_nrbrds = i + 1;
+			return(i);
+		}
+	}
+	return(-1);
+}
+
+/*****************************************************************************/
+
 #ifdef	CONFIG_PCI
 
 /*
@@ -2533,42 +2773,32 @@
  *	configuration space.
  */
 
-static inline int stl_initpcibrd(int brdtype, struct pci_dev *dev)
+static inline int stl_initpcibrd(int brdtype, struct pci_dev *devp)
 {
-	unsigned int	bar[4];
 	stlbrd_t	*brdp;
-	int		i;
-	unsigned char	irq;
 
 #if DEBUG
-	printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n",
-		brdtype, dev->bus->number, dev->devfn);
+	printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n", brdtype,
+		dev->bus->number, dev->devfn);
 #endif
 
-	brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
-	if (brdp == (stlbrd_t *) NULL) {
-		printk("STALLION: failed to allocate memory (size=%d)\n",
-			sizeof(stlbrd_t));
+	if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL)
 		return(-ENOMEM);
+	if ((brdp->brdnr = stl_getbrdnr()) < 0) {
+		printk("STALLION: too many boards found, "
+			"maximum supported %d\n", STL_MAXBRDS);
+		return(0);
 	}
-
-	memset(brdp, 0, sizeof(stlbrd_t));
-	brdp->magic = STL_BOARDMAGIC;
-	brdp->brdnr = stl_nrbrds++;
 	brdp->brdtype = brdtype;
 
 /*
- *	Read in all the BAR registers from this board. Different Stallion
- *	boards use these in different ways, so we just read in the whole
- *	lot and then figure out what is what later.
- */
-	for (i = 0; (i < 4); i++)
-		bar[i] = dev->base_address[i];
-	irq = dev->irq;
-
+ *	Different Stallion boards use the BAR registers in different ways,
+ *	so set up io addresses based on board type.
+ */
 #if DEBUG
 	printk("%s(%d): BAR[]=%x,%x,%x,%x IRQ=%x\n", __FILE__, __LINE__,
-		bar[0], bar[1], bar[2], bar[3], irq);
+		devp->base_address[0], devp->base_address[1],
+		devp->base_address[2], devp->base_address[3], devp->irq);
 #endif
 
 /*
@@ -2577,29 +2807,34 @@
  */
 	switch (brdtype) {
 	case BRD_ECHPCI:
-		brdp->ioaddr2 = (bar[0] & PCI_BASE_ADDRESS_IO_MASK);
-		brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr2 = (devp->base_address[0] &
+			PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr1 = (devp->base_address[1] &
+			PCI_BASE_ADDRESS_IO_MASK);
 		break;
 	case BRD_ECH64PCI:
-		brdp->ioaddr2 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK);
-		brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr2 = (devp->base_address[2] &
+			PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr1 = (devp->base_address[1] &
+			PCI_BASE_ADDRESS_IO_MASK);
 		break;
 	case BRD_EASYIOPCI:
-		brdp->ioaddr1 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK);
-		brdp->ioaddr2 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr1 = (devp->base_address[2] &
+			PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr2 = (devp->base_address[1] &
+			PCI_BASE_ADDRESS_IO_MASK);
 		break;
 	default:
 		printk("STALLION: unknown PCI board type=%d\n", brdtype);
 		break;
 	}
 
-	brdp->irq = irq;
+	brdp->irq = devp->irq;
 	stl_brdinit(brdp);
 
 	return(0);
 }
 
-
 /*****************************************************************************/
 
 /*
@@ -2621,17 +2856,8 @@
 		return(0);
 
 	for (i = 0; (i < stl_nrpcibrds); i++)
-		while ((dev = pci_find_device(stl_pcibrds[i].vendid, stl_pcibrds[i].devid, dev))) {
-
-/*
- *			Check that we can handle more boards...
- */
-			if (stl_nrbrds >= STL_MAXBRDS) {
-				printk("STALLION: too many boards found, "
-					"maximum supported %d\n", STL_MAXBRDS);
-				i = stl_nrpcibrds;
-				break;
-			}
+		while ((dev = pci_find_device(stl_pcibrds[i].vendid,
+		    stl_pcibrds[i].devid, dev))) {
 
 /*
  *			Found a device on the PCI bus that has our vendor and
@@ -2680,15 +2906,11 @@
  */
 	for (i = 0; (i < stl_nrbrds); i++) {
 		confp = &stl_brdconf[i];
-		brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
-		if (brdp == (stlbrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory "
-				"(size=%d)\n", sizeof(stlbrd_t));
+#ifdef MODULE
+		stl_parsebrd(confp, stl_brdsp[i]);
+#endif
+		if ((brdp = stl_allocbrd()) == (stlbrd_t *) NULL)
 			return(-ENOMEM);
-		}
-		memset(brdp, 0, sizeof(stlbrd_t));
-
-		brdp->magic = STL_BOARDMAGIC;
 		brdp->brdnr = i;
 		brdp->brdtype = confp->brdtype;
 		brdp->ioaddr1 = confp->ioaddr1;
@@ -2698,11 +2920,14 @@
 		stl_brdinit(brdp);
 	}
 
-#ifdef CONFIG_PCI
 /*
- *	If the PCI BIOS support is compiled in then let's go looking for
- *	ECH-PCI boards.
+ *	Find any dynamically supported boards. That is via module load
+ *	line options or auto-detected on the PCI bus.
  */
+#ifdef MODULE
+	stl_argbrds();
+#endif
+#ifdef CONFIG_PCI
 	stl_findpcibrds();
 #endif
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)