summaryrefslogtreecommitdiffstats
path: root/bcmdhd/dhdutil/dhdu_nl80211.c
blob: 8e681f845b7820ea903eb7362def76a80cdf9944 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * nl80211 linux driver interface.
 *
 * Copyright (C) 1999-2013, Broadcom Corporation
 * 
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * $Id: $
 */
#include <errno.h>
#include <linux/nl80211.h>
#include <dhdioctl.h>
#include "dhdu_nl80211.h"

/* libnl 1.x compatibility code */
#if !defined(CONFIG_LIBNL20) && !defined(CONFIG_LIBNL30)
static inline struct nl_handle *nl_socket_alloc(void)
{
	return nl_handle_alloc();
}

static inline void nl_socket_free(struct nl_sock *h)
{
	nl_handle_destroy(h);
}
#endif /* CONFIG_LIBNL20 && CONFIG_LIBNL30 */

static int dhd_nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
{
	int *ret = arg;
	*ret = err->error;
	return NL_STOP;
}

static int dhd_nl_finish_handler(struct nl_msg *msg, void *arg)
{
	int *ret = arg;
	*ret = 0;
	return NL_SKIP;
}

static int dhd_nl_ack_handler(struct nl_msg *msg, void *arg)
{
	int *ret = arg;
	*ret = 0;
	return NL_STOP;
}

static int dhd_nl_valid_handler(struct nl_msg *msg, void *arg)
{
	return NL_SKIP;
}

int dhd_nl_sock_connect(struct dhd_netlink_info *dhd_nli)
{
	dhd_nli->nl = nl_socket_alloc();
	if (dhd_nli->nl == NULL)
		return -1;

	if (genl_connect(dhd_nli->nl) < 0) {
		fprintf(stderr, "netlink connection failed\n");
		goto err;
	}

	dhd_nli->nl_id = genl_ctrl_resolve(dhd_nli->nl, "nl80211");
	if (dhd_nli->nl_id < 0) {
		fprintf(stderr, "'nl80211' netlink not found\n");
		goto err;
	}

	dhd_nli->cb = nl_cb_alloc(NL_CB_DEBUG);
	if (dhd_nli->cb == NULL)
		goto err;

	nl_socket_set_cb(dhd_nli->nl, dhd_nli->cb);
	return 0;

err:
	nl_cb_put(dhd_nli->cb);
	nl_socket_free(dhd_nli->nl);
	fprintf(stderr, "nl80211 connection failed\n");
	return -1;
}

void dhd_nl_sock_disconnect(struct dhd_netlink_info *dhd_nli)
{
	nl_cb_put(dhd_nli->cb);
	nl_socket_free(dhd_nli->nl);
}

int dhd_nl_do_testmode(struct dhd_netlink_info *dhd_nli, dhd_ioctl_t *ioc)
{
	struct nl_msg *msg;
	int err;

	msg = nlmsg_alloc();
	if (msg == NULL)
		return -ENOMEM;

	/* fill testmode message */
	genlmsg_put(msg, 0, 0, dhd_nli->nl_id, 0, 0,
		NL80211_CMD_TESTMODE, 0);
	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dhd_nli->ifidx);
	NLA_PUT(msg, NL80211_ATTR_TESTDATA, sizeof(*ioc), ioc);

	err = nl_send_auto_complete(dhd_nli->nl, msg);
	if (err < 0)
		goto out;

	err = 1;
	nl_cb_err(dhd_nli->cb, NL_CB_CUSTOM, dhd_nl_error_handler, &err);
	nl_cb_set(dhd_nli->cb, NL_CB_ACK, NL_CB_CUSTOM,
		dhd_nl_ack_handler, &err);
	nl_cb_set(dhd_nli->cb, NL_CB_FINISH, NL_CB_CUSTOM,
		dhd_nl_finish_handler, &err);
	nl_cb_set(dhd_nli->cb, NL_CB_VALID, NL_CB_CUSTOM,
		dhd_nl_valid_handler, &err);
	while (err > 0)
		nl_recvmsgs(dhd_nli->nl, dhd_nli->cb);
out:
	nlmsg_free(msg);
	return err;

nla_put_failure:
	fprintf(stderr, "setting netlink attribute failed\n");
	err = -EFAULT;
	goto out;
}