// SPDX-License-Identifier: GPL-2.0-only #include #include #include "img_mac80211_types.h" #include "umac_hal_hpqm.h" #include #include #include #include #include #include #include "uccrt.h" #include "etherdevice.h" #include "wme.h" #include "umac_debugs.h" #include #include #include #include #include #include #include "img_resource_config.h" #include "umac_hal.h" #include "img_rx_multi_pkt_proc.h" #define IMG_DATA_TRACE_BUFFS 1 RETENTION_MEM_SECTION_UNINITIALIZED extern struct cfg80211_registered_device *cfg80211_rdev; RETENTION_MEM_SECTION_UNINITIALIZED struct img_data_struct img_data_struct; extern struct umac_interface_buffers *pktram_gp; void umac_send_open(); void umac_modules_init(); int rpu_prog_submit_rx_buffer(unsigned char buff_cnt, struct rx_mpdu_ctrl_info *mpdu_ctrl); void img_data_reset_skb_data_info(struct sk_buff *skb) { skb->head = NULL; skb->data = NULL; skb->tail = (sk_buff_data_t)0; skb->end = (sk_buff_data_t)0; return; } int img_data_tx_buff_convert_2_ext_addr(struct nrf_wifi_tx_buff *tx_buff) { int pkt_cnt; int err = 0; for(pkt_cnt = 0; pkt_cnt < tx_buff->num_tx_pkts; pkt_cnt++) { if(tx_buff->tx_buff_info[pkt_cnt].ddr_ptr & ~(IMG_DATA_EXT_MEM_SIZE - 1)) { err = 1; } if((tx_buff->tx_buff_info[pkt_cnt].ddr_ptr + tx_buff->tx_buff_info[pkt_cnt].pkt_length) & ~(IMG_DATA_EXT_MEM_SIZE - 1)) { err = 2; } if(err) { img_data_struct.unexpected_code_path ++; return 1; } } return 0; } int img_data_packet_info_offset_for_ext_addr(struct nrf_wifi_packet_info *packet_info) { int err = 0; if(((packet_info->head & (unsigned int)(~(IMG_DATA_EXT_MEM_SIZE - 1))) != IMG_DATA_EXT_MEM_BASE_ADDR) || ((packet_info->data & (unsigned int)(~(IMG_DATA_EXT_MEM_SIZE - 1))) != IMG_DATA_EXT_MEM_BASE_ADDR) || ((packet_info->tail & (unsigned int)(~(IMG_DATA_EXT_MEM_SIZE - 1))) != IMG_DATA_EXT_MEM_BASE_ADDR) || ((packet_info->end & (unsigned int)(~(IMG_DATA_EXT_MEM_SIZE - 1))) != IMG_DATA_EXT_MEM_BASE_ADDR)) { err = 1; } packet_info->head &= (IMG_DATA_EXT_MEM_SIZE - 1); packet_info->data &= (IMG_DATA_EXT_MEM_SIZE - 1); packet_info->tail &= (IMG_DATA_EXT_MEM_SIZE - 1); packet_info->end &= (IMG_DATA_EXT_MEM_SIZE - 1); if(err) { img_data_struct.unexpected_code_path ++; return 1; } return 0; } void img_data_tx_packet_destructor(struct sk_buff *skb) { struct img_data_tx_buff_desc *tx_buff_desc = (struct img_data_tx_buff_desc *)skb->rpu_ref; struct sk_buff *new_skb; unsigned int num_tx_pkts; unsigned char *cmd_ptr = ((unsigned char *)(tx_buff_desc->tx_buff)) - sizeof(struct host_rpu_msg); num_tx_pkts = tx_buff_desc->tx_buff->num_tx_pkts; tx_buff_desc->tx_buff = NULL; #ifdef IMG_DATA_TRACE_BUFFS { int i; IMG_TX_DBG_PARAM_INCR(tx_done_events_send_to_host, 1); for(i = 0; i < num_tx_pkts; i ++) { if(tx_buff_desc->tx_done_status[i] == NRF_WIFI_TX_STATUS_SUCCESS) { IMG_TX_DBG_PARAM_INCR(tx_done_success_pkts_to_host, 1); } else { IMG_TX_DBG_PARAM_INCR(tx_done_failure_pkts_to_host, 1); } } } #endif /* IMG_DATA_TRACE_BUFFS */ img_data_send_tx_buff_done(cmd_ptr, tx_buff_desc->desc_num, num_tx_pkts, tx_buff_desc->tx_done_status, tx_buff_desc->timestamp_t1, tx_buff_desc->timestamp_t4); free(skb->head - COPY_TX_PKT_PRE_GUARD); img_data_reset_skb_data_info(skb); /* Allocate the new skb for next tx pacekt */ new_skb = alloc_skb(0, GFP_ATOMIC); if(new_skb == NULL) { img_data_struct.unexpected_code_path ++; img_host_txrx_err("ERRPR: alloc_skb failed for size 0"); return; } new_skb->rpu_ref = (unsigned long)tx_buff_desc; new_skb->destructor = img_data_tx_packet_destructor; tx_buff_desc->tx_skb = new_skb; tx_buff_desc->usage_status = IMG_DATA_DESC_FREE_TX; #ifdef IMG_DATA_TRACE_BUFFS IMG_TX_DBG_PARAM_DECR(tx_cmds_currently_in_use, 1); #endif /* IMG_DATA_TRACE_BUFFS */ return; } /* 2k rx packets go to host through function variants of netif_rx. This functions may be called when rx packets droped. */ void img_data_rx_2k_packet_destructor(struct sk_buff *skb) { unsigned int header_ptr; if(skb->is_dup_skb) { if(skb->pkt_info && (*((unsigned int *)skb->pkt_info) == RX_SINGLE_PKT_INFO)) { free(skb->pkt_info); skb->pkt_info = NULL; } else skb->pkt_info = NULL; img_data_reset_skb_data_info(skb); } else { struct ieee80211_hdr *hdr; img_data_send_rx_buff(skb, 0, NRF_WIFI_RX_PKT_BCN_PRB_RSP); hdr = (struct ieee80211_hdr *)skb->data; if(hdr != NULL) { if(!ieee80211_is_data(hdr->frame_control)) { header_ptr = (unsigned int)skb->head; if((header_ptr & 0xF0000000) == 0x80000000) { free(skb->head); } } } img_data_reset_skb_data_info(skb); } } /* destructor function which is used to keep the freed mgmt skb to mgmt free list */ void img_data_mgmt_destructor(struct sk_buff *skb) { struct pkt_info *pkt_info; int count = 1; pkt_info = (struct pkt_info *)skb->pkt_info; img_write_lifo(img_data_struct.mgmt_buff_ddr_ptrs, &pkt_info->ddr_ptr, &count); if(count != 1) { img_data_struct.unexpected_code_path ++; img_host_txrx_err("%s: %d img_write_lifo failed\n", __FUNCTION__, __LINE__); return; } /* this is to free only skb structure and not to free data */ return; } int img_data_tx_init(void) { int i; struct sk_buff *skb; for(i = 0; i < IMG_DATA_MAX_TX_DESCS; i ++) { img_data_struct.tx_buff_desc[i].usage_status = IMG_DATA_DESC_FREE_TX; img_data_struct.tx_buff_desc[i].desc_num = i; img_data_struct.tx_buff_desc[i].desc_type = IMG_DATA_DESC_TYPE_TX; #ifdef ENABLE_COALESCING_TX_PKTS img_data_struct.tx_buff_desc[i].tx_buff = NULL; #endif /* ENABLE_COALESCING_TX_PKTS */ skb = alloc_skb(0, GFP_ATOMIC); if(skb == NULL) { img_data_struct.unexpected_code_path ++; img_host_txrx_err("alloc_skb failed for size 0"); return -1; } skb->rpu_ref = (unsigned long)&img_data_struct.tx_buff_desc[i]; skb->destructor = img_data_tx_packet_destructor; img_data_struct.tx_buff_desc[i].tx_skb = skb; } return 0; } #ifdef INLINE_MODE struct pktram_mem_info *pktram_buff = (struct pktram_mem_info *)RPU_ADDR_PKTRAM_START; #endif int img_data_mgmt_init(void) { int i, count; unsigned int mgmt_buff; img_init_lifo(img_data_struct.mgmt_buff_ddr_ptrs, sizeof(unsigned int), IMG_DATA_MAX_MGMT_DESCS); for(i=0; i < (IMG_DATA_MAX_MGMT_DESCS - 1); i++) { count = 1; #ifdef INLINE_MODE mgmt_buff = (((unsigned int)pktram_buff->mgmt_buffers[i]) & 0x01ffffff); #else mgmt_buff = (((unsigned int)pktram_gp->mgmt_buffers[i]) & 0x01ffffff); #endif img_write_lifo(img_data_struct.mgmt_buff_ddr_ptrs, &mgmt_buff, &count); } return 0; } int img_data_init(void) { IMG_DATA_DBG_PARAM_INCR(img_data_init_cnt, 1); img_data_tx_init(); IF_UMAC_COLD_BOOT img_data_mgmt_init(); ENDIF_UMAC_COLD_BOOT return 0; } struct nrf_wifi_tx_buff *img_data_get_tx_buff(struct sk_buff *skb) { if((void *)skb->rpu_ref == NULL) { return NULL; } if(((struct img_data_tx_buff_desc *)skb->rpu_ref)->desc_type == IMG_DATA_DESC_TYPE_TX){ return ((struct img_data_tx_buff_desc *)skb->rpu_ref)->tx_buff; } return NULL; } unsigned int img_data_get_num_tx_buff_pkts(struct sk_buff *skb) { if((void *)skb->rpu_ref == NULL) { return 0; } if(((struct img_data_tx_buff_desc *)skb->rpu_ref)->desc_type == IMG_DATA_DESC_TYPE_TX){ return ((struct nrf_wifi_tx_buff *)((struct img_data_tx_buff_desc *)skb->rpu_ref)->tx_buff)->num_tx_pkts; } return 0; } int img_data_get_tx_buff_pkt_index(struct sk_buff *skb, unsigned int tx_cmd_desc_num, unsigned int tx_cmd_frame_num, unsigned int *tx_buff_pkt_ref) { struct img_data_tx_buff_desc *tx_buff_desc; struct nrf_wifi_tx_buff *tx_buff; tx_buff_desc = (struct img_data_tx_buff_desc *)skb->rpu_ref; if(tx_buff_desc == NULL) { /* This is not belongs to data from host */ return -2; } if(tx_buff_desc->desc_type == IMG_DATA_DESC_TYPE_TX){ tx_buff = tx_buff_desc->tx_buff; if(tx_buff == NULL) { return -3; } if(tx_buff_desc->tx_index < tx_buff->num_tx_pkts) { *tx_buff_pkt_ref = (tx_buff_desc->desc_num << 16) | tx_buff_desc->tx_index; tx_buff_desc->tx_index ++; return (int)((*tx_buff_pkt_ref) & 0xFFFF); } } return -1; } int img_data_are_all_tx_dons_rcvd(struct sk_buff *skb) { struct img_data_tx_buff_desc *tx_buff_desc; struct nrf_wifi_tx_buff *tx_buff; int tx_index; if((void *)skb->rpu_ref == NULL) { /* This is not belongs to data from host */ return 1; } tx_buff_desc = (struct img_data_tx_buff_desc *)skb->rpu_ref; if(tx_buff_desc->desc_type == IMG_DATA_DESC_TYPE_TX){ tx_buff = tx_buff_desc->tx_buff; if(tx_buff == NULL) { return 1; } if(tx_buff_desc->tx_done_rcvd_cnt < tx_buff_desc->tx_buff->num_tx_pkts) { return 0; } else if(tx_buff_desc->tx_done_rcvd_cnt == tx_buff_desc->tx_buff->num_tx_pkts) { return 1; } else { img_host_txrx_err("%s:%d ERR: how can tx_done_rcvd_cnt (%d) > num_tx_pkts (%d) tx_index: %d\n", __FUNCTION__, __LINE__, tx_buff_desc->tx_done_rcvd_cnt, tx_buff_desc->tx_buff->num_tx_pkts, tx_buff_desc->tx_index); img_data_struct.unexpected_code_path ++; } } else if(tx_buff_desc->desc_type == IMG_DATA_DESC_TYPE_MGMT){ return 1; } else { img_host_txrx_err("%s:%d ERR: should call this function only for tx pkt\n", __FUNCTION__, __LINE__); img_data_struct.unexpected_code_path ++; } return -1; } void img_data_rcvd_tx_done(unsigned int tx_buff_pkt_ref, unsigned char status, unsigned char retries_num, unsigned char rate, unsigned char pkt_idx, unsigned char *timestamp_t1, unsigned char *timestamp_t4) { unsigned int tx_desc_num = tx_buff_pkt_ref >> 16; struct img_data_tx_buff_desc *tx_buff_desc = &img_data_struct.tx_buff_desc[tx_desc_num]; int tx_index = tx_buff_pkt_ref & 0xFFFF; if(pkt_idx == 0){ memcpy(tx_buff_desc->timestamp_t1, timestamp_t1, 6); memcpy(tx_buff_desc->timestamp_t4, timestamp_t4, 6); } tx_buff_desc->tx_done_status[tx_index] = status; tx_buff_desc->status = status; tx_buff_desc->retries_num = retries_num; tx_buff_desc->rate = rate; tx_buff_desc->tx_done_rcvd_cnt ++; if(tx_buff_desc->tx_buff && tx_buff_desc->tx_done_rcvd_cnt > tx_buff_desc->tx_buff->num_tx_pkts) { img_host_txrx_err("ERROR: %s: %d how can tx_buff_desc->tx_done_rcvd_cnt (%d) > tx_buff_desc->tx_buff->num_tx_pkts (%d) tx_buff_desc->tx_index: %d tx_desc_num: %d tx_index: %d\n", __FUNCTION__, __LINE__, tx_buff_desc->tx_done_rcvd_cnt, tx_buff_desc->tx_buff->num_tx_pkts, tx_buff_desc->tx_index, tx_desc_num, tx_index); img_data_struct.unexpected_code_path ++; } return; } void img_data_get_tx_done_values(unsigned int tx_buff_pkt_ref, unsigned char *status, unsigned char *retries_num, unsigned char *rate) { unsigned int tx_desc_num = tx_buff_pkt_ref >> 16; struct img_data_tx_buff_desc *tx_buff_desc = &img_data_struct.tx_buff_desc[tx_desc_num]; *status = tx_buff_desc->status; *retries_num = tx_buff_desc->retries_num; *rate = tx_buff_desc->rate; return; } void img_data_prepare_tx_skb(struct sk_buff *skb, struct nrf_wifi_tx_buff *tx_buff) { unsigned char *payload_ptr = NULL; unsigned int payload_len; unsigned int tail_room = 64; unsigned int hdrlen = ieee80211_hdrlen(tx_buff->mac_hdr_info.fc); int encaps_len; int skip_header_bytes; int i; int ethtype_len = 0; unsigned char dest[6]; memcpy(dest, tx_buff->mac_hdr_info.dest, 6); skip_header_bytes = ETH_HLEN; if (tx_buff->mac_hdr_info.etype == ETH_P_AARP || tx_buff->mac_hdr_info.etype == ETH_P_IPX) { encaps_len = sizeof(bridge_tunnel_header); ethtype_len = 2; } else if (tx_buff->mac_hdr_info.etype >= ETH_P_802_3_MIN) { encaps_len = sizeof(umac_rfc1042_header); ethtype_len = 2; } else { encaps_len = 0; } payload_len = tx_buff->tx_buff_info[0].pkt_length; if(ieee80211_has_protected(tx_buff->mac_hdr_info.fc)) { IMG_TX_DBG_PARAM_INCR(tx_cmds_with_crypto_pkts_rcvd_from_host, 1); } else { IMG_TX_DBG_PARAM_INCR(tx_cmds_with_non_cryptot_pkts_rcvd_from_host, 1); } if(is_broadcast_ether_addr(dest)) { IMG_TX_DBG_PARAM_INCR(tx_cmds_with_broadcast_pkts_rcvd_from_host, 1); } else if(is_multicast_ether_addr(dest)) { IMG_TX_DBG_PARAM_INCR(tx_cmds_with_multicast_pkts_rcvd_from_host, 1); } else { IMG_TX_DBG_PARAM_INCR(tx_cmds_with_unicast_pkts_rcvd_from_host, 1); } skb->head = kzalloc(COPY_TX_PKT_PRE_GUARD + img_data_struct.head_needed + COPY_TX_PKT_COPY_SIZE + COPY_TX_PKT_POST_GUARD, GFP_KERNEL); skb->head += COPY_TX_PKT_PRE_GUARD; skb->data = skb->head + img_data_struct.head_needed; memcpy(skb->data, tx_buff->mac_hdr_info.dest, ETH_ALEN); memcpy(skb->data + ETH_ALEN, tx_buff->mac_hdr_info.src, ETH_ALEN); skb->data[12] = (unsigned char)(tx_buff->mac_hdr_info.etype >> 8); skb->data[13] = (unsigned char)(tx_buff->mac_hdr_info.etype & 0x00FF); skb->tail = skb->data + payload_len; skb->end = skb->head + 2048 - COPY_TX_PKT_PRE_GUARD; skb->len = skb->tail - skb->data; return; } int img_data_proc_tx_buff(void *msg) { struct nrf_wifi_tx_buff *tx_buff = (struct nrf_wifi_tx_buff *)msg; int tx_desc_num = tx_buff->tx_desc_num; struct img_data_tx_buff_desc *tx_buff_desc = &img_data_struct.tx_buff_desc[tx_desc_num]; struct sk_buff *skb = tx_buff_desc->tx_skb; u16 queue_index; struct ieee80211_sub_if_data *sdata; struct wireless_dev *wdev = NULL; IMG_TX_DBG_PARAM_INCR(tx_cmd, 1); list_for_each_entry(wdev, &cfg80211_rdev->wiphy.wdev_list, list) { if (wdev->identifier == (u32)tx_buff->wdev_id) { break; } } sdata = IEEE80211_DEV_TO_SUB_IF(wdev->netdev); img_data_tx_buff_convert_2_ext_addr(tx_buff); if(tx_buff->num_tx_pkts == 1) { IMG_TX_DBG_PARAM_INCR(tx_non_coalescing_pkts_rcvd_from_host, 1); } else { IMG_TX_DBG_PARAM_INCR(tx_coalescing_pkts_rcvd_from_host, 1); } if(tx_buff->num_tx_pkts > IMG_TX_DBG_PARAM_VAL(tx_max_coalescing_pkts_rcvd_from_host)) { IMG_TX_DBG_PARAM_INCR(tx_max_coalescing_pkts_rcvd_from_host, 1); } IMG_TX_DBG_PARAM_INCR(tx_cmds_currently_in_use, 1); if(IMG_TX_DBG_PARAM_VAL(tx_cmds_currently_in_use) > IMG_TX_DBG_PARAM_VAL(tx_cmds_max_used)) { IMG_TX_DBG_PARAM_INCR(tx_cmds_max_used, 1); } img_data_prepare_tx_skb(skb, tx_buff); tx_buff_desc->tx_buff = tx_buff; tx_buff_desc->tx_index = 0; tx_buff_desc->tx_done_rcvd_cnt = 0; skb_set_network_header(skb, ETH_HLEN); skb->protocol = eth_type_trans(skb, wdev->netdev); /* Need to use the above function if the host driver is giving the 802.3 packet without converting to mac80211 packet.*/ /* For now we use below function because host driver is converting the 802.3 packet to mac80211 packet and passing dscp_or_tos */ queue_index = img_ieee80211_select_queue(sdata, skb, tx_buff->mac_hdr_info.dscp_or_tos); skb_set_queue_mapping(skb, queue_index); tx_buff_desc->usage_status = IMG_DATA_DESC_USING_TX; { int i; for(i = 0; i < MAX_TX_AGG_SIZE; i ++) { tx_buff_desc->tx_done_status[i] = 0xff; } } skb->destructor = img_data_tx_packet_destructor; ieee80211_subif_start_xmit(skb, wdev->netdev); return 0; } /* system calls this function when init command is received from host. img_data_proc_init_msg send the IMG_CMD_TXRX_BUFF_CONFIG message to the host to inform the buffers count. */ int img_data_proc_init_msg(void *msg) { (void)msg; img_data_struct.rdev = cfg80211_rdev; img_data_struct.wiphy = &(cfg80211_rdev->wiphy); img_data_struct.local = wiphy_priv(img_data_struct.wiphy); img_data_struct.sdata = list_first_entry(&img_data_struct.local->interfaces, struct ieee80211_sub_if_data, list); img_data_struct.wdev = &img_data_struct.sdata->wdev; img_data_struct.ndev = img_data_struct.sdata->dev; img_data_struct.head_needed = sizeof(struct ieee80211_hdr) + img_data_struct.local->tx_headroom + img_data_struct.sdata->encrypt_headroom; img_data_struct.tail_needed = IEEE80211_ENCRYPT_TAILROOM; return 0; } int img_data_proc_host_msg(void *msg) { struct host_rpu_msg *host_rpu_msg = (struct host_rpu_msg *)msg; int host_rpu_msg_total_len = host_rpu_msg->hdr.len; struct nrf_wifi_umac_head *umac_head = (struct nrf_wifi_umac_head *)host_rpu_msg->msg; unsigned int processed_cmd = 0; processed_cmd = umac_head->cmd; IMG_DATA_DBG_PARAM_INCR(cmds_rcvd_cnt, 1); host_rpu_msg_total_len = host_rpu_msg_total_len - sizeof(struct host_rpu_msg); while(host_rpu_msg_total_len > 0) { switch (umac_head->cmd) { case NRF_WIFI_CMD_TX_BUFF: { img_data_proc_tx_buff(umac_head); return 0; } default: { img_data_struct.unexpected_code_path ++; img_host_txrx_err("unhandled msg %d rcvd\n", umac_head->cmd); break; } } host_rpu_msg_total_len = host_rpu_msg_total_len - umac_head->len; umac_head = (struct nrf_wifi_umac_head *)((unsigned long)umac_head + umac_head->len); } umac_hal_resubmit_cmd_buff(msg); return 0; } /* This function gives a skb from mgmt pool. */ void *img_data_get_l2_eapol_skb(void) { struct sk_buff *skb; int count = 1; skb = alloc_skb(0, GFP_ATOMIC); if(skb == NULL) { img_data_struct.unexpected_code_path ++; img_host_txrx_err("ERRPR: alloc_skb failed for size 0"); return NULL; } img_data_reset_skb_data_info(skb); img_read_lifo(img_data_struct.mgmt_buff_ddr_ptrs, &(skb->head), &count); if(count != 1) { img_data_struct.unexpected_code_path ++; return NULL; } skb->data = skb->head; skb->tail = skb->data; skb->end = skb->head + (2*1024); skb->destructor = img_data_mgmt_destructor; return skb; } int wlan_data_xmit_skb(void *pkt, int pkt_len, struct net_device *netdev) { struct sk_buff *skb; unsigned char *data; skb = img_data_get_l2_eapol_skb(); if(skb == NULL) { img_data_struct.unexpected_code_path ++; img_host_txrx_err("%s: %d img_data_get_l2_eapol_skb failed\n", __FUNCTION__, __LINE__); return -1; } skb_reserve(skb, img_data_struct.head_needed); data = skb_put(skb, pkt_len); memcpy(data, pkt, pkt_len); ieee80211_subif_start_xmit(skb, netdev); return 0; } /* umac calls this function wihle sendign mgmt data to lmac This functions replaces the data buffer with free mgmt buffer and copyes data content to the assigned buffer. This skb should not be cloned previously or in future. */ void *img_data_skb_replace_mgmt_buff(struct sk_buff *skb) { DEFINE_SPIN_LOCK_IRQ(oldIPL); struct nrf_wifi_packet_info packet_info; void *ret_ptr; IPL_T oldipl; unsigned int ext_addr; int count = 1; unsigned int ext_addr_64bit; struct pkt_info *mgmt_addr_info; unsigned char *rcvd_skb_head = skb->head; img_read_lifo(img_data_struct.mgmt_buff_ddr_ptrs, &ext_addr_64bit, &count); if(count != 1) { img_data_struct.unexpected_code_path ++; return NULL; } #ifdef EXTMEM_UNALIGNED_ACCESS_NOT_PERMITED spin_lock_irqsave(&oldIPL, 0); memcpy_unaligned((void *)img_umac_ext_ram_access_addr(ext_addr_64bit), skb->data, skb->len); spin_unlock_irqrestore(&oldIPL, 0); #else /* EXTMEM_UNALIGNED_ACCESS_NOT_PERMITED */ memcpy(skb->data, (unsigned char *)rcvd_skb_head + (skb->data - skb->head), (skb->tail - skb->data)); #endif /* EXTMEM_UNALIGNED_ACCESS_NOT_PERMITED */ mgmt_addr_info = malloc(sizeof(struct pkt_info)); if(mgmt_addr_info == NULL) { printk("%s:%d malloc failed size %lu\n", __FUNCTION__, __LINE__, sizeof(struct primary_pkt_info)); } mgmt_addr_info->info_type = MGMT_EXT_ADDR_INFO; mgmt_addr_info->ddr_ptr = ext_addr_64bit; skb->pkt_info = mgmt_addr_info; skb->destructor = img_data_mgmt_destructor; return skb; } int netif_receive_skb(struct sk_buff *skb) { struct sk_buff *new_skb; int i; img_data_send_rx_buff(skb, 0, NRF_WIFI_RX_PKT_DATA); img_data_reset_skb_data_info(skb); if(skb->is_dup_skb) { skb->pkt_info = NULL; } skb->destructor = NULL; kfree_skb(skb); return 0; } int netif_rx(struct sk_buff *skb) { int ret; ret = netif_receive_skb(skb); return ret; } int netif_rx_ni(struct sk_buff *skb) { int ret; ret = netif_receive_skb(skb); return ret; } void netif_carrier_on(struct net_device *dev) { (void)dev; IMG_DATA_DBG_PARAM_INCR(carrier_on_send_to_host, 1); img_data_send_carrier_state(1, dev->ieee80211_ptr->identifier); return; } void netif_carrier_off(struct net_device *dev) { (void)dev; IMG_DATA_DBG_PARAM_INCR(carrier_off_send_to_host, 1); img_data_send_carrier_state(0, dev->ieee80211_ptr->identifier); return; }