4 * Copyright (C) 2005 Felix Fietkau <openwrt@nbd.name>
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.
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.
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.
21 * Basic doc of driver's /proc interface:
22 * /proc/switch/<interface>/
23 * registers: read-only
25 * reset: write causes hardware reset
26 * enable_vlan: "0", "1"
29 * media: "AUTO", "1000FD", "1000HD", "100FD", "100HD", "10FD", "10HD"
31 * ports: same syntax as for nvram's vlan*ports (eg. "1 2 3 4 5*")
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>
40 #include "switch-core.h"
42 static int drv_num
= 0;
43 static struct proc_dir_entry
*switch_root
;
44 switch_driver drivers
;
47 struct list_head list
;
48 struct proc_dir_entry
*parent
;
51 switch_config handler
;
52 } switch_proc_handler
;
55 struct proc_dir_entry
*driver_dir
, *port_dir
, *vlan_dir
;
56 struct proc_dir_entry
**ports
, **vlans
;
57 switch_proc_handler data
;
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
);
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
69 static ssize_t
switch_proc_read(struct file
*file
, char *buf
, size_t count
, loff_t
*ppos
)
71 struct proc_dir_entry
*dent
= PDE(file
->f_dentry
->d_inode
);
75 if ((page
= kmalloc(SWITCH_MAX_BUFSZ
, GFP_KERNEL
)) == NULL
)
78 if (dent
->data
!= NULL
) {
79 switch_proc_handler
*handler
= (switch_proc_handler
*) dent
->data
;
80 if (handler
->handler
.read
!= NULL
)
81 len
+= handler
->handler
.read(handler
->driver
, page
+ len
, handler
->nr
);
86 len
= min_t(int, len
- *ppos
, count
);
87 if (copy_to_user(buf
, (page
+ *ppos
), len
)) {
101 static ssize_t
switch_proc_write(struct file
*file
, const char *buf
, size_t count
, void *data
)
103 struct proc_dir_entry
*dent
= PDE(file
->f_dentry
->d_inode
);
107 if ((page
= kmalloc(count
+ 1, GFP_KERNEL
)) == NULL
)
110 if (copy_from_user(page
, buf
, count
)) {
116 if (dent
->data
!= NULL
) {
117 switch_proc_handler
*handler
= (switch_proc_handler
*) dent
->data
;
118 if (handler
->handler
.write
!= NULL
) {
119 if ((ret
= handler
->handler
.write(handler
->driver
, page
, handler
->nr
)) >= 0)
128 static int handle_driver_name(void *driver
, char *buf
, int nr
)
130 const char *name
= ((switch_driver
*) driver
)->name
;
131 return sprintf(buf
, "%s\n", name
);
134 static int handle_driver_version(void *driver
, char *buf
, int nr
)
136 const char *version
= ((switch_driver
*) driver
)->version
;
137 strcpy(buf
, version
);
138 return sprintf(buf
, "%s\n", version
);
141 static int handle_driver_cpuport(void *driver
, char *buf
, int nr
)
143 int cpuport
= ((switch_driver
*) driver
)->cpuport
;
144 return sprintf(buf
, "%i\n", cpuport
);
147 static int handle_driver_ports(void *driver
, char *buf
, int nr
)
149 int ports
= ((switch_driver
*) driver
)->ports
;
150 return sprintf(buf
, "%i\n", ports
);
153 static int handle_driver_vlans(void *driver
, char *buf
, int nr
)
155 int vlans
= ((switch_driver
*) driver
)->vlans
;
156 return sprintf(buf
, "%i\n", vlans
);
159 static int handle_driver_dev_name(void *driver
, char *buf
, int nr
)
161 char *dev_name
= ((switch_driver
*) driver
)->dev_name
;
162 return sprintf(buf
, "%s\n", dev_name
);
165 static void add_handler(switch_driver
*driver
, const switch_config
*handler
, struct proc_dir_entry
*parent
, int nr
)
167 switch_priv
*priv
= (switch_priv
*) driver
->data
;
168 struct proc_dir_entry
*p
;
171 switch_proc_handler
*tmp
;
172 tmp
= (switch_proc_handler
*) kmalloc(sizeof(switch_proc_handler
), GFP_KERNEL
);
175 INIT_LIST_HEAD(&tmp
->list
);
176 tmp
->parent
= parent
;
178 tmp
->driver
= driver
;
179 memcpy(&tmp
->handler
, handler
, sizeof(switch_config
));
180 list_add(&tmp
->list
, &priv
->data
.list
);
183 if (handler
->read
!= NULL
) mode
|= S_IRUSR
;
184 if (handler
->write
!= NULL
) mode
|= S_IWUSR
;
186 if ((p
= create_proc_entry(handler
->name
, mode
, parent
)) != NULL
) {
187 p
->data
= (void *) tmp
;
188 p
->proc_fops
= &switch_proc_fops
;
192 static inline void add_handlers(switch_driver
*driver
, const switch_config
*handlers
, struct proc_dir_entry
*parent
, int nr
)
196 for (i
= 0; handlers
[i
].name
!= NULL
; i
++) {
197 add_handler(driver
, &(handlers
[i
]), parent
, nr
);
201 static void remove_handlers(switch_priv
*priv
)
203 struct list_head
*pos
, *q
;
204 switch_proc_handler
*tmp
;
206 list_for_each_safe(pos
, q
, &priv
->data
.list
) {
207 tmp
= list_entry(pos
, switch_proc_handler
, list
);
209 remove_proc_entry(tmp
->handler
.name
, tmp
->parent
);
215 static void do_unregister(switch_driver
*driver
)
219 switch_priv
*priv
= (switch_priv
*) driver
->data
;
221 remove_handlers(priv
);
223 for(i
= 0; priv
->ports
[i
] != NULL
; i
++) {
224 sprintf(buf
, "%d", i
);
225 remove_proc_entry(buf
, priv
->port_dir
);
228 remove_proc_entry("port", priv
->driver_dir
);
230 for(i
= 0; priv
->vlans
[i
] != NULL
; i
++) {
231 sprintf(buf
, "%d", i
);
232 remove_proc_entry(buf
, priv
->vlan_dir
);
235 remove_proc_entry("vlan", priv
->driver_dir
);
237 remove_proc_entry(driver
->interface
, switch_root
);
239 if (priv
->nr
== (drv_num
- 1))
245 switch_config global_driver_handlers
[] = {
246 {"driver", handle_driver_name
, NULL
},
247 {"version", handle_driver_version
, NULL
},
248 {"cpuport", handle_driver_cpuport
, NULL
},
249 {"ports", handle_driver_ports
, NULL
},
250 {"vlans", handle_driver_vlans
, NULL
},
251 {"dev_name", handle_driver_dev_name
, NULL
},
255 static int do_register(switch_driver
*driver
)
261 priv
= kmalloc(sizeof(switch_priv
), GFP_KERNEL
);
264 driver
->data
= (void *) priv
;
266 priv
->ports
= kmalloc((driver
->ports
+ 1) * sizeof(struct proc_dir_entry
*),
272 priv
->vlans
= kmalloc((driver
->vlans
+ 1) * sizeof(struct proc_dir_entry
*),
280 INIT_LIST_HEAD(&priv
->data
.list
);
282 priv
->nr
= drv_num
++;
283 priv
->driver_dir
= proc_mkdir(driver
->interface
, switch_root
);
284 if (driver
->driver_handlers
!= NULL
) {
285 add_handlers(driver
, driver
->driver_handlers
, priv
->driver_dir
, 0);
286 add_handlers(driver
, global_driver_handlers
, priv
->driver_dir
, 0);
289 priv
->port_dir
= proc_mkdir("port", priv
->driver_dir
);
290 for (i
= 0; i
< driver
->ports
; i
++) {
291 sprintf(buf
, "%d", i
);
292 priv
->ports
[i
] = proc_mkdir(buf
, priv
->port_dir
);
293 if (driver
->port_handlers
!= NULL
)
294 add_handlers(driver
, driver
->port_handlers
, priv
->ports
[i
], i
);
296 priv
->ports
[i
] = NULL
;
298 priv
->vlan_dir
= proc_mkdir("vlan", priv
->driver_dir
);
299 for (i
= 0; i
< driver
->vlans
; i
++) {
300 sprintf(buf
, "%d", i
);
301 priv
->vlans
[i
] = proc_mkdir(buf
, priv
->vlan_dir
);
302 if (driver
->vlan_handlers
!= NULL
)
303 add_handlers(driver
, driver
->vlan_handlers
, priv
->vlans
[i
], i
);
305 priv
->vlans
[i
] = NULL
;
311 static inline int isspace(char c
) {
323 #define toupper(c) (islower(c) ? ((c) ^ 0x20) : (c))
324 #define islower(c) (((unsigned char)((c) - 'a')) < 26)
326 int switch_parse_media(char *buf
)
330 *buf
= toupper(*buf
);
334 if (strncmp(str
, "AUTO", 4) == 0)
335 return SWITCH_MEDIA_AUTO
;
336 else if (strncmp(str
, "1000FD", 6) == 0)
337 return SWITCH_MEDIA_1000
| SWITCH_MEDIA_FD
;
338 else if (strncmp(str
, "1000HD", 6) == 0)
339 return SWITCH_MEDIA_1000
;
340 else if (strncmp(str
, "100FD", 5) == 0)
341 return SWITCH_MEDIA_100
| SWITCH_MEDIA_FD
;
342 else if (strncmp(str
, "100HD", 5) == 0)
343 return SWITCH_MEDIA_100
;
344 else if (strncmp(str
, "10FD", 4) == 0)
345 return SWITCH_MEDIA_FD
;
346 else if (strncmp(str
, "10HD", 4) == 0)
351 int switch_print_media(char *buf
, int media
)
355 if (media
& SWITCH_MEDIA_AUTO
)
356 len
= sprintf(buf
, "Auto");
357 else if (media
== (SWITCH_MEDIA_1000
| SWITCH_MEDIA_FD
))
358 len
= sprintf(buf
, "1000FD");
359 else if (media
== SWITCH_MEDIA_1000
)
360 len
= sprintf(buf
, "1000HD");
361 else if (media
== (SWITCH_MEDIA_100
| SWITCH_MEDIA_FD
))
362 len
= sprintf(buf
, "100FD");
363 else if (media
== SWITCH_MEDIA_100
)
364 len
= sprintf(buf
, "100HD");
365 else if (media
== SWITCH_MEDIA_FD
)
366 len
= sprintf(buf
, "10FD");
368 len
= sprintf(buf
, "10HD");
370 len
= sprintf(buf
, "Invalid");
375 switch_vlan_config
*switch_parse_vlan(switch_driver
*driver
, char *buf
)
377 switch_vlan_config
*c
;
380 c
= kzalloc(sizeof(switch_vlan_config
), GFP_KERNEL
);
384 while (isspace(*buf
)) buf
++;
386 while (*buf
>= '0' && *buf
<= '9') {
390 u
= ((j
== driver
->cpuport
) ? 0 : 1);
392 s
= !(*buf
>= '0' && *buf
<= '9');
395 while (s
&& !isspace(*buf
) && (*buf
!= 0)) {
411 c
->untag
|= (1 << j
);
418 while (isspace(*buf
)) buf
++;
425 c
->port
&= (1 << driver
->ports
) - 1;
426 c
->untag
&= (1 << driver
->ports
) - 1;
427 c
->pvid
&= (1 << driver
->ports
) - 1;
433 int switch_device_registered (char* device
) {
434 struct list_head
*pos
;
436 list_for_each(pos
, &drivers
.list
) {
437 if (strcmp(list_entry(pos
, switch_driver
, list
)->interface
, device
) == 0) {
438 printk("There is already a switch registered on the device '%s'\n", device
);
447 int switch_register_driver(switch_driver
*driver
)
449 struct list_head
*pos
;
453 list_for_each(pos
, &drivers
.list
) {
454 if (strcmp(list_entry(pos
, switch_driver
, list
)->name
, driver
->name
) == 0) {
455 printk("Switch driver '%s' already exists in the kernel\n", driver
->name
);
458 if (strcmp(list_entry(pos
, switch_driver
, list
)->interface
, driver
->interface
) == 0) {
459 printk("There is already a switch registered on the device '%s'\n", driver
->interface
);
464 new = kmalloc(sizeof(switch_driver
), GFP_KERNEL
);
467 memcpy(new, driver
, sizeof(switch_driver
));
468 new->name
= strdup(driver
->name
);
469 new->interface
= strdup(driver
->interface
);
471 if ((ret
= do_register(new)) < 0) {
476 INIT_LIST_HEAD(&new->list
);
477 list_add(&new->list
, &drivers
.list
);
482 void switch_unregister_driver(char *name
) {
483 struct list_head
*pos
, *q
;
486 list_for_each_safe(pos
, q
, &drivers
.list
) {
487 tmp
= list_entry(pos
, switch_driver
, list
);
488 if (strcmp(tmp
->name
, name
) == 0) {
499 static int __init
switch_init(void)
501 if ((switch_root
= proc_mkdir("switch", NULL
)) == NULL
) {
502 printk("%s: proc_mkdir failed.\n", __FILE__
);
506 INIT_LIST_HEAD(&drivers
.list
);
511 static void __exit
switch_exit(void)
513 remove_proc_entry("switch", NULL
);
516 MODULE_AUTHOR("Felix Fietkau <openwrt@nbd.name>");
517 MODULE_LICENSE("GPL");
519 EXPORT_SYMBOL(switch_device_registered
);
520 EXPORT_SYMBOL(switch_register_driver
);
521 EXPORT_SYMBOL(switch_unregister_driver
);
522 EXPORT_SYMBOL(switch_parse_vlan
);
523 EXPORT_SYMBOL(switch_parse_media
);
524 EXPORT_SYMBOL(switch_print_media
);
526 module_init(switch_init
);
527 module_exit(switch_exit
);