1/*
2 * nl80211 linux driver interface.
3 *
4 * Copyright (C) 1999-2013, Broadcom Corporation
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: $
19 */
20#include <errno.h>
21#include <linux/nl80211.h>
22#include <dhdioctl.h>
23#include "dhdu_nl80211.h"
24
25/* libnl 1.x compatibility code */
26#if !defined(CONFIG_LIBNL20) && !defined(CONFIG_LIBNL30)
27static inline struct nl_handle *nl_socket_alloc(void)
28{
29	return nl_handle_alloc();
30}
31
32static inline void nl_socket_free(struct nl_sock *h)
33{
34	nl_handle_destroy(h);
35}
36#endif /* CONFIG_LIBNL20 && CONFIG_LIBNL30 */
37
38static int dhd_nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
39{
40	int *ret = arg;
41	*ret = err->error;
42	return NL_STOP;
43}
44
45static int dhd_nl_finish_handler(struct nl_msg *msg, void *arg)
46{
47	int *ret = arg;
48	*ret = 0;
49	return NL_SKIP;
50}
51
52static int dhd_nl_ack_handler(struct nl_msg *msg, void *arg)
53{
54	int *ret = arg;
55	*ret = 0;
56	return NL_STOP;
57}
58
59static int dhd_nl_valid_handler(struct nl_msg *msg, void *arg)
60{
61	return NL_SKIP;
62}
63
64int dhd_nl_sock_connect(struct dhd_netlink_info *dhd_nli)
65{
66	dhd_nli->nl = nl_socket_alloc();
67	if (dhd_nli->nl == NULL)
68		return -1;
69
70	if (genl_connect(dhd_nli->nl) < 0) {
71		fprintf(stderr, "netlink connection failed\n");
72		goto err;
73	}
74
75	dhd_nli->nl_id = genl_ctrl_resolve(dhd_nli->nl, "nl80211");
76	if (dhd_nli->nl_id < 0) {
77		fprintf(stderr, "'nl80211' netlink not found\n");
78		goto err;
79	}
80
81	dhd_nli->cb = nl_cb_alloc(NL_CB_DEBUG);
82	if (dhd_nli->cb == NULL)
83		goto err;
84
85	nl_socket_set_cb(dhd_nli->nl, dhd_nli->cb);
86	return 0;
87
88err:
89	nl_cb_put(dhd_nli->cb);
90	nl_socket_free(dhd_nli->nl);
91	fprintf(stderr, "nl80211 connection failed\n");
92	return -1;
93}
94
95void dhd_nl_sock_disconnect(struct dhd_netlink_info *dhd_nli)
96{
97	nl_cb_put(dhd_nli->cb);
98	nl_socket_free(dhd_nli->nl);
99}
100
101int dhd_nl_do_testmode(struct dhd_netlink_info *dhd_nli, dhd_ioctl_t *ioc)
102{
103	struct nl_msg *msg;
104	int err;
105
106	msg = nlmsg_alloc();
107	if (msg == NULL)
108		return -ENOMEM;
109
110	/* fill testmode message */
111	genlmsg_put(msg, 0, 0, dhd_nli->nl_id, 0, 0,
112		NL80211_CMD_TESTMODE, 0);
113	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dhd_nli->ifidx);
114	NLA_PUT(msg, NL80211_ATTR_TESTDATA, sizeof(*ioc), ioc);
115
116	err = nl_send_auto_complete(dhd_nli->nl, msg);
117	if (err < 0)
118		goto out;
119
120	err = 1;
121	nl_cb_err(dhd_nli->cb, NL_CB_CUSTOM, dhd_nl_error_handler, &err);
122	nl_cb_set(dhd_nli->cb, NL_CB_ACK, NL_CB_CUSTOM,
123		dhd_nl_ack_handler, &err);
124	nl_cb_set(dhd_nli->cb, NL_CB_FINISH, NL_CB_CUSTOM,
125		dhd_nl_finish_handler, &err);
126	nl_cb_set(dhd_nli->cb, NL_CB_VALID, NL_CB_CUSTOM,
127		dhd_nl_valid_handler, &err);
128	while (err > 0)
129		nl_recvmsgs(dhd_nli->nl, dhd_nli->cb);
130out:
131	nlmsg_free(msg);
132	return err;
133
134nla_put_failure:
135	fprintf(stderr, "setting netlink attribute failed\n");
136	err = -EFAULT;
137	goto out;
138}
139