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.
23 #include <linux/config.h>
24 #include <linux/module.h>
25 #include <linux/init.h>
26 #include <asm/uaccess.h>
27 #include <linux/proc_fs.h>
28 #include <linux/list.h>
30 #include "switch-core.h"
32 static int drv_num
= 0;
33 static struct proc_dir_entry
*switch_root
;
34 switch_driver drivers
;
37 struct list_head list
;
38 struct proc_dir_entry
*parent
;
40 switch_config handler
;
41 } switch_proc_handler
;
44 struct proc_dir_entry
*driver_dir
, *port_dir
, *vlan_dir
;
45 struct proc_dir_entry
**ports
, **vlans
;
46 switch_proc_handler data
;
51 static ssize_t
switch_proc_read(struct file
*file
, char *buf
, size_t count
, loff_t
*ppos
);
52 static ssize_t
switch_proc_write(struct file
*file
, const char *buf
, size_t count
, void *data
);
54 static struct file_operations switch_proc_fops
= {
55 read
: switch_proc_read
,
56 write
: switch_proc_write
59 static char *strdup(char *str
)
61 char *new = kmalloc(strlen(str
) + 1, GFP_KERNEL
);
66 static ssize_t
switch_proc_read(struct file
*file
, char *buf
, size_t count
, loff_t
*ppos
)
69 struct inode
*inode
= file
->f_dentry
->d_inode
;
70 struct proc_dir_entry
*dent
= inode
->u
.generic_ip
;
72 struct proc_dir_entry
*dent
= PDE(file
->f_dentry
->d_inode
);
77 if ((page
= kmalloc(SWITCH_MAX_BUFSZ
, GFP_KERNEL
)) == NULL
)
80 if (dent
->data
!= NULL
) {
81 switch_proc_handler
*handler
= (switch_proc_handler
*) dent
->data
;
82 if (handler
->handler
.read
!= NULL
)
83 len
+= handler
->handler
.read(page
+ len
, handler
->nr
);
88 len
= min_t(int, len
- *ppos
, count
);
89 if (copy_to_user(buf
, (page
+ *ppos
), len
)) {
102 static ssize_t
switch_proc_write(struct file
*file
, const char *buf
, size_t count
, void *data
)
105 struct inode
*inode
= file
->f_dentry
->d_inode
;
106 struct proc_dir_entry
*dent
= inode
->u
.generic_ip
;
108 struct proc_dir_entry
*dent
= PDE(file
->f_dentry
->d_inode
);
113 if ((page
= kmalloc(count
+ 1, GFP_KERNEL
)) == NULL
)
116 if (copy_from_user(page
, buf
, count
)) {
122 if (dent
->data
!= NULL
) {
123 switch_proc_handler
*handler
= (switch_proc_handler
*) dent
->data
;
124 if (handler
->handler
.write
!= NULL
) {
125 if ((ret
= handler
->handler
.write(page
, handler
->nr
)) >= 0)
134 static void add_handlers(switch_priv
*priv
, switch_config
*handlers
, struct proc_dir_entry
*parent
, int nr
)
136 switch_proc_handler
*tmp
;
138 struct proc_dir_entry
*p
;
140 for (i
= 0; handlers
[i
].name
!= NULL
; i
++) {
141 tmp
= kmalloc(sizeof(switch_proc_handler
), GFP_KERNEL
);
142 INIT_LIST_HEAD(&tmp
->list
);
143 tmp
->parent
= parent
;
145 memcpy(&tmp
->handler
, &(handlers
[i
]), sizeof(switch_config
));
146 list_add(&tmp
->list
, &priv
->data
.list
);
149 if (handlers
[i
].read
!= NULL
) mode
|= S_IRUSR
;
150 if (handlers
[i
].write
!= NULL
) mode
|= S_IWUSR
;
152 if ((p
= create_proc_entry(handlers
[i
].name
, mode
, parent
)) != NULL
) {
153 p
->data
= (void *) tmp
;
154 p
->proc_fops
= &switch_proc_fops
;
159 static void remove_handlers(switch_priv
*priv
)
161 struct list_head
*pos
, *q
;
162 switch_proc_handler
*tmp
;
164 list_for_each_safe(pos
, q
, &priv
->data
.list
) {
165 tmp
= list_entry(pos
, switch_proc_handler
, list
);
167 remove_proc_entry(tmp
->handler
.name
, tmp
->parent
);
173 static void do_unregister(switch_driver
*driver
)
177 switch_priv
*priv
= (switch_priv
*) driver
->data
;
179 remove_handlers(priv
);
181 for(i
= 0; priv
->ports
[i
] != NULL
; i
++) {
182 sprintf(buf
, "%d", i
);
183 remove_proc_entry(buf
, priv
->port_dir
);
186 remove_proc_entry("port", priv
->driver_dir
);
188 for(i
= 0; priv
->vlans
[i
] != NULL
; i
++) {
189 sprintf(buf
, "%d", i
);
190 remove_proc_entry(buf
, priv
->vlan_dir
);
193 remove_proc_entry("vlan", priv
->driver_dir
);
195 remove_proc_entry(driver
->name
, switch_root
);
197 if (priv
->nr
== (drv_num
- 1))
203 static int do_register(switch_driver
*driver
)
209 if ((priv
= kmalloc(sizeof(switch_priv
), GFP_KERNEL
)) == NULL
)
211 driver
->data
= (void *) priv
;
213 INIT_LIST_HEAD(&priv
->data
.list
);
215 priv
->nr
= drv_num
++;
216 sprintf(buf
, "%d", priv
->nr
);
217 priv
->driver_dir
= proc_mkdir(buf
, switch_root
);
218 if (driver
->driver_handlers
!= NULL
)
219 add_handlers(priv
, driver
->driver_handlers
, priv
->driver_dir
, 0);
221 priv
->port_dir
= proc_mkdir("port", priv
->driver_dir
);
222 priv
->ports
= kmalloc((driver
->ports
+ 1) * sizeof(struct proc_dir_entry
*), GFP_KERNEL
);
223 for (i
= 0; i
< driver
->ports
; i
++) {
224 sprintf(buf
, "%d", i
);
225 priv
->ports
[i
] = proc_mkdir(buf
, priv
->port_dir
);
226 if (driver
->port_handlers
!= NULL
)
227 add_handlers(priv
, driver
->port_handlers
, priv
->ports
[i
], i
);
229 priv
->ports
[i
] = NULL
;
231 priv
->vlan_dir
= proc_mkdir("vlan", priv
->driver_dir
);
232 priv
->vlans
= kmalloc((driver
->vlans
+ 1) * sizeof(struct proc_dir_entry
*), GFP_KERNEL
);
233 for (i
= 0; i
< driver
->vlans
; i
++) {
234 sprintf(buf
, "%d", i
);
235 priv
->vlans
[i
] = proc_mkdir(buf
, priv
->vlan_dir
);
236 if (driver
->vlan_handlers
!= NULL
)
237 add_handlers(priv
, driver
->vlan_handlers
, priv
->vlans
[i
], i
);
239 priv
->vlans
[i
] = NULL
;
245 static int isspace(char c
) {
257 #define toupper(c) (islower(c) ? ((c) ^ 0x20) : (c))
258 #define islower(c) (((unsigned char)((c) - 'a')) < 26)
260 int switch_parse_media(char *buf
)
264 *buf
= toupper(*buf
);
268 if (strncmp(str
, "AUTO", 4) == 0)
269 return SWITCH_MEDIA_AUTO
;
270 else if (strncmp(str
, "100FD", 5) == 0)
271 return SWITCH_MEDIA_100
| SWITCH_MEDIA_FD
;
272 else if (strncmp(str
, "100HD", 5) == 0)
273 return SWITCH_MEDIA_100
;
274 else if (strncmp(str
, "10FD", 4) == 0)
275 return SWITCH_MEDIA_FD
;
276 else if (strncmp(str
, "10HD", 4) == 0)
281 int switch_print_media(char *buf
, int media
)
285 if (media
& SWITCH_MEDIA_AUTO
)
286 len
= sprintf(buf
, "Auto");
287 else if (media
== (SWITCH_MEDIA_100
| SWITCH_MEDIA_FD
))
288 len
= sprintf(buf
, "100FD");
289 else if (media
== SWITCH_MEDIA_100
)
290 len
= sprintf(buf
, "100HD");
291 else if (media
== SWITCH_MEDIA_FD
)
292 len
= sprintf(buf
, "10FD");
294 len
= sprintf(buf
, "10HD");
296 len
= sprintf(buf
, "Invalid");
301 int switch_parse_vlan(char *buf
)
303 char vlan
= 0, tag
= 0, pvid_port
= 0;
306 while (isspace(*buf
)) buf
++;
308 while (*buf
>= '0' && *buf
<= '9') {
313 /* untag if needed, CPU port requires special handling */
314 if (*buf
== 'u' || (j
!= 5 && (isspace(*buf
) || *buf
== 0))) {
317 } else if (*buf
== '*') {
318 pvid_port
|= (1 << j
);
320 } else if (*buf
== 't' || isspace(*buf
)) {
327 while (isspace(*buf
)) buf
++;
333 return (pvid_port
<< 16) | (tag
<< 8) | vlan
;
337 int switch_register_driver(switch_driver
*driver
)
339 struct list_head
*pos
;
343 list_for_each(pos
, &drivers
.list
) {
344 if (strcmp(list_entry(pos
, switch_driver
, list
)->name
, driver
->name
) == 0) {
345 printk("Switch driver '%s' already exists in the kernel\n", driver
->name
);
350 new = kmalloc(sizeof(switch_driver
), GFP_KERNEL
);
351 memcpy(new, driver
, sizeof(switch_driver
));
352 new->name
= strdup(driver
->name
);
354 if ((ret
= do_register(new)) < 0) {
359 INIT_LIST_HEAD(&new->list
);
360 list_add(&new->list
, &drivers
.list
);
365 void switch_unregister_driver(char *name
) {
366 struct list_head
*pos
, *q
;
369 list_for_each_safe(pos
, q
, &drivers
.list
) {
370 tmp
= list_entry(pos
, switch_driver
, list
);
371 if (strcmp(tmp
->name
, name
) == 0) {
382 static int __init
switch_init()
384 if ((switch_root
= proc_mkdir("switch", NULL
)) == NULL
) {
385 printk("%s: proc_mkdir failed.\n", __FILE__
);
389 INIT_LIST_HEAD(&drivers
.list
);
394 static void __exit
switch_exit()
396 remove_proc_entry("vlan", NULL
);
399 MODULE_AUTHOR("Felix Fietkau <openwrt@nbd.name>");
400 MODULE_LICENSE("GPL");
402 EXPORT_SYMBOL(switch_register_driver
);
403 EXPORT_SYMBOL(switch_unregister_driver
);
404 EXPORT_SYMBOL(switch_parse_vlan
);
405 EXPORT_SYMBOL(switch_parse_media
);
406 EXPORT_SYMBOL(switch_print_media
);
408 module_init(switch_init
);
409 module_exit(switch_exit
);