include missing ext in iptables again after 2.6 move to xtables
[openwrt.git] / openwrt / target / linux / package / switch / src / switch-core.c
index 8b4419b..6b59b9b 100644 (file)
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
  * $Id: $
+ *
+ * Basic doc of driver's /proc interface:
+ * /proc/switch/<interface>/
+ *   registers:              read-only
+ *   counters:               read-only
+ *   reset:                  write causes hardware reset
+ *   enable_vlan:            "0", "1"
+ *   port/<port-number>/
+ *     enabled:              "0", "1"
+ *     media:                "AUTO", "100FD", "100HD", "10FD", "10HD"
+ *   vlan/<port-number>/
+ *     ports: same syntax as for nvram's vlan*ports (eg. "1 2 3 4 5*")
  */
 
 #include <linux/config.h>
@@ -37,6 +49,7 @@ typedef struct {
        struct list_head list;
        struct proc_dir_entry *parent;
        int nr;
+       void *driver;
        switch_config handler;
 } switch_proc_handler;
 
@@ -47,7 +60,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,13 +68,6 @@ static struct file_operations switch_proc_fops = {
        write: switch_proc_write
 };
 
-static char *strdup(char *str)
-{
-       char *new = kmalloc(strlen(str) + 1, GFP_KERNEL);
-       strcpy(new, str);
-       return new;
-}
-
 static ssize_t switch_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
 {
 #ifdef LINUX_2_4
@@ -80,7 +85,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 +127,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,28 +136,50 @@ 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 int handle_driver_name(void *driver, char *buf, int nr)
 {
-       switch_proc_handler *tmp;
-       int i, mode;
+       char *name = ((switch_driver *) driver)->name;
+       return sprintf(buf, "%s\n", name);
+}
+
+static int handle_driver_version(void *driver, char *buf, int nr)
+{
+       char *version = ((switch_driver *) driver)->version;
+       strcpy(buf, version);
+       return sprintf(buf, "%s\n", version);
+}
+
+static void add_handler(switch_driver *driver, switch_config *handler, struct proc_dir_entry *parent, int nr)
+{
+       switch_priv *priv = (switch_priv *) driver->data;
        struct proc_dir_entry *p;
+       int mode;
+
+       switch_proc_handler *tmp;
+       tmp = (switch_proc_handler *) kmalloc(sizeof(switch_proc_handler), GFP_KERNEL);
+       INIT_LIST_HEAD(&tmp->list);
+       tmp->parent = parent;
+       tmp->nr = nr;
+       tmp->driver = driver;
+       memcpy(&tmp->handler, handler, sizeof(switch_config));
+       list_add(&tmp->list, &priv->data.list);
+       
+       mode = 0;
+       if (handler->read != NULL) mode |= S_IRUSR;
+       if (handler->write != NULL) mode |= S_IWUSR;
+       
+       if ((p = create_proc_entry(handler->name, mode, parent)) != NULL) {
+               p->data = (void *) tmp;
+               p->proc_fops = &switch_proc_fops;
+       }
+}
+
+static inline void add_handlers(switch_driver *driver, switch_config *handlers, struct proc_dir_entry *parent, int nr)
+{
+       int i;
        
        for (i = 0; handlers[i].name != NULL; i++) {
-               tmp = kmalloc(sizeof(switch_proc_handler), GFP_KERNEL);
-               INIT_LIST_HEAD(&tmp->list);
-               tmp->parent = parent;
-               tmp->nr = nr;
-               memcpy(&tmp->handler, &(handlers[i]), sizeof(switch_config));
-               list_add(&tmp->list, &priv->data.list);
-               
-               mode = 0;
-               if (handlers[i].read != NULL) mode |= S_IRUSR;
-               if (handlers[i].write != NULL) mode |= S_IWUSR;
-               
-               if ((p = create_proc_entry(handlers[i].name, mode, parent)) != NULL) {
-                       p->data = (void *) tmp;
-                       p->proc_fops = &switch_proc_fops;
-               }
+               add_handler(driver, &(handlers[i]), parent, nr);
        }
 }              
 
@@ -192,7 +219,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--;
@@ -200,6 +227,12 @@ static void do_unregister(switch_driver *driver)
        kfree(priv);
 }
 
+switch_config global_driver_handlers[] = {
+       {"driver", handle_driver_name, NULL},
+       {"version", handle_driver_version, NULL},
+       {NULL, NULL, NULL}
+};
+
 static int do_register(switch_driver *driver)
 {
        switch_priv *priv;
@@ -213,10 +246,11 @@ 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);
-       if (driver->driver_handlers != NULL)
-               add_handlers(priv, driver->driver_handlers, priv->driver_dir, 0);
+       priv->driver_dir = proc_mkdir(driver->interface, switch_root);
+       if (driver->driver_handlers != NULL) {
+               add_handlers(driver, driver->driver_handlers, priv->driver_dir, 0);
+               add_handlers(driver, global_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 +258,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 +268,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 +276,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 +332,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 +397,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);
@@ -393,7 +450,7 @@ static int __init switch_init()
 
 static void __exit switch_exit()
 {
-       remove_proc_entry("vlan", NULL);
+       remove_proc_entry("switch", NULL);
 }
 
 MODULE_AUTHOR("Felix Fietkau <openwrt@nbd.name>");
This page took 0.026687 seconds and 4 git commands to generate.