[package] switch: do not include linux/autoconf.h (#6513)
[openwrt.git] / package / switch / src / switch-core.c
1 /*
2  * switch-core.c
3  *
4  * Copyright (C) 2005 Felix Fietkau <openwrt@nbd.name>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  *
21  * Basic doc of driver's /proc interface:
22  * /proc/switch/<interface>/
23  *   registers:              read-only
24  *   counters:               read-only
25  *   reset:                  write causes hardware reset
26  *   enable_vlan:            "0", "1"
27  *   port/<port-number>/
28  *     enabled:              "0", "1"
29  *     media:                "AUTO", "100FD", "100HD", "10FD", "10HD"
30  *   vlan/<port-number>/
31  *     ports: same syntax as for nvram's vlan*ports (eg. "1 2 3 4 5*")
32  */
33
34 #include <linux/module.h>
35 #include <linux/init.h>
36 #include <asm/uaccess.h>
37 #include <linux/proc_fs.h>
38 #include <linux/list.h>
39
40 #include "switch-core.h"
41
42 static int drv_num = 0;
43 static struct proc_dir_entry *switch_root;
44 switch_driver drivers;
45
46 typedef struct {
47         struct list_head list;
48         struct proc_dir_entry *parent;
49         int nr;
50         void *driver;
51         switch_config handler;
52 } switch_proc_handler;
53
54 typedef struct {
55         struct proc_dir_entry *driver_dir, *port_dir, *vlan_dir;
56         struct proc_dir_entry **ports, **vlans;
57         switch_proc_handler data;
58         int nr;
59 } switch_priv;
60
61 static ssize_t switch_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos);
62 static ssize_t switch_proc_write(struct file *file, const char *buf, size_t count, void *data);
63
64 static struct file_operations switch_proc_fops = {
65         .read = (ssize_t (*) (struct file *, char __user *, size_t, loff_t *))switch_proc_read,
66         .write = (ssize_t (*) (struct file *, const char __user *, size_t, loff_t *))switch_proc_write
67 };
68
69 static ssize_t switch_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
70 {
71 #ifdef LINUX_2_4
72         struct inode *inode = file->f_dentry->d_inode;
73         struct proc_dir_entry *dent = inode->u.generic_ip;
74 #else
75         struct proc_dir_entry *dent = PDE(file->f_dentry->d_inode);
76 #endif
77         char *page;
78         int len = 0;
79         
80         if ((page = kmalloc(SWITCH_MAX_BUFSZ, GFP_KERNEL)) == NULL)
81                 return -ENOBUFS;
82         
83         if (dent->data != NULL) {
84                 switch_proc_handler *handler = (switch_proc_handler *) dent->data;
85                 if (handler->handler.read != NULL)
86                         len += handler->handler.read(handler->driver, page + len, handler->nr);
87         }
88         len += 1;
89
90         if (*ppos < len) {
91                 len = min_t(int, len - *ppos, count);
92                 if (copy_to_user(buf, (page + *ppos), len)) {
93                         kfree(page);
94                         return -EFAULT;
95                 }
96                 *ppos += len;
97         } else {
98                 len = 0;
99         }
100
101         kfree(page);
102         return len;
103 }
104
105
106 static ssize_t switch_proc_write(struct file *file, const char *buf, size_t count, void *data)
107 {
108 #ifdef LINUX_2_4
109         struct inode *inode = file->f_dentry->d_inode;
110         struct proc_dir_entry *dent = inode->u.generic_ip;
111 #else
112         struct proc_dir_entry *dent = PDE(file->f_dentry->d_inode);
113 #endif
114         char *page;
115         int ret = -EINVAL;
116
117         if ((page = kmalloc(count + 1, GFP_KERNEL)) == NULL)
118                 return -ENOBUFS;
119
120         if (copy_from_user(page, buf, count)) {
121                 kfree(page);
122                 return -EINVAL;
123         }
124         page[count] = 0;
125         
126         if (dent->data != NULL) {
127                 switch_proc_handler *handler = (switch_proc_handler *) dent->data;
128                 if (handler->handler.write != NULL) {
129                         if ((ret = handler->handler.write(handler->driver, page, handler->nr)) >= 0)
130                                 ret = count;
131                 }
132         }
133
134         kfree(page);
135         return ret;
136 }
137
138 static int handle_driver_name(void *driver, char *buf, int nr)
139 {
140         const char *name = ((switch_driver *) driver)->name;
141         return sprintf(buf, "%s\n", name);
142 }
143
144 static int handle_driver_version(void *driver, char *buf, int nr)
145 {
146         const char *version = ((switch_driver *) driver)->version;
147         strcpy(buf, version);
148         return sprintf(buf, "%s\n", version);
149 }
150
151 static void add_handler(switch_driver *driver, const switch_config *handler, struct proc_dir_entry *parent, int nr)
152 {
153         switch_priv *priv = (switch_priv *) driver->data;
154         struct proc_dir_entry *p;
155         int mode;
156
157         switch_proc_handler *tmp;
158         tmp = (switch_proc_handler *) kmalloc(sizeof(switch_proc_handler), GFP_KERNEL);
159         if (!tmp)
160                 return;
161         INIT_LIST_HEAD(&tmp->list);
162         tmp->parent = parent;
163         tmp->nr = nr;
164         tmp->driver = driver;
165         memcpy(&tmp->handler, handler, sizeof(switch_config));
166         list_add(&tmp->list, &priv->data.list);
167         
168         mode = 0;
169         if (handler->read != NULL) mode |= S_IRUSR;
170         if (handler->write != NULL) mode |= S_IWUSR;
171         
172         if ((p = create_proc_entry(handler->name, mode, parent)) != NULL) {
173                 p->data = (void *) tmp;
174                 p->proc_fops = &switch_proc_fops;
175         }
176 }
177
178 static inline void add_handlers(switch_driver *driver, const switch_config *handlers, struct proc_dir_entry *parent, int nr)
179 {
180         int i;
181         
182         for (i = 0; handlers[i].name != NULL; i++) {
183                 add_handler(driver, &(handlers[i]), parent, nr);
184         }
185 }               
186
187 static void remove_handlers(switch_priv *priv)
188 {
189         struct list_head *pos, *q;
190         switch_proc_handler *tmp;
191
192         list_for_each_safe(pos, q, &priv->data.list) {
193                 tmp = list_entry(pos, switch_proc_handler, list);
194                 list_del(pos);
195                 remove_proc_entry(tmp->handler.name, tmp->parent);
196                 kfree(tmp);
197         }
198 }
199
200
201 static void do_unregister(switch_driver *driver)
202 {
203         char buf[4];
204         int i;
205         switch_priv *priv = (switch_priv *) driver->data;
206
207         remove_handlers(priv);
208         
209         for(i = 0; priv->ports[i] != NULL; i++) {
210                 sprintf(buf, "%d", i);
211                 remove_proc_entry(buf, priv->port_dir);
212         }
213         kfree(priv->ports);
214         remove_proc_entry("port", priv->driver_dir);
215
216         for(i = 0; priv->vlans[i] != NULL; i++) {
217                 sprintf(buf, "%d", i);
218                 remove_proc_entry(buf, priv->vlan_dir);
219         }
220         kfree(priv->vlans);
221         remove_proc_entry("vlan", priv->driver_dir);
222
223         remove_proc_entry(driver->interface, switch_root);
224                         
225         if (priv->nr == (drv_num - 1))
226                 drv_num--;
227
228         kfree(priv);
229 }
230
231 switch_config global_driver_handlers[] = {
232         {"driver", handle_driver_name, NULL},
233         {"version", handle_driver_version, NULL},
234         {NULL, NULL, NULL}
235 };
236
237 static int do_register(switch_driver *driver)
238 {
239         switch_priv *priv;
240         int i;
241         char buf[4];
242
243         priv = kmalloc(sizeof(switch_priv), GFP_KERNEL);
244         if (!priv)
245                 return -ENOMEM;
246         driver->data = (void *) priv;
247
248         priv->ports = kmalloc((driver->ports + 1) * sizeof(struct proc_dir_entry *),
249                               GFP_KERNEL);
250         if (!priv->ports) {
251                 kfree(priv);
252                 return -ENOMEM;
253         }
254         priv->vlans = kmalloc((driver->vlans + 1) * sizeof(struct proc_dir_entry *),
255                               GFP_KERNEL);
256         if (!priv->vlans) {
257                 kfree(priv->ports);
258                 kfree(priv);
259                 return -ENOMEM;
260         }
261
262         INIT_LIST_HEAD(&priv->data.list);
263         
264         priv->nr = drv_num++;
265         priv->driver_dir = proc_mkdir(driver->interface, switch_root);
266         if (driver->driver_handlers != NULL) {
267                 add_handlers(driver, driver->driver_handlers, priv->driver_dir, 0);
268                 add_handlers(driver, global_driver_handlers, priv->driver_dir, 0);
269         }
270         
271         priv->port_dir = proc_mkdir("port", priv->driver_dir);
272         for (i = 0; i < driver->ports; i++) {
273                 sprintf(buf, "%d", i);
274                 priv->ports[i] = proc_mkdir(buf, priv->port_dir);
275                 if (driver->port_handlers != NULL)
276                         add_handlers(driver, driver->port_handlers, priv->ports[i], i);
277         }
278         priv->ports[i] = NULL;
279         
280         priv->vlan_dir = proc_mkdir("vlan", priv->driver_dir);
281         for (i = 0; i < driver->vlans; i++) {
282                 sprintf(buf, "%d", i);
283                 priv->vlans[i] = proc_mkdir(buf, priv->vlan_dir);
284                 if (driver->vlan_handlers != NULL)
285                         add_handlers(driver, driver->vlan_handlers, priv->vlans[i], i);
286         }
287         priv->vlans[i] = NULL;
288         
289
290         return 0;
291 }
292
293 static inline int isspace(char c) {
294         switch(c) {
295                 case ' ':
296                 case 0x09:
297                 case 0x0a:
298                 case 0x0d:
299                         return 1;
300                 default:
301                         return 0;
302         }
303 }
304
305 #define toupper(c) (islower(c) ? ((c) ^ 0x20) : (c))
306 #define islower(c) (((unsigned char)((c) - 'a')) < 26)
307                                                                                                                  
308 int switch_parse_media(char *buf)
309 {
310         char *str = buf;
311         while (*buf != 0) {
312                 *buf = toupper(*buf);
313                 buf++;
314         }
315
316         if (strncmp(str, "AUTO", 4) == 0)
317                 return SWITCH_MEDIA_AUTO;
318         else if (strncmp(str, "100FD", 5) == 0)
319                 return SWITCH_MEDIA_100 | SWITCH_MEDIA_FD;
320         else if (strncmp(str, "100HD", 5) == 0)
321                 return SWITCH_MEDIA_100;
322         else if (strncmp(str, "10FD", 4) == 0)
323                 return SWITCH_MEDIA_FD;
324         else if (strncmp(str, "10HD", 4) == 0)
325                 return 0;
326         else return -1;
327 }
328
329 int switch_print_media(char *buf, int media)
330 {
331         int len = 0;
332
333         if (media & SWITCH_MEDIA_AUTO)
334                 len = sprintf(buf, "Auto");
335         else if (media == (SWITCH_MEDIA_100 | SWITCH_MEDIA_FD))
336                 len = sprintf(buf, "100FD");
337         else if (media == SWITCH_MEDIA_100)
338                 len = sprintf(buf,  "100HD");
339         else if (media == SWITCH_MEDIA_FD)
340                 len = sprintf(buf,  "10FD");
341         else if (media == 0)
342                 len = sprintf(buf,  "10HD");
343         else
344                 len = sprintf(buf, "Invalid");
345
346         return len;
347 }
348
349 switch_vlan_config *switch_parse_vlan(switch_driver *driver, char *buf)
350 {
351         switch_vlan_config *c;
352         int j, u, p, s;
353         
354         c = kmalloc(sizeof(switch_vlan_config), GFP_KERNEL);
355         if (!c)
356                 return NULL;
357         memset(c, 0, sizeof(switch_vlan_config));
358
359         while (isspace(*buf)) buf++;
360         j = 0;
361         while (*buf >= '0' && *buf <= '9') {
362                 j *= 10;
363                 j += *buf++ - '0';
364
365                 u = ((j == driver->cpuport) ? 0 : 1);
366                 p = 0;
367                 s = !(*buf >= '0' && *buf <= '9');
368         
369                 if (s) {
370                         while (s && !isspace(*buf) && (*buf != 0)) {
371                                 switch(*buf) {
372                                         case 'u':
373                                                 u = 1;
374                                                 break;
375                                         case 't':
376                                                 u = 0;
377                                                 break;
378                                         case '*':
379                                                 p = 1;
380                                                 break;
381                                 }
382                                 buf++;
383                         }
384                         c->port |= (1 << j);
385                         if (u)
386                                 c->untag |= (1 << j);
387                         if (p)
388                                 c->pvid |= (1 << j);
389
390                         j = 0;
391                 }
392                 
393                 while (isspace(*buf)) buf++;
394         }
395         if (*buf != 0) return NULL;
396
397         c->port &= (1 << driver->ports) - 1;
398         c->untag &= (1 << driver->ports) - 1;
399         c->pvid &= (1 << driver->ports) - 1;
400         
401         return c;
402 }
403
404
405 int switch_device_registered (char* device) {
406         struct list_head *pos;
407         switch_driver *new;
408
409         list_for_each(pos, &drivers.list) {
410                 if (strcmp(list_entry(pos, switch_driver, list)->interface, device) == 0) {
411                         printk("There is already a switch registered on the device '%s'\n", device);
412                         return -EINVAL;
413                 }
414         }
415
416         return 0;
417 }
418
419
420 int switch_register_driver(switch_driver *driver)
421 {
422         struct list_head *pos;
423         switch_driver *new;
424         int ret;
425
426         list_for_each(pos, &drivers.list) {
427                 if (strcmp(list_entry(pos, switch_driver, list)->name, driver->name) == 0) {
428                         printk("Switch driver '%s' already exists in the kernel\n", driver->name);
429                         return -EINVAL;
430                 }
431                 if (strcmp(list_entry(pos, switch_driver, list)->interface, driver->interface) == 0) {
432                         printk("There is already a switch registered on the device '%s'\n", driver->interface);
433                         return -EINVAL;
434                 }
435         }
436
437         new = kmalloc(sizeof(switch_driver), GFP_KERNEL);
438         if (!new)
439                 return -ENOMEM;
440         memcpy(new, driver, sizeof(switch_driver));
441         new->name = strdup(driver->name);
442         new->interface = strdup(driver->interface);
443
444         if ((ret = do_register(new)) < 0) {
445                 kfree(new->name);
446                 kfree(new);
447                 return ret;
448         }
449         INIT_LIST_HEAD(&new->list);
450         list_add(&new->list, &drivers.list);
451
452         return 0;
453 }
454
455 void switch_unregister_driver(char *name) {
456         struct list_head *pos, *q;
457         switch_driver *tmp;
458
459         list_for_each_safe(pos, q, &drivers.list) {
460                 tmp = list_entry(pos, switch_driver, list);
461                 if (strcmp(tmp->name, name) == 0) {
462                         do_unregister(tmp);
463                         list_del(pos);
464                         kfree(tmp->name);
465                         kfree(tmp);
466
467                         return;
468                 }
469         }
470 }
471
472 static int __init switch_init(void)
473 {
474         if ((switch_root = proc_mkdir("switch", NULL)) == NULL) {
475                 printk("%s: proc_mkdir failed.\n", __FILE__);
476                 return -ENODEV;
477         }
478
479         INIT_LIST_HEAD(&drivers.list);
480         
481         return 0;
482 }
483
484 static void __exit switch_exit(void)
485 {
486         remove_proc_entry("switch", NULL);
487 }
488
489 MODULE_AUTHOR("Felix Fietkau <openwrt@nbd.name>");
490 MODULE_LICENSE("GPL");
491
492 EXPORT_SYMBOL(switch_device_registered);
493 EXPORT_SYMBOL(switch_register_driver);
494 EXPORT_SYMBOL(switch_unregister_driver);
495 EXPORT_SYMBOL(switch_parse_vlan);
496 EXPORT_SYMBOL(switch_parse_media);
497 EXPORT_SYMBOL(switch_print_media);
498
499 module_init(switch_init);
500 module_exit(switch_exit);