From 224e0930dfd50da5015a931273303ce883a75bee Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@openwrt.org>
Date: Wed, 4 Jan 2006 19:10:06 +0000
Subject: [PATCH] kmod-switch: vlan parser rewrite, some api changes, minor
 fixes

SVN-Revision: 2819
---
 .../linux/package/switch/src/switch-adm.c     | 58 ++++++-----
 .../linux/package/switch/src/switch-core.c    | 98 ++++++++++++-------
 .../linux/package/switch/src/switch-core.h    | 12 ++-
 .../linux/package/switch/src/switch-robo.c    | 66 +++++--------
 4 files changed, 128 insertions(+), 106 deletions(-)

diff --git a/openwrt/target/linux/package/switch/src/switch-adm.c b/openwrt/target/linux/package/switch/src/switch-adm.c
index 0b70a694c1..6ad98447b3 100644
--- a/openwrt/target/linux/package/switch/src/switch-adm.c
+++ b/openwrt/target/linux/package/switch/src/switch-adm.c
@@ -240,7 +240,7 @@ static int port_conf[] = { 0x01, 0x03, 0x05, 0x07, 0x08, 0x09 };
 /* Bits in VLAN port mapping */
 static int vlan_ports[] = { 1 << 0, 1 << 2, 1 << 4, 1 << 6, 1 << 7, 1 << 8 };
 
-static int handle_vlan_port_read(char *buf, int nr)
+static int handle_vlan_port_read(void *driver, char *buf, int nr)
 {
 	int ports, i, c, len = 0;
 			
@@ -261,30 +261,33 @@ static int handle_vlan_port_read(char *buf, int nr)
 	return len;
 }
 
-static int handle_vlan_port_write(char *buf, int nr)
+static int handle_vlan_port_write(void *driver, char *buf, int nr)
 {
-	int i, c, ports;
-	int map = switch_parse_vlan(buf);
+	int i, cfg, ports;
+	switch_driver *d = (switch_driver *) driver;
+	switch_vlan_config *c = switch_parse_vlan(d, buf);
 
-	if (map == -1)
+	if (c == NULL)
 		return -1;
 
 	ports = adm_rreg(0, 0x13 + nr);
-	for (i = 0; i <= 5; i++) {
-		if (map & (1 << i)) {
+	for (i = 0; i < d->ports; i++) {
+		if (c->port & (1 << i)) {
 			ports |= vlan_ports[i];
 
-			c = adm_rreg(0, port_conf[i]);
+			cfg = adm_rreg(0, port_conf[i]);
 			
 			/* Tagging */
-			if (map & (1 << (8 + i)))
-				c |= (1 << 4);
+			if (c->untag & (1 << i))
+				cfg &= ~(1 << 4);
 			else
-				c &= ~(1 << 4);
-
-			c = (c & ~(0xf << 10)) | (nr << 10);
+				cfg |= (1 << 4);
 			
-			adm_wreg(port_conf[i], (__u16) c);
+			if ((c->untag | c->pvid) & (1 << i)) {
+				cfg = (cfg & ~(0xf << 10)) | (nr << 10);
+			}
+			
+			adm_wreg(port_conf[i], (__u16) cfg);
 		} else {
 			ports &= ~(vlan_ports[i]);
 		}
@@ -294,12 +297,12 @@ static int handle_vlan_port_write(char *buf, int nr)
 	return 0;
 }
 
-static int handle_port_enable_read(char *buf, int nr)
+static int handle_port_enable_read(void *driver, char *buf, int nr)
 {
 	return sprintf(buf, "%d\n", ((adm_rreg(0, port_conf[nr]) & (1 << 5)) ? 0 : 1));
 }
 
-static int handle_port_enable_write(char *buf, int nr)
+static int handle_port_enable_write(void *driver, char *buf, int nr)
 {
 	int reg = adm_rreg(0, port_conf[nr]);
 	
@@ -313,7 +316,7 @@ static int handle_port_enable_write(char *buf, int nr)
 	return 0;
 }
 
-static int handle_port_media_read(char *buf, int nr)
+static int handle_port_media_read(void *driver, char *buf, int nr)
 {
 	int len;
 	int media = 0;
@@ -330,7 +333,7 @@ static int handle_port_media_read(char *buf, int nr)
 	return len + sprintf(buf + len, "\n");
 }
 
-static int handle_port_media_write(char *buf, int nr)
+static int handle_port_media_write(void *driver, char *buf, int nr)
 {
 	int media = switch_parse_media(buf);
 	int reg = adm_rreg(0, port_conf[nr]);
@@ -351,12 +354,12 @@ static int handle_port_media_write(char *buf, int nr)
 	return 0;
 }
 
-static int handle_vlan_enable_read(char *buf, int nr)
+static int handle_vlan_enable_read(void *driver, char *buf, int nr)
 {
 	return sprintf(buf, "%d\n", ((adm_rreg(0, 0x11) & (1 << 5)) ? 1 : 0));
 }
 
-static int handle_vlan_enable_write(char *buf, int nr)
+static int handle_vlan_enable_write(void *driver, char *buf, int nr)
 {
 	int reg = adm_rreg(0, 0x11);
 	
@@ -370,7 +373,7 @@ static int handle_vlan_enable_write(char *buf, int nr)
 	return 0;
 }
 
-static int handle_reset(char *buf, int nr)
+static int handle_reset(void *driver, char *buf, int nr)
 {
 	int i;
 
@@ -412,7 +415,7 @@ static int handle_reset(char *buf, int nr)
 	return 0;
 }
 
-static int handle_registers(char *buf, int nr)
+static int handle_registers(void *driver, char *buf, int nr)
 {
 	int i, len = 0;
 	
@@ -423,7 +426,7 @@ static int handle_registers(char *buf, int nr)
 	return len;
 }
 
-static int handle_counters(char *buf, int nr)
+static int handle_counters(void *driver, char *buf, int nr)
 {
 	int i, len = 0;
 
@@ -451,6 +454,13 @@ static int detect_adm()
 #else
 	ret = 1;
 #endif
+	if (ret == 1) {
+		int i = adm_rreg(0, 0);
+		if ((i == 0) || (i == 0xffff)) {
+			printk("No ADM6996 chip detected.\n");
+			ret = 0;
+		}
+	}
 
 	return ret;
 }
@@ -475,7 +485,9 @@ static int __init adm_init()
 	};
 	switch_driver driver = {
 		name: DRIVER_NAME,
+		interface: "eth0",
 		ports: 6,
+		cpuport: 5,
 		vlans: 16,
 		driver_handlers: cfg,
 		port_handlers: port,
diff --git a/openwrt/target/linux/package/switch/src/switch-core.c b/openwrt/target/linux/package/switch/src/switch-core.c
index 8b4419b298..c0e5cc9eaa 100644
--- a/openwrt/target/linux/package/switch/src/switch-core.c
+++ b/openwrt/target/linux/package/switch/src/switch-core.c
@@ -37,6 +37,7 @@ typedef struct {
 	struct list_head list;
 	struct proc_dir_entry *parent;
 	int nr;
+	void *driver;
 	switch_config handler;
 } switch_proc_handler;
 
@@ -47,7 +48,6 @@ typedef struct {
 	int nr;
 } switch_priv;
 
-
 static ssize_t switch_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos);
 static ssize_t switch_proc_write(struct file *file, const char *buf, size_t count, void *data);
 
@@ -56,7 +56,7 @@ static struct file_operations switch_proc_fops = {
 	write: switch_proc_write
 };
 
-static char *strdup(char *str)
+static inline char *strdup(char *str)
 {
 	char *new = kmalloc(strlen(str) + 1, GFP_KERNEL);
 	strcpy(new, str);
@@ -80,7 +80,7 @@ static ssize_t switch_proc_read(struct file *file, char *buf, size_t count, loff
 	if (dent->data != NULL) {
 		switch_proc_handler *handler = (switch_proc_handler *) dent->data;
 		if (handler->handler.read != NULL)
-			len += handler->handler.read(page + len, handler->nr);
+			len += handler->handler.read(handler->driver, page + len, handler->nr);
 	}
 	len += 1;
 
@@ -122,7 +122,7 @@ static ssize_t switch_proc_write(struct file *file, const char *buf, size_t coun
 	if (dent->data != NULL) {
 		switch_proc_handler *handler = (switch_proc_handler *) dent->data;
 		if (handler->handler.write != NULL) {
-			if ((ret = handler->handler.write(page, handler->nr)) >= 0)
+			if ((ret = handler->handler.write(handler->driver, page, handler->nr)) >= 0)
 				ret = count;
 		}
 	}
@@ -131,8 +131,9 @@ static ssize_t switch_proc_write(struct file *file, const char *buf, size_t coun
 	return ret;
 }
 
-static void add_handlers(switch_priv *priv, switch_config *handlers, struct proc_dir_entry *parent, int nr)
+static void add_handlers(switch_driver *driver, switch_config *handlers, struct proc_dir_entry *parent, int nr)
 {
+	switch_priv *priv = (switch_priv *) driver->data;
 	switch_proc_handler *tmp;
 	int i, mode;
 	struct proc_dir_entry *p;
@@ -142,6 +143,7 @@ static void add_handlers(switch_priv *priv, switch_config *handlers, struct proc
 		INIT_LIST_HEAD(&tmp->list);
 		tmp->parent = parent;
 		tmp->nr = nr;
+		tmp->driver = driver;
 		memcpy(&tmp->handler, &(handlers[i]), sizeof(switch_config));
 		list_add(&tmp->list, &priv->data.list);
 		
@@ -192,7 +194,7 @@ static void do_unregister(switch_driver *driver)
 	kfree(priv->vlans);
 	remove_proc_entry("vlan", priv->driver_dir);
 
-	remove_proc_entry(driver->name, switch_root);
+	remove_proc_entry(driver->interface, switch_root);
 			
 	if (priv->nr == (drv_num - 1))
 		drv_num--;
@@ -213,10 +215,9 @@ static int do_register(switch_driver *driver)
 	INIT_LIST_HEAD(&priv->data.list);
 	
 	priv->nr = drv_num++;
-	sprintf(buf, "%d", priv->nr);
-	priv->driver_dir = proc_mkdir(buf, switch_root);
+	priv->driver_dir = proc_mkdir(driver->interface, switch_root);
 	if (driver->driver_handlers != NULL)
-		add_handlers(priv, driver->driver_handlers, priv->driver_dir, 0);
+		add_handlers(driver, driver->driver_handlers, priv->driver_dir, 0);
 	
 	priv->port_dir = proc_mkdir("port", priv->driver_dir);
 	priv->ports = kmalloc((driver->ports + 1) * sizeof(struct proc_dir_entry *), GFP_KERNEL);
@@ -224,7 +225,7 @@ static int do_register(switch_driver *driver)
 		sprintf(buf, "%d", i);
 		priv->ports[i] = proc_mkdir(buf, priv->port_dir);
 		if (driver->port_handlers != NULL)
-			add_handlers(priv, driver->port_handlers, priv->ports[i], i);
+			add_handlers(driver, driver->port_handlers, priv->ports[i], i);
 	}
 	priv->ports[i] = NULL;
 	
@@ -234,7 +235,7 @@ static int do_register(switch_driver *driver)
 		sprintf(buf, "%d", i);
 		priv->vlans[i] = proc_mkdir(buf, priv->vlan_dir);
 		if (driver->vlan_handlers != NULL)
-			add_handlers(priv, driver->vlan_handlers, priv->vlans[i], i);
+			add_handlers(driver, driver->vlan_handlers, priv->vlans[i], i);
 	}
 	priv->vlans[i] = NULL;
 	
@@ -242,7 +243,7 @@ static int do_register(switch_driver *driver)
 	return 0;
 }
 
-static int isspace(char c) {
+static inline int isspace(char c) {
 	switch(c) {
 		case ' ':
 		case 0x09:
@@ -298,39 +299,57 @@ int switch_print_media(char *buf, int media)
 	return len;
 }
 
-int switch_parse_vlan(char *buf)
+switch_vlan_config *switch_parse_vlan(switch_driver *driver, char *buf)
 {
-	char vlan = 0, tag = 0, pvid_port = 0;
-	int untag, j;
+	switch_vlan_config *c;
+	int j, u, p, s;
+	
+	c = kmalloc(sizeof(switch_vlan_config), GFP_KERNEL);
+	memset(c, 0, sizeof(switch_vlan_config));
 
 	while (isspace(*buf)) buf++;
-	
+	j = 0;
 	while (*buf >= '0' && *buf <= '9') {
-		j = *buf++ - '0';
-		vlan |= 1 << j;
-		
-		untag = 0;
-		/* untag if needed, CPU port requires special handling */
-		if (*buf == 'u' || (j != 5 && (isspace(*buf) || *buf == 0))) {
-			untag = 1;
-			if (*buf) buf++;
-		} else if (*buf == '*') {
-			pvid_port |= (1 << j);
-			buf++;
-		} else if (*buf == 't' || isspace(*buf)) {
-			buf++;
-		} else break;
-
-		if (!untag)
-			tag |= 1 << j;
+		j *= 10;
+		j += *buf++ - '0';
+
+		u = ((j == driver->cpuport) ? 0 : 1);
+		p = 0;
+		s = !(*buf >= '0' && *buf <= '9');
+	
+		if (s) {
+			while (s && !isspace(*buf) && (*buf != 0)) {
+				switch(*buf) {
+					case 'u':
+						u = 1;
+						break;
+					case 't':
+						u = 0;
+						break;
+					case '*':
+						p = 1;
+						break;
+				}
+				buf++;
+			}
+			c->port |= (1 << j);
+			if (u)
+				c->untag |= (1 << j);
+			if (p)
+				c->pvid |= (1 << j);
+
+			j = 0;
+		}
 		
 		while (isspace(*buf)) buf++;
 	}
-	
-	if (*buf)
-		return -1;
+	if (*buf != 0) return NULL;
 
-	return (pvid_port << 16) | (tag << 8) | vlan;
+	c->port &= (1 << driver->ports) - 1;
+	c->untag &= (1 << driver->ports) - 1;
+	c->pvid &= (1 << driver->ports) - 1;
+	
+	return c;
 }
 
 
@@ -345,11 +364,16 @@ int switch_register_driver(switch_driver *driver)
 			printk("Switch driver '%s' already exists in the kernel\n", driver->name);
 			return -EINVAL;
 		}
+		if (strcmp(list_entry(pos, switch_driver, list)->interface, driver->interface) == 0) {
+			printk("There is already a switch registered on the device '%s'\n", driver->interface);
+			return -EINVAL;
+		}
 	}
 
 	new = kmalloc(sizeof(switch_driver), GFP_KERNEL);
 	memcpy(new, driver, sizeof(switch_driver));
 	new->name = strdup(driver->name);
+	new->interface = strdup(driver->interface);
 	
 	if ((ret = do_register(new)) < 0) {
 		kfree(new->name);
diff --git a/openwrt/target/linux/package/switch/src/switch-core.h b/openwrt/target/linux/package/switch/src/switch-core.h
index 9927e85192..bdf0ae1f89 100644
--- a/openwrt/target/linux/package/switch/src/switch-core.h
+++ b/openwrt/target/linux/package/switch/src/switch-core.h
@@ -17,27 +17,33 @@
 #define LINUX_2_4
 #endif
 
-typedef int (*switch_handler)(char *buf, int nr);
+typedef int (*switch_handler)(void *driver, char *buf, int nr);
 
 typedef struct {
 	char *name;
 	switch_handler read, write;
 } switch_config;
 
-
 typedef struct {
 	struct list_head list;
 	char *name;
+	char *interface;
+	int cpuport;
 	int ports;
 	int vlans;
 	switch_config *driver_handlers, *port_handlers, *vlan_handlers;
 	void *data;
+	void *priv;
 } switch_driver;
 
+typedef struct {
+	u32 port, untag, pvid;
+} switch_vlan_config;
+
 
 extern int switch_register_driver(switch_driver *driver);
 extern void switch_unregister_driver(char *name);
-extern int switch_parse_vlan(char *buf);
+extern switch_vlan_config *switch_parse_vlan(switch_driver *driver, char *buf);
 extern int switch_parse_media(char *buf);
 extern int switch_print_media(char *buf, int media);
 
diff --git a/openwrt/target/linux/package/switch/src/switch-robo.c b/openwrt/target/linux/package/switch/src/switch-robo.c
index d596dc6536..57978bce06 100644
--- a/openwrt/target/linux/package/switch/src/switch-robo.c
+++ b/openwrt/target/linux/package/switch/src/switch-robo.c
@@ -56,18 +56,6 @@ static int max_vlans, max_ports;
 static struct ifreq ifr;
 static struct net_device *dev;
 
-static int isspace(char c) {
-	switch(c) {
-		case ' ':
-		case 0x09:
-		case 0x0a:
-		case 0x0d:
-			return 1;
-		default:
-			return 0;
-	}
-}
-
 static int do_ioctl(int cmd, void *buf)
 {
 	mm_segment_t old_fs = get_fs();
@@ -297,7 +285,7 @@ static int robo_probe(char *devname)
 }
 
 
-static int handle_vlan_port_read(char *buf, int nr)
+static int handle_vlan_port_read(void *driver, char *buf, int nr)
 {
 	__u16 val16;
 	int len = 0;
@@ -337,45 +325,34 @@ static int handle_vlan_port_read(char *buf, int nr)
 	return len;
 }
 
-static int handle_vlan_port_write(char *buf, int nr)
+static int handle_vlan_port_write(void *driver, char *buf, int nr)
 {
-	int untag = 0;
-	int member = 0;
+	switch_driver *d = (switch_driver *) driver;
+	switch_vlan_config *c = switch_parse_vlan(d, buf);
 	int j;
 	__u16 val16;
 	
-	while (*buf >= '0' && *buf <= '9') {
-		j = *buf++ - '0';
-		member |= 1 << j;
-		
-		/* untag if needed, CPU port requires special handling */
-		if (*buf == 'u' || (j != 5 && (isspace(*buf) || *buf == 0))) {
-			untag |= 1 << j;
-			if (*buf) buf++;
+	if (c == NULL)
+		return -EINVAL;
+
+	for (j = 0; j < d->ports; j++) {
+		if ((c->untag | c->pvid) & (1 << j))
 			/* change default vlan tag */
 			robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (j << 1), nr);
-		} else if (*buf == '*' || *buf == 't' || isspace(*buf)) {
-			buf++;
-		} else break;
-		
-		while (isspace(*buf)) buf++;
 	}
-	
-	if (*buf) {
-		return -1;
+
+	/* write config now */
+	val16 = (nr) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
+	if (is_5350) {
+		robo_write32(ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350,
+			(1 << 20) /* valid */ | (c->untag << 6) | c->port);
+		robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
 	} else {
-		/* write config now */
-		val16 = (nr) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
-		if (is_5350) {
-			robo_write32(ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350,
-				(1 << 20) /* valid */ | (untag << 6) | member);
-			robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
-		} else {
-			robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_WRITE,
-				(1 << 14)  /* valid */ | (untag << 7) | member);
-			robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
-		}
+		robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_WRITE,
+			(1 << 14)  /* valid */ | (c->untag << 7) | c->port);
+		robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
 	}
+
 	return 0;
 }
 
@@ -387,6 +364,7 @@ static int __init robo_init()
 	for (device[3] = '0'; (device[3] <= '3') && notfound; device[3]++) {
 		notfound = robo_probe(device);
 	}
+	device[3]--;
 	
 	if (notfound)
 		return -ENODEV;
@@ -397,6 +375,8 @@ static int __init robo_init()
 		};
 		switch_driver driver = {
 			name: DRIVER_NAME,
+			interface: device,
+			cpuport: max_ports - 1,
 			ports: max_ports,
 			vlans: max_vlans,
 			driver_handlers: NULL,
-- 
2.30.2