blob: 240d43aede818d892920d3d401485f292a2b40f4 [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
2 * Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
3 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/*========================================================================
29
30 \file epping_main.c
31
32 \brief WLAN End Point Ping test tool implementation
33
34 ========================================================================*/
35
36/*--------------------------------------------------------------------------
37 Include Files
38 ------------------------------------------------------------------------*/
39#include <cds_api.h>
40#include <cds_sched.h>
41#include <linux/etherdevice.h>
42#include <linux/firmware.h>
43#include <wni_api.h>
44#include <wlan_ptt_sock_svc.h>
45#include <linux/wireless.h>
46#include <net/cfg80211.h>
47#if defined(MSM_PLATFORM) && defined(HIF_PCI)
48#include <net/cnss.h>
49#endif /* MSM_PLATFORM */
50#include <linux/rtnetlink.h>
51#include <linux/semaphore.h>
52#include <linux/ctype.h>
53#include "epping_main.h"
54#include "epping_internal.h"
55
56static int epping_start_adapter(epping_adapter_t *pAdapter);
57static void epping_stop_adapter(epping_adapter_t *pAdapter);
58
59static void epping_timer_expire(void *data)
60{
61 struct net_device *dev = (struct net_device *)data;
62 epping_adapter_t *pAdapter;
63
64 if (dev == NULL) {
65 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
66 "%s: netdev = NULL", __func__);
67 return;
68 }
69
70 pAdapter = netdev_priv(dev);
71 if (pAdapter == NULL) {
72 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
73 "%s: adapter = NULL", __func__);
74 return;
75 }
76 pAdapter->epping_timer_state = EPPING_TX_TIMER_STOPPED;
77 epping_tx_timer_expire(pAdapter);
78}
79
80static int epping_ndev_open(struct net_device *dev)
81{
82 epping_adapter_t *pAdapter;
83 int ret = 0;
84
85 pAdapter = netdev_priv(dev);
86 epping_start_adapter(pAdapter);
87 return ret;
88}
89
90static int epping_ndev_stop(struct net_device *dev)
91{
92 epping_adapter_t *pAdapter;
93 int ret = 0;
94
95 pAdapter = netdev_priv(dev);
96 if (NULL == pAdapter) {
97 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
98 "%s: EPPING adapter context is Null", __func__);
99 ret = -ENODEV;
100 goto end;
101 }
102 epping_stop_adapter(pAdapter);
103end:
104 return ret;
105}
106
107static void epping_ndev_uninit(struct net_device *dev)
108{
109 epping_adapter_t *pAdapter;
110
111 pAdapter = netdev_priv(dev);
112 if (NULL == pAdapter) {
113 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
114 "%s: EPPING adapter context is Null", __func__);
115 goto end;
116 }
117 epping_stop_adapter(pAdapter);
118end:
119 return;
120}
121
122void epping_tx_queue_timeout(struct net_device *dev)
123{
124 epping_adapter_t *pAdapter;
125
126 pAdapter = netdev_priv(dev);
127 if (NULL == pAdapter) {
128 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
129 "%s: EPPING adapter context is Null", __func__);
130 goto end;
131 }
132
133 EPPING_LOG(CDF_TRACE_LEVEL_ERROR,
134 "%s: Transmission timeout occurred, pAdapter->started= %d",
135 __func__, pAdapter->started);
136
137 /* Getting here implies we disabled the TX queues
138 * for too long. Since this is epping
139 * (not because of disassociation or low resource scenarios),
140 * try to restart the queue
141 */
142 if (pAdapter->started)
143 netif_wake_queue(dev);
144end:
145 return;
146
147}
148
149int epping_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
150{
151 epping_adapter_t *pAdapter;
152 int ret = 0;
153
154 pAdapter = netdev_priv(dev);
155 if (NULL == pAdapter) {
156 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
157 "%s: EPPING adapter context is Null", __func__);
158 ret = -ENODEV;
159 goto end;
160 }
161 ret = epping_tx_send(skb, pAdapter);
162end:
163 return ret;
164}
165
166struct net_device_stats *epping_get_stats(struct net_device *dev)
167{
168 epping_adapter_t *pAdapter = netdev_priv(dev);
169
170 if (NULL == pAdapter) {
171 EPPING_LOG(CDF_TRACE_LEVEL_ERROR, "%s: pAdapter = NULL",
172 __func__);
173 return NULL;
174 }
175
176 return &pAdapter->stats;
177}
178
179int epping_ndev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
180{
181 epping_adapter_t *pAdapter;
182 int ret = 0;
183
184 pAdapter = netdev_priv(dev);
185 if (NULL == pAdapter) {
186 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
187 "%s: EPPING adapter context is Null", __func__);
188 ret = -ENODEV;
189 goto end;
190 }
191 if (dev != pAdapter->dev) {
192 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
193 "%s: HDD adapter/dev inconsistency", __func__);
194 ret = -ENODEV;
195 goto end;
196 }
197
198 if ((!ifr) || (!ifr->ifr_data)) {
199 ret = -EINVAL;
200 goto end;
201 }
202
203 switch (cmd) {
204 case (SIOCDEVPRIVATE + 1):
205 EPPING_LOG(CDF_TRACE_LEVEL_ERROR,
206 "%s: do not support ioctl %d (SIOCDEVPRIVATE + 1)",
207 __func__, cmd);
208 break;
209 default:
210 EPPING_LOG(CDF_TRACE_LEVEL_ERROR, "%s: unknown ioctl %d",
211 __func__, cmd);
212 ret = -EINVAL;
213 break;
214 }
215
216end:
217 return ret;
218}
219
220static int epping_set_mac_address(struct net_device *dev, void *addr)
221{
222 epping_adapter_t *pAdapter = netdev_priv(dev);
223 struct sockaddr *psta_mac_addr = addr;
224 cdf_mem_copy(&pAdapter->macAddressCurrent,
225 psta_mac_addr->sa_data, ETH_ALEN);
226 cdf_mem_copy(dev->dev_addr, psta_mac_addr->sa_data, ETH_ALEN);
227 return 0;
228}
229
230static void epping_stop_adapter(epping_adapter_t *pAdapter)
231{
232 if (pAdapter && pAdapter->started) {
233 EPPING_LOG(LOG1, FL("Disabling queues"));
234 netif_tx_disable(pAdapter->dev);
235 netif_carrier_off(pAdapter->dev);
236 pAdapter->started = false;
237#if defined(MSM_PLATFORM) && defined(HIF_PCI) && defined(CONFIG_CNSS)
238 cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_LOW);
239#endif
240 }
241}
242
243static int epping_start_adapter(epping_adapter_t *pAdapter)
244{
245 if (!pAdapter) {
246 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
247 "%s: pAdapter= NULL\n", __func__);
248 return -1;
249 }
250 if (!pAdapter->started) {
251#if defined(MSM_PLATFORM) && defined(HIF_PCI) && defined(CONFIG_CNSS)
252 cnss_request_bus_bandwidth(CNSS_BUS_WIDTH_HIGH);
253#endif
254 netif_carrier_on(pAdapter->dev);
255 EPPING_LOG(LOG1, FL("Enabling queues"));
256 netif_tx_start_all_queues(pAdapter->dev);
257 pAdapter->started = true;
258 } else {
259 EPPING_LOG(CDF_TRACE_LEVEL_WARN,
260 "%s: pAdapter %p already started\n", __func__,
261 pAdapter);
262 }
263 return 0;
264}
265
266static int epping_register_adapter(epping_adapter_t *pAdapter)
267{
268 int ret = 0;
269
270 if ((ret = register_netdev(pAdapter->dev)) != 0) {
271 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
272 "%s: unable to register device\n",
273 pAdapter->dev->name);
274 } else {
275 pAdapter->registered = true;
276 }
277 return ret;
278}
279
280static void epping_unregister_adapter(epping_adapter_t *pAdapter)
281{
282 if (pAdapter) {
283 epping_stop_adapter(pAdapter);
284 if (pAdapter->registered) {
285 unregister_netdev(pAdapter->dev);
286 pAdapter->registered = false;
287 }
288 } else {
289 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
290 "%s: pAdapter = NULL, unable to unregister device\n",
291 __func__);
292 }
293}
294
295void epping_destroy_adapter(epping_adapter_t *pAdapter)
296{
297 struct net_device *dev = NULL;
298 epping_context_t *pEpping_ctx;
299
300 if (!pAdapter || !pAdapter->pEpping_ctx) {
301 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
302 "%s: pAdapter = NULL\n", __func__);
303 return;
304 }
305
306 dev = pAdapter->dev;
307 pEpping_ctx = pAdapter->pEpping_ctx;
308 epping_unregister_adapter(pAdapter);
309
310 cdf_spinlock_destroy(&pAdapter->data_lock);
311 cdf_softirq_timer_free(&pAdapter->epping_timer);
312 pAdapter->epping_timer_state = EPPING_TX_TIMER_STOPPED;
313
314 while (cdf_nbuf_queue_len(&pAdapter->nodrop_queue)) {
315 cdf_nbuf_t tmp_nbuf = NULL;
316 tmp_nbuf = cdf_nbuf_queue_remove(&pAdapter->nodrop_queue);
317 if (tmp_nbuf)
318 cdf_nbuf_free(tmp_nbuf);
319 }
320
321 free_netdev(dev);
322 if (!pEpping_ctx)
323 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
324 "%s: pEpping_ctx = NULL\n", __func__);
325 else
326 pEpping_ctx->epping_adapter = NULL;
327}
328
329static struct net_device_ops epping_drv_ops = {
330 .ndo_open = epping_ndev_open,
331 .ndo_stop = epping_ndev_stop,
332 .ndo_uninit = epping_ndev_uninit,
333 .ndo_start_xmit = epping_hard_start_xmit,
334 .ndo_tx_timeout = epping_tx_queue_timeout,
335 .ndo_get_stats = epping_get_stats,
336 .ndo_do_ioctl = epping_ndev_ioctl,
337 .ndo_set_mac_address = epping_set_mac_address,
338 .ndo_select_queue = NULL,
339};
340
341#define EPPING_TX_QUEUE_MAX_LEN 128 /* need to be power of 2 */
342
343epping_adapter_t *epping_add_adapter(epping_context_t *pEpping_ctx,
344 tSirMacAddr macAddr,
345 tCDF_CON_MODE device_mode)
346{
347 struct net_device *dev;
348 epping_adapter_t *pAdapter;
349
350 dev = alloc_netdev(sizeof(epping_adapter_t), "wifi%d",
351#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
352 NET_NAME_UNKNOWN,
353#endif
354 ether_setup);
355 if (dev == NULL) {
356 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
357 "%s: Cannot allocate epping_adapter_t\n", __func__);
358 return NULL;
359 }
360
361 pAdapter = netdev_priv(dev);
362 cdf_mem_zero(pAdapter, sizeof(*pAdapter));
363 pAdapter->dev = dev;
364 pAdapter->pEpping_ctx = pEpping_ctx;
365 pAdapter->device_mode = device_mode; /* station, SAP, etc */
366 cdf_mem_copy(dev->dev_addr, (void *)macAddr, sizeof(tSirMacAddr));
367 cdf_mem_copy(pAdapter->macAddressCurrent.bytes,
368 macAddr, sizeof(tSirMacAddr));
369 cdf_spinlock_init(&pAdapter->data_lock);
370 cdf_nbuf_queue_init(&pAdapter->nodrop_queue);
371 pAdapter->epping_timer_state = EPPING_TX_TIMER_STOPPED;
372 cdf_softirq_timer_init(epping_get_cdf_ctx(), &pAdapter->epping_timer,
373 epping_timer_expire, dev, CDF_TIMER_TYPE_SW);
374 dev->type = ARPHRD_IEEE80211;
375 dev->netdev_ops = &epping_drv_ops;
376 dev->watchdog_timeo = 5 * HZ; /* XXX */
377 dev->tx_queue_len = EPPING_TXBUF - 1; /* 1 for mgmt frame */
378 if (epping_register_adapter(pAdapter) == 0) {
379 EPPING_LOG(LOG1, FL("Disabling queues"));
380 netif_tx_disable(dev);
381 netif_carrier_off(dev);
382 return pAdapter;
383 } else {
384 epping_destroy_adapter(pAdapter);
385 return NULL;
386 }
387}
388
389int epping_connect_service(epping_context_t *pEpping_ctx)
390{
391 int status, i;
392 HTC_SERVICE_CONNECT_REQ connect;
393 HTC_SERVICE_CONNECT_RESP response;
394
395 cdf_mem_zero(&connect, sizeof(connect));
396 cdf_mem_zero(&response, sizeof(response));
397
398 /* these fields are the same for all service endpoints */
399 connect.EpCallbacks.pContext = pEpping_ctx;
400 connect.EpCallbacks.EpTxCompleteMultiple = epping_tx_complete_multiple;
401 connect.EpCallbacks.EpRecv = epping_rx;
402 /* epping_tx_complete use Multiple version */
403 connect.EpCallbacks.EpTxComplete = NULL;
404 connect.MaxSendQueueDepth = 64;
405
406#ifdef HIF_SDIO
407 connect.EpCallbacks.EpRecvRefill = epping_refill;
408 connect.EpCallbacks.EpSendFull =
409 epping_tx_queue_full /* ar6000_tx_queue_full */;
410#elif defined(HIF_USB) || defined(HIF_PCI)
411 connect.EpCallbacks.EpRecvRefill = NULL /* provided by HIF */;
412 connect.EpCallbacks.EpSendFull = NULL /* provided by HIF */;
413 /* disable flow control for hw flow control */
414 connect.ConnectionFlags |= HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL;
415#endif
416
417 /* connect to service */
418 connect.ServiceID = WMI_DATA_BE_SVC;
419 status = htc_connect_service(pEpping_ctx->HTCHandle, &connect, &response);
420 if (status != EOK) {
421 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
422 "Failed to connect to Endpoint Ping BE service status:%d \n",
423 status);
424 return -1;;
425 } else {
426 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
427 "eppingtest BE endpoint:%d\n", response.Endpoint);
428 }
429 pEpping_ctx->EppingEndpoint[0] = response.Endpoint;
430
431#if defined(HIF_PCI) || defined(HIF_USB)
432 connect.ServiceID = WMI_DATA_BK_SVC;
433 status = htc_connect_service(pEpping_ctx->HTCHandle, &connect, &response);
434 if (status != EOK) {
435 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
436 "Failed to connect to Endpoint Ping BK service status:%d \n",
437 status);
438 return -1;;
439 } else {
440 EPPING_LOG(CDF_TRACE_LEVEL_FATAL,
441 "eppingtest BK endpoint:%d\n", response.Endpoint);
442 }
443 pEpping_ctx->EppingEndpoint[1] = response.Endpoint;
444 /* Since we do not create other two SVC use BK endpoint
445 * for rest ACs (2, 3) */
446 for (i = 2; i < EPPING_MAX_NUM_EPIDS; i++) {
447 pEpping_ctx->EppingEndpoint[i] = response.Endpoint;
448 }
449#else
450 /* we only use one endpoint for high latenance bus.
451 * Map all AC's EPIDs to the same endpoint ID returned by HTC */
452 for (i = 0; i < EPPING_MAX_NUM_EPIDS; i++) {
453 pEpping_ctx->EppingEndpoint[i] = response.Endpoint;
454 }
455#endif
456 return 0;
457}