18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * NXP Wireless LAN device driver: USB specific handling 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2011-2020 NXP 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software file (the "File") is distributed by NXP 78c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License Version 2, June 1991 88c2ecf20Sopenharmony_ci * (the "License"). You may use, redistribute and/or modify this File in 98c2ecf20Sopenharmony_ci * accordance with the terms and conditions of the License, a copy of which 108c2ecf20Sopenharmony_ci * is available by writing to the Free Software Foundation, Inc., 118c2ecf20Sopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 128c2ecf20Sopenharmony_ci * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 158c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 168c2ecf20Sopenharmony_ci * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 178c2ecf20Sopenharmony_ci * this warranty disclaimer. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "main.h" 218c2ecf20Sopenharmony_ci#include "usb.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define USB_VERSION "1.0" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic struct mwifiex_if_ops usb_ops; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const struct usb_device_id mwifiex_usb_table[] = { 288c2ecf20Sopenharmony_ci /* 8766 */ 298c2ecf20Sopenharmony_ci {USB_DEVICE(USB8XXX_VID, USB8766_PID_1)}, 308c2ecf20Sopenharmony_ci {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8766_PID_2, 318c2ecf20Sopenharmony_ci USB_CLASS_VENDOR_SPEC, 328c2ecf20Sopenharmony_ci USB_SUBCLASS_VENDOR_SPEC, 0xff)}, 338c2ecf20Sopenharmony_ci /* 8797 */ 348c2ecf20Sopenharmony_ci {USB_DEVICE(USB8XXX_VID, USB8797_PID_1)}, 358c2ecf20Sopenharmony_ci {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8797_PID_2, 368c2ecf20Sopenharmony_ci USB_CLASS_VENDOR_SPEC, 378c2ecf20Sopenharmony_ci USB_SUBCLASS_VENDOR_SPEC, 0xff)}, 388c2ecf20Sopenharmony_ci /* 8801 */ 398c2ecf20Sopenharmony_ci {USB_DEVICE(USB8XXX_VID, USB8801_PID_1)}, 408c2ecf20Sopenharmony_ci {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8801_PID_2, 418c2ecf20Sopenharmony_ci USB_CLASS_VENDOR_SPEC, 428c2ecf20Sopenharmony_ci USB_SUBCLASS_VENDOR_SPEC, 0xff)}, 438c2ecf20Sopenharmony_ci /* 8997 */ 448c2ecf20Sopenharmony_ci {USB_DEVICE(USB8XXX_VID, USB8997_PID_1)}, 458c2ecf20Sopenharmony_ci {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8997_PID_2, 468c2ecf20Sopenharmony_ci USB_CLASS_VENDOR_SPEC, 478c2ecf20Sopenharmony_ci USB_SUBCLASS_VENDOR_SPEC, 0xff)}, 488c2ecf20Sopenharmony_ci { } /* Terminating entry */ 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, mwifiex_usb_table); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* This function handles received packet. Necessary action is taken based on 568c2ecf20Sopenharmony_ci * cmd/event/data. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistatic int mwifiex_usb_recv(struct mwifiex_adapter *adapter, 598c2ecf20Sopenharmony_ci struct sk_buff *skb, u8 ep) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci u32 recv_type; 628c2ecf20Sopenharmony_ci __le32 tmp; 638c2ecf20Sopenharmony_ci int ret; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (adapter->hs_activated) 668c2ecf20Sopenharmony_ci mwifiex_process_hs_config(adapter); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (skb->len < INTF_HEADER_LEN) { 698c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 708c2ecf20Sopenharmony_ci "%s: invalid skb->len\n", __func__); 718c2ecf20Sopenharmony_ci return -1; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci switch (ep) { 758c2ecf20Sopenharmony_ci case MWIFIEX_USB_EP_CMD_EVENT: 768c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 778c2ecf20Sopenharmony_ci "%s: EP_CMD_EVENT\n", __func__); 788c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, &tmp, INTF_HEADER_LEN); 798c2ecf20Sopenharmony_ci recv_type = le32_to_cpu(tmp); 808c2ecf20Sopenharmony_ci skb_pull(skb, INTF_HEADER_LEN); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci switch (recv_type) { 838c2ecf20Sopenharmony_ci case MWIFIEX_USB_TYPE_CMD: 848c2ecf20Sopenharmony_ci if (skb->len > MWIFIEX_SIZE_OF_CMD_BUFFER) { 858c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 868c2ecf20Sopenharmony_ci "CMD: skb->len too large\n"); 878c2ecf20Sopenharmony_ci ret = -1; 888c2ecf20Sopenharmony_ci goto exit_restore_skb; 898c2ecf20Sopenharmony_ci } else if (!adapter->curr_cmd) { 908c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, WARN, "CMD: no curr_cmd\n"); 918c2ecf20Sopenharmony_ci if (adapter->ps_state == PS_STATE_SLEEP_CFM) { 928c2ecf20Sopenharmony_ci mwifiex_process_sleep_confirm_resp( 938c2ecf20Sopenharmony_ci adapter, skb->data, 948c2ecf20Sopenharmony_ci skb->len); 958c2ecf20Sopenharmony_ci ret = 0; 968c2ecf20Sopenharmony_ci goto exit_restore_skb; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci ret = -1; 998c2ecf20Sopenharmony_ci goto exit_restore_skb; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci adapter->curr_cmd->resp_skb = skb; 1038c2ecf20Sopenharmony_ci adapter->cmd_resp_received = true; 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci case MWIFIEX_USB_TYPE_EVENT: 1068c2ecf20Sopenharmony_ci if (skb->len < sizeof(u32)) { 1078c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 1088c2ecf20Sopenharmony_ci "EVENT: skb->len too small\n"); 1098c2ecf20Sopenharmony_ci ret = -1; 1108c2ecf20Sopenharmony_ci goto exit_restore_skb; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, &tmp, sizeof(u32)); 1138c2ecf20Sopenharmony_ci adapter->event_cause = le32_to_cpu(tmp); 1148c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 1158c2ecf20Sopenharmony_ci "event_cause %#x\n", adapter->event_cause); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (skb->len > MAX_EVENT_SIZE) { 1188c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 1198c2ecf20Sopenharmony_ci "EVENT: event body too large\n"); 1208c2ecf20Sopenharmony_ci ret = -1; 1218c2ecf20Sopenharmony_ci goto exit_restore_skb; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci memcpy(adapter->event_body, skb->data + 1258c2ecf20Sopenharmony_ci MWIFIEX_EVENT_HEADER_LEN, skb->len); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci adapter->event_received = true; 1288c2ecf20Sopenharmony_ci adapter->event_skb = skb; 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci default: 1318c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 1328c2ecf20Sopenharmony_ci "unknown recv_type %#x\n", recv_type); 1338c2ecf20Sopenharmony_ci ret = -1; 1348c2ecf20Sopenharmony_ci goto exit_restore_skb; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci case MWIFIEX_USB_EP_DATA: 1388c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, "%s: EP_DATA\n", __func__); 1398c2ecf20Sopenharmony_ci if (skb->len > MWIFIEX_RX_DATA_BUF_SIZE) { 1408c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 1418c2ecf20Sopenharmony_ci "DATA: skb->len too large\n"); 1428c2ecf20Sopenharmony_ci return -1; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci skb_queue_tail(&adapter->rx_data_q, skb); 1468c2ecf20Sopenharmony_ci adapter->data_received = true; 1478c2ecf20Sopenharmony_ci atomic_inc(&adapter->rx_pending); 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci default: 1508c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 1518c2ecf20Sopenharmony_ci "%s: unknown endport %#x\n", __func__, ep); 1528c2ecf20Sopenharmony_ci return -1; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return -EINPROGRESS; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ciexit_restore_skb: 1588c2ecf20Sopenharmony_ci /* The buffer will be reused for further cmds/events */ 1598c2ecf20Sopenharmony_ci skb_push(skb, INTF_HEADER_LEN); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void mwifiex_usb_rx_complete(struct urb *urb) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct urb_context *context = (struct urb_context *)urb->context; 1678c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = context->adapter; 1688c2ecf20Sopenharmony_ci struct sk_buff *skb = context->skb; 1698c2ecf20Sopenharmony_ci struct usb_card_rec *card; 1708c2ecf20Sopenharmony_ci int recv_length = urb->actual_length; 1718c2ecf20Sopenharmony_ci int size, status; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (!adapter || !adapter->card) { 1748c2ecf20Sopenharmony_ci pr_err("mwifiex adapter or card structure is not valid\n"); 1758c2ecf20Sopenharmony_ci return; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci card = (struct usb_card_rec *)adapter->card; 1798c2ecf20Sopenharmony_ci if (card->rx_cmd_ep == context->ep) 1808c2ecf20Sopenharmony_ci atomic_dec(&card->rx_cmd_urb_pending); 1818c2ecf20Sopenharmony_ci else 1828c2ecf20Sopenharmony_ci atomic_dec(&card->rx_data_urb_pending); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (recv_length) { 1858c2ecf20Sopenharmony_ci if (urb->status || 1868c2ecf20Sopenharmony_ci test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags)) { 1878c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 1888c2ecf20Sopenharmony_ci "URB status is failed: %d\n", urb->status); 1898c2ecf20Sopenharmony_ci /* Do not free skb in case of command ep */ 1908c2ecf20Sopenharmony_ci if (card->rx_cmd_ep != context->ep) 1918c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1928c2ecf20Sopenharmony_ci goto setup_for_next; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci if (skb->len > recv_length) 1958c2ecf20Sopenharmony_ci skb_trim(skb, recv_length); 1968c2ecf20Sopenharmony_ci else 1978c2ecf20Sopenharmony_ci skb_put(skb, recv_length - skb->len); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci status = mwifiex_usb_recv(adapter, skb, context->ep); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 2028c2ecf20Sopenharmony_ci "info: recv_length=%d, status=%d\n", 2038c2ecf20Sopenharmony_ci recv_length, status); 2048c2ecf20Sopenharmony_ci if (status == -EINPROGRESS) { 2058c2ecf20Sopenharmony_ci mwifiex_queue_main_work(adapter); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* urb for data_ep is re-submitted now; 2088c2ecf20Sopenharmony_ci * urb for cmd_ep will be re-submitted in callback 2098c2ecf20Sopenharmony_ci * mwifiex_usb_recv_complete 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci if (card->rx_cmd_ep == context->ep) 2128c2ecf20Sopenharmony_ci return; 2138c2ecf20Sopenharmony_ci } else { 2148c2ecf20Sopenharmony_ci if (status == -1) 2158c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 2168c2ecf20Sopenharmony_ci "received data processing failed!\n"); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* Do not free skb in case of command ep */ 2198c2ecf20Sopenharmony_ci if (card->rx_cmd_ep != context->ep) 2208c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } else if (urb->status) { 2238c2ecf20Sopenharmony_ci if (!test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { 2248c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, FATAL, 2258c2ecf20Sopenharmony_ci "Card is removed: %d\n", urb->status); 2268c2ecf20Sopenharmony_ci set_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2298c2ecf20Sopenharmony_ci return; 2308c2ecf20Sopenharmony_ci } else { 2318c2ecf20Sopenharmony_ci /* Do not free skb in case of command ep */ 2328c2ecf20Sopenharmony_ci if (card->rx_cmd_ep != context->ep) 2338c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* fall through setup_for_next */ 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cisetup_for_next: 2398c2ecf20Sopenharmony_ci if (card->rx_cmd_ep == context->ep) 2408c2ecf20Sopenharmony_ci size = MWIFIEX_RX_CMD_BUF_SIZE; 2418c2ecf20Sopenharmony_ci else 2428c2ecf20Sopenharmony_ci size = MWIFIEX_RX_DATA_BUF_SIZE; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (card->rx_cmd_ep == context->ep) { 2458c2ecf20Sopenharmony_ci mwifiex_usb_submit_rx_urb(context, size); 2468c2ecf20Sopenharmony_ci } else { 2478c2ecf20Sopenharmony_ci if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING) { 2488c2ecf20Sopenharmony_ci mwifiex_usb_submit_rx_urb(context, size); 2498c2ecf20Sopenharmony_ci } else { 2508c2ecf20Sopenharmony_ci context->skb = NULL; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void mwifiex_usb_tx_complete(struct urb *urb) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct urb_context *context = (struct urb_context *)(urb->context); 2608c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = context->adapter; 2618c2ecf20Sopenharmony_ci struct usb_card_rec *card = adapter->card; 2628c2ecf20Sopenharmony_ci struct usb_tx_data_port *port; 2638c2ecf20Sopenharmony_ci int i; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 2668c2ecf20Sopenharmony_ci "%s: status: %d\n", __func__, urb->status); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (context->ep == card->tx_cmd_ep) { 2698c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, CMD, 2708c2ecf20Sopenharmony_ci "%s: CMD\n", __func__); 2718c2ecf20Sopenharmony_ci atomic_dec(&card->tx_cmd_urb_pending); 2728c2ecf20Sopenharmony_ci adapter->cmd_sent = false; 2738c2ecf20Sopenharmony_ci } else { 2748c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 2758c2ecf20Sopenharmony_ci "%s: DATA\n", __func__); 2768c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, context->skb, 0, 2778c2ecf20Sopenharmony_ci urb->status ? -1 : 0); 2788c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { 2798c2ecf20Sopenharmony_ci port = &card->port[i]; 2808c2ecf20Sopenharmony_ci if (context->ep == port->tx_data_ep) { 2818c2ecf20Sopenharmony_ci atomic_dec(&port->tx_data_urb_pending); 2828c2ecf20Sopenharmony_ci port->block_status = false; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci adapter->data_sent = false; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (card->mc_resync_flag) 2908c2ecf20Sopenharmony_ci mwifiex_multi_chan_resync(adapter); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci mwifiex_queue_main_work(adapter); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = ctx->adapter; 3008c2ecf20Sopenharmony_ci struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { 3038c2ecf20Sopenharmony_ci if (card->rx_cmd_ep == ctx->ep) { 3048c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "%s: free rx_cmd skb\n", 3058c2ecf20Sopenharmony_ci __func__); 3068c2ecf20Sopenharmony_ci dev_kfree_skb_any(ctx->skb); 3078c2ecf20Sopenharmony_ci ctx->skb = NULL; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 3108c2ecf20Sopenharmony_ci "%s: card removed/suspended, EP %d rx_cmd URB submit skipped\n", 3118c2ecf20Sopenharmony_ci __func__, ctx->ep); 3128c2ecf20Sopenharmony_ci return -1; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (card->rx_cmd_ep != ctx->ep) { 3168c2ecf20Sopenharmony_ci ctx->skb = dev_alloc_skb(size); 3178c2ecf20Sopenharmony_ci if (!ctx->skb) { 3188c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 3198c2ecf20Sopenharmony_ci "%s: dev_alloc_skb failed\n", __func__); 3208c2ecf20Sopenharmony_ci return -ENOMEM; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (card->rx_cmd_ep == ctx->ep && 3258c2ecf20Sopenharmony_ci card->rx_cmd_ep_type == USB_ENDPOINT_XFER_INT) 3268c2ecf20Sopenharmony_ci usb_fill_int_urb(ctx->urb, card->udev, 3278c2ecf20Sopenharmony_ci usb_rcvintpipe(card->udev, ctx->ep), 3288c2ecf20Sopenharmony_ci ctx->skb->data, size, mwifiex_usb_rx_complete, 3298c2ecf20Sopenharmony_ci (void *)ctx, card->rx_cmd_interval); 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci usb_fill_bulk_urb(ctx->urb, card->udev, 3328c2ecf20Sopenharmony_ci usb_rcvbulkpipe(card->udev, ctx->ep), 3338c2ecf20Sopenharmony_ci ctx->skb->data, size, mwifiex_usb_rx_complete, 3348c2ecf20Sopenharmony_ci (void *)ctx); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (card->rx_cmd_ep == ctx->ep) 3378c2ecf20Sopenharmony_ci atomic_inc(&card->rx_cmd_urb_pending); 3388c2ecf20Sopenharmony_ci else 3398c2ecf20Sopenharmony_ci atomic_inc(&card->rx_data_urb_pending); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (usb_submit_urb(ctx->urb, GFP_ATOMIC)) { 3428c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "usb_submit_urb failed\n"); 3438c2ecf20Sopenharmony_ci dev_kfree_skb_any(ctx->skb); 3448c2ecf20Sopenharmony_ci ctx->skb = NULL; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (card->rx_cmd_ep == ctx->ep) 3478c2ecf20Sopenharmony_ci atomic_dec(&card->rx_cmd_urb_pending); 3488c2ecf20Sopenharmony_ci else 3498c2ecf20Sopenharmony_ci atomic_dec(&card->rx_data_urb_pending); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return -1; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void mwifiex_usb_free(struct usb_card_rec *card) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct usb_tx_data_port *port; 3608c2ecf20Sopenharmony_ci int i, j; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) 3638c2ecf20Sopenharmony_ci usb_kill_urb(card->rx_cmd.urb); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci usb_free_urb(card->rx_cmd.urb); 3668c2ecf20Sopenharmony_ci card->rx_cmd.urb = NULL; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (atomic_read(&card->rx_data_urb_pending)) 3698c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) 3708c2ecf20Sopenharmony_ci if (card->rx_data_list[i].urb) 3718c2ecf20Sopenharmony_ci usb_kill_urb(card->rx_data_list[i].urb); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { 3748c2ecf20Sopenharmony_ci usb_free_urb(card->rx_data_list[i].urb); 3758c2ecf20Sopenharmony_ci card->rx_data_list[i].urb = NULL; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { 3798c2ecf20Sopenharmony_ci port = &card->port[i]; 3808c2ecf20Sopenharmony_ci for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { 3818c2ecf20Sopenharmony_ci usb_kill_urb(port->tx_data_list[j].urb); 3828c2ecf20Sopenharmony_ci usb_free_urb(port->tx_data_list[j].urb); 3838c2ecf20Sopenharmony_ci port->tx_data_list[j].urb = NULL; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci usb_free_urb(card->tx_cmd.urb); 3888c2ecf20Sopenharmony_ci card->tx_cmd.urb = NULL; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* This function probes an mwifiex device and registers it. It allocates 3948c2ecf20Sopenharmony_ci * the card structure, initiates the device registration and initialization 3958c2ecf20Sopenharmony_ci * procedure by adding a logical interface. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_cistatic int mwifiex_usb_probe(struct usb_interface *intf, 3988c2ecf20Sopenharmony_ci const struct usb_device_id *id) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 4018c2ecf20Sopenharmony_ci struct usb_host_interface *iface_desc = intf->cur_altsetting; 4028c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *epd; 4038c2ecf20Sopenharmony_ci int ret, i; 4048c2ecf20Sopenharmony_ci struct usb_card_rec *card; 4058c2ecf20Sopenharmony_ci u16 id_vendor, id_product, bcd_device; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci card = devm_kzalloc(&intf->dev, sizeof(*card), GFP_KERNEL); 4088c2ecf20Sopenharmony_ci if (!card) 4098c2ecf20Sopenharmony_ci return -ENOMEM; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci init_completion(&card->fw_done); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci id_vendor = le16_to_cpu(udev->descriptor.idVendor); 4148c2ecf20Sopenharmony_ci id_product = le16_to_cpu(udev->descriptor.idProduct); 4158c2ecf20Sopenharmony_ci bcd_device = le16_to_cpu(udev->descriptor.bcdDevice); 4168c2ecf20Sopenharmony_ci pr_debug("info: VID/PID = %X/%X, Boot2 version = %X\n", 4178c2ecf20Sopenharmony_ci id_vendor, id_product, bcd_device); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* PID_1 is used for firmware downloading only */ 4208c2ecf20Sopenharmony_ci switch (id_product) { 4218c2ecf20Sopenharmony_ci case USB8766_PID_1: 4228c2ecf20Sopenharmony_ci case USB8797_PID_1: 4238c2ecf20Sopenharmony_ci case USB8801_PID_1: 4248c2ecf20Sopenharmony_ci case USB8997_PID_1: 4258c2ecf20Sopenharmony_ci card->usb_boot_state = USB8XXX_FW_DNLD; 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci case USB8766_PID_2: 4288c2ecf20Sopenharmony_ci case USB8797_PID_2: 4298c2ecf20Sopenharmony_ci case USB8801_PID_2: 4308c2ecf20Sopenharmony_ci case USB8997_PID_2: 4318c2ecf20Sopenharmony_ci card->usb_boot_state = USB8XXX_FW_READY; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci default: 4348c2ecf20Sopenharmony_ci pr_warn("unknown id_product %#x\n", id_product); 4358c2ecf20Sopenharmony_ci card->usb_boot_state = USB8XXX_FW_DNLD; 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci card->udev = udev; 4408c2ecf20Sopenharmony_ci card->intf = intf; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci pr_debug("info: bcdUSB=%#x Device Class=%#x SubClass=%#x Protocol=%#x\n", 4438c2ecf20Sopenharmony_ci le16_to_cpu(udev->descriptor.bcdUSB), 4448c2ecf20Sopenharmony_ci udev->descriptor.bDeviceClass, 4458c2ecf20Sopenharmony_ci udev->descriptor.bDeviceSubClass, 4468c2ecf20Sopenharmony_ci udev->descriptor.bDeviceProtocol); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 4498c2ecf20Sopenharmony_ci epd = &iface_desc->endpoint[i].desc; 4508c2ecf20Sopenharmony_ci if (usb_endpoint_dir_in(epd) && 4518c2ecf20Sopenharmony_ci usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && 4528c2ecf20Sopenharmony_ci (usb_endpoint_xfer_bulk(epd) || 4538c2ecf20Sopenharmony_ci usb_endpoint_xfer_int(epd))) { 4548c2ecf20Sopenharmony_ci card->rx_cmd_ep_type = usb_endpoint_type(epd); 4558c2ecf20Sopenharmony_ci card->rx_cmd_interval = epd->bInterval; 4568c2ecf20Sopenharmony_ci pr_debug("info: Rx CMD/EVT:: max pkt size: %d, addr: %d, ep_type: %d\n", 4578c2ecf20Sopenharmony_ci le16_to_cpu(epd->wMaxPacketSize), 4588c2ecf20Sopenharmony_ci epd->bEndpointAddress, card->rx_cmd_ep_type); 4598c2ecf20Sopenharmony_ci card->rx_cmd_ep = usb_endpoint_num(epd); 4608c2ecf20Sopenharmony_ci atomic_set(&card->rx_cmd_urb_pending, 0); 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci if (usb_endpoint_dir_in(epd) && 4638c2ecf20Sopenharmony_ci usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && 4648c2ecf20Sopenharmony_ci usb_endpoint_xfer_bulk(epd)) { 4658c2ecf20Sopenharmony_ci pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", 4668c2ecf20Sopenharmony_ci le16_to_cpu(epd->wMaxPacketSize), 4678c2ecf20Sopenharmony_ci epd->bEndpointAddress); 4688c2ecf20Sopenharmony_ci card->rx_data_ep = usb_endpoint_num(epd); 4698c2ecf20Sopenharmony_ci atomic_set(&card->rx_data_urb_pending, 0); 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci if (usb_endpoint_dir_out(epd) && 4728c2ecf20Sopenharmony_ci usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && 4738c2ecf20Sopenharmony_ci usb_endpoint_xfer_bulk(epd)) { 4748c2ecf20Sopenharmony_ci pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", 4758c2ecf20Sopenharmony_ci le16_to_cpu(epd->wMaxPacketSize), 4768c2ecf20Sopenharmony_ci epd->bEndpointAddress); 4778c2ecf20Sopenharmony_ci card->port[0].tx_data_ep = usb_endpoint_num(epd); 4788c2ecf20Sopenharmony_ci atomic_set(&card->port[0].tx_data_urb_pending, 0); 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci if (usb_endpoint_dir_out(epd) && 4818c2ecf20Sopenharmony_ci usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA_CH2 && 4828c2ecf20Sopenharmony_ci usb_endpoint_xfer_bulk(epd)) { 4838c2ecf20Sopenharmony_ci pr_debug("info: bulk OUT chan2:\t" 4848c2ecf20Sopenharmony_ci "max pkt size: %d, addr: %d\n", 4858c2ecf20Sopenharmony_ci le16_to_cpu(epd->wMaxPacketSize), 4868c2ecf20Sopenharmony_ci epd->bEndpointAddress); 4878c2ecf20Sopenharmony_ci card->port[1].tx_data_ep = usb_endpoint_num(epd); 4888c2ecf20Sopenharmony_ci atomic_set(&card->port[1].tx_data_urb_pending, 0); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci if (usb_endpoint_dir_out(epd) && 4918c2ecf20Sopenharmony_ci usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && 4928c2ecf20Sopenharmony_ci (usb_endpoint_xfer_bulk(epd) || 4938c2ecf20Sopenharmony_ci usb_endpoint_xfer_int(epd))) { 4948c2ecf20Sopenharmony_ci card->tx_cmd_ep_type = usb_endpoint_type(epd); 4958c2ecf20Sopenharmony_ci card->tx_cmd_interval = epd->bInterval; 4968c2ecf20Sopenharmony_ci pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", 4978c2ecf20Sopenharmony_ci le16_to_cpu(epd->wMaxPacketSize), 4988c2ecf20Sopenharmony_ci epd->bEndpointAddress); 4998c2ecf20Sopenharmony_ci pr_debug("info: Tx CMD:: max pkt size: %d, addr: %d, ep_type: %d\n", 5008c2ecf20Sopenharmony_ci le16_to_cpu(epd->wMaxPacketSize), 5018c2ecf20Sopenharmony_ci epd->bEndpointAddress, card->tx_cmd_ep_type); 5028c2ecf20Sopenharmony_ci card->tx_cmd_ep = usb_endpoint_num(epd); 5038c2ecf20Sopenharmony_ci atomic_set(&card->tx_cmd_urb_pending, 0); 5048c2ecf20Sopenharmony_ci card->bulk_out_maxpktsize = 5058c2ecf20Sopenharmony_ci le16_to_cpu(epd->wMaxPacketSize); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci switch (card->usb_boot_state) { 5108c2ecf20Sopenharmony_ci case USB8XXX_FW_DNLD: 5118c2ecf20Sopenharmony_ci /* Reject broken descriptors. */ 5128c2ecf20Sopenharmony_ci if (!card->rx_cmd_ep || !card->tx_cmd_ep) 5138c2ecf20Sopenharmony_ci return -ENODEV; 5148c2ecf20Sopenharmony_ci if (card->bulk_out_maxpktsize == 0) 5158c2ecf20Sopenharmony_ci return -ENODEV; 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci case USB8XXX_FW_READY: 5188c2ecf20Sopenharmony_ci /* Assume the driver can handle missing endpoints for now. */ 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci default: 5218c2ecf20Sopenharmony_ci WARN_ON(1); 5228c2ecf20Sopenharmony_ci return -ENODEV; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci usb_set_intfdata(intf, card); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci ret = mwifiex_add_card(card, &card->fw_done, &usb_ops, 5288c2ecf20Sopenharmony_ci MWIFIEX_USB, &card->udev->dev); 5298c2ecf20Sopenharmony_ci if (ret) { 5308c2ecf20Sopenharmony_ci pr_err("%s: mwifiex_add_card failed: %d\n", __func__, ret); 5318c2ecf20Sopenharmony_ci usb_reset_device(udev); 5328c2ecf20Sopenharmony_ci return ret; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci usb_get_dev(udev); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/* Kernel needs to suspend all functions separately. Therefore all 5418c2ecf20Sopenharmony_ci * registered functions must have drivers with suspend and resume 5428c2ecf20Sopenharmony_ci * methods. Failing that the kernel simply removes the whole card. 5438c2ecf20Sopenharmony_ci * 5448c2ecf20Sopenharmony_ci * If already not suspended, this function allocates and sends a 5458c2ecf20Sopenharmony_ci * 'host sleep activate' request to the firmware and turns off the traffic. 5468c2ecf20Sopenharmony_ci */ 5478c2ecf20Sopenharmony_cistatic int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct usb_card_rec *card = usb_get_intfdata(intf); 5508c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 5518c2ecf20Sopenharmony_ci struct usb_tx_data_port *port; 5528c2ecf20Sopenharmony_ci int i, j; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Might still be loading firmware */ 5558c2ecf20Sopenharmony_ci wait_for_completion(&card->fw_done); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci adapter = card->adapter; 5588c2ecf20Sopenharmony_ci if (!adapter) { 5598c2ecf20Sopenharmony_ci dev_err(&intf->dev, "card is not valid\n"); 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (unlikely(test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags))) 5648c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, WARN, 5658c2ecf20Sopenharmony_ci "Device already suspended\n"); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Enable the Host Sleep */ 5688c2ecf20Sopenharmony_ci if (!mwifiex_enable_hs(adapter)) { 5698c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 5708c2ecf20Sopenharmony_ci "cmd: failed to suspend\n"); 5718c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); 5728c2ecf20Sopenharmony_ci return -EFAULT; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* 'MWIFIEX_IS_SUSPENDED' bit indicates device is suspended. 5778c2ecf20Sopenharmony_ci * It must be set here before the usb_kill_urb() calls. Reason 5788c2ecf20Sopenharmony_ci * is in the complete handlers, urb->status(= -ENOENT) and 5798c2ecf20Sopenharmony_ci * this flag is used in combination to distinguish between a 5808c2ecf20Sopenharmony_ci * 'suspended' state and a 'disconnect' one. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ci set_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); 5838c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) 5868c2ecf20Sopenharmony_ci usb_kill_urb(card->rx_cmd.urb); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (atomic_read(&card->rx_data_urb_pending)) 5898c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) 5908c2ecf20Sopenharmony_ci if (card->rx_data_list[i].urb) 5918c2ecf20Sopenharmony_ci usb_kill_urb(card->rx_data_list[i].urb); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { 5948c2ecf20Sopenharmony_ci port = &card->port[i]; 5958c2ecf20Sopenharmony_ci for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { 5968c2ecf20Sopenharmony_ci if (port->tx_data_list[j].urb) 5978c2ecf20Sopenharmony_ci usb_kill_urb(port->tx_data_list[j].urb); 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (card->tx_cmd.urb) 6028c2ecf20Sopenharmony_ci usb_kill_urb(card->tx_cmd.urb); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci/* Kernel needs to suspend all functions separately. Therefore all 6088c2ecf20Sopenharmony_ci * registered functions must have drivers with suspend and resume 6098c2ecf20Sopenharmony_ci * methods. Failing that the kernel simply removes the whole card. 6108c2ecf20Sopenharmony_ci * 6118c2ecf20Sopenharmony_ci * If already not resumed, this function turns on the traffic and 6128c2ecf20Sopenharmony_ci * sends a 'host sleep cancel' request to the firmware. 6138c2ecf20Sopenharmony_ci */ 6148c2ecf20Sopenharmony_cistatic int mwifiex_usb_resume(struct usb_interface *intf) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct usb_card_rec *card = usb_get_intfdata(intf); 6178c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 6188c2ecf20Sopenharmony_ci int i; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (!card->adapter) { 6218c2ecf20Sopenharmony_ci dev_err(&intf->dev, "%s: card->adapter is NULL\n", 6228c2ecf20Sopenharmony_ci __func__); 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci adapter = card->adapter; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (unlikely(!test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags))) { 6288c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, WARN, 6298c2ecf20Sopenharmony_ci "Device already resumed\n"); 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* Indicate device resumed. The netdev queue will be resumed only 6348c2ecf20Sopenharmony_ci * after the urbs have been re-submitted 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (!atomic_read(&card->rx_data_urb_pending)) 6398c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) 6408c2ecf20Sopenharmony_ci mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], 6418c2ecf20Sopenharmony_ci MWIFIEX_RX_DATA_BUF_SIZE); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (!atomic_read(&card->rx_cmd_urb_pending)) { 6448c2ecf20Sopenharmony_ci card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); 6458c2ecf20Sopenharmony_ci if (card->rx_cmd.skb) 6468c2ecf20Sopenharmony_ci mwifiex_usb_submit_rx_urb(&card->rx_cmd, 6478c2ecf20Sopenharmony_ci MWIFIEX_RX_CMD_BUF_SIZE); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* Disable Host Sleep */ 6518c2ecf20Sopenharmony_ci if (adapter->hs_activated) 6528c2ecf20Sopenharmony_ci mwifiex_cancel_hs(mwifiex_get_priv(adapter, 6538c2ecf20Sopenharmony_ci MWIFIEX_BSS_ROLE_ANY), 6548c2ecf20Sopenharmony_ci MWIFIEX_ASYNC_CMD); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci return 0; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic void mwifiex_usb_disconnect(struct usb_interface *intf) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci struct usb_card_rec *card = usb_get_intfdata(intf); 6628c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci wait_for_completion(&card->fw_done); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci adapter = card->adapter; 6678c2ecf20Sopenharmony_ci if (!adapter || !adapter->priv_num) 6688c2ecf20Sopenharmony_ci return; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (card->udev->state != USB_STATE_NOTATTACHED && !adapter->mfg_mode) { 6718c2ecf20Sopenharmony_ci mwifiex_deauthenticate_all(adapter); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, 6748c2ecf20Sopenharmony_ci MWIFIEX_BSS_ROLE_ANY), 6758c2ecf20Sopenharmony_ci MWIFIEX_FUNC_SHUTDOWN); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, FATAL, 6798c2ecf20Sopenharmony_ci "%s: removing card\n", __func__); 6808c2ecf20Sopenharmony_ci mwifiex_remove_card(adapter); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci usb_put_dev(interface_to_usbdev(intf)); 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic void mwifiex_usb_coredump(struct device *dev) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci struct usb_interface *intf = to_usb_interface(dev); 6888c2ecf20Sopenharmony_ci struct usb_card_rec *card = usb_get_intfdata(intf); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci mwifiex_fw_dump_event(mwifiex_get_priv(card->adapter, 6918c2ecf20Sopenharmony_ci MWIFIEX_BSS_ROLE_ANY)); 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic struct usb_driver mwifiex_usb_driver = { 6958c2ecf20Sopenharmony_ci .name = "mwifiex_usb", 6968c2ecf20Sopenharmony_ci .probe = mwifiex_usb_probe, 6978c2ecf20Sopenharmony_ci .disconnect = mwifiex_usb_disconnect, 6988c2ecf20Sopenharmony_ci .id_table = mwifiex_usb_table, 6998c2ecf20Sopenharmony_ci .suspend = mwifiex_usb_suspend, 7008c2ecf20Sopenharmony_ci .resume = mwifiex_usb_resume, 7018c2ecf20Sopenharmony_ci .soft_unbind = 1, 7028c2ecf20Sopenharmony_ci .drvwrap.driver = { 7038c2ecf20Sopenharmony_ci .coredump = mwifiex_usb_coredump, 7048c2ecf20Sopenharmony_ci }, 7058c2ecf20Sopenharmony_ci}; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic int mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, 7088c2ecf20Sopenharmony_ci u32 *len, u8 ep, u32 timeout) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct usb_card_rec *card = adapter->card; 7118c2ecf20Sopenharmony_ci int actual_length, ret; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (!(*len % card->bulk_out_maxpktsize)) 7148c2ecf20Sopenharmony_ci (*len)++; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* Send the data block */ 7178c2ecf20Sopenharmony_ci ret = usb_bulk_msg(card->udev, usb_sndbulkpipe(card->udev, ep), pbuf, 7188c2ecf20Sopenharmony_ci *len, &actual_length, timeout); 7198c2ecf20Sopenharmony_ci if (ret) { 7208c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 7218c2ecf20Sopenharmony_ci "usb_bulk_msg for tx failed: %d\n", ret); 7228c2ecf20Sopenharmony_ci return ret; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci *len = actual_length; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci return ret; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, 7318c2ecf20Sopenharmony_ci u32 *len, u8 ep, u32 timeout) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct usb_card_rec *card = adapter->card; 7348c2ecf20Sopenharmony_ci int actual_length, ret; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci /* Receive the data response */ 7378c2ecf20Sopenharmony_ci ret = usb_bulk_msg(card->udev, usb_rcvbulkpipe(card->udev, ep), pbuf, 7388c2ecf20Sopenharmony_ci *len, &actual_length, timeout); 7398c2ecf20Sopenharmony_ci if (ret) { 7408c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 7418c2ecf20Sopenharmony_ci "usb_bulk_msg for rx failed: %d\n", ret); 7428c2ecf20Sopenharmony_ci return ret; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci *len = actual_length; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci return ret; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic void mwifiex_usb_port_resync(struct mwifiex_adapter *adapter) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci struct usb_card_rec *card = adapter->card; 7538c2ecf20Sopenharmony_ci u8 active_port = MWIFIEX_USB_EP_DATA; 7548c2ecf20Sopenharmony_ci struct mwifiex_private *priv = NULL; 7558c2ecf20Sopenharmony_ci int i; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (adapter->usb_mc_status) { 7588c2ecf20Sopenharmony_ci for (i = 0; i < adapter->priv_num; i++) { 7598c2ecf20Sopenharmony_ci priv = adapter->priv[i]; 7608c2ecf20Sopenharmony_ci if (!priv) 7618c2ecf20Sopenharmony_ci continue; 7628c2ecf20Sopenharmony_ci if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && 7638c2ecf20Sopenharmony_ci !priv->bss_started) || 7648c2ecf20Sopenharmony_ci (priv->bss_role == MWIFIEX_BSS_ROLE_STA && 7658c2ecf20Sopenharmony_ci !priv->media_connected)) 7668c2ecf20Sopenharmony_ci priv->usb_port = MWIFIEX_USB_EP_DATA; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) 7698c2ecf20Sopenharmony_ci card->port[i].block_status = false; 7708c2ecf20Sopenharmony_ci } else { 7718c2ecf20Sopenharmony_ci for (i = 0; i < adapter->priv_num; i++) { 7728c2ecf20Sopenharmony_ci priv = adapter->priv[i]; 7738c2ecf20Sopenharmony_ci if (!priv) 7748c2ecf20Sopenharmony_ci continue; 7758c2ecf20Sopenharmony_ci if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && 7768c2ecf20Sopenharmony_ci priv->bss_started) || 7778c2ecf20Sopenharmony_ci (priv->bss_role == MWIFIEX_BSS_ROLE_STA && 7788c2ecf20Sopenharmony_ci priv->media_connected)) { 7798c2ecf20Sopenharmony_ci active_port = priv->usb_port; 7808c2ecf20Sopenharmony_ci break; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci for (i = 0; i < adapter->priv_num; i++) { 7848c2ecf20Sopenharmony_ci priv = adapter->priv[i]; 7858c2ecf20Sopenharmony_ci if (priv) 7868c2ecf20Sopenharmony_ci priv->usb_port = active_port; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { 7898c2ecf20Sopenharmony_ci if (active_port == card->port[i].tx_data_ep) 7908c2ecf20Sopenharmony_ci card->port[i].block_status = false; 7918c2ecf20Sopenharmony_ci else 7928c2ecf20Sopenharmony_ci card->port[i].block_status = true; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic bool mwifiex_usb_is_port_ready(struct mwifiex_private *priv) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci struct usb_card_rec *card = priv->adapter->card; 8008c2ecf20Sopenharmony_ci int idx; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { 8038c2ecf20Sopenharmony_ci if (priv->usb_port == card->port[idx].tx_data_ep) 8048c2ecf20Sopenharmony_ci return !card->port[idx].block_status; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci return false; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic inline u8 mwifiex_usb_data_sent(struct mwifiex_adapter *adapter) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct usb_card_rec *card = adapter->card; 8138c2ecf20Sopenharmony_ci int i; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) 8168c2ecf20Sopenharmony_ci if (!card->port[i].block_status) 8178c2ecf20Sopenharmony_ci return false; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci return true; 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic int mwifiex_usb_construct_send_urb(struct mwifiex_adapter *adapter, 8238c2ecf20Sopenharmony_ci struct usb_tx_data_port *port, u8 ep, 8248c2ecf20Sopenharmony_ci struct urb_context *context, 8258c2ecf20Sopenharmony_ci struct sk_buff *skb_send) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct usb_card_rec *card = adapter->card; 8288c2ecf20Sopenharmony_ci int ret = -EINPROGRESS; 8298c2ecf20Sopenharmony_ci struct urb *tx_urb; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci context->adapter = adapter; 8328c2ecf20Sopenharmony_ci context->ep = ep; 8338c2ecf20Sopenharmony_ci context->skb = skb_send; 8348c2ecf20Sopenharmony_ci tx_urb = context->urb; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (ep == card->tx_cmd_ep && 8378c2ecf20Sopenharmony_ci card->tx_cmd_ep_type == USB_ENDPOINT_XFER_INT) 8388c2ecf20Sopenharmony_ci usb_fill_int_urb(tx_urb, card->udev, 8398c2ecf20Sopenharmony_ci usb_sndintpipe(card->udev, ep), skb_send->data, 8408c2ecf20Sopenharmony_ci skb_send->len, mwifiex_usb_tx_complete, 8418c2ecf20Sopenharmony_ci (void *)context, card->tx_cmd_interval); 8428c2ecf20Sopenharmony_ci else 8438c2ecf20Sopenharmony_ci usb_fill_bulk_urb(tx_urb, card->udev, 8448c2ecf20Sopenharmony_ci usb_sndbulkpipe(card->udev, ep), 8458c2ecf20Sopenharmony_ci skb_send->data, skb_send->len, 8468c2ecf20Sopenharmony_ci mwifiex_usb_tx_complete, (void *)context); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci tx_urb->transfer_flags |= URB_ZERO_PACKET; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (ep == card->tx_cmd_ep) 8518c2ecf20Sopenharmony_ci atomic_inc(&card->tx_cmd_urb_pending); 8528c2ecf20Sopenharmony_ci else 8538c2ecf20Sopenharmony_ci atomic_inc(&port->tx_data_urb_pending); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (ep != card->tx_cmd_ep && 8568c2ecf20Sopenharmony_ci atomic_read(&port->tx_data_urb_pending) == 8578c2ecf20Sopenharmony_ci MWIFIEX_TX_DATA_URB) { 8588c2ecf20Sopenharmony_ci port->block_status = true; 8598c2ecf20Sopenharmony_ci adapter->data_sent = mwifiex_usb_data_sent(adapter); 8608c2ecf20Sopenharmony_ci ret = -ENOSR; 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { 8648c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 8658c2ecf20Sopenharmony_ci "%s: usb_submit_urb failed\n", __func__); 8668c2ecf20Sopenharmony_ci if (ep == card->tx_cmd_ep) { 8678c2ecf20Sopenharmony_ci atomic_dec(&card->tx_cmd_urb_pending); 8688c2ecf20Sopenharmony_ci } else { 8698c2ecf20Sopenharmony_ci atomic_dec(&port->tx_data_urb_pending); 8708c2ecf20Sopenharmony_ci port->block_status = false; 8718c2ecf20Sopenharmony_ci adapter->data_sent = false; 8728c2ecf20Sopenharmony_ci if (port->tx_data_ix) 8738c2ecf20Sopenharmony_ci port->tx_data_ix--; 8748c2ecf20Sopenharmony_ci else 8758c2ecf20Sopenharmony_ci port->tx_data_ix = MWIFIEX_TX_DATA_URB; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci ret = -1; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci return ret; 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic int mwifiex_usb_prepare_tx_aggr_skb(struct mwifiex_adapter *adapter, 8848c2ecf20Sopenharmony_ci struct usb_tx_data_port *port, 8858c2ecf20Sopenharmony_ci struct sk_buff **skb_send) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci struct sk_buff *skb_aggr, *skb_tmp; 8888c2ecf20Sopenharmony_ci u8 *payload, pad; 8898c2ecf20Sopenharmony_ci u16 align = adapter->bus_aggr.tx_aggr_align; 8908c2ecf20Sopenharmony_ci struct mwifiex_txinfo *tx_info = NULL; 8918c2ecf20Sopenharmony_ci bool is_txinfo_set = false; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* Packets in aggr_list will be send in either skb_aggr or 8948c2ecf20Sopenharmony_ci * write complete, delete the tx_aggr timer 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ci if (port->tx_aggr.timer_cnxt.is_hold_timer_set) { 8978c2ecf20Sopenharmony_ci del_timer(&port->tx_aggr.timer_cnxt.hold_timer); 8988c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.is_hold_timer_set = false; 8998c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.hold_tmo_msecs = 0; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci skb_aggr = mwifiex_alloc_dma_align_buf(port->tx_aggr.aggr_len, 9038c2ecf20Sopenharmony_ci GFP_ATOMIC); 9048c2ecf20Sopenharmony_ci if (!skb_aggr) { 9058c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 9068c2ecf20Sopenharmony_ci "%s: alloc skb_aggr failed\n", __func__); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci while ((skb_tmp = skb_dequeue(&port->tx_aggr.aggr_list))) 9098c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb_tmp, 0, -1); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci port->tx_aggr.aggr_num = 0; 9128c2ecf20Sopenharmony_ci port->tx_aggr.aggr_len = 0; 9138c2ecf20Sopenharmony_ci return -EBUSY; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci tx_info = MWIFIEX_SKB_TXCB(skb_aggr); 9178c2ecf20Sopenharmony_ci memset(tx_info, 0, sizeof(*tx_info)); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci while ((skb_tmp = skb_dequeue(&port->tx_aggr.aggr_list))) { 9208c2ecf20Sopenharmony_ci /* padding for aligning next packet header*/ 9218c2ecf20Sopenharmony_ci pad = (align - (skb_tmp->len & (align - 1))) % align; 9228c2ecf20Sopenharmony_ci payload = skb_put(skb_aggr, skb_tmp->len + pad); 9238c2ecf20Sopenharmony_ci memcpy(payload, skb_tmp->data, skb_tmp->len); 9248c2ecf20Sopenharmony_ci if (skb_queue_empty(&port->tx_aggr.aggr_list)) { 9258c2ecf20Sopenharmony_ci /* do not padding for last packet*/ 9268c2ecf20Sopenharmony_ci *(u16 *)payload = cpu_to_le16(skb_tmp->len); 9278c2ecf20Sopenharmony_ci *(u16 *)&payload[2] = 9288c2ecf20Sopenharmony_ci cpu_to_le16(MWIFIEX_TYPE_AGGR_DATA_V2 | 0x80); 9298c2ecf20Sopenharmony_ci skb_trim(skb_aggr, skb_aggr->len - pad); 9308c2ecf20Sopenharmony_ci } else { 9318c2ecf20Sopenharmony_ci /* add aggregation interface header */ 9328c2ecf20Sopenharmony_ci *(u16 *)payload = cpu_to_le16(skb_tmp->len + pad); 9338c2ecf20Sopenharmony_ci *(u16 *)&payload[2] = 9348c2ecf20Sopenharmony_ci cpu_to_le16(MWIFIEX_TYPE_AGGR_DATA_V2); 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (!is_txinfo_set) { 9388c2ecf20Sopenharmony_ci tx_info->bss_num = MWIFIEX_SKB_TXCB(skb_tmp)->bss_num; 9398c2ecf20Sopenharmony_ci tx_info->bss_type = MWIFIEX_SKB_TXCB(skb_tmp)->bss_type; 9408c2ecf20Sopenharmony_ci is_txinfo_set = true; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci port->tx_aggr.aggr_num--; 9448c2ecf20Sopenharmony_ci port->tx_aggr.aggr_len -= (skb_tmp->len + pad); 9458c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb_tmp, 0, 0); 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci tx_info->pkt_len = skb_aggr->len - 9498c2ecf20Sopenharmony_ci (sizeof(struct txpd) + adapter->intf_hdr_len); 9508c2ecf20Sopenharmony_ci tx_info->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci port->tx_aggr.aggr_num = 0; 9538c2ecf20Sopenharmony_ci port->tx_aggr.aggr_len = 0; 9548c2ecf20Sopenharmony_ci *skb_send = skb_aggr; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci return 0; 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci/* This function prepare data packet to be send under usb tx aggregation 9608c2ecf20Sopenharmony_ci * protocol, check current usb aggregation status, link packet to aggrgation 9618c2ecf20Sopenharmony_ci * list if possible, work flow as below: 9628c2ecf20Sopenharmony_ci * (1) if only 1 packet available, add usb tx aggregation header and send. 9638c2ecf20Sopenharmony_ci * (2) if packet is able to aggregated, link it to current aggregation list. 9648c2ecf20Sopenharmony_ci * (3) if packet is not able to aggregated, aggregate and send exist packets 9658c2ecf20Sopenharmony_ci * in aggrgation list. Then, link packet in the list if there is more 9668c2ecf20Sopenharmony_ci * packet in transmit queue, otherwise try to transmit single packet. 9678c2ecf20Sopenharmony_ci */ 9688c2ecf20Sopenharmony_cistatic int mwifiex_usb_aggr_tx_data(struct mwifiex_adapter *adapter, u8 ep, 9698c2ecf20Sopenharmony_ci struct sk_buff *skb, 9708c2ecf20Sopenharmony_ci struct mwifiex_tx_param *tx_param, 9718c2ecf20Sopenharmony_ci struct usb_tx_data_port *port) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci u8 *payload, pad; 9748c2ecf20Sopenharmony_ci u16 align = adapter->bus_aggr.tx_aggr_align; 9758c2ecf20Sopenharmony_ci struct sk_buff *skb_send = NULL; 9768c2ecf20Sopenharmony_ci struct urb_context *context = NULL; 9778c2ecf20Sopenharmony_ci struct txpd *local_tx_pd = 9788c2ecf20Sopenharmony_ci (struct txpd *)((u8 *)skb->data + adapter->intf_hdr_len); 9798c2ecf20Sopenharmony_ci u8 f_send_aggr_buf = 0; 9808c2ecf20Sopenharmony_ci u8 f_send_cur_buf = 0; 9818c2ecf20Sopenharmony_ci u8 f_precopy_cur_buf = 0; 9828c2ecf20Sopenharmony_ci u8 f_postcopy_cur_buf = 0; 9838c2ecf20Sopenharmony_ci u32 timeout; 9848c2ecf20Sopenharmony_ci int ret; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci /* padding to ensure each packet alginment */ 9878c2ecf20Sopenharmony_ci pad = (align - (skb->len & (align - 1))) % align; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (tx_param && tx_param->next_pkt_len) { 9908c2ecf20Sopenharmony_ci /* next packet available in tx queue*/ 9918c2ecf20Sopenharmony_ci if (port->tx_aggr.aggr_len + skb->len + pad > 9928c2ecf20Sopenharmony_ci adapter->bus_aggr.tx_aggr_max_size) { 9938c2ecf20Sopenharmony_ci f_send_aggr_buf = 1; 9948c2ecf20Sopenharmony_ci f_postcopy_cur_buf = 1; 9958c2ecf20Sopenharmony_ci } else { 9968c2ecf20Sopenharmony_ci /* current packet could be aggregated*/ 9978c2ecf20Sopenharmony_ci f_precopy_cur_buf = 1; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (port->tx_aggr.aggr_len + skb->len + pad + 10008c2ecf20Sopenharmony_ci tx_param->next_pkt_len > 10018c2ecf20Sopenharmony_ci adapter->bus_aggr.tx_aggr_max_size || 10028c2ecf20Sopenharmony_ci port->tx_aggr.aggr_num + 2 > 10038c2ecf20Sopenharmony_ci adapter->bus_aggr.tx_aggr_max_num) { 10048c2ecf20Sopenharmony_ci /* next packet could not be aggregated 10058c2ecf20Sopenharmony_ci * send current aggregation buffer 10068c2ecf20Sopenharmony_ci */ 10078c2ecf20Sopenharmony_ci f_send_aggr_buf = 1; 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci } else { 10118c2ecf20Sopenharmony_ci /* last packet in tx queue */ 10128c2ecf20Sopenharmony_ci if (port->tx_aggr.aggr_num > 0) { 10138c2ecf20Sopenharmony_ci /* pending packets in aggregation buffer*/ 10148c2ecf20Sopenharmony_ci if (port->tx_aggr.aggr_len + skb->len + pad > 10158c2ecf20Sopenharmony_ci adapter->bus_aggr.tx_aggr_max_size) { 10168c2ecf20Sopenharmony_ci /* current packet not be able to aggregated, 10178c2ecf20Sopenharmony_ci * send aggr buffer first, then send packet. 10188c2ecf20Sopenharmony_ci */ 10198c2ecf20Sopenharmony_ci f_send_cur_buf = 1; 10208c2ecf20Sopenharmony_ci } else { 10218c2ecf20Sopenharmony_ci /* last packet, Aggregation and send */ 10228c2ecf20Sopenharmony_ci f_precopy_cur_buf = 1; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci f_send_aggr_buf = 1; 10268c2ecf20Sopenharmony_ci } else { 10278c2ecf20Sopenharmony_ci /* no pending packets in aggregation buffer, 10288c2ecf20Sopenharmony_ci * send current packet immediately 10298c2ecf20Sopenharmony_ci */ 10308c2ecf20Sopenharmony_ci f_send_cur_buf = 1; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci if (local_tx_pd->flags & MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET) { 10358c2ecf20Sopenharmony_ci /* Send NULL packet immediately*/ 10368c2ecf20Sopenharmony_ci if (f_precopy_cur_buf) { 10378c2ecf20Sopenharmony_ci if (skb_queue_empty(&port->tx_aggr.aggr_list)) { 10388c2ecf20Sopenharmony_ci f_precopy_cur_buf = 0; 10398c2ecf20Sopenharmony_ci f_send_aggr_buf = 0; 10408c2ecf20Sopenharmony_ci f_send_cur_buf = 1; 10418c2ecf20Sopenharmony_ci } else { 10428c2ecf20Sopenharmony_ci f_send_aggr_buf = 1; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci } else if (f_postcopy_cur_buf) { 10458c2ecf20Sopenharmony_ci f_send_cur_buf = 1; 10468c2ecf20Sopenharmony_ci f_postcopy_cur_buf = 0; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (f_precopy_cur_buf) { 10518c2ecf20Sopenharmony_ci skb_queue_tail(&port->tx_aggr.aggr_list, skb); 10528c2ecf20Sopenharmony_ci port->tx_aggr.aggr_len += (skb->len + pad); 10538c2ecf20Sopenharmony_ci port->tx_aggr.aggr_num++; 10548c2ecf20Sopenharmony_ci if (f_send_aggr_buf) 10558c2ecf20Sopenharmony_ci goto send_aggr_buf; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* packet will not been send immediately, 10588c2ecf20Sopenharmony_ci * set a timer to make sure it will be sent under 10598c2ecf20Sopenharmony_ci * strict time limit. Dynamically fit the timeout 10608c2ecf20Sopenharmony_ci * value, according to packets number in aggr_list 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_ci if (!port->tx_aggr.timer_cnxt.is_hold_timer_set) { 10638c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.hold_tmo_msecs = 10648c2ecf20Sopenharmony_ci MWIFIEX_USB_TX_AGGR_TMO_MIN; 10658c2ecf20Sopenharmony_ci timeout = 10668c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.hold_tmo_msecs; 10678c2ecf20Sopenharmony_ci mod_timer(&port->tx_aggr.timer_cnxt.hold_timer, 10688c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(timeout)); 10698c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.is_hold_timer_set = true; 10708c2ecf20Sopenharmony_ci } else { 10718c2ecf20Sopenharmony_ci if (port->tx_aggr.timer_cnxt.hold_tmo_msecs < 10728c2ecf20Sopenharmony_ci MWIFIEX_USB_TX_AGGR_TMO_MAX) { 10738c2ecf20Sopenharmony_ci /* Dyanmic fit timeout */ 10748c2ecf20Sopenharmony_ci timeout = 10758c2ecf20Sopenharmony_ci ++port->tx_aggr.timer_cnxt.hold_tmo_msecs; 10768c2ecf20Sopenharmony_ci mod_timer(&port->tx_aggr.timer_cnxt.hold_timer, 10778c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(timeout)); 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cisend_aggr_buf: 10838c2ecf20Sopenharmony_ci if (f_send_aggr_buf) { 10848c2ecf20Sopenharmony_ci ret = mwifiex_usb_prepare_tx_aggr_skb(adapter, port, &skb_send); 10858c2ecf20Sopenharmony_ci if (!ret) { 10868c2ecf20Sopenharmony_ci context = &port->tx_data_list[port->tx_data_ix++]; 10878c2ecf20Sopenharmony_ci ret = mwifiex_usb_construct_send_urb(adapter, port, ep, 10888c2ecf20Sopenharmony_ci context, skb_send); 10898c2ecf20Sopenharmony_ci if (ret == -1) 10908c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb_send, 10918c2ecf20Sopenharmony_ci 0, -1); 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (f_send_cur_buf) { 10968c2ecf20Sopenharmony_ci if (f_send_aggr_buf) { 10978c2ecf20Sopenharmony_ci if (atomic_read(&port->tx_data_urb_pending) >= 10988c2ecf20Sopenharmony_ci MWIFIEX_TX_DATA_URB) { 10998c2ecf20Sopenharmony_ci port->block_status = true; 11008c2ecf20Sopenharmony_ci adapter->data_sent = 11018c2ecf20Sopenharmony_ci mwifiex_usb_data_sent(adapter); 11028c2ecf20Sopenharmony_ci /* no available urb, postcopy packet*/ 11038c2ecf20Sopenharmony_ci f_postcopy_cur_buf = 1; 11048c2ecf20Sopenharmony_ci goto postcopy_cur_buf; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) 11088c2ecf20Sopenharmony_ci port->tx_data_ix = 0; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci payload = skb->data; 11128c2ecf20Sopenharmony_ci *(u16 *)&payload[2] = 11138c2ecf20Sopenharmony_ci cpu_to_le16(MWIFIEX_TYPE_AGGR_DATA_V2 | 0x80); 11148c2ecf20Sopenharmony_ci *(u16 *)payload = cpu_to_le16(skb->len); 11158c2ecf20Sopenharmony_ci skb_send = skb; 11168c2ecf20Sopenharmony_ci context = &port->tx_data_list[port->tx_data_ix++]; 11178c2ecf20Sopenharmony_ci return mwifiex_usb_construct_send_urb(adapter, port, ep, 11188c2ecf20Sopenharmony_ci context, skb_send); 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cipostcopy_cur_buf: 11228c2ecf20Sopenharmony_ci if (f_postcopy_cur_buf) { 11238c2ecf20Sopenharmony_ci skb_queue_tail(&port->tx_aggr.aggr_list, skb); 11248c2ecf20Sopenharmony_ci port->tx_aggr.aggr_len += (skb->len + pad); 11258c2ecf20Sopenharmony_ci port->tx_aggr.aggr_num++; 11268c2ecf20Sopenharmony_ci /* New aggregation begin, start timer */ 11278c2ecf20Sopenharmony_ci if (!port->tx_aggr.timer_cnxt.is_hold_timer_set) { 11288c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.hold_tmo_msecs = 11298c2ecf20Sopenharmony_ci MWIFIEX_USB_TX_AGGR_TMO_MIN; 11308c2ecf20Sopenharmony_ci timeout = port->tx_aggr.timer_cnxt.hold_tmo_msecs; 11318c2ecf20Sopenharmony_ci mod_timer(&port->tx_aggr.timer_cnxt.hold_timer, 11328c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(timeout)); 11338c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.is_hold_timer_set = true; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci return -EINPROGRESS; 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic void mwifiex_usb_tx_aggr_tmo(struct timer_list *t) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci struct urb_context *urb_cnxt = NULL; 11438c2ecf20Sopenharmony_ci struct sk_buff *skb_send = NULL; 11448c2ecf20Sopenharmony_ci struct tx_aggr_tmr_cnxt *timer_context = 11458c2ecf20Sopenharmony_ci from_timer(timer_context, t, hold_timer); 11468c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = timer_context->adapter; 11478c2ecf20Sopenharmony_ci struct usb_tx_data_port *port = timer_context->port; 11488c2ecf20Sopenharmony_ci int err = 0; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci spin_lock_bh(&port->tx_aggr_lock); 11518c2ecf20Sopenharmony_ci err = mwifiex_usb_prepare_tx_aggr_skb(adapter, port, &skb_send); 11528c2ecf20Sopenharmony_ci if (err) { 11538c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 11548c2ecf20Sopenharmony_ci "prepare tx aggr skb failed, err=%d\n", err); 11558c2ecf20Sopenharmony_ci goto unlock; 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (atomic_read(&port->tx_data_urb_pending) >= 11598c2ecf20Sopenharmony_ci MWIFIEX_TX_DATA_URB) { 11608c2ecf20Sopenharmony_ci port->block_status = true; 11618c2ecf20Sopenharmony_ci adapter->data_sent = 11628c2ecf20Sopenharmony_ci mwifiex_usb_data_sent(adapter); 11638c2ecf20Sopenharmony_ci err = -1; 11648c2ecf20Sopenharmony_ci goto done; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) 11688c2ecf20Sopenharmony_ci port->tx_data_ix = 0; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci urb_cnxt = &port->tx_data_list[port->tx_data_ix++]; 11718c2ecf20Sopenharmony_ci err = mwifiex_usb_construct_send_urb(adapter, port, port->tx_data_ep, 11728c2ecf20Sopenharmony_ci urb_cnxt, skb_send); 11738c2ecf20Sopenharmony_cidone: 11748c2ecf20Sopenharmony_ci if (err == -1) 11758c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb_send, 0, -1); 11768c2ecf20Sopenharmony_ciunlock: 11778c2ecf20Sopenharmony_ci spin_unlock_bh(&port->tx_aggr_lock); 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/* This function write a command/data packet to card. */ 11818c2ecf20Sopenharmony_cistatic int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, 11828c2ecf20Sopenharmony_ci struct sk_buff *skb, 11838c2ecf20Sopenharmony_ci struct mwifiex_tx_param *tx_param) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci struct usb_card_rec *card = adapter->card; 11868c2ecf20Sopenharmony_ci struct urb_context *context = NULL; 11878c2ecf20Sopenharmony_ci struct usb_tx_data_port *port = NULL; 11888c2ecf20Sopenharmony_ci int idx, ret; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { 11918c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 11928c2ecf20Sopenharmony_ci "%s: not allowed while suspended\n", __func__); 11938c2ecf20Sopenharmony_ci return -1; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags)) { 11978c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "%s: device removed\n", __func__); 11988c2ecf20Sopenharmony_ci return -1; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (ep == card->tx_cmd_ep) { 12048c2ecf20Sopenharmony_ci context = &card->tx_cmd; 12058c2ecf20Sopenharmony_ci } else { 12068c2ecf20Sopenharmony_ci /* get the data port structure for endpoint */ 12078c2ecf20Sopenharmony_ci for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { 12088c2ecf20Sopenharmony_ci if (ep == card->port[idx].tx_data_ep) { 12098c2ecf20Sopenharmony_ci port = &card->port[idx]; 12108c2ecf20Sopenharmony_ci if (atomic_read(&port->tx_data_urb_pending) 12118c2ecf20Sopenharmony_ci >= MWIFIEX_TX_DATA_URB) { 12128c2ecf20Sopenharmony_ci port->block_status = true; 12138c2ecf20Sopenharmony_ci adapter->data_sent = 12148c2ecf20Sopenharmony_ci mwifiex_usb_data_sent(adapter); 12158c2ecf20Sopenharmony_ci return -EBUSY; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) 12188c2ecf20Sopenharmony_ci port->tx_data_ix = 0; 12198c2ecf20Sopenharmony_ci break; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (!port) { 12248c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n"); 12258c2ecf20Sopenharmony_ci return -1; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (adapter->bus_aggr.enable) { 12298c2ecf20Sopenharmony_ci spin_lock_bh(&port->tx_aggr_lock); 12308c2ecf20Sopenharmony_ci ret = mwifiex_usb_aggr_tx_data(adapter, ep, skb, 12318c2ecf20Sopenharmony_ci tx_param, port); 12328c2ecf20Sopenharmony_ci spin_unlock_bh(&port->tx_aggr_lock); 12338c2ecf20Sopenharmony_ci return ret; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci context = &port->tx_data_list[port->tx_data_ix++]; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci return mwifiex_usb_construct_send_urb(adapter, port, ep, context, skb); 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cistatic int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; 12458c2ecf20Sopenharmony_ci struct usb_tx_data_port *port; 12468c2ecf20Sopenharmony_ci int i, j; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci card->tx_cmd.adapter = adapter; 12498c2ecf20Sopenharmony_ci card->tx_cmd.ep = card->tx_cmd_ep; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci card->tx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); 12528c2ecf20Sopenharmony_ci if (!card->tx_cmd.urb) 12538c2ecf20Sopenharmony_ci return -ENOMEM; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { 12568c2ecf20Sopenharmony_ci port = &card->port[i]; 12578c2ecf20Sopenharmony_ci if (!port->tx_data_ep) 12588c2ecf20Sopenharmony_ci continue; 12598c2ecf20Sopenharmony_ci port->tx_data_ix = 0; 12608c2ecf20Sopenharmony_ci skb_queue_head_init(&port->tx_aggr.aggr_list); 12618c2ecf20Sopenharmony_ci if (port->tx_data_ep == MWIFIEX_USB_EP_DATA) 12628c2ecf20Sopenharmony_ci port->block_status = false; 12638c2ecf20Sopenharmony_ci else 12648c2ecf20Sopenharmony_ci port->block_status = true; 12658c2ecf20Sopenharmony_ci for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { 12668c2ecf20Sopenharmony_ci port->tx_data_list[j].adapter = adapter; 12678c2ecf20Sopenharmony_ci port->tx_data_list[j].ep = port->tx_data_ep; 12688c2ecf20Sopenharmony_ci port->tx_data_list[j].urb = 12698c2ecf20Sopenharmony_ci usb_alloc_urb(0, GFP_KERNEL); 12708c2ecf20Sopenharmony_ci if (!port->tx_data_list[j].urb) 12718c2ecf20Sopenharmony_ci return -ENOMEM; 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.adapter = adapter; 12758c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.port = port; 12768c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.is_hold_timer_set = false; 12778c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.hold_tmo_msecs = 0; 12788c2ecf20Sopenharmony_ci timer_setup(&port->tx_aggr.timer_cnxt.hold_timer, 12798c2ecf20Sopenharmony_ci mwifiex_usb_tx_aggr_tmo, 0); 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci return 0; 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_cistatic int mwifiex_usb_rx_init(struct mwifiex_adapter *adapter) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; 12888c2ecf20Sopenharmony_ci int i; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci card->rx_cmd.adapter = adapter; 12918c2ecf20Sopenharmony_ci card->rx_cmd.ep = card->rx_cmd_ep; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci card->rx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); 12948c2ecf20Sopenharmony_ci if (!card->rx_cmd.urb) 12958c2ecf20Sopenharmony_ci return -ENOMEM; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); 12988c2ecf20Sopenharmony_ci if (!card->rx_cmd.skb) 12998c2ecf20Sopenharmony_ci return -ENOMEM; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci if (mwifiex_usb_submit_rx_urb(&card->rx_cmd, MWIFIEX_RX_CMD_BUF_SIZE)) 13028c2ecf20Sopenharmony_ci return -1; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { 13058c2ecf20Sopenharmony_ci card->rx_data_list[i].adapter = adapter; 13068c2ecf20Sopenharmony_ci card->rx_data_list[i].ep = card->rx_data_ep; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci card->rx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); 13098c2ecf20Sopenharmony_ci if (!card->rx_data_list[i].urb) 13108c2ecf20Sopenharmony_ci return -1; 13118c2ecf20Sopenharmony_ci if (mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], 13128c2ecf20Sopenharmony_ci MWIFIEX_RX_DATA_BUF_SIZE)) 13138c2ecf20Sopenharmony_ci return -1; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci return 0; 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci/* This function register usb device and initialize parameter. */ 13208c2ecf20Sopenharmony_cistatic int mwifiex_register_dev(struct mwifiex_adapter *adapter) 13218c2ecf20Sopenharmony_ci{ 13228c2ecf20Sopenharmony_ci struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci card->adapter = adapter; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci switch (le16_to_cpu(card->udev->descriptor.idProduct)) { 13278c2ecf20Sopenharmony_ci case USB8997_PID_1: 13288c2ecf20Sopenharmony_ci case USB8997_PID_2: 13298c2ecf20Sopenharmony_ci adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; 13308c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, USB8997_DEFAULT_FW_NAME); 13318c2ecf20Sopenharmony_ci adapter->ext_scan = true; 13328c2ecf20Sopenharmony_ci break; 13338c2ecf20Sopenharmony_ci case USB8766_PID_1: 13348c2ecf20Sopenharmony_ci case USB8766_PID_2: 13358c2ecf20Sopenharmony_ci adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; 13368c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, USB8766_DEFAULT_FW_NAME); 13378c2ecf20Sopenharmony_ci adapter->ext_scan = true; 13388c2ecf20Sopenharmony_ci break; 13398c2ecf20Sopenharmony_ci case USB8801_PID_1: 13408c2ecf20Sopenharmony_ci case USB8801_PID_2: 13418c2ecf20Sopenharmony_ci adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; 13428c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, USB8801_DEFAULT_FW_NAME); 13438c2ecf20Sopenharmony_ci adapter->ext_scan = false; 13448c2ecf20Sopenharmony_ci break; 13458c2ecf20Sopenharmony_ci case USB8797_PID_1: 13468c2ecf20Sopenharmony_ci case USB8797_PID_2: 13478c2ecf20Sopenharmony_ci default: 13488c2ecf20Sopenharmony_ci adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; 13498c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME); 13508c2ecf20Sopenharmony_ci break; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci adapter->usb_mc_status = false; 13548c2ecf20Sopenharmony_ci adapter->usb_mc_setup = false; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci return 0; 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic void mwifiex_usb_cleanup_tx_aggr(struct mwifiex_adapter *adapter) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; 13628c2ecf20Sopenharmony_ci struct usb_tx_data_port *port; 13638c2ecf20Sopenharmony_ci struct sk_buff *skb_tmp; 13648c2ecf20Sopenharmony_ci int idx; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { 13678c2ecf20Sopenharmony_ci port = &card->port[idx]; 13688c2ecf20Sopenharmony_ci if (adapter->bus_aggr.enable) 13698c2ecf20Sopenharmony_ci while ((skb_tmp = 13708c2ecf20Sopenharmony_ci skb_dequeue(&port->tx_aggr.aggr_list))) 13718c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb_tmp, 13728c2ecf20Sopenharmony_ci 0, -1); 13738c2ecf20Sopenharmony_ci if (port->tx_aggr.timer_cnxt.hold_timer.function) 13748c2ecf20Sopenharmony_ci del_timer_sync(&port->tx_aggr.timer_cnxt.hold_timer); 13758c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.is_hold_timer_set = false; 13768c2ecf20Sopenharmony_ci port->tx_aggr.timer_cnxt.hold_tmo_msecs = 0; 13778c2ecf20Sopenharmony_ci } 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cistatic void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) 13818c2ecf20Sopenharmony_ci{ 13828c2ecf20Sopenharmony_ci struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci mwifiex_usb_free(card); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci mwifiex_usb_cleanup_tx_aggr(adapter); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci card->adapter = NULL; 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_cistatic int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, 13928c2ecf20Sopenharmony_ci struct mwifiex_fw_image *fw) 13938c2ecf20Sopenharmony_ci{ 13948c2ecf20Sopenharmony_ci int ret = 0; 13958c2ecf20Sopenharmony_ci u8 *firmware = fw->fw_buf, *recv_buff; 13968c2ecf20Sopenharmony_ci u32 retries = USB8XXX_FW_MAX_RETRY + 1; 13978c2ecf20Sopenharmony_ci u32 dlen; 13988c2ecf20Sopenharmony_ci u32 fw_seqnum = 0, tlen = 0, dnld_cmd = 0; 13998c2ecf20Sopenharmony_ci struct fw_data *fwdata; 14008c2ecf20Sopenharmony_ci struct fw_sync_header sync_fw; 14018c2ecf20Sopenharmony_ci u8 check_winner = 1; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci if (!firmware) { 14048c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14058c2ecf20Sopenharmony_ci "No firmware image found! Terminating download\n"); 14068c2ecf20Sopenharmony_ci ret = -1; 14078c2ecf20Sopenharmony_ci goto fw_exit; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci /* Allocate memory for transmit */ 14118c2ecf20Sopenharmony_ci fwdata = kzalloc(FW_DNLD_TX_BUF_SIZE, GFP_KERNEL); 14128c2ecf20Sopenharmony_ci if (!fwdata) { 14138c2ecf20Sopenharmony_ci ret = -ENOMEM; 14148c2ecf20Sopenharmony_ci goto fw_exit; 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci /* Allocate memory for receive */ 14188c2ecf20Sopenharmony_ci recv_buff = kzalloc(FW_DNLD_RX_BUF_SIZE, GFP_KERNEL); 14198c2ecf20Sopenharmony_ci if (!recv_buff) { 14208c2ecf20Sopenharmony_ci ret = -ENOMEM; 14218c2ecf20Sopenharmony_ci goto cleanup; 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci do { 14258c2ecf20Sopenharmony_ci /* Send pseudo data to check winner status first */ 14268c2ecf20Sopenharmony_ci if (check_winner) { 14278c2ecf20Sopenharmony_ci memset(&fwdata->fw_hdr, 0, sizeof(struct fw_header)); 14288c2ecf20Sopenharmony_ci dlen = 0; 14298c2ecf20Sopenharmony_ci } else { 14308c2ecf20Sopenharmony_ci /* copy the header of the fw_data to get the length */ 14318c2ecf20Sopenharmony_ci memcpy(&fwdata->fw_hdr, &firmware[tlen], 14328c2ecf20Sopenharmony_ci sizeof(struct fw_header)); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci dlen = le32_to_cpu(fwdata->fw_hdr.data_len); 14358c2ecf20Sopenharmony_ci dnld_cmd = le32_to_cpu(fwdata->fw_hdr.dnld_cmd); 14368c2ecf20Sopenharmony_ci tlen += sizeof(struct fw_header); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci /* Command 7 doesn't have data length field */ 14398c2ecf20Sopenharmony_ci if (dnld_cmd == FW_CMD_7) 14408c2ecf20Sopenharmony_ci dlen = 0; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci memcpy(fwdata->data, &firmware[tlen], dlen); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci fwdata->seq_num = cpu_to_le32(fw_seqnum); 14458c2ecf20Sopenharmony_ci tlen += dlen; 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci /* If the send/receive fails or CRC occurs then retry */ 14498c2ecf20Sopenharmony_ci while (--retries) { 14508c2ecf20Sopenharmony_ci u8 *buf = (u8 *)fwdata; 14518c2ecf20Sopenharmony_ci u32 len = FW_DATA_XMIT_SIZE; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci /* send the firmware block */ 14548c2ecf20Sopenharmony_ci ret = mwifiex_write_data_sync(adapter, buf, &len, 14558c2ecf20Sopenharmony_ci MWIFIEX_USB_EP_CMD_EVENT, 14568c2ecf20Sopenharmony_ci MWIFIEX_USB_TIMEOUT); 14578c2ecf20Sopenharmony_ci if (ret) { 14588c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14598c2ecf20Sopenharmony_ci "write_data_sync: failed: %d\n", 14608c2ecf20Sopenharmony_ci ret); 14618c2ecf20Sopenharmony_ci continue; 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci buf = recv_buff; 14658c2ecf20Sopenharmony_ci len = FW_DNLD_RX_BUF_SIZE; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci /* Receive the firmware block response */ 14688c2ecf20Sopenharmony_ci ret = mwifiex_read_data_sync(adapter, buf, &len, 14698c2ecf20Sopenharmony_ci MWIFIEX_USB_EP_CMD_EVENT, 14708c2ecf20Sopenharmony_ci MWIFIEX_USB_TIMEOUT); 14718c2ecf20Sopenharmony_ci if (ret) { 14728c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14738c2ecf20Sopenharmony_ci "read_data_sync: failed: %d\n", 14748c2ecf20Sopenharmony_ci ret); 14758c2ecf20Sopenharmony_ci continue; 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci memcpy(&sync_fw, recv_buff, 14798c2ecf20Sopenharmony_ci sizeof(struct fw_sync_header)); 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci /* check 1st firmware block resp for highest bit set */ 14828c2ecf20Sopenharmony_ci if (check_winner) { 14838c2ecf20Sopenharmony_ci if (le32_to_cpu(sync_fw.cmd) & 0x80000000) { 14848c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, WARN, 14858c2ecf20Sopenharmony_ci "USB is not the winner %#x\n", 14868c2ecf20Sopenharmony_ci sync_fw.cmd); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci /* returning success */ 14898c2ecf20Sopenharmony_ci ret = 0; 14908c2ecf20Sopenharmony_ci goto cleanup; 14918c2ecf20Sopenharmony_ci } 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, 14948c2ecf20Sopenharmony_ci "start to download FW...\n"); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci check_winner = 0; 14978c2ecf20Sopenharmony_ci break; 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci /* check the firmware block response for CRC errors */ 15018c2ecf20Sopenharmony_ci if (sync_fw.cmd) { 15028c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 15038c2ecf20Sopenharmony_ci "FW received block with CRC %#x\n", 15048c2ecf20Sopenharmony_ci sync_fw.cmd); 15058c2ecf20Sopenharmony_ci ret = -1; 15068c2ecf20Sopenharmony_ci continue; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci retries = USB8XXX_FW_MAX_RETRY + 1; 15108c2ecf20Sopenharmony_ci break; 15118c2ecf20Sopenharmony_ci } 15128c2ecf20Sopenharmony_ci fw_seqnum++; 15138c2ecf20Sopenharmony_ci } while ((dnld_cmd != FW_HAS_LAST_BLOCK) && retries); 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_cicleanup: 15168c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, 15178c2ecf20Sopenharmony_ci "info: FW download over, size %d bytes\n", tlen); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci kfree(recv_buff); 15208c2ecf20Sopenharmony_ci kfree(fwdata); 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci if (retries) 15238c2ecf20Sopenharmony_ci ret = 0; 15248c2ecf20Sopenharmony_cifw_exit: 15258c2ecf20Sopenharmony_ci return ret; 15268c2ecf20Sopenharmony_ci} 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_cistatic int mwifiex_usb_dnld_fw(struct mwifiex_adapter *adapter, 15298c2ecf20Sopenharmony_ci struct mwifiex_fw_image *fw) 15308c2ecf20Sopenharmony_ci{ 15318c2ecf20Sopenharmony_ci int ret; 15328c2ecf20Sopenharmony_ci struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci if (card->usb_boot_state == USB8XXX_FW_DNLD) { 15358c2ecf20Sopenharmony_ci ret = mwifiex_prog_fw_w_helper(adapter, fw); 15368c2ecf20Sopenharmony_ci if (ret) 15378c2ecf20Sopenharmony_ci return -1; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci /* Boot state changes after successful firmware download */ 15408c2ecf20Sopenharmony_ci if (card->usb_boot_state == USB8XXX_FW_DNLD) 15418c2ecf20Sopenharmony_ci return -1; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci ret = mwifiex_usb_rx_init(adapter); 15458c2ecf20Sopenharmony_ci if (!ret) 15468c2ecf20Sopenharmony_ci ret = mwifiex_usb_tx_init(adapter); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci return ret; 15498c2ecf20Sopenharmony_ci} 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_cistatic void mwifiex_submit_rx_urb(struct mwifiex_adapter *adapter, u8 ep) 15528c2ecf20Sopenharmony_ci{ 15538c2ecf20Sopenharmony_ci struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci skb_push(card->rx_cmd.skb, INTF_HEADER_LEN); 15568c2ecf20Sopenharmony_ci if ((ep == card->rx_cmd_ep) && 15578c2ecf20Sopenharmony_ci (!atomic_read(&card->rx_cmd_urb_pending))) 15588c2ecf20Sopenharmony_ci mwifiex_usb_submit_rx_urb(&card->rx_cmd, 15598c2ecf20Sopenharmony_ci MWIFIEX_RX_CMD_BUF_SIZE); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci return; 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cistatic int mwifiex_usb_cmd_event_complete(struct mwifiex_adapter *adapter, 15658c2ecf20Sopenharmony_ci struct sk_buff *skb) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci mwifiex_submit_rx_urb(adapter, MWIFIEX_USB_EP_CMD_EVENT); 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci return 0; 15708c2ecf20Sopenharmony_ci} 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci/* This function wakes up the card. */ 15738c2ecf20Sopenharmony_cistatic int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci /* Simulation of HS_AWAKE event */ 15768c2ecf20Sopenharmony_ci adapter->pm_wakeup_fw_try = false; 15778c2ecf20Sopenharmony_ci del_timer(&adapter->wakeup_timer); 15788c2ecf20Sopenharmony_ci adapter->pm_wakeup_card_req = false; 15798c2ecf20Sopenharmony_ci adapter->ps_state = PS_STATE_AWAKE; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci return 0; 15828c2ecf20Sopenharmony_ci} 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_cistatic void mwifiex_usb_submit_rem_rx_urbs(struct mwifiex_adapter *adapter) 15858c2ecf20Sopenharmony_ci{ 15868c2ecf20Sopenharmony_ci struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; 15878c2ecf20Sopenharmony_ci int i; 15888c2ecf20Sopenharmony_ci struct urb_context *ctx; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { 15918c2ecf20Sopenharmony_ci if (card->rx_data_list[i].skb) 15928c2ecf20Sopenharmony_ci continue; 15938c2ecf20Sopenharmony_ci ctx = &card->rx_data_list[i]; 15948c2ecf20Sopenharmony_ci mwifiex_usb_submit_rx_urb(ctx, MWIFIEX_RX_DATA_BUF_SIZE); 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci/* This function is called after the card has woken up. */ 15998c2ecf20Sopenharmony_cistatic inline int 16008c2ecf20Sopenharmony_cimwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) 16018c2ecf20Sopenharmony_ci{ 16028c2ecf20Sopenharmony_ci return 0; 16038c2ecf20Sopenharmony_ci} 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_cistatic struct mwifiex_if_ops usb_ops = { 16068c2ecf20Sopenharmony_ci .register_dev = mwifiex_register_dev, 16078c2ecf20Sopenharmony_ci .unregister_dev = mwifiex_unregister_dev, 16088c2ecf20Sopenharmony_ci .wakeup = mwifiex_pm_wakeup_card, 16098c2ecf20Sopenharmony_ci .wakeup_complete = mwifiex_pm_wakeup_card_complete, 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci /* USB specific */ 16128c2ecf20Sopenharmony_ci .dnld_fw = mwifiex_usb_dnld_fw, 16138c2ecf20Sopenharmony_ci .cmdrsp_complete = mwifiex_usb_cmd_event_complete, 16148c2ecf20Sopenharmony_ci .event_complete = mwifiex_usb_cmd_event_complete, 16158c2ecf20Sopenharmony_ci .host_to_card = mwifiex_usb_host_to_card, 16168c2ecf20Sopenharmony_ci .submit_rem_rx_urbs = mwifiex_usb_submit_rem_rx_urbs, 16178c2ecf20Sopenharmony_ci .multi_port_resync = mwifiex_usb_port_resync, 16188c2ecf20Sopenharmony_ci .is_port_ready = mwifiex_usb_is_port_ready, 16198c2ecf20Sopenharmony_ci}; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_cimodule_usb_driver(mwifiex_usb_driver); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 16248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell WiFi-Ex USB Driver version" USB_VERSION); 16258c2ecf20Sopenharmony_ciMODULE_VERSION(USB_VERSION); 16268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 16278c2ecf20Sopenharmony_ciMODULE_FIRMWARE(USB8766_DEFAULT_FW_NAME); 16288c2ecf20Sopenharmony_ciMODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME); 16298c2ecf20Sopenharmony_ciMODULE_FIRMWARE(USB8801_DEFAULT_FW_NAME); 16308c2ecf20Sopenharmony_ciMODULE_FIRMWARE(USB8997_DEFAULT_FW_NAME); 1631