Resync with broadcom drivers 5.100.138.20 and utilities.
[tomato.git] / release / src-rt / emf / igs / igs_linux.c
blobfb220040d61005399542ce412557e1c4c5e38c52
1 /*
2 * IGMP Snooping layer linux specific code
4 * Copyright (C) 2009, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
8 * the contents of this file may not be disclosed to third parties, copied
9 * or duplicated in any form, in whole or in part, without the prior
10 * written permission of Broadcom Corporation.
12 * $Id: igs_linux.c,v 1.7 2009/04/28 00:08:23 Exp $
14 #include <linux/module.h>
15 #include <linux/netdevice.h>
16 #include <linux/proc_fs.h>
17 #include <linux/netlink.h>
18 #include <net/sock.h>
19 #include <linux/if.h>
20 #include <proto/ethernet.h>
21 #include <bcmnvram.h>
22 #include <bcmutils.h>
23 #include <osl.h>
24 #include <emf/igs/osl_linux.h>
25 #include <emf/igs/igs_cfg.h>
26 #include <emf/igs/igsc_export.h>
27 #include "igs_linux.h"
29 MODULE_LICENSE("Proprietary");
31 static igs_struct_t igs;
34 * Description: This function is called by IGS Common code when it wants
35 * to send a packet on to all the LAN ports. It allocates
36 * the native OS packet buffer, adds mac header and forwards
37 * a copy of frame on to LAN ports.
39 * Input: igs_info - IGS instance information.
40 * ip - Pointer to the buffer containing the frame to
41 * send.
42 * length - Length of the buffer.
43 * mgrp_ip - Multicast destination address.
45 * Return: SUCCESS or FAILURE
47 int32
48 igs_broadcast(igs_info_t *igs_info, uint8 *ip, uint32 length, uint32 mgrp_ip)
50 struct sk_buff *skb;
51 struct net_device *br_dev;
52 struct ether_header *eh;
54 br_dev = igs_info->br_dev;
56 ASSERT(br_dev);
58 if ((br_dev->flags & IFF_UP) == 0)
60 IGS_ERROR("Bridge interface %s is down\n", br_dev->name);
61 return (FAILURE);
64 skb = dev_alloc_skb(length + ETHER_HDR_LEN);
66 if (skb == NULL)
68 IGS_ERROR("Out of memory allocating IGMP Query packet\n");
69 return (FAILURE);
72 IGS_DEBUG("Allocated pkt buffer for IGMP Query\n");
74 skb_pull(skb, ETHER_HDR_LEN);
75 memcpy(skb->data, ip, length);
76 skb_put(skb, length);
78 /* Add the ethernet header */
79 eh = (struct ether_header *)skb_push(skb, ETH_HLEN);
80 eh->ether_type = __constant_htons(ETH_P_IP);
81 eh->ether_dhost[0] = 0x01;
82 eh->ether_dhost[1] = 0x00;
83 eh->ether_dhost[2] = 0x5e;
84 eh->ether_dhost[5] = mgrp_ip & 0xff; mgrp_ip >>= 8;
85 eh->ether_dhost[4] = mgrp_ip & 0xff; mgrp_ip >>= 8;
86 eh->ether_dhost[3] = mgrp_ip & 0x7f;
88 /* Send the frame on to the bridge device */
89 memcpy(eh->ether_shost, br_dev->dev_addr, br_dev->addr_len);
90 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
91 skb_reset_mac_header(skb);
92 #else
93 skb->mac.raw = skb->data;
94 #endif
95 skb->dev = br_dev;
96 dev_queue_xmit(skb);
98 IGS_DEBUG("IGMP Query sent on %s\n", br_dev->name);
100 return (SUCCESS);
103 #ifdef CONFIG_PROC_FS
105 * IGSL Packet Counters/Statistics Function
107 static int32
108 igs_stats_get(char *buf, char **start, off_t offset, int32 size,
109 int32 *eof, void *data)
111 igs_info_t *igs_info;
112 igs_cfg_request_t cfg;
113 igs_stats_t *stats;
114 struct bcmstrbuf b;
116 igs_info = (igs_info_t *)data;
118 strcpy(cfg.inst_id, igs_info->inst_id);
119 cfg.command_id = IGSCFG_CMD_IGS_STATS;
120 cfg.oper_type = IGSCFG_OPER_TYPE_GET;
121 cfg.size = sizeof(cfg.arg);
122 stats = (igs_stats_t *)cfg.arg;
124 igsc_cfg_request_process(igs_info->igsc_info, &cfg);
125 if (cfg.status != IGSCFG_STATUS_SUCCESS)
127 IGS_ERROR("Unable to get the IGS stats\n");
128 return (FAILURE);
131 bcm_binit(&b, buf, size);
132 bcm_bprintf(&b, "IgmpPkts IgmpQueries "
133 "IgmpReports IgmpV2Reports IgmpLeaves\n");
134 bcm_bprintf(&b, "%-15d %-15d %-15d %-15d %d\n",
135 stats->igmp_packets, stats->igmp_queries,
136 stats->igmp_reports, stats->igmp_v2reports,
137 stats->igmp_leaves);
138 bcm_bprintf(&b, "IgmpNotHandled McastGroups "
139 "McastMembers MemTimeouts\n");
140 bcm_bprintf(&b, "%-15d %-15d %-15d %d\n",
141 stats->igmp_not_handled, stats->igmp_mcast_groups,
142 stats->igmp_mcast_members, stats->igmp_mem_timeouts);
144 if (b.size == 0)
146 IGS_ERROR("Input buffer overflow\n");
147 return (FAILURE);
150 return (b.buf - b.origbuf);
153 static int32
154 igs_sdb_list(char *buf, char **start, off_t offset, int32 size,
155 int32 *eof, void *data)
157 igs_info_t *igs_info;
158 igs_cfg_request_t cfg;
159 igs_cfg_sdb_list_t *list;
160 int32 i;
161 struct bcmstrbuf b;
163 igs_info = (igs_info_t *)data;
165 strcpy(cfg.inst_id, igs_info->inst_id);
166 cfg.command_id = IGSCFG_CMD_IGSDB_LIST;
167 cfg.oper_type = IGSCFG_OPER_TYPE_GET;
168 cfg.size = sizeof(cfg.arg);
169 list = (igs_cfg_sdb_list_t *)cfg.arg;
171 igsc_cfg_request_process(igs_info->igsc_info, &cfg);
172 if (cfg.status != IGSCFG_STATUS_SUCCESS)
174 IGS_ERROR("Unable to get the IGSDB list\n");
175 return (FAILURE);
178 bcm_binit(&b, buf, size);
179 bcm_bprintf(&b, "Group Members Interface\n");
181 for (i = 0; i < list->num_entries; i++)
183 bcm_bprintf(&b, "%08x ", list->sdb_entry[i].mgrp_ip);
184 bcm_bprintf(&b, "%08x ", list->sdb_entry[i].mh_ip);
185 bcm_bprintf(&b, "%s\n", list->sdb_entry[i].if_name);
188 if (b.size == 0)
190 IGS_ERROR("Input buffer overflow\n");
191 return (FAILURE);
194 return (b.buf - b.origbuf);
196 #endif /* CONFIG_PROC_FS */
199 * Description: This function is called when user application enables snooping
200 * on a bridge interface. It primarily allocates memory for IGS
201 * instance data and calls common code initialization function.
203 static igs_info_t *
204 igs_instance_add(int8 *inst_id, struct net_device *br_ptr)
206 igs_info_t *igs_info;
207 osl_t *osh;
208 uint8 proc_name[64];
209 igsc_wrapper_t igsl;
211 if (igs.inst_count > IGS_MAX_INST)
213 IGS_ERROR("Max instance limit %d exceeded\n", IGS_MAX_INST);
214 return (NULL);
217 igs.inst_count++;
219 IGS_INFO("Creating IGS instance for %s\n", inst_id);
221 osh = osl_attach(NULL, PCI_BUS, FALSE);
223 ASSERT(osh);
225 /* Allocate os specfic IGS info object */
226 igs_info = MALLOC(osh, sizeof(igs_info_t));
227 if (igs_info == NULL)
229 IGS_ERROR("Out of memory allocating igs_info\n");
230 osl_detach(osh);
231 return (NULL);
234 igs_info->osh = osh;
236 /* Save the IGS instance identifier */
237 strncpy(igs_info->inst_id, inst_id, IFNAMSIZ);
238 igs_info->inst_id[IFNAMSIZ - 1] = 0;
240 /* Save the device pointer */
241 igs_info->br_dev = br_ptr;
243 /* Fill in linux specific wrapper functions*/
244 igsl.igs_broadcast = (igs_broadcast_fn_ptr)igs_broadcast;
246 /* Initialize IGSC layer */
247 if ((igs_info->igsc_info = igsc_init(inst_id, (void *)igs_info, osh, &igsl)) == NULL)
249 IGS_ERROR("IGSC init failed\n");
250 MFREE(osh, igs_info, sizeof(igs_info_t));
251 osl_detach(osh);
252 return (NULL);
255 #ifdef CONFIG_PROC_FS
256 sprintf(proc_name, "net/igs_stats_%s", inst_id);
257 create_proc_read_entry(proc_name, 0, 0, igs_stats_get, igs_info);
258 sprintf(proc_name, "net/igsdb_%s", inst_id);
259 create_proc_read_entry(proc_name, 0, 0, igs_sdb_list, igs_info);
260 #endif /* CONFIG_PROC_FS */
262 IGS_INFO("Created IGSC instance for %s\n", inst_id);
264 /* Add to global IGS instance list */
265 OSL_LOCK(igs.lock);
266 igs_info->next = igs.list_head;
267 igs.list_head = igs_info;
268 OSL_UNLOCK(igs.lock);
270 return (igs_info);
273 static int32
274 igs_instance_del(igs_info_t *igs_info)
276 bool found = FALSE;
277 osl_t *osh;
278 igs_info_t *ptr, *prev;
279 uint8 proc_name[64];
281 OSL_LOCK(igs.lock);
283 /* Delete the IGS instance */
284 prev = NULL;
285 for (ptr = igs.list_head; ptr != NULL; prev = ptr, ptr = ptr->next)
287 if (ptr == igs_info)
289 found = TRUE;
290 if (prev != NULL)
291 prev->next = ptr->next;
292 else
293 igs.list_head = NULL;
294 break;
298 OSL_UNLOCK(igs.lock);
300 if (!found)
302 IGS_ERROR("IGS instance not found\n");
303 return (FAILURE);
306 /* Free the IGS instance */
307 igsc_exit(igs_info->igsc_info);
309 #ifdef CONFIG_PROC_FS
310 sprintf(proc_name, "net/igs_stats_%s", igs_info->inst_id);
311 remove_proc_entry(proc_name, 0);
312 sprintf(proc_name, "net/igsdb_%s", igs_info->inst_id);
313 remove_proc_entry(proc_name, 0);
314 #endif /* CONFIG_PROC_FS */
316 osh = igs_info->osh;
317 MFREE(igs_info->osh, igs_info, sizeof(igs_info_t));
318 osl_detach(osh);
320 return (SUCCESS);
323 static void
324 igs_instances_clear(void)
326 igs_info_t *ptr, *tmp;
328 OSL_LOCK(igs.lock);
330 ptr = igs.list_head;
332 while (ptr != NULL)
334 tmp = ptr->next;
335 igs_instance_del(ptr);
336 ptr = tmp;
339 OSL_UNLOCK(igs.lock);
341 return;
344 static igs_info_t *
345 igs_instance_find(int8 *inst_id)
347 igs_info_t *igs_info;
349 ASSERT(inst_id != NULL);
351 OSL_LOCK(igs.lock);
353 for (igs_info = igs.list_head; igs_info != NULL; igs_info = igs_info->next)
355 if (strcmp(igs_info->inst_id, inst_id) == 0)
357 OSL_UNLOCK(igs.lock);
358 return (igs_info);
362 OSL_UNLOCK(igs.lock);
364 return (NULL);
367 static void *
368 igs_if_name_validate(uint8 *if_name)
370 struct net_device *dev;
372 /* Get the interface pointer */
373 dev = dev_get_by_name(if_name);
375 if (dev == NULL)
377 IGS_ERROR("Interface %s doesn't exist\n", if_name);
378 return (NULL);
381 dev_put(dev);
383 return (dev);
387 * Description: This function handles the OS specific processing
388 * required for configuration commands.
390 * Input: data - Configuration command parameters
392 void
393 igs_cfg_request_process(igs_cfg_request_t *cfg)
395 igs_info_t *igs_info;
396 struct net_device *br_ptr;
398 if (cfg == NULL)
400 cfg->status = IGSCFG_STATUS_FAILURE;
401 cfg->size = sprintf(cfg->arg, "Invalid input buffer passed\n");
402 return;
405 /* Validate the instance identifier */
406 br_ptr = igs_if_name_validate(cfg->inst_id);
407 if (br_ptr == NULL)
409 cfg->status = IGSCFG_STATUS_FAILURE;
410 cfg->size = sprintf(cfg->arg, "Unknown instance identifier %s\n",
411 cfg->inst_id);
412 return;
415 /* Locate the IGS instance */
416 igs_info = igs_instance_find(cfg->inst_id);
417 if ((igs_info == NULL) && (cfg->command_id != IGSCFG_CMD_BR_ADD))
419 cfg->status = IGSCFG_STATUS_FAILURE;
420 cfg->size = sprintf(cfg->arg, "Invalid instance identifier %s\n",
421 cfg->inst_id);
422 return;
425 /* Convert the interface name in arguments to interface pointer */
426 switch (cfg->command_id)
428 case IGSCFG_CMD_BR_ADD:
429 if (igs_info != NULL)
431 cfg->status = IGSCFG_STATUS_FAILURE;
432 cfg->size = sprintf(cfg->arg,
433 "IGMP Snooping already enabled on %s\n",
434 cfg->inst_id);
435 return;
438 /* Create a new IGS instance corresponding to the bridge
439 * interface.
441 igs_info = igs_instance_add(cfg->inst_id, br_ptr);
443 if (igs_info == NULL)
445 cfg->status = IGSCFG_STATUS_FAILURE;
446 cfg->size = sprintf(cfg->arg,
447 "IGMP Snooping enable on %s failed\n",
448 cfg->inst_id);
449 return;
452 cfg->status = IGSCFG_STATUS_SUCCESS;
453 break;
455 case IGSCFG_CMD_BR_DEL:
456 /* Delete and free the IGS instance */
457 if (igs_instance_del(igs_info) != SUCCESS)
459 cfg->status = IGSCFG_STATUS_FAILURE;
460 cfg->size = sprintf(cfg->arg,
461 "IGMP Snooping disable failed\n");
462 return;
465 cfg->status = IGSCFG_STATUS_SUCCESS;
466 break;
468 case IGSCFG_CMD_BR_LIST:
469 break;
471 default:
472 igsc_cfg_request_process(igs_info->igsc_info, cfg);
473 break;
475 return;
479 * Description: This function is called by Linux kernel when user
480 * applications sends a message on netlink socket. It
481 * dequeues the message, calls the functions to process
482 * the commands and sends the result back to user.
484 * Input: skb - Kernel socket structure
486 static void
487 igs_netlink_sock_cb(struct sk_buff *skb)
489 struct nlmsghdr *nlh;
491 nlh = nlmsg_hdr(skb);
492 IGS_DEBUG("Length of the command buffer %d\n", nlh->nlmsg_len);
494 /* Check the buffer for min size */
495 if (skb->len < NLMSG_SPACE(0) || skb->len < nlh->nlmsg_len ||
496 nlh->nlmsg_len < NLMSG_LENGTH(sizeof(igs_cfg_request_t)))
498 IGS_ERROR("Configuration request size not > %d\n",
499 sizeof(igs_cfg_request_t));
500 return;
503 skb = skb_clone(skb, GFP_KERNEL);
504 if (skb == NULL)
505 return;
506 nlh = nlmsg_hdr(skb);
508 /* Process the message */
509 igs_cfg_request_process((igs_cfg_request_t *)NLMSG_DATA(nlh));
511 /* Send the result to user process */
512 NETLINK_CB(skb).pid = nlh->nlmsg_pid;
513 NETLINK_CB(skb).dst_group = 0;
515 netlink_unicast(igs.nl_sk, skb, nlh->nlmsg_pid, MSG_DONTWAIT);
519 * Description: This function is called during module load time. It
520 * primarily allocates memory for IGS OS specific instance
521 * data and calls the common code initialization function.
523 static int32 __init
524 igs_module_init(void)
526 #define NETLINK_IGSC 18
527 igs.nl_sk = netlink_kernel_create(NETLINK_IGSC, 0, igs_netlink_sock_cb,
528 NULL, THIS_MODULE);
530 if (igs.nl_sk == NULL)
532 IGS_ERROR("Netlink kernel socket create failed\n");
533 return (FAILURE);
536 igs.lock = OSL_LOCK_CREATE("IGS Instance List");
538 if (igs.lock == NULL)
540 IGS_ERROR("IGS instance list lock create failed\n");
541 return (FAILURE);
544 return (SUCCESS);
547 static void __exit
548 igs_module_exit(void)
550 sock_release(igs.nl_sk->sk_socket);
551 igs_instances_clear();
552 OSL_LOCK_DESTROY(igs.lock);
554 return;
557 module_init(igs_module_init);
558 module_exit(igs_module_exit);
560 EXPORT_SYMBOL(igsc_init);
561 EXPORT_SYMBOL(igsc_exit);
562 EXPORT_SYMBOL(igsc_sdb_interface_del);
563 EXPORT_SYMBOL(igsc_interface_rtport_del);