Root/target/linux/ubicom32/files/arch/ubicom32/mach-common/switch-core.c

1/*
2 * arch/ubicom32/mach-common/switch-core.c
3 * Ubicom32 architecture switch and /proc/switch/... implementation.
4 *
5 * (C) Copyright 2009, Ubicom, Inc.
6 * Copyright (C) 2005 Felix Fietkau <openwrt@nbd.name>
7 *
8 * This file is part of the Ubicom32 Linux Kernel Port.
9 *
10 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
11 * it and/or modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation, either version 2 of the
13 * License, or (at your option) any later version.
14 *
15 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
16 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
17 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
18 * the GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with the Ubicom32 Linux Kernel Port. If not,
22 * see <http://www.gnu.org/licenses/>.
23 *
24 * Ubicom32 implementation derived from (with many thanks):
25 * arch/m68knommu
26 * arch/blackfin
27 * arch/parisc
28 *
29 * Basic doc of driver's /proc interface:
30 * /proc/switch/<interface>/
31 * registers: read-only
32 * counters: read-only
33 * reset: write causes hardware reset
34 * enable: "0", "1"
35 * enable_vlan: "0", "1"
36 * port/<port-number>/
37 * enabled: "0", "1"
38 * link state: read-only
39 * media: "AUTO", "1000FD", "100FD", "100HD", "10FD", "10HD"
40 * vlan/<port-number>/
41 * ports: same syntax as for nvram's vlan*ports (eg. "1 2 3 4 5*")
42 */
43
44#include <linux/module.h>
45#include <linux/init.h>
46#include <linux/uaccess.h>
47#include <linux/ctype.h>
48#include <linux/proc_fs.h>
49#include <linux/list.h>
50#include <linux/rwsem.h>
51#include <linux/device.h>
52
53#include "switch-core.h"
54
55/*
56 * Pointer to the root of our filesystem
57 */
58static struct proc_dir_entry *switch_root;
59
60/*
61 * Lock used to manage access to the switch list
62 */
63DECLARE_RWSEM(switch_list_lock);
64EXPORT_SYMBOL_GPL(switch_list_lock);
65
66/*
67 * List of switches we are managing
68 */
69LIST_HEAD(switch_list);
70EXPORT_SYMBOL_GPL(switch_list);
71
72/*
73 * List of handlers we have
74 */
75LIST_HEAD(switch_handler_list);
76EXPORT_SYMBOL_GPL(switch_handler_list);
77
78/*
79 * Keep track of all the handlers we added
80 */
81struct switch_handler_entry {
82    struct list_head node;
83    struct proc_dir_entry *parent;
84    struct switch_device *dev;
85    const struct switch_handler *handler;
86    int inst;
87};
88
89/*
90 * Keep track of all VLAN dirs we created
91 */
92struct switch_vlan_entry {
93    struct list_head node;
94    struct proc_dir_entry *pde;
95    int vlan_id;
96    const struct switch_handler *handlers;
97};
98
99/*
100 * switch_parse_vlan_ports
101 * Parse the vlan properties written to <driver>/vlan/<vlan_id>/ports
102 */
103void switch_parse_vlan_ports(struct switch_device *switch_dev,
104                 char *buf, u32_t *untag,
105                 u32_t *ports, u32_t *def)
106{
107    u32_t tag = 0;
108    *untag = 0;
109    *ports = 0;
110    *def = 0;
111
112
113    /*
114     * Skip any leading spaces
115     */
116    while (isspace(*buf)) {
117        buf++;
118    }
119
120    /*
121     * Parse out the string
122     */
123    while (*buf) {
124        u32_t port = simple_strtoul(buf, &buf, 10);
125        u32_t mask = (1 << port);
126
127        /*
128         * Parse out any flags
129         */
130        while (*buf && !isspace(*buf)) {
131            switch (*buf++) {
132            case 't':
133                tag |= mask;
134                break;
135            case '*':
136                *def |= mask;
137                break;
138            }
139        }
140        *ports |= mask;
141
142        /*
143         * Skip any spaces
144         */
145        while (isspace(*buf)) {
146            buf++;
147        }
148    }
149
150    *untag = ~tag & *ports;
151}
152
153/*
154 * switch_proc_read
155 * Handle reads from the procfs, dispatches the driver specific handler
156 */
157static ssize_t switch_proc_read(struct file *file, char *buf, size_t count,
158                loff_t *ppos)
159{
160    struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
161    char *page;
162    int len = 0;
163
164    page = kmalloc(SWITCH_MAX_BUFSZ, GFP_KERNEL);
165    if (!page) {
166        return -ENOBUFS;
167    }
168
169    if (pde->data != NULL) {
170        struct switch_handler_entry *she =
171            (struct switch_handler_entry *)pde->data;
172        if (she->handler->read) {
173            len += she->handler->read(she->dev, page + len,
174                          she->inst);
175        }
176    }
177    len += 1;
178
179    if (*ppos < len) {
180        len = min_t(int, len - *ppos, count);
181        if (copy_to_user(buf, (page + *ppos), len)) {
182            kfree(page);
183            return -EFAULT;
184        }
185        *ppos += len;
186    } else {
187        len = 0;
188    }
189
190    kfree(page);
191
192    return len;
193}
194
195/*
196 * switch_proc_write
197 * Handle writes from the procfs, dispatches the driver specific handler
198 */
199static ssize_t switch_proc_write(struct file *file, const char *buf,
200                 size_t count, loff_t *data)
201{
202    struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
203    char *page;
204    int ret = -EINVAL;
205
206    page = kmalloc(count + 1, GFP_KERNEL);
207    if (page == NULL)
208        return -ENOBUFS;
209
210    if (copy_from_user(page, buf, count)) {
211        kfree(page);
212        return -EINVAL;
213    }
214    page[count] = 0;
215
216    if (pde->data != NULL) {
217        struct switch_handler_entry *she =
218            (struct switch_handler_entry *)pde->data;
219        if (she->handler->write) {
220            ret = she->handler->write(she->dev, page, she->inst);
221            if (ret >= 0) {
222                ret = count;
223            }
224        }
225    }
226
227    kfree(page);
228    return ret;
229}
230
231/*
232 * File operations for the proc_fs, we must cast here since proc_fs' definitions
233 * differ from file_operations definitions.
234 */
235static struct file_operations switch_proc_fops = {
236    .read = (ssize_t (*) (struct file *, char __user *,
237                  size_t, loff_t *))switch_proc_read,
238    .write = (ssize_t (*) (struct file *, const char __user *,
239                   size_t, loff_t *))switch_proc_write,
240};
241
242/*
243 * switch_add_handler
244 */
245static int switch_add_handler(struct switch_device *switch_dev,
246                  struct proc_dir_entry *parent,
247                  const struct switch_handler *handler,
248                  int inst)
249{
250    struct switch_handler_entry *she;
251    struct proc_dir_entry *pde;
252    int mode;
253
254    she = (struct switch_handler_entry *)
255        kzalloc(sizeof(struct switch_handler_entry), GFP_KERNEL);
256    if (!she) {
257        return -ENOMEM;
258    }
259
260    INIT_LIST_HEAD(&she->node);
261    she->parent = parent;
262    she->dev = switch_dev;
263    she->inst = inst;
264    she->handler = handler;
265    list_add(&she->node, &switch_dev->handlers);
266
267    mode = 0;
268    if (handler->read != NULL) {
269        mode |= S_IRUSR;
270    }
271    if (handler->write != NULL) {
272        mode |= S_IWUSR;
273    }
274
275    pde = create_proc_entry(handler->name, mode, parent);
276    if (!pde) {
277        kfree(she);
278        printk("Failed to create node '%s' in parent %p\n",
279               handler->name, parent);
280        return -ENOMEM;
281    }
282    pde->data = (void *)she;
283    pde->proc_fops = &switch_proc_fops;
284
285    return 0;
286}
287
288/*
289 * switch_add_handlers
290 */
291static int switch_add_handlers(struct switch_device *switch_dev,
292                   struct proc_dir_entry *parent,
293                   const struct switch_handler *handlers,
294                   int inst)
295{
296    while (handlers->name) {
297        int ret = switch_add_handler(switch_dev,
298                         parent, handlers, inst);
299        if (ret) {
300            return ret;
301        }
302        handlers++;
303    }
304
305    return 0;
306}
307
308/*
309 * switch_remove_vlan_dirs
310 * Removes all vlan directories
311 *
312 * Assumes all vlan directories are empty, should be called after
313 * switch_remove_handlers
314 */
315static void switch_remove_vlan_dirs(struct switch_device *switch_dev)
316{
317    struct list_head *pos;
318    struct list_head *tmp;
319    struct switch_vlan_entry *sve;
320
321    list_for_each_safe(pos, tmp, &switch_dev->vlan_dirs) {
322        sve = list_entry(pos, struct switch_vlan_entry, node);
323        list_del(pos);
324        remove_proc_entry(sve->pde->name, switch_dev->vlan_dir);
325        kfree(sve);
326    }
327}
328
329/*
330 * switch_remove_handlers
331 * Removes all handlers registered to the given switch_device
332 */
333static void switch_remove_handlers(struct switch_device *switch_dev)
334{
335    struct list_head *pos;
336    struct list_head *tmp;
337    struct switch_handler_entry *she;
338
339    list_for_each_safe(pos, tmp, &switch_dev->handlers) {
340        she = list_entry(pos, struct switch_handler_entry, node);
341        list_del(pos);
342        remove_proc_entry(she->handler->name, she->parent);
343        kfree(she);
344    }
345}
346
347/*
348 * switch_unregister_proc_nodes
349 * Unregisters all proc nodes related to switch_dev
350 */
351void switch_unregister_proc_nodes(struct switch_device *switch_dev)
352{
353    switch_remove_handlers(switch_dev);
354
355    if (switch_dev->port_dirs) {
356        int i;
357
358        for (i = 0; i < switch_dev->ports; i++) {
359            if (switch_dev->port_dirs[i]) {
360                remove_proc_entry(
361                    switch_dev->port_dirs[i]->name,
362                    switch_dev->port_dir);
363            }
364        }
365    }
366
367    if (switch_dev->port_dir) {
368        remove_proc_entry("port", switch_dev->driver_dir);
369        switch_dev->port_dir = NULL;
370    }
371
372    if (switch_dev->reg_dir) {
373        remove_proc_entry("reg", switch_dev->reg_dir);
374        switch_dev->reg_dir = NULL;
375    }
376
377    if (switch_dev->vlan_dir) {
378        switch_remove_vlan_dirs(switch_dev);
379        remove_proc_entry("vlan", switch_dev->driver_dir);
380        switch_dev->vlan_dir = NULL;
381    }
382
383    if (switch_dev->driver_dir) {
384        remove_proc_entry(switch_dev->name, switch_root);
385        switch_dev->driver_dir = NULL;
386    }
387}
388
389/*
390 * switch_remove_vlan_dir
391 * Removes vlan dir in switch/<switch_driver>/vlan/<vlan_id>
392 */
393int switch_remove_vlan_dir(struct switch_device *switch_dev, int vlan_id)
394{
395    struct list_head *pos;
396    struct switch_vlan_entry *sve = NULL;
397
398    list_for_each(pos, &switch_dev->vlan_dirs) {
399        struct switch_vlan_entry *tmp =
400            list_entry(pos, struct switch_vlan_entry, node);
401        if (tmp->vlan_id == vlan_id) {
402            sve = tmp;
403            break;
404        }
405    }
406
407    if (!sve) {
408        return -ENOENT;
409    }
410
411    /*
412     * Remove it from the list
413     */
414    list_del(pos);
415
416    /*
417     * Remove the handlers
418     */
419    while (sve->handlers->name) {
420        remove_proc_entry(sve->handlers->name, sve->pde);
421        sve->handlers++;
422    }
423
424    /*
425     * Remove the proc entry for the <vlan_id> dir
426     */
427    remove_proc_entry(sve->pde->name, switch_dev->vlan_dir);
428
429    kfree(sve);
430
431    return 0;
432}
433
434/*
435 * switch_create_vlan_dir
436 * Creates vlan dir in switch/<switch_driver>/vlan/<vlan_id>
437 */
438int switch_create_vlan_dir(struct switch_device *switch_dev,
439               int vlan_id, const struct switch_handler *handlers)
440{
441    char s[14];
442    struct proc_dir_entry *pde = NULL;
443    struct switch_vlan_entry *sve = NULL;
444    int ret;
445    struct list_head *pos;
446
447    /*
448     * Check to see if it exists already
449     */
450    list_for_each(pos, &switch_dev->vlan_dirs) {
451        sve = list_entry(pos, struct switch_vlan_entry, node);
452        if (sve->vlan_id == vlan_id) {
453            return -EEXIST;
454        }
455    }
456    sve = NULL;
457
458    /*
459     * Create the vlan directory if we didn't have it before
460     */
461    if (!switch_dev->vlan_dir) {
462        switch_dev->vlan_dir = proc_mkdir("vlan",
463                          switch_dev->driver_dir);
464        if (!switch_dev->vlan_dir) {
465            goto fail;
466        }
467        if (switch_dev->vlan_handlers) {
468            ret = switch_add_handlers(switch_dev,
469                          switch_dev->vlan_dir,
470                          switch_dev->vlan_handlers, 0);
471            if (ret) {
472                goto fail;
473            }
474        }
475    }
476
477    /*
478     * Create the vlan_id directory
479     */
480    snprintf(s, 14, "%d", vlan_id);
481    pde = proc_mkdir(s, switch_dev->vlan_dir);
482    if (!pde) {
483        goto fail;
484    }
485
486    /*
487     * Create the handlers for this vlan
488     */
489    if (handlers) {
490        ret = switch_add_handlers(switch_dev, pde, handlers, vlan_id);
491        if (ret) {
492            goto fail;
493        }
494    }
495
496    /*
497     * Keep track of all the switch vlan entries created
498     */
499    sve = (struct switch_vlan_entry *)
500        kzalloc(sizeof(struct switch_vlan_entry), GFP_KERNEL);
501    if (!sve) {
502        goto fail;
503    }
504    INIT_LIST_HEAD(&sve->node);
505    sve->handlers = handlers;
506    sve->vlan_id = vlan_id;
507    sve->pde = pde;
508    list_add(&sve->node, &switch_dev->vlan_dirs);
509
510    return 0;
511
512fail:
513    if (sve) {
514        kfree(sve);
515    }
516
517    if (pde) {
518        /*
519         * Remove any proc entries we might have created
520         */
521        while (handlers->name) {
522            remove_proc_entry(handlers->name, pde);
523            handlers++;
524        }
525
526        remove_proc_entry(s, switch_dev->driver_dir);
527    }
528
529    return -ENOMEM;
530}
531
532/*
533 * switch_register_proc_nodes
534 */
535int switch_register_proc_nodes(struct switch_device *switch_dev)
536{
537    int i;
538    int n;
539
540    switch_dev->port_dirs = kzalloc(switch_dev->ports *
541                    sizeof(struct proc_dir_entry *),
542                    GFP_KERNEL);
543    if (!switch_dev->port_dirs) {
544        return -ENOMEM;
545    }
546
547    /*
548     * Create a new proc entry for this switch
549     */
550    switch_dev->driver_dir = proc_mkdir(switch_dev->name, switch_root);
551    if (!switch_dev->driver_dir) {
552        goto fail;
553    }
554    if (switch_dev->driver_handlers) {
555        switch_add_handlers(switch_dev,
556                    switch_dev->driver_dir,
557                    switch_dev->driver_handlers,
558                    0);
559    }
560
561    /*
562     * Create the ports
563     */
564    switch_dev->port_dir = proc_mkdir("port", switch_dev->driver_dir);
565    if (!switch_dev->port_dir) {
566        goto fail;
567    }
568    for (n = 0, i = 0; i < (SWITCH_PORT_MASK_SIZE * 32); i++) {
569        if (switch_dev->port_mask[i / 32] & (1 << i % 32)) {
570            char s[14];
571
572            snprintf(s, 14, "%d", i);
573            switch_dev->port_dirs[n] =
574                proc_mkdir(s, switch_dev->port_dir);
575            if (!switch_dev->port_dirs[n]) {
576                goto fail;
577            }
578            if (switch_dev->port_handlers) {
579                switch_add_handlers(switch_dev,
580                            switch_dev->port_dirs[n],
581                            switch_dev->port_handlers,
582                            i);
583            }
584            n++;
585        }
586    }
587
588    /*
589     * Create the register directory for switch register access.
590     */
591    if (switch_dev->reg_handlers) {
592        switch_dev->reg_dir = proc_mkdir("reg", switch_dev->driver_dir);
593        if (!switch_dev->reg_dir) {
594            goto fail;
595        }
596
597        switch_add_handlers(switch_dev,
598                    switch_dev->reg_dir,
599                    switch_dev->reg_handlers,
600                    0);
601    }
602
603    /*
604     * Create the vlan directory
605     */
606    if (switch_dev->vlan_handlers) {
607        switch_dev->vlan_dir = proc_mkdir("vlan",
608                          switch_dev->driver_dir);
609        if (!switch_dev->vlan_dir) {
610            goto fail;
611        }
612        if (switch_dev->vlan_handlers) {
613            switch_add_handlers(switch_dev,
614                        switch_dev->vlan_dir,
615                        switch_dev->vlan_handlers,
616                        0);
617        }
618    }
619
620    return 0;
621
622fail:
623    switch_unregister_proc_nodes(switch_dev);
624    return -ENOMEM;
625}
626
627/*
628 * switch_release
629 */
630void switch_release(struct switch_device *switch_dev)
631{
632    kfree(switch_dev);
633}
634
635/*
636 * switch_alloc
637 */
638struct switch_device *switch_alloc(void)
639{
640    struct switch_device *switch_dev =
641        kzalloc(sizeof(struct switch_device),
642                        GFP_KERNEL);
643    INIT_LIST_HEAD(&switch_dev->node);
644    INIT_LIST_HEAD(&switch_dev->vlan_dirs);
645    INIT_LIST_HEAD(&switch_dev->handlers);
646    return switch_dev;
647}
648
649/*
650 * switch_register
651 */
652int switch_register(struct switch_device *switch_dev)
653{
654    int ret;
655    int i;
656
657    /*
658     * Make sure that the number of ports and the port mask make sense
659     */
660    for (ret = 0, i = 0; i < (SWITCH_PORT_MASK_SIZE * 32); i++) {
661        if (switch_dev->port_mask[i / 32] & (1 << i % 32)) {
662            ret++;
663        }
664    }
665    if (ret > switch_dev->ports) {
666        return -EINVAL;
667    }
668
669    /*
670     * Create the /proc entries
671     */
672    ret = switch_register_proc_nodes(switch_dev);
673    if (ret) {
674        return ret;
675    }
676
677    /*
678     * Add it to the list of switches
679     */
680    down_write(&switch_list_lock);
681    list_add_tail(&switch_dev->node, &switch_list);
682    up_write(&switch_list_lock);
683
684    printk(KERN_INFO "Registered switch device: %s\n", switch_dev->name);
685
686    return 0;
687}
688EXPORT_SYMBOL_GPL(switch_register);
689
690/*
691 * switch_unregister
692 * Unregisters a previously registered switch_device object
693 */
694void switch_unregister(struct switch_device *switch_dev)
695{
696    /*
697     * remove the proc entries
698     */
699    switch_unregister_proc_nodes(switch_dev);
700
701    /*
702     * Remove it from the list of switches
703     */
704    down_write(&switch_list_lock);
705    list_del(&switch_dev->node);
706    up_write(&switch_list_lock);
707
708    printk(KERN_INFO "Unregistered switch device: %s\n", switch_dev->name);
709}
710EXPORT_SYMBOL_GPL(switch_unregister);
711
712/*
713 * switch_init
714 */
715static int __init switch_init(void)
716{
717    switch_root = proc_mkdir("switch", NULL);
718    if (!switch_root) {
719        printk(KERN_WARNING "Failed to make root switch node\n");
720        return -ENODEV;
721    }
722    return 0;
723}
724module_init(switch_init);
725
726/*
727 * switch_exit
728 */
729static void __exit switch_exit(void)
730{
731    remove_proc_entry("switch", NULL);
732}
733module_exit(switch_exit);
734
735MODULE_AUTHOR("Patrick Tjin");
736MODULE_LICENSE("GPL");
737MODULE_DESCRIPTION("Ethernet Switch Class Interface");
738

Archive Download this file



interactive