3375 lines · 118.5 KB · C
RawDownload
/* * --------------------------------------------------------------------------- * FILE:     netdev.c * * PURPOSE: *      This file provides the upper edge interface to the linux netdevice *      and wireless extensions. *      It is part of the porting exercise. * * Copyright (C) 2005-2010 by Cambridge Silicon Radio Ltd. * * Refer to LICENSE.txt included with this source code for details on * the license terms. * * --------------------------------------------------------------------------- */ /* * Porting Notes: * This file implements the data plane of the UniFi linux driver. * * All the Tx packets are passed to the HIP core lib, using the * unifi_send_signal() API. For EAPOL packets use the MLME-EAPOL.req * signal, for all other use the MLME-UNITDATA.req. The unifi_send_signal() * expects the wire-formatted (packed) signal. For convenience, in the OS * layer we only use the native (unpacked) signal structures. The HIP core lib * provides the write_pack() helper function to convert to the packed signal. * The packet is stored in the bulk data of the signal. We do not need to * allocate new memory to store the packet, because unifi_net_data_malloc() * is implemented to return a skb, which is the format of packet in Linux. * The HIP core lib frees the bulk data buffers, so we do not need to do * this in the OS layer. * * All the Rx packets are MLME-UNITDATA.ind signals, passed by the HIP core lib * in unifi_receive_event(). We do not need to allocate an skb and copy the * received packet because the HIP core lib has stored in memory allocated by * unifi_net_data_malloc(). Also, we can perform the 802.11 to Ethernet * translation in-place because we allocate the extra memory allocated in * unifi_net_data_malloc(). * * If possible, the porting exercise should appropriately implement * unifi_net_data_malloc() and unifi_net_data_free() to save copies between * network and driver buffers. */ #include <linux/types.h>#include <linux/etherdevice.h>#include <linux/mutex.h>#include <linux/semaphore.h>#include <linux/version.h>#include <linux/vmalloc.h>#include "csr_wifi_hip_unifi.h"#include "csr_wifi_hip_conversions.h"#include "unifi_priv.h"#include <net/pkt_sched.h>  /* Wext handler is suported only if CSR_SUPPORT_WEXT is defined */#ifdef CSR_SUPPORT_WEXTextern struct iw_handler_def unifi_iw_handler_def;#endif /* CSR_SUPPORT_WEXT */static void check_ba_frame_age_timeout( unifi_priv_t *priv,                                            netInterface_priv_t *interfacePriv,                                            ba_session_rx_struct *ba_session);static void process_ba_frame(unifi_priv_t *priv,                             netInterface_priv_t *interfacePriv,                             ba_session_rx_struct *ba_session,                             frame_desc_struct *frame_desc);static void process_ba_complete(unifi_priv_t *priv, netInterface_priv_t *interfacePriv);static void process_ma_packet_error_ind(unifi_priv_t *priv, CSR_SIGNAL *signal, bulk_data_param_t *bulkdata);static void process_amsdu(unifi_priv_t *priv, CSR_SIGNAL *signal, bulk_data_param_t *bulkdata);static int uf_net_open(struct net_device *dev);static int uf_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);static int uf_net_stop(struct net_device *dev);static struct net_device_stats *uf_net_get_stats(struct net_device *dev);static u16 uf_net_select_queue(struct net_device *dev, struct sk_buff *skb);static netdev_tx_t uf_net_xmit(struct sk_buff *skb, struct net_device *dev);static void uf_set_multicast_list(struct net_device *dev);  typedef int (*tx_signal_handler)(unifi_priv_t *priv, struct sk_buff *skb, const struct ethhdr *ehdr, CSR_PRIORITY priority); #ifdef CONFIG_NET_SCHED/* * Queueing Discipline Interface * Only used if kernel is configured with CONFIG_NET_SCHED */ /* * The driver uses the qdisc interface to buffer and control all * outgoing traffic. We create a root qdisc, register our qdisc operations * and later we create two subsiduary pfifo queues for the uncontrolled * and controlled ports. * * The network stack delivers all outgoing packets in our enqueue handler. * There, we classify the packet and decide whether to store it or drop it * (if the controlled port state is set to "discard"). * If the packet is enqueued, the network stack call our dequeue handler. * There, we decide whether we can send the packet, delay it or drop it * (the controlled port configuration might have changed meanwhile). * If a packet is dequeued, then the network stack calls our hard_start_xmit * handler where finally we send the packet. * * If the hard_start_xmit handler fails to send the packet, we return * NETDEV_TX_BUSY and the network stack call our requeue handler where * we put the packet back in the same queue in came from. * */ struct uf_sched_data{    /* Traffic Classifier TBD */    struct tcf_proto *filter_list;    /* Our two queues */    struct Qdisc *queues[UNIFI_TRAFFIC_Q_MAX];}; struct uf_tx_packet_data {    /* Queue the packet is stored in */    unifi_TrafficQueue queue;    /* QoS Priority determined when enqueing packet */    CSR_PRIORITY priority;    /* Debug */    unsigned long host_tag;}; #endif /* CONFIG_NET_SCHED */ static const struct net_device_ops uf_netdev_ops ={    .ndo_open = uf_net_open,    .ndo_stop = uf_net_stop,    .ndo_start_xmit = uf_net_xmit,    .ndo_do_ioctl = uf_net_ioctl,    .ndo_get_stats = uf_net_get_stats, /* called by /proc/net/dev */    .ndo_set_rx_mode = uf_set_multicast_list,    .ndo_select_queue = uf_net_select_queue,}; static u8 oui_rfc1042[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };static u8 oui_8021h[P80211_OUI_LEN]   = { 0x00, 0x00, 0xf8 };  /* Callback for event logging to blocking clients */static void netdev_mlme_event_handler(ul_client_t  *client,                                      const u8 *sig_packed, int sig_len,                                      const bulk_data_param_t *bulkdata,                                      int dir); #ifdef CSR_SUPPORT_WEXT/* Declare netdev_notifier block which will contain the state change * handler callback function */static struct notifier_block uf_netdev_notifier;#endif /* * --------------------------------------------------------------------------- *  uf_alloc_netdevice * *      Allocate memory for the net_device and device private structs *      for this interface. *      Fill in the fields, but don't register the interface yet. *      We need to configure the UniFi first. * *  Arguments: *      sdio_dev        Pointer to SDIO context handle to use for all *                      SDIO ops. *      bus_id          A small number indicating the SDIO card position on the *                      bus. Typically this is the slot number, e.g. 0, 1 etc. *                      Valid values are 0 to MAX_UNIFI_DEVS-1. * *  Returns: *      Pointer to device private struct. * *  Notes: *      The net_device and device private structs are allocated together *      and should be freed by freeing the net_device pointer. * --------------------------------------------------------------------------- */unifi_priv_t *uf_alloc_netdevice(CsrSdioFunction *sdio_dev, int bus_id){    struct net_device *dev;    unifi_priv_t *priv;    netInterface_priv_t *interfacePriv;#ifdef CSR_SUPPORT_WEXT    int rc;#endif    unsigned char i; /* loop index */     /*     * Allocate netdevice struct, assign name template and     * setup as an ethernet device.     * The net_device and private structs are zeroed. Ether_setup() then     * sets up ethernet handlers and values.     * The RedHat 9 redhat-config-network tool doesn't recognise wlan* devices,     * so use "eth*" (like other wireless extns drivers).     */    dev = alloc_etherdev_mq(sizeof(unifi_priv_t) + sizeof(netInterface_priv_t), UNIFI_TRAFFIC_Q_MAX);     if (dev == NULL) {        return NULL;    }     /* Set up back pointer from priv to netdev */    interfacePriv = (netInterface_priv_t *)netdev_priv(dev);    priv = (unifi_priv_t *)(interfacePriv + 1);    interfacePriv->privPtr = priv;    interfacePriv->InterfaceTag = 0;      /* Initialize all supported netdev interface to be NULL */    for(i=0; i<CSR_WIFI_NUM_INTERFACES; i++) {        priv->netdev[i] = NULL;        priv->interfacePriv[i] = NULL;    }    priv->netdev[0] = dev;    priv->interfacePriv[0] = interfacePriv;     /* Setup / override net_device fields */    dev->netdev_ops = &uf_netdev_ops; #ifdef CSR_SUPPORT_WEXT    dev->wireless_handlers = &unifi_iw_handler_def;#if IW_HANDLER_VERSION < 6    dev->get_wireless_stats = unifi_get_wireless_stats;#endif /* IW_HANDLER_VERSION */#endif /* CSR_SUPPORT_WEXT */     /* This gives us enough headroom to add the 802.11 header */    dev->needed_headroom = 32;     /* Use bus_id as instance number */    priv->instance = bus_id;    /* Store SDIO pointer to pass in the core */    priv->sdio = sdio_dev;     sdio_dev->driverData = (void*)priv;    /* Consider UniFi to be uninitialised */    priv->init_progress = UNIFI_INIT_NONE;     priv->prev_queue = 0;     /*     * Initialise the clients structure array.     * We do not need protection around ul_init_clients() because     * the character device can not be used until uf_alloc_netdevice()     * returns and Unifi_instances[bus_id]=priv is set, since unifi_open()     * will return -ENODEV.     */    ul_init_clients(priv);     /*     * Register a new ul client to send the multicast list signals.     * Note: priv->instance must be set before calling this.     */    priv->netdev_client = ul_register_client(priv,            0,            netdev_mlme_event_handler);    if (priv->netdev_client == NULL) {        unifi_error(priv,                "Failed to register a unifi client for background netdev processing\n");        free_netdev(priv->netdev[0]);        return NULL;    }    unifi_trace(priv, UDBG2, "Netdev %p client (id:%d s:0x%X) is registered\n",            dev, priv->netdev_client->client_id, priv->netdev_client->sender_id);     priv->sta_wmm_capabilities = 0; #if (defined(CSR_WIFI_SECURITY_WAPI_ENABLE) && defined(CSR_SUPPORT_SME))    priv->wapi_multicast_filter = 0;    priv->wapi_unicast_filter = 0;    priv->wapi_unicast_queued_pkt_filter = 0;#ifdef CSR_WIFI_SECURITY_WAPI_QOSCTRL_MIC_WORKAROUND    priv->isWapiConnection = FALSE;#endif#endif     /* Enable all queues by default */    interfacePriv->queueEnabled[0] = 1;    interfacePriv->queueEnabled[1] = 1;    interfacePriv->queueEnabled[2] = 1;    interfacePriv->queueEnabled[3] = 1; #ifdef CSR_SUPPORT_SME    priv->allPeerDozing = 0;#endif    /*     * Initialise the OS private struct.     */    /*     * Instead of deciding in advance to use 11bg or 11a, we could do a more     * clever scan on both radios.     */    if (use_5g) {        priv->if_index = CSR_INDEX_5G;        unifi_info(priv, "Using the 802.11a radio\n");    } else {        priv->if_index = CSR_INDEX_2G4;    }     /* Initialise bh thread structure */    priv->bh_thread.thread_task = NULL;    priv->bh_thread.block_thread = 1;    init_waitqueue_head(&priv->bh_thread.wakeup_q);    priv->bh_thread.wakeup_flag = 0;    sprintf(priv->bh_thread.name, "uf_bh_thread");     /* reset the connected state for the interface */    interfacePriv->connected = UnifiConnectedUnknown;  /* -1 unknown, 0 no, 1 yes */ #ifdef USE_DRIVER_LOCK    sema_init(&priv->lock, 1);#endif /* USE_DRIVER_LOCK */     spin_lock_init(&priv->send_signal_lock);     spin_lock_init(&priv->m4_lock);    sema_init(&priv->ba_mutex, 1); #if (defined(CSR_WIFI_SECURITY_WAPI_ENABLE) && defined(CSR_WIFI_SECURITY_WAPI_SW_ENCRYPTION))    spin_lock_init(&priv->wapi_lock);#endif #ifdef CSR_SUPPORT_SME    spin_lock_init(&priv->staRecord_lock);    spin_lock_init(&priv->tx_q_lock);#endif     /* Create the Traffic Analysis workqueue */    priv->unifi_workqueue = create_singlethread_workqueue("unifi_workq");    if (priv->unifi_workqueue == NULL) {        /* Deregister priv->netdev_client */        ul_deregister_client(priv->netdev_client);        free_netdev(priv->netdev[0]);        return NULL;    } #ifdef CSR_SUPPORT_SME    /* Create the Multicast Addresses list work structure */    INIT_WORK(&priv->multicast_list_task, uf_multicast_list_wq);     /* Create m4 buffering work structure */    INIT_WORK(&interfacePriv->send_m4_ready_task, uf_send_m4_ready_wq); #if (defined(CSR_WIFI_SECURITY_WAPI_ENABLE) && defined(CSR_WIFI_SECURITY_WAPI_SW_ENCRYPTION))    /* Create work structure to buffer the WAPI data packets to be sent to SME for encryption */    INIT_WORK(&interfacePriv->send_pkt_to_encrypt, uf_send_pkt_to_encrypt);#endif#endif     priv->ref_count = 1;     priv->amp_client = NULL;    priv->coredump_mode = 0;    priv->ptest_mode = 0;    priv->wol_suspend = FALSE;    INIT_LIST_HEAD(&interfacePriv->rx_uncontrolled_list);    INIT_LIST_HEAD(&interfacePriv->rx_controlled_list);    sema_init(&priv->rx_q_sem, 1); #ifdef CSR_SUPPORT_WEXT    interfacePriv->netdev_callback_registered = FALSE;    interfacePriv->wait_netdev_change = FALSE;    /* Register callback for netdevice state changes */    if ((rc = register_netdevice_notifier(&uf_netdev_notifier)) == 0) {        interfacePriv->netdev_callback_registered = TRUE;    }    else {        unifi_warning(priv, "Failed to register netdevice notifier : %d %p\n", rc, dev);    }#endif /* CSR_SUPPORT_WEXT */ #ifdef CSR_WIFI_SPLIT_PATCH    /* set it to some invalid value */    priv->pending_mode_set.common.destination = 0xaaaa;#endif     return priv;} /* uf_alloc_netdevice() */ /* *--------------------------------------------------------------------------- *  uf_alloc_netdevice_for_other_interfaces * *      Allocate memory for the net_device and device private structs *      for this interface. *      Fill in the fields, but don't register the interface yet. *      We need to configure the UniFi first. * *  Arguments: *      interfaceTag   Interface number. *      sdio_dev        Pointer to SDIO context handle to use for all *                      SDIO ops. *      bus_id          A small number indicating the SDIO card position on the *                      bus. Typically this is the slot number, e.g. 0, 1 etc. *                      Valid values are 0 to MAX_UNIFI_DEVS-1. * *  Returns: *      Pointer to device private struct. * *  Notes: *      The device private structure contains the interfaceTag and pointer to the unifi_priv *      structure created allocated by net_device od interface0. *      The net_device and device private structs are allocated together *      and should be freed by freeing the net_device pointer. * --------------------------------------------------------------------------- */u8uf_alloc_netdevice_for_other_interfaces(unifi_priv_t *priv, u16 interfaceTag){    struct net_device *dev;    netInterface_priv_t *interfacePriv;     /*     * Allocate netdevice struct, assign name template and     * setup as an ethernet device.     * The net_device and private structs are zeroed. Ether_setup() then     * sets up ethernet handlers and values.     * The RedHat 9 redhat-config-network tool doesn't recognise wlan* devices,     * so use "eth*" (like other wireless extns drivers).     */    dev = alloc_etherdev_mq(sizeof(netInterface_priv_t), 1);    if (dev == NULL) {        return FALSE;    }     if (interfaceTag >= CSR_WIFI_NUM_INTERFACES) {        unifi_error(priv, "uf_alloc_netdevice_for_other_interfaces bad interfaceTag\n");        return FALSE;    }     /* Set up back pointer from priv to netdev */    interfacePriv = (netInterface_priv_t *)netdev_priv(dev);    interfacePriv->privPtr = priv;    interfacePriv->InterfaceTag = interfaceTag;    priv->netdev[interfaceTag] = dev;    priv->interfacePriv[interfacePriv->InterfaceTag] = interfacePriv;     /* reset the connected state for the interface */    interfacePriv->connected = UnifiConnectedUnknown;  /* -1 unknown, 0 no, 1 yes */    INIT_LIST_HEAD(&interfacePriv->rx_uncontrolled_list);    INIT_LIST_HEAD(&interfacePriv->rx_controlled_list);     /* Setup / override net_device fields */    dev->netdev_ops = &uf_netdev_ops; #ifdef CSR_SUPPORT_WEXT    dev->wireless_handlers = &unifi_iw_handler_def;#if IW_HANDLER_VERSION < 6    dev->get_wireless_stats = unifi_get_wireless_stats;#endif /* IW_HANDLER_VERSION */#endif /* CSR_SUPPORT_WEXT */    return TRUE;} /* uf_alloc_netdevice() */   /* * --------------------------------------------------------------------------- *  uf_free_netdevice * *      Unregister the network device and free the memory allocated for it. *      NB This includes the memory for the priv struct. * *  Arguments: *      priv            Device private pointer. * *  Returns: *      None. * --------------------------------------------------------------------------- */intuf_free_netdevice(unifi_priv_t *priv){    int i;    unsigned long flags;     func_enter();     unifi_trace(priv, UDBG1, "uf_free_netdevice\n");     if (!priv) {        return -EINVAL;    }     /*     * Free any buffers used for holding firmware     */    uf_release_firmware_files(priv); #if (defined CSR_SUPPORT_SME) && (defined CSR_SUPPORT_WEXT)    if (priv->connection_config.mlmeAssociateReqInformationElements) {        kfree(priv->connection_config.mlmeAssociateReqInformationElements);    }    priv->connection_config.mlmeAssociateReqInformationElements = NULL;    priv->connection_config.mlmeAssociateReqInformationElementsLength = 0; 

Lines 1–500 of 3375.