Merge branch 'master' of git+ssh://openadk.org/git/openadk
[openadk.git] / target / linux / patches / 2.6.30.4 / swconfig.patch
blob3297bb11697ecd8b4de1d64466c19f05370a4648
1 diff -Nur linux-2.6.30.orig/drivers/net/phy/Kconfig linux-2.6.30/drivers/net/phy/Kconfig
2 --- linux-2.6.30.orig/drivers/net/phy/Kconfig 2009-06-10 05:05:27.000000000 +0200
3 +++ linux-2.6.30/drivers/net/phy/Kconfig 2009-06-11 09:22:50.000000000 +0200
4 @@ -13,6 +13,12 @@
6 if PHYLIB
8 +config SWCONFIG
9 + tristate "Switch configuration API"
10 + ---help---
11 + Switch configuration API using netlink. This allows
12 + you to configure the VLAN features of certain switches.
14 comment "MII PHY device drivers"
16 config MARVELL_PHY
17 diff -Nur linux-2.6.30.orig/drivers/net/phy/Makefile linux-2.6.30/drivers/net/phy/Makefile
18 --- linux-2.6.30.orig/drivers/net/phy/Makefile 2009-06-10 05:05:27.000000000 +0200
19 +++ linux-2.6.30/drivers/net/phy/Makefile 2009-06-11 09:22:50.000000000 +0200
20 @@ -3,6 +3,7 @@
21 libphy-objs := phy.o phy_device.o mdio_bus.o
23 obj-$(CONFIG_PHYLIB) += libphy.o
24 +obj-$(CONFIG_SWCONFIG) += swconfig.o
25 obj-$(CONFIG_MARVELL_PHY) += marvell.o
26 obj-$(CONFIG_DAVICOM_PHY) += davicom.o
27 obj-$(CONFIG_CICADA_PHY) += cicada.o
28 diff -Nur linux-2.6.30.orig/drivers/net/phy/swconfig.c linux-2.6.30/drivers/net/phy/swconfig.c
29 --- linux-2.6.30.orig/drivers/net/phy/swconfig.c 1970-01-01 01:00:00.000000000 +0100
30 +++ linux-2.6.30/drivers/net/phy/swconfig.c 2009-06-11 09:22:50.000000000 +0200
31 @@ -0,0 +1,872 @@
32 +/*
33 + * swconfig.c: Switch configuration API
34 + *
35 + * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
36 + *
37 + * This program is free software; you can redistribute it and/or
38 + * modify it under the terms of the GNU General Public License
39 + * as published by the Free Software Foundation; either version 2
40 + * of the License, or (at your option) any later version.
41 + *
42 + * This program is distributed in the hope that it will be useful,
43 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
44 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45 + * GNU General Public License for more details.
46 + */
48 +#include <linux/types.h>
49 +#include <linux/module.h>
50 +#include <linux/init.h>
51 +#include <linux/list.h>
52 +#include <linux/if.h>
53 +#include <linux/if_ether.h>
54 +#include <linux/capability.h>
55 +#include <linux/skbuff.h>
56 +#include <linux/switch.h>
58 +//#define DEBUG 1
59 +#ifdef DEBUG
60 +#define DPRINTF(format, ...) printk("%s: " format, __func__, ##__VA_ARGS__)
61 +#else
62 +#define DPRINTF(...) do {} while(0)
63 +#endif
65 +MODULE_AUTHOR("Felix Fietkau <nbd@openwrt.org>");
66 +MODULE_LICENSE("GPL");
68 +static int swdev_id = 0;
69 +static struct list_head swdevs;
70 +static spinlock_t swdevs_lock = SPIN_LOCK_UNLOCKED;
71 +struct swconfig_callback;
73 +struct swconfig_callback
75 + struct sk_buff *msg;
76 + struct genlmsghdr *hdr;
77 + struct genl_info *info;
78 + int cmd;
80 + /* callback for filling in the message data */
81 + int (*fill)(struct swconfig_callback *cb, void *arg);
83 + /* callback for closing the message before sending it */
84 + int (*close)(struct swconfig_callback *cb, void *arg);
86 + struct nlattr *nest[4];
87 + int args[4];
88 +};
90 +/* defaults */
92 +static int
93 +swconfig_get_vlan_ports(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
95 + int ret;
96 + if (val->port_vlan >= dev->vlans)
97 + return -EINVAL;
99 + if (!dev->get_vlan_ports)
100 + return -EOPNOTSUPP;
102 + ret = dev->get_vlan_ports(dev, val);
103 + printk("SET PORTS %d\n", val->len);
104 + return ret;
107 +static int
108 +swconfig_set_vlan_ports(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
110 + int i;
112 + if (val->port_vlan >= dev->vlans)
113 + return -EINVAL;
115 + /* validate ports */
116 + if (val->len > dev->ports)
117 + return -EINVAL;
119 + for (i = 0; i < val->len; i++) {
120 + if (val->value.ports[i].id >= dev->ports)
121 + return -EINVAL;
124 + if (!dev->set_vlan_ports)
125 + return -EOPNOTSUPP;
127 + printk("SET PORTS %d\n", val->len);
128 + return dev->set_vlan_ports(dev, val);
131 +static int
132 +swconfig_apply_config(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
134 + /* don't complain if not supported by the switch driver */
135 + if (!dev->apply_config)
136 + return 0;
138 + return dev->apply_config(dev);
142 +enum global_defaults {
143 + GLOBAL_APPLY,
146 +enum vlan_defaults {
147 + VLAN_PORTS,
150 +enum port_defaults {
151 + PORT_LINK,
154 +static struct switch_attr default_global[] = {
155 + [GLOBAL_APPLY] = {
156 + .type = SWITCH_TYPE_NOVAL,
157 + .name = "apply",
158 + .description = "Activate changes in the hardware",
159 + .set = swconfig_apply_config,
163 +static struct switch_attr default_port[] = {
164 + [PORT_LINK] = {
165 + .type = SWITCH_TYPE_INT,
166 + .name = "link",
167 + .description = "Current link speed",
171 +static struct switch_attr default_vlan[] = {
172 + [VLAN_PORTS] = {
173 + .type = SWITCH_TYPE_PORTS,
174 + .name = "ports",
175 + .description = "VLAN port mapping",
176 + .set = swconfig_set_vlan_ports,
177 + .get = swconfig_get_vlan_ports,
178 + },
182 +static void swconfig_defaults_init(struct switch_dev *dev)
184 + dev->def_global = 0;
185 + dev->def_vlan = 0;
186 + dev->def_port = 0;
188 + if (dev->get_vlan_ports || dev->set_vlan_ports)
189 + set_bit(VLAN_PORTS, &dev->def_vlan);
191 + /* always present, can be no-op */
192 + set_bit(GLOBAL_APPLY, &dev->def_global);
196 +static struct genl_family switch_fam = {
197 + .id = GENL_ID_GENERATE,
198 + .name = "switch",
199 + .hdrsize = 0,
200 + .version = 1,
201 + .maxattr = SWITCH_ATTR_MAX,
204 +static const struct nla_policy switch_policy[SWITCH_ATTR_MAX+1] = {
205 + [SWITCH_ATTR_ID] = { .type = NLA_U32 },
206 + [SWITCH_ATTR_OP_ID] = { .type = NLA_U32 },
207 + [SWITCH_ATTR_OP_PORT] = { .type = NLA_U32 },
208 + [SWITCH_ATTR_OP_VLAN] = { .type = NLA_U32 },
209 + [SWITCH_ATTR_OP_VALUE_INT] = { .type = NLA_U32 },
210 + [SWITCH_ATTR_OP_VALUE_STR] = { .type = NLA_NUL_STRING },
211 + [SWITCH_ATTR_OP_VALUE_PORTS] = { .type = NLA_NESTED },
212 + [SWITCH_ATTR_TYPE] = { .type = NLA_U32 },
215 +static const struct nla_policy port_policy[SWITCH_PORT_ATTR_MAX+1] = {
216 + [SWITCH_PORT_ID] = { .type = NLA_U32 },
217 + [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },
220 +static inline void
221 +swconfig_lock(void)
223 + spin_lock(&swdevs_lock);
226 +static inline void
227 +swconfig_unlock(void)
229 + spin_unlock(&swdevs_lock);
232 +static struct switch_dev *
233 +swconfig_get_dev(struct genl_info *info)
235 + struct switch_dev *dev = NULL;
236 + struct switch_dev *p;
237 + int id;
239 + if (!info->attrs[SWITCH_ATTR_ID])
240 + goto done;
242 + id = nla_get_u32(info->attrs[SWITCH_ATTR_ID]);
243 + swconfig_lock();
244 + list_for_each_entry(p, &swdevs, dev_list) {
245 + if (id != p->id)
246 + continue;
248 + dev = p;
249 + break;
251 + if (dev)
252 + spin_lock(&dev->lock);
253 + else
254 + DPRINTF("device %d not found\n", id);
255 + swconfig_unlock();
256 +done:
257 + return dev;
260 +static inline void
261 +swconfig_put_dev(struct switch_dev *dev)
263 + spin_unlock(&dev->lock);
266 +static int
267 +swconfig_dump_attr(struct swconfig_callback *cb, void *arg)
269 + struct switch_attr *op = arg;
270 + struct genl_info *info = cb->info;
271 + struct sk_buff *msg = cb->msg;
272 + int id = cb->args[0];
273 + void *hdr;
275 + hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, &switch_fam,
276 + NLM_F_MULTI, SWITCH_CMD_NEW_ATTR);
277 + if (IS_ERR(hdr))
278 + return -1;
280 + NLA_PUT_U32(msg, SWITCH_ATTR_OP_ID, id);
281 + NLA_PUT_U32(msg, SWITCH_ATTR_OP_TYPE, op->type);
282 + NLA_PUT_STRING(msg, SWITCH_ATTR_OP_NAME, op->name);
283 + if (op->description)
284 + NLA_PUT_STRING(msg, SWITCH_ATTR_OP_DESCRIPTION,
285 + op->description);
287 + return genlmsg_end(msg, hdr);
288 +nla_put_failure:
289 + genlmsg_cancel(msg, hdr);
290 + return -EMSGSIZE;
293 +/* spread multipart messages across multiple message buffers */
294 +static int
295 +swconfig_send_multipart(struct swconfig_callback *cb, void *arg)
297 + struct genl_info *info = cb->info;
298 + int restart = 0;
299 + int err;
301 + do {
302 + if (!cb->msg) {
303 + cb->msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
304 + if (cb->msg == NULL)
305 + goto error;
308 + if (!(cb->fill(cb, arg) < 0))
309 + break;
311 + /* fill failed, check if this was already the second attempt */
312 + if (restart)
313 + goto error;
315 + /* try again in a new message, send the current one */
316 + restart = 1;
317 + if (cb->close) {
318 + if (cb->close(cb, arg) < 0)
319 + goto error;
321 + err = genlmsg_unicast(cb->msg, info->snd_pid);
322 + cb->msg = NULL;
323 + if (err < 0)
324 + goto error;
326 + } while (restart);
328 + return 0;
330 +error:
331 + if (cb->msg)
332 + nlmsg_free(cb->msg);
333 + return -1;
336 +static int
337 +swconfig_list_attrs(struct sk_buff *skb, struct genl_info *info)
339 + struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
340 + const struct switch_attrlist *alist;
341 + struct switch_dev *dev;
342 + struct swconfig_callback cb;
343 + int err = -EINVAL;
344 + int i;
346 + /* defaults */
347 + struct switch_attr *def_list;
348 + unsigned long *def_active;
349 + int n_def;
351 + dev = swconfig_get_dev(info);
352 + if (!dev)
353 + return -EINVAL;
355 + switch(hdr->cmd) {
356 + case SWITCH_CMD_LIST_GLOBAL:
357 + alist = &dev->attr_global;
358 + def_list = default_global;
359 + def_active = &dev->def_global;
360 + n_def = ARRAY_SIZE(default_global);
361 + break;
362 + case SWITCH_CMD_LIST_VLAN:
363 + alist = &dev->attr_vlan;
364 + def_list = default_vlan;
365 + def_active = &dev->def_vlan;
366 + n_def = ARRAY_SIZE(default_vlan);
367 + break;
368 + case SWITCH_CMD_LIST_PORT:
369 + alist = &dev->attr_port;
370 + def_list = default_port;
371 + def_active = &dev->def_port;
372 + n_def = ARRAY_SIZE(default_port);
373 + break;
374 + default:
375 + WARN_ON(1);
376 + goto out;
379 + memset(&cb, 0, sizeof(cb));
380 + cb.info = info;
381 + cb.fill = swconfig_dump_attr;
382 + for (i = 0; i < alist->n_attr; i++) {
383 + if (alist->attr[i].disabled)
384 + continue;
385 + cb.args[0] = i;
386 + err = swconfig_send_multipart(&cb, &alist->attr[i]);
387 + if (err < 0)
388 + goto error;
391 + /* defaults */
392 + for (i = 0; i < n_def; i++) {
393 + if (!test_bit(i, def_active))
394 + continue;
395 + cb.args[0] = SWITCH_ATTR_DEFAULTS_OFFSET + i;
396 + err = swconfig_send_multipart(&cb, &def_list[i]);
397 + if (err < 0)
398 + goto error;
400 + swconfig_put_dev(dev);
402 + if (!cb.msg)
403 + return 0;
405 + return genlmsg_unicast(cb.msg, info->snd_pid);
407 +error:
408 + if (cb.msg)
409 + nlmsg_free(cb.msg);
410 +out:
411 + swconfig_put_dev(dev);
412 + return err;
415 +static struct switch_attr *
416 +swconfig_lookup_attr(struct switch_dev *dev, struct genl_info *info,
417 + struct switch_val *val)
419 + struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
420 + const struct switch_attrlist *alist;
421 + struct switch_attr *attr = NULL;
422 + int attr_id;
424 + /* defaults */
425 + struct switch_attr *def_list;
426 + unsigned long *def_active;
427 + int n_def;
429 + if (!info->attrs[SWITCH_ATTR_OP_ID])
430 + goto done;
432 + switch(hdr->cmd) {
433 + case SWITCH_CMD_SET_GLOBAL:
434 + case SWITCH_CMD_GET_GLOBAL:
435 + alist = &dev->attr_global;
436 + def_list = default_global;
437 + def_active = &dev->def_global;
438 + n_def = ARRAY_SIZE(default_global);
439 + break;
440 + case SWITCH_CMD_SET_VLAN:
441 + case SWITCH_CMD_GET_VLAN:
442 + alist = &dev->attr_vlan;
443 + def_list = default_vlan;
444 + def_active = &dev->def_vlan;
445 + n_def = ARRAY_SIZE(default_vlan);
446 + if (!info->attrs[SWITCH_ATTR_OP_VLAN])
447 + goto done;
448 + val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_VLAN]);
449 + break;
450 + case SWITCH_CMD_SET_PORT:
451 + case SWITCH_CMD_GET_PORT:
452 + alist = &dev->attr_port;
453 + def_list = default_port;
454 + def_active = &dev->def_port;
455 + n_def = ARRAY_SIZE(default_port);
456 + if (!info->attrs[SWITCH_ATTR_OP_PORT])
457 + goto done;
458 + val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_PORT]);
459 + break;
460 + default:
461 + WARN_ON(1);
462 + goto done;
465 + if (!alist)
466 + goto done;
468 + attr_id = nla_get_u32(info->attrs[SWITCH_ATTR_OP_ID]);
469 + if (attr_id >= SWITCH_ATTR_DEFAULTS_OFFSET) {
470 + attr_id -= SWITCH_ATTR_DEFAULTS_OFFSET;
471 + if (attr_id >= n_def)
472 + goto done;
473 + if (!test_bit(attr_id, def_active))
474 + goto done;
475 + attr = &def_list[attr_id];
476 + } else {
477 + if (attr_id >= alist->n_attr)
478 + goto done;
479 + attr = &alist->attr[attr_id];
482 + if (attr->disabled)
483 + attr = NULL;
485 +done:
486 + if (!attr)
487 + DPRINTF("attribute lookup failed\n");
488 + val->attr = attr;
489 + return attr;
492 +static int
493 +swconfig_parse_ports(struct sk_buff *msg, struct nlattr *head,
494 + struct switch_val *val, int max)
496 + struct nlattr *nla;
497 + int rem;
499 + val->len = 0;
500 + nla_for_each_nested(nla, head, rem) {
501 + struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1];
502 + struct switch_port *port = &val->value.ports[val->len];
504 + if (val->len >= max)
505 + return -EINVAL;
507 + if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla,
508 + port_policy))
509 + return -EINVAL;
511 + if (!tb[SWITCH_PORT_ID])
512 + return -EINVAL;
514 + port->id = nla_get_u32(tb[SWITCH_PORT_ID]);
515 + if (tb[SWITCH_PORT_FLAG_TAGGED])
516 + port->flags |= (1 << SWITCH_PORT_FLAG_TAGGED);
517 + val->len++;
520 + return 0;
523 +static int
524 +swconfig_set_attr(struct sk_buff *skb, struct genl_info *info)
526 + struct switch_attr *attr;
527 + struct switch_dev *dev;
528 + struct switch_val val;
529 + int err = -EINVAL;
531 + dev = swconfig_get_dev(info);
532 + if (!dev)
533 + return -EINVAL;
535 + memset(&val, 0, sizeof(val));
536 + attr = swconfig_lookup_attr(dev, info, &val);
537 + if (!attr || !attr->set)
538 + goto error;
540 + val.attr = attr;
541 + switch(attr->type) {
542 + case SWITCH_TYPE_NOVAL:
543 + break;
544 + case SWITCH_TYPE_INT:
545 + if (!info->attrs[SWITCH_ATTR_OP_VALUE_INT])
546 + goto error;
547 + val.value.i =
548 + nla_get_u32(info->attrs[SWITCH_ATTR_OP_VALUE_INT]);
549 + break;
550 + case SWITCH_TYPE_STRING:
551 + if (!info->attrs[SWITCH_ATTR_OP_VALUE_STR])
552 + goto error;
553 + val.value.s =
554 + nla_data(info->attrs[SWITCH_ATTR_OP_VALUE_STR]);
555 + break;
556 + case SWITCH_TYPE_PORTS:
557 + val.value.ports = dev->portbuf;
558 + memset(dev->portbuf, 0,
559 + sizeof(struct switch_port) * dev->ports);
561 + /* TODO: implement multipart? */
562 + if (info->attrs[SWITCH_ATTR_OP_VALUE_PORTS]) {
563 + err = swconfig_parse_ports(skb,
564 + info->attrs[SWITCH_ATTR_OP_VALUE_PORTS], &val, dev->ports);
565 + if (err < 0)
566 + goto error;
567 + } else {
568 + val.len = 0;
569 + err = 0;
571 + break;
572 + default:
573 + goto error;
576 + err = attr->set(dev, attr, &val);
577 +error:
578 + swconfig_put_dev(dev);
579 + return err;
582 +static int
583 +swconfig_close_portlist(struct swconfig_callback *cb, void *arg)
585 + if (cb->nest[0])
586 + nla_nest_end(cb->msg, cb->nest[0]);
587 + return 0;
590 +static int
591 +swconfig_send_port(struct swconfig_callback *cb, void *arg)
593 + const struct switch_port *port = arg;
594 + struct nlattr *p = NULL;
596 + if (!cb->nest[0]) {
597 + cb->nest[0] = nla_nest_start(cb->msg, cb->cmd);
598 + if (!cb->nest[0])
599 + return -1;
602 + p = nla_nest_start(cb->msg, SWITCH_ATTR_PORT);
603 + if (!p)
604 + goto error;
606 + NLA_PUT_U32(cb->msg, SWITCH_PORT_ID, port->id);
607 + if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
608 + NLA_PUT_FLAG(cb->msg, SWITCH_PORT_FLAG_TAGGED);
610 + nla_nest_end(cb->msg, p);
611 + return 0;
613 +nla_put_failure:
614 + nla_nest_cancel(cb->msg, p);
615 +error:
616 + nla_nest_cancel(cb->msg, cb->nest[0]);
617 + return -1;
620 +static int
621 +swconfig_send_ports(struct sk_buff **msg, struct genl_info *info, int attr,
622 + const struct switch_val *val)
624 + struct swconfig_callback cb;
625 + int err = 0;
626 + int i;
628 + if (!val->value.ports)
629 + return -EINVAL;
631 + memset(&cb, 0, sizeof(cb));
632 + cb.cmd = attr;
633 + cb.msg = *msg;
634 + cb.info = info;
635 + cb.fill = swconfig_send_port;
636 + cb.close = swconfig_close_portlist;
638 + cb.nest[0] = nla_nest_start(cb.msg, cb.cmd);
639 + for (i = 0; i < val->len; i++) {
640 + err = swconfig_send_multipart(&cb, &val->value.ports[i]);
641 + if (err)
642 + goto done;
644 + err = val->len;
645 + swconfig_close_portlist(&cb, NULL);
646 + *msg = cb.msg;
648 +done:
649 + return err;
652 +static int
653 +swconfig_get_attr(struct sk_buff *skb, struct genl_info *info)
655 + struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
656 + struct switch_attr *attr;
657 + struct switch_dev *dev;
658 + struct sk_buff *msg = NULL;
659 + struct switch_val val;
660 + int err = -EINVAL;
661 + int cmd = hdr->cmd;
663 + dev = swconfig_get_dev(info);
664 + if (!dev)
665 + return -EINVAL;
667 + memset(&val, 0, sizeof(val));
668 + attr = swconfig_lookup_attr(dev, info, &val);
669 + if (!attr || !attr->get)
670 + goto error_dev;
672 + if (attr->type == SWITCH_TYPE_PORTS) {
673 + val.value.ports = dev->portbuf;
674 + memset(dev->portbuf, 0,
675 + sizeof(struct switch_port) * dev->ports);
678 + err = attr->get(dev, attr, &val);
679 + if (err)
680 + goto error;
682 + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
683 + if (!msg)
684 + goto error;
686 + hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, &switch_fam,
687 + 0, cmd);
688 + if (IS_ERR(hdr))
689 + goto nla_put_failure;
691 + switch(attr->type) {
692 + case SWITCH_TYPE_INT:
693 + NLA_PUT_U32(msg, SWITCH_ATTR_OP_VALUE_INT, val.value.i);
694 + break;
695 + case SWITCH_TYPE_STRING:
696 + NLA_PUT_STRING(msg, SWITCH_ATTR_OP_VALUE_STR, val.value.s);
697 + break;
698 + case SWITCH_TYPE_PORTS:
699 + err = swconfig_send_ports(&msg, info,
700 + SWITCH_ATTR_OP_VALUE_PORTS, &val);
701 + if (err < 0)
702 + goto nla_put_failure;
703 + break;
704 + default:
705 + DPRINTF("invalid type in attribute\n");
706 + err = -EINVAL;
707 + goto error;
709 + err = genlmsg_end(msg, hdr);
710 + if (err < 0)
711 + goto nla_put_failure;
713 + swconfig_put_dev(dev);
714 + return genlmsg_unicast(msg, info->snd_pid);
716 +nla_put_failure:
717 + if (msg)
718 + nlmsg_free(msg);
719 +error_dev:
720 + swconfig_put_dev(dev);
721 +error:
722 + if (!err)
723 + err = -ENOMEM;
724 + return err;
727 +static int
728 +swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags,
729 + const struct switch_dev *dev)
731 + void *hdr;
733 + hdr = genlmsg_put(msg, pid, seq, &switch_fam, flags,
734 + SWITCH_CMD_NEW_ATTR);
735 + if (IS_ERR(hdr))
736 + return -1;
738 + NLA_PUT_U32(msg, SWITCH_ATTR_ID, dev->id);
739 + NLA_PUT_STRING(msg, SWITCH_ATTR_NAME, dev->name);
740 + NLA_PUT_STRING(msg, SWITCH_ATTR_DEV_NAME, dev->devname);
741 + NLA_PUT_U32(msg, SWITCH_ATTR_VLANS, dev->vlans);
742 + NLA_PUT_U32(msg, SWITCH_ATTR_PORTS, dev->ports);
744 + return genlmsg_end(msg, hdr);
745 +nla_put_failure:
746 + genlmsg_cancel(msg, hdr);
747 + return -EMSGSIZE;
750 +static int swconfig_dump_switches(struct sk_buff *skb,
751 + struct netlink_callback *cb)
753 + struct switch_dev *dev;
754 + int start = cb->args[0];
755 + int idx = 0;
757 + swconfig_lock();
758 + list_for_each_entry(dev, &swdevs, dev_list) {
759 + if (++idx <= start)
760 + continue;
761 + if (swconfig_send_switch(skb, NETLINK_CB(cb->skb).pid,
762 + cb->nlh->nlmsg_seq, NLM_F_MULTI,
763 + dev) < 0)
764 + break;
766 + swconfig_unlock();
767 + cb->args[0] = idx;
769 + return skb->len;
772 +static int
773 +swconfig_done(struct netlink_callback *cb)
775 + return 0;
778 +static struct genl_ops swconfig_ops[] = {
780 + .cmd = SWITCH_CMD_LIST_GLOBAL,
781 + .doit = swconfig_list_attrs,
782 + .policy = switch_policy,
783 + },
785 + .cmd = SWITCH_CMD_LIST_VLAN,
786 + .doit = swconfig_list_attrs,
787 + .policy = switch_policy,
788 + },
790 + .cmd = SWITCH_CMD_LIST_PORT,
791 + .doit = swconfig_list_attrs,
792 + .policy = switch_policy,
793 + },
795 + .cmd = SWITCH_CMD_GET_GLOBAL,
796 + .doit = swconfig_get_attr,
797 + .policy = switch_policy,
798 + },
800 + .cmd = SWITCH_CMD_GET_VLAN,
801 + .doit = swconfig_get_attr,
802 + .policy = switch_policy,
803 + },
805 + .cmd = SWITCH_CMD_GET_PORT,
806 + .doit = swconfig_get_attr,
807 + .policy = switch_policy,
808 + },
810 + .cmd = SWITCH_CMD_SET_GLOBAL,
811 + .doit = swconfig_set_attr,
812 + .policy = switch_policy,
813 + },
815 + .cmd = SWITCH_CMD_SET_VLAN,
816 + .doit = swconfig_set_attr,
817 + .policy = switch_policy,
818 + },
820 + .cmd = SWITCH_CMD_SET_PORT,
821 + .doit = swconfig_set_attr,
822 + .policy = switch_policy,
823 + },
825 + .cmd = SWITCH_CMD_GET_SWITCH,
826 + .dumpit = swconfig_dump_switches,
827 + .policy = switch_policy,
828 + .done = swconfig_done,
832 +int
833 +register_switch(struct switch_dev *dev, struct net_device *netdev)
835 + INIT_LIST_HEAD(&dev->dev_list);
836 + if (netdev) {
837 + dev->netdev = netdev;
838 + if (!dev->devname)
839 + dev->devname = netdev->name;
841 + BUG_ON(!dev->devname);
843 + if (dev->ports > 0) {
844 + dev->portbuf = kzalloc(sizeof(struct switch_port) * dev->ports,
845 + GFP_KERNEL);
846 + if (!dev->portbuf)
847 + return -ENOMEM;
849 + dev->id = ++swdev_id;
850 + swconfig_defaults_init(dev);
851 + spin_lock_init(&dev->lock);
852 + swconfig_lock();
853 + list_add(&dev->dev_list, &swdevs);
854 + swconfig_unlock();
856 + return 0;
858 +EXPORT_SYMBOL_GPL(register_switch);
860 +void
861 +unregister_switch(struct switch_dev *dev)
863 + kfree(dev->portbuf);
864 + spin_lock(&dev->lock);
865 + swconfig_lock();
866 + list_del(&dev->dev_list);
867 + swconfig_unlock();
869 +EXPORT_SYMBOL_GPL(unregister_switch);
872 +static int __init
873 +swconfig_init(void)
875 + int i, err;
877 + INIT_LIST_HEAD(&swdevs);
878 + err = genl_register_family(&switch_fam);
879 + if (err)
880 + return err;
882 + for (i = 0; i < ARRAY_SIZE(swconfig_ops); i++) {
883 + err = genl_register_ops(&switch_fam, &swconfig_ops[i]);
884 + if (err)
885 + goto unregister;
888 + return 0;
890 +unregister:
891 + genl_unregister_family(&switch_fam);
892 + return err;
895 +static void __exit
896 +swconfig_exit(void)
898 + genl_unregister_family(&switch_fam);
901 +module_init(swconfig_init);
902 +module_exit(swconfig_exit);
904 diff -Nur linux-2.6.30.orig/include/linux/switch.h linux-2.6.30/include/linux/switch.h
905 --- linux-2.6.30.orig/include/linux/switch.h 1970-01-01 01:00:00.000000000 +0100
906 +++ linux-2.6.30/include/linux/switch.h 2009-06-11 09:22:50.000000000 +0200
907 @@ -0,0 +1,168 @@
909 + * switch.h: Switch configuration API
911 + * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
913 + * This program is free software; you can redistribute it and/or
914 + * modify it under the terms of the GNU General Public License
915 + * as published by the Free Software Foundation; either version 2
916 + * of the License, or (at your option) any later version.
918 + * This program is distributed in the hope that it will be useful,
919 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
920 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
921 + * GNU General Public License for more details.
922 + */
924 +#ifndef __LINUX_SWITCH_H
925 +#define __LINUX_SWITCH_H
927 +#include <linux/types.h>
928 +#include <linux/netdevice.h>
929 +#include <linux/netlink.h>
930 +#include <linux/genetlink.h>
931 +#ifndef __KERNEL__
932 +#include <netlink/netlink.h>
933 +#include <netlink/genl/genl.h>
934 +#include <netlink/genl/ctrl.h>
935 +#else
936 +#include <net/genetlink.h>
937 +#endif
939 +/* main attributes */
940 +enum {
941 + SWITCH_ATTR_UNSPEC,
942 + /* global */
943 + SWITCH_ATTR_TYPE,
944 + /* device */
945 + SWITCH_ATTR_ID,
946 + SWITCH_ATTR_NAME,
947 + SWITCH_ATTR_DEV_NAME,
948 + SWITCH_ATTR_VLANS,
949 + SWITCH_ATTR_PORTS,
950 + /* attributes */
951 + SWITCH_ATTR_OP_ID,
952 + SWITCH_ATTR_OP_TYPE,
953 + SWITCH_ATTR_OP_NAME,
954 + SWITCH_ATTR_OP_PORT,
955 + SWITCH_ATTR_OP_VLAN,
956 + SWITCH_ATTR_OP_VALUE_INT,
957 + SWITCH_ATTR_OP_VALUE_STR,
958 + SWITCH_ATTR_OP_VALUE_PORTS,
959 + SWITCH_ATTR_OP_DESCRIPTION,
960 + /* port lists */
961 + SWITCH_ATTR_PORT,
962 + SWITCH_ATTR_MAX
965 +/* commands */
966 +enum {
967 + SWITCH_CMD_UNSPEC,
968 + SWITCH_CMD_GET_SWITCH,
969 + SWITCH_CMD_NEW_ATTR,
970 + SWITCH_CMD_LIST_GLOBAL,
971 + SWITCH_CMD_GET_GLOBAL,
972 + SWITCH_CMD_SET_GLOBAL,
973 + SWITCH_CMD_LIST_PORT,
974 + SWITCH_CMD_GET_PORT,
975 + SWITCH_CMD_SET_PORT,
976 + SWITCH_CMD_LIST_VLAN,
977 + SWITCH_CMD_GET_VLAN,
978 + SWITCH_CMD_SET_VLAN
981 +/* data types */
982 +enum switch_val_type {
983 + SWITCH_TYPE_UNSPEC,
984 + SWITCH_TYPE_INT,
985 + SWITCH_TYPE_STRING,
986 + SWITCH_TYPE_PORTS,
987 + SWITCH_TYPE_NOVAL,
990 +/* port nested attributes */
991 +enum {
992 + SWITCH_PORT_UNSPEC,
993 + SWITCH_PORT_ID,
994 + SWITCH_PORT_FLAG_TAGGED,
995 + SWITCH_PORT_ATTR_MAX
998 +#define SWITCH_ATTR_DEFAULTS_OFFSET 0x1000
1000 +#ifdef __KERNEL__
1002 +struct switch_dev;
1003 +struct switch_op;
1004 +struct switch_val;
1005 +struct switch_attr;
1006 +struct switch_attrlist;
1008 +int register_switch(struct switch_dev *dev, struct net_device *netdev);
1009 +void unregister_switch(struct switch_dev *dev);
1011 +struct switch_attrlist {
1012 + /* filled in by the driver */
1013 + int n_attr;
1014 + struct switch_attr *attr;
1018 +struct switch_dev {
1019 + int id;
1020 + void *priv;
1021 + const char *name;
1023 + /* NB: either devname or netdev must be set */
1024 + const char *devname;
1025 + struct net_device *netdev;
1027 + int ports;
1028 + int vlans;
1029 + int cpu_port;
1030 + struct switch_attrlist attr_global, attr_port, attr_vlan;
1032 + spinlock_t lock;
1033 + struct switch_port *portbuf;
1034 + struct list_head dev_list;
1035 + unsigned long def_global, def_port, def_vlan;
1037 + int (*get_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
1038 + int (*set_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
1039 + int (*apply_config)(struct switch_dev *dev);
1042 +struct switch_port {
1043 + u32 id;
1044 + u32 flags;
1047 +struct switch_val {
1048 + struct switch_attr *attr;
1049 + int port_vlan;
1050 + int len;
1051 + union {
1052 + const char *s;
1053 + u32 i;
1054 + struct switch_port *ports;
1055 + } value;
1058 +struct switch_attr {
1059 + int disabled;
1060 + int type;
1061 + const char *name;
1062 + const char *description;
1064 + int (*set)(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val);
1065 + int (*get)(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val);
1067 + /* for driver internal use */
1068 + int id;
1069 + int ofs;
1070 + int max;
1073 +#endif
1075 +#endif