Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame^] | 1 | /* |
| 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 | |
| 56 | static int epping_start_adapter(epping_adapter_t *pAdapter); |
| 57 | static void epping_stop_adapter(epping_adapter_t *pAdapter); |
| 58 | |
| 59 | static 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 | |
| 80 | static 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 | |
| 90 | static 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); |
| 103 | end: |
| 104 | return ret; |
| 105 | } |
| 106 | |
| 107 | static 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); |
| 118 | end: |
| 119 | return; |
| 120 | } |
| 121 | |
| 122 | void 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); |
| 144 | end: |
| 145 | return; |
| 146 | |
| 147 | } |
| 148 | |
| 149 | int 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); |
| 162 | end: |
| 163 | return ret; |
| 164 | } |
| 165 | |
| 166 | struct 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 | |
| 179 | int 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 | |
| 216 | end: |
| 217 | return ret; |
| 218 | } |
| 219 | |
| 220 | static 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 | |
| 230 | static 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 | |
| 243 | static 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 | |
| 266 | static 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 | |
| 280 | static 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 | |
| 295 | void 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 | |
| 329 | static 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 | |
| 343 | epping_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 | |
| 389 | int 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 | } |