blob: a63fca2842fda8934f4d15dd405a36e5da721ad3 [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with the following added to such license:
12
13 As a special exception, the copyright holders of this software give
14 you permission to link this software with independent modules, and
15 to copy and distribute the resulting executable under terms of your
16 choice, provided that you also meet, for each linked independent
17 module, the terms and conditions of the license of that module.
18 An independent module is a module which is not derived from this
19 software. The special exception does not apply to any modifications
20 of the software.
21
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written consent.
26
27:>
28 */
29
30/*
31 * nl_transport.c
32 *
33 * NL-socket based transport. Kernel space module
34 *
35 */
36#include <linux/module.h> /* Needed by all modules */
37#include <linux/kernel.h> /* Needed for KERN_INFO */
38#include <net/netlink.h>
39#include <net/sock.h>
40#include <linux/netdevice.h>
41#include <linux/version.h>
42
43#include <bcmolt_tr_mux.h>
44#include <bcmolt_tr_nl_driver.h>
45#include "bcmolt_tr_nl.h"
46
47#define BCMTR_MEM_PACKETS 10000
48#define BCMTR_MEM_OVERHEAD SKB_TRUESIZE(1024)
49#define BCMTR_MIN_RCVBUF_SIZE (BCMTR_MEM_PACKETS * BCMTR_MEM_OVERHEAD)
50
51struct sock *bcmtr_nl_sock = NULL;
52
53/* Netlink operations. This driver overrides a couple */
54static struct proto_ops bcmtr_nl_ops;
55static int (*netlink_release_org)(struct socket *sock);
56
57struct bcmtr_nl
58{
59 bcmtrmux_channel channel[BCMTR_MAX_OLTS];
60 int pid;
61 struct sock *sk;
62};
63
64static int bcmtr_netlink_release(struct socket *sock)
65{
66 struct sock *sk = sock->sk;
67 struct bcmtr_nl *nlb = sk->sk_user_data;
68 if (nlb)
69 {
70 int i;
71 for (i = 0; i < BCMTR_MAX_OLTS; i++)
72 {
73 if (nlb->channel[i])
74 bcmtrmux_channel_unregister(i, nlb->channel[i]);
75 }
76 kfree(nlb);
77 sk->sk_user_data = NULL;
78 }
79 return netlink_release_org(sock);
80}
81
82/** Receive message handler */
83static void _bcmtr_nl_recv(bcmolt_devid device, bcmos_buf *buf, bcmtrmux_channel channel, void *data)
84{
85 uint32_t data_length = bcmos_buf_length(buf);
86 uint32_t device_u32 = (uint32_t)device;
87 struct bcmtr_nl *nlb = (struct bcmtr_nl *)data;
88 uint8_t *p_device;
89 int r;
90
91 NETLINK_CB(&(buf->skb)).dst_group = 0; /* not in mcast group */
92
93 /* Now a tricky part. nlmsg_put() assumes that skb is empty and extends it
94 * by total msg size. We don't want it. Reserve room for nlmsg header and then
95 * set size to 0 and let nlmsg_put() restore it
96 */
97 if (unlikely(skb_headroom(&buf->skb) < NLMSG_HDRLEN + sizeof(uint32_t)))
98 {
99 printk(KERN_INFO "%s: can't prepend NL header\n", __FUNCTION__);
100 bcmos_free(buf);
101 return;
102 }
103 __skb_push(&buf->skb, NLMSG_HDRLEN + sizeof(uint32_t));
104 /* Store device immediately after the NL header */
105 p_device = buf->skb.data + NLMSG_HDRLEN;
106 memcpy(p_device, &device_u32, sizeof(uint32_t));
107 __skb_trim(&buf->skb, 0);
108 nlmsg_put(&buf->skb, 0, 0, NLMSG_DONE, data_length + sizeof(uint32_t), 0);
109
110 r = nlmsg_unicast(nlb->sk, &buf->skb, nlb->pid);
111 if (r)
112 {
113 printk(KERN_INFO "%s: nlmsg_unicast() --> %d len=%u rm_alloc=%d recvbuf=%d\n",
114 __FUNCTION__, r, data_length, atomic_read(&nlb->sk->sk_rmem_alloc), nlb->sk->sk_rcvbuf);
115 }
116}
117
118void bcmtr_nl_input(struct sk_buff *skb)
119{
120 struct nlmsghdr *nlh;
121 struct bcmtr_nl *nlb;
122 uint32_t device;
123 bcmos_errno rc;
124
125 nlh = nlmsg_hdr(skb);
126
127 if(NLMSG_OK(nlh, skb->len))
128 {
129#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
130 struct sock *sk = NETLINK_CB(skb).sk;
131#else
132 struct sock *sk = NETLINK_CB(skb).ssk;
133#endif
134
135 /* channel is stored in user_data. If it is 0 - that means that channel
136 * is not allocated yet. Do it here
137 */
138 if (!sk->sk_user_data)
139 {
140 nlb = kmalloc(sizeof(*nlb), GFP_KERNEL);
141 if (!nlb)
142 {
143 printk(KERN_INFO "%s: can't allocate NLB\n", __FUNCTION__);
144 return;
145 }
146 memset(nlb, 0, sizeof(*nlb));
147 nlb->pid = nlh->nlmsg_pid;
148 nlb->sk = sk;
149 sk->sk_user_data = nlb;
150
151 /* Override release callback in order to cleanup */
152 bcmtr_nl_ops = *sk->sk_socket->ops;
153 netlink_release_org = bcmtr_nl_ops.release;
154 bcmtr_nl_ops.release = bcmtr_netlink_release;
155 sk->sk_socket->ops = &bcmtr_nl_ops;
156
157 /* Override sk_rcvbuf size. Set min */
158 if (sk->sk_rcvbuf < BCMTR_MIN_RCVBUF_SIZE)
159 {
160 sk->sk_rcvbuf = BCMTR_MIN_RCVBUF_SIZE;
161 }
162 }
163 else
164 {
165 nlb = (struct bcmtr_nl *)sk->sk_user_data;
166 }
167
168 /* Device id follows NLMSG header */
169 device = skb->data[NLMSG_HDRLEN];
170 if (device >= BCMTR_MAX_OLTS)
171 {
172 printk(KERN_INFO "%s: device %u is insane\n", __FUNCTION__, device);
173 return;
174 }
175
176 if (!nlb->channel[device])
177 {
178 bcmtrmux_channel channel = BCMTRMUX_CHANNEL_AUTO_ASSIGN;
179
180 rc = bcmtrmux_channel_register(device, &channel, _bcmtr_nl_recv, nlb);
181 if (rc)
182 {
183 printk(KERN_INFO "%s: can't register channel for device %u. rc=%d\n", __FUNCTION__, device, (int)rc);
184 return;
185 }
186 nlb->channel[device] = channel;
187 }
188
189
190 /* Cut NL header + device */
191 skb_pull(skb, NLMSG_HDRLEN + sizeof(uint32_t));
192
193 /* Clone skb because caller will release the original one */
194 skb = skb_clone(skb, 0);
195 if (!skb)
196 {
197
198 printk(KERN_ERR "Failed to allocate new skb\n");
199 return;
200 }
201
202 /* Forward to the mux */
203 bcmtrmux_rx_from_host(device, nlb->channel[device], (bcmos_buf *)skb);
204 }
205 else
206 {
207 printk(KERN_INFO "%s: invalid message received frpm pid %d\n", __FUNCTION__, nlh->nlmsg_pid);
208 }
209}
210
211static struct netlink_kernel_cfg bcmtr_nl_cfg = {
212 .input = bcmtr_nl_input,
213};
214
215bcmos_errno bcmtr_nl_init(void)
216{
217 printk(KERN_INFO "Maple netlink-based raw driver transport driver\n");
218 bcmtr_nl_sock = netlink_kernel_create(&init_net, BCMTR_NL_FAMILY, &bcmtr_nl_cfg);
219 if(!bcmtr_nl_sock)
220 return BCM_ERR_NOMEM;
221 return BCM_ERR_OK;
222}
223
224void bcmtr_nl_exit(void)
225{
226 printk("Maple netlink-based raw driver terminated\n");
227 if (bcmtr_nl_sock)
228 {
229 netlink_kernel_release(bcmtr_nl_sock);
230 bcmtr_nl_sock = NULL;
231 }
232}