/* * --------------------------------------------------------------------------- * 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/vmalloc.h>#include "csr_wifi_hip_unifi.h"#include "csr_wifi_hip_conversions.h"#include "unifi_priv.h"#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)#include <net/iw_handler.h>#endif#include <net/pkt_sched.h> /* ALLOW_Q_PAUSE: Pre 2.6.28 kernels do not support multiple driver queues (required for QoS). * In order to support QoS in these kernels, multiple queues are implemented in the driver. But since * there is only a single queue in the kernel (leading to multiple queues in the driver) there is no possibility * of stopping a particular queue in the kernel. Stopping the single kernel queue leads to undesirable starvation * of driver queues. One of the proposals is to not stop the kernel queue but to prevent dequeuing from the * 'stopped' driver queue. Allow q pause is an experimental implementation of this scheme for pre 2.6.28 kernels. * When NOT defined, queues are paused locally in the driver and packets are dequeued for transmission only from the * unpaused queues. When Allow q pause is defined the kernel queue is stopped whenever any driver queue is paused. */#define ALLOW_Q_PAUSE #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)#ifdef UNIFI_NET_NAME#define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \ do { \ static char name[8]; \ sprintf(name, "%s%s", UNIFI_NET_NAME, _name); \ _dev = alloc_netdev_mq(_size, name, _setup, _num_of_queues); \ } while (0);#else#define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \ do { \ _dev = alloc_etherdev_mq(_size, _num_of_queues); \ } while (0);#endif /* UNIFI_NET_NAME */#else#ifdef UNIFI_NET_NAME#define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \ do { \ static char name[8]; \ sprintf(name, "%s%s", UNIFI_NET_NAME, _name); \ _dev = alloc_netdev(_size, name, _setup); \ } while (0);#else#define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \ do { \ _dev = alloc_etherdev(_size); \ } while (0);#endif /* UNIFI_NET_NAME */#endif /* LINUX_VERSION_CODE */ /* 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);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)static u16 uf_net_select_queue(struct net_device *dev, struct sk_buff *skb);#endif#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)static netdev_tx_t uf_net_xmit(struct sk_buff *skb, struct net_device *dev);#elsestatic int uf_net_xmit(struct sk_buff *skb, struct net_device *dev);#ifndef NETDEV_TX_OK#define NETDEV_TX_OK 0#endif#ifndef NETDEV_TX_BUSY#define NETDEV_TX_BUSY 1#endif#endifstatic 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;}; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)static int uf_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd);static int uf_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd);static struct sk_buff *uf_qdiscop_dequeue(struct Qdisc* qd);static void uf_qdiscop_reset(struct Qdisc* qd);static void uf_qdiscop_destroy(struct Qdisc* qd);static int uf_qdiscop_dump(struct Qdisc *qd, struct sk_buff *skb);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)static int uf_qdiscop_tune(struct Qdisc *qd, struct nlattr *opt);static int uf_qdiscop_init(struct Qdisc *qd, struct nlattr *opt);#elsestatic int uf_qdiscop_tune(struct Qdisc *qd, struct rtattr *opt);static int uf_qdiscop_init(struct Qdisc *qd, struct rtattr *opt);#endif#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)/* queueing discipline operations */static struct Qdisc_ops uf_qdisc_ops ={ .next = NULL, .cl_ops = NULL, .id = "UniFi Qdisc", .priv_size = sizeof(struct uf_sched_data), .enqueue = uf_qdiscop_enqueue, .dequeue = uf_qdiscop_dequeue, .requeue = uf_qdiscop_requeue, .drop = NULL, /* drop not needed since we are always the root qdisc */ .init = uf_qdiscop_init, .reset = uf_qdiscop_reset, .destroy = uf_qdiscop_destroy, .change = uf_qdiscop_tune, .dump = uf_qdiscop_dump,};#endif /* LINUX_VERSION_CODE */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)#define UF_QDISC_CREATE_DFLT(_dev, _ops, _root)#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)#define UF_QDISC_CREATE_DFLT(_dev, _ops, _root) \ qdisc_create_dflt(dev, netdev_get_tx_queue(_dev, 0), _ops, _root)#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)#define UF_QDISC_CREATE_DFLT(_dev, _ops, _root) \ qdisc_create_dflt(dev, _ops, _root)#else#define UF_QDISC_CREATE_DFLT(_dev, _ops, _root) \ qdisc_create_dflt(dev, _ops)#endif /* LINUX_VERSION_CODE */ #endif /* CONFIG_NET_SCHED */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)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,};#endif 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). */ UF_ALLOC_NETDEV(dev, sizeof(unifi_priv_t)+sizeof(netInterface_priv_t), "%d", ether_setup, 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 */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) dev->netdev_ops = &uf_netdev_ops;#else dev->open = uf_net_open; dev->stop = uf_net_stop; dev->hard_start_xmit = uf_net_xmit; dev->do_ioctl = uf_net_ioctl; /* called by /proc/net/dev */ dev->get_stats = uf_net_get_stats; dev->set_multicast_list = uf_set_multicast_list;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) dev->select_queue = uf_net_select_queue;#endif#endif #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#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) sema_init(&priv->lock, 1);#else init_MUTEX(&priv->lock);#endif#endif /* USE_DRIVER_LOCK */ spin_lock_init(&priv->send_signal_lock); spin_lock_init(&priv->m4_lock);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) sema_init(&priv->ba_mutex, 1);#else init_MUTEX(&priv->ba_mutex);#endif #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 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)#ifdef CONFIG_NET_SCHED /* Register the qdisc operations */ register_qdisc(&uf_qdisc_ops);#endif /* CONFIG_NET_SCHED */#endif /* LINUX_VERSION_CODE */ priv->ref_count = 1; priv->amp_client = NULL;Lines 1–500 of 3993. Load more
/* * --------------------------------------------------------------------------- * 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/vmalloc.h> #include "csr_wifi_hip_unifi.h" #include "csr_wifi_hip_conversions.h" #include "unifi_priv.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) #include <net/iw_handler.h> #endif #include <net/pkt_sched.h> /* ALLOW_Q_PAUSE: Pre 2.6.28 kernels do not support multiple driver queues (required for QoS). * In order to support QoS in these kernels, multiple queues are implemented in the driver. But since * there is only a single queue in the kernel (leading to multiple queues in the driver) there is no possibility * of stopping a particular queue in the kernel. Stopping the single kernel queue leads to undesirable starvation * of driver queues. One of the proposals is to not stop the kernel queue but to prevent dequeuing from the * 'stopped' driver queue. Allow q pause is an experimental implementation of this scheme for pre 2.6.28 kernels. * When NOT defined, queues are paused locally in the driver and packets are dequeued for transmission only from the * unpaused queues. When Allow q pause is defined the kernel queue is stopped whenever any driver queue is paused. */ #define ALLOW_Q_PAUSE #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) #ifdef UNIFI_NET_NAME #define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \ do { \ static char name[8]; \ sprintf(name, "%s%s", UNIFI_NET_NAME, _name); \ _dev = alloc_netdev_mq(_size, name, _setup, _num_of_queues); \ } while (0); #else #define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \ do { \ _dev = alloc_etherdev_mq(_size, _num_of_queues); \ } while (0); #endif /* UNIFI_NET_NAME */ #else #ifdef UNIFI_NET_NAME #define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \ do { \ static char name[8]; \ sprintf(name, "%s%s", UNIFI_NET_NAME, _name); \ _dev = alloc_netdev(_size, name, _setup); \ } while (0); #else #define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \ do { \ _dev = alloc_etherdev(_size); \ } while (0); #endif /* UNIFI_NET_NAME */ #endif /* LINUX_VERSION_CODE */ /* Wext handler is suported only if CSR_SUPPORT_WEXT is defined */ #ifdef CSR_SUPPORT_WEXT extern 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); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) static u16 uf_net_select_queue(struct net_device *dev, struct sk_buff *skb); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) static netdev_tx_t uf_net_xmit(struct sk_buff *skb, struct net_device *dev); #else static int uf_net_xmit(struct sk_buff *skb, struct net_device *dev); #ifndef NETDEV_TX_OK #define NETDEV_TX_OK 0 #endif #ifndef NETDEV_TX_BUSY #define NETDEV_TX_BUSY 1 #endif #endif 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; }; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) static int uf_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd); static int uf_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd); static struct sk_buff *uf_qdiscop_dequeue(struct Qdisc* qd); static void uf_qdiscop_reset(struct Qdisc* qd); static void uf_qdiscop_destroy(struct Qdisc* qd); static int uf_qdiscop_dump(struct Qdisc *qd, struct sk_buff *skb); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) static int uf_qdiscop_tune(struct Qdisc *qd, struct nlattr *opt); static int uf_qdiscop_init(struct Qdisc *qd, struct nlattr *opt); #else static int uf_qdiscop_tune(struct Qdisc *qd, struct rtattr *opt); static int uf_qdiscop_init(struct Qdisc *qd, struct rtattr *opt); #endif #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) /* queueing discipline operations */ static struct Qdisc_ops uf_qdisc_ops = { .next = NULL, .cl_ops = NULL, .id = "UniFi Qdisc", .priv_size = sizeof(struct uf_sched_data), .enqueue = uf_qdiscop_enqueue, .dequeue = uf_qdiscop_dequeue, .requeue = uf_qdiscop_requeue, .drop = NULL, /* drop not needed since we are always the root qdisc */ .init = uf_qdiscop_init, .reset = uf_qdiscop_reset, .destroy = uf_qdiscop_destroy, .change = uf_qdiscop_tune, .dump = uf_qdiscop_dump, }; #endif /* LINUX_VERSION_CODE */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) #define UF_QDISC_CREATE_DFLT(_dev, _ops, _root) #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) #define UF_QDISC_CREATE_DFLT(_dev, _ops, _root) \ qdisc_create_dflt(dev, netdev_get_tx_queue(_dev, 0), _ops, _root) #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) #define UF_QDISC_CREATE_DFLT(_dev, _ops, _root) \ qdisc_create_dflt(dev, _ops, _root) #else #define UF_QDISC_CREATE_DFLT(_dev, _ops, _root) \ qdisc_create_dflt(dev, _ops) #endif /* LINUX_VERSION_CODE */ #endif /* CONFIG_NET_SCHED */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) 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, }; #endif 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). */ UF_ALLOC_NETDEV(dev, sizeof(unifi_priv_t)+sizeof(netInterface_priv_t), "%d", ether_setup, 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 */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) dev->netdev_ops = &uf_netdev_ops; #else dev->open = uf_net_open; dev->stop = uf_net_stop; dev->hard_start_xmit = uf_net_xmit; dev->do_ioctl = uf_net_ioctl; /* called by /proc/net/dev */ dev->get_stats = uf_net_get_stats; dev->set_multicast_list = uf_set_multicast_list; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) dev->select_queue = uf_net_select_queue; #endif #endif #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 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) sema_init(&priv->lock, 1); #else init_MUTEX(&priv->lock); #endif #endif /* USE_DRIVER_LOCK */ spin_lock_init(&priv->send_signal_lock); spin_lock_init(&priv->m4_lock); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) sema_init(&priv->ba_mutex, 1); #else init_MUTEX(&priv->ba_mutex); #endif #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 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) #ifdef CONFIG_NET_SCHED /* Register the qdisc operations */ register_qdisc(&uf_qdisc_ops); #endif /* CONFIG_NET_SCHED */ #endif /* LINUX_VERSION_CODE */ priv->ref_count = 1; priv->amp_client = NULL;