Root/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
42static int drv_num = 0;
43static struct proc_dir_entry *switch_root;
44switch_driver drivers;
45
46typedef 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
54typedef 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
61static ssize_t switch_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos);
62static ssize_t switch_proc_write(struct file *file, const char *buf, size_t count, void *data);
63
64static 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
69static 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
106static 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
138static 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
144static 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
151static 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
178static 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
187static 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
201static 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
231switch_config global_driver_handlers[] = {
232    {"driver", handle_driver_name, NULL},
233    {"version", handle_driver_version, NULL},
234    {NULL, NULL, NULL}
235};
236
237static 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
293static 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                                                         
308int 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
329int 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
349switch_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
405int 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
420int 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
455void 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
472static 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
484static void __exit switch_exit(void)
485{
486    remove_proc_entry("switch", NULL);
487}
488
489MODULE_AUTHOR("Felix Fietkau <openwrt@nbd.name>");
490MODULE_LICENSE("GPL");
491
492EXPORT_SYMBOL(switch_device_registered);
493EXPORT_SYMBOL(switch_register_driver);
494EXPORT_SYMBOL(switch_unregister_driver);
495EXPORT_SYMBOL(switch_parse_vlan);
496EXPORT_SYMBOL(switch_parse_media);
497EXPORT_SYMBOL(switch_print_media);
498
499module_init(switch_init);
500module_exit(switch_exit);
501

Archive Download this file



interactive