/*
* ---------------------------------------------------------------------------
* FILE: io.c
*
* PURPOSE:
* This file contains routines that the SDIO driver can call when a
* UniFi card is first inserted (or detected) and removed.
*
* When used with sdioemb, the udev scripts (at least on Ubuntu) don't
* recognise a UniFi being added to the system. This is because sdioemb
* does not register itself as a device_driver, it uses it's own code
* to handle insert and remove.
* To have Ubuntu recognise UniFi, edit /etc/udev/rules.d/85-ifupdown.rules
* to change this line:
* SUBSYSTEM=="net", DRIVERS=="?*", GOTO="net_start"
* to these:
* #SUBSYSTEM=="net", DRIVERS=="?*", GOTO="net_start"
* SUBSYSTEM=="net", GOTO="net_start"
*
* Then you can add a stanza to /etc/network/interfaces like this:
* auto eth1
* iface eth1 inet dhcp
* wpa-conf /etc/wpa_supplicant.conf
* This will then automatically associate when a car dis inserted.
*
* Copyright (C) 2006-2009 by Cambridge Silicon Radio Ltd.
*
* Refer to LICENSE.txt included with this source code for details on
* the license terms.
*
* ---------------------------------------------------------------------------
*/
#include <linux/proc_fs.h>
#include "csr_wifi_hip_unifi.h"
#include "csr_wifi_hip_unifiversion.h"
#include "csr_wifi_hip_unifi_udi.h" /* for unifi_print_status() */
#include "unifiio.h"
#include "unifi_priv.h"
/*
* Array of pointers to context structs for unifi devices that are present.
* The index in the array corresponds to the wlan interface number
* (if "wlan*" is used). If "eth*" is used, the eth* numbers are allocated
* after any Ethernet cards.
*
* The Arasan PCI-SDIO controller card supported by this driver has 2 slots,
* hence a max of 2 devices.
*/
static unifi_priv_t *Unifi_instances[MAX_UNIFI_DEVS];
/* Array of pointers to netdev objects used by the UniFi driver, as there
* are now many per instance. This is used to determine which netdev events
* are for UniFi as opposed to other net interfaces.
*/
static netInterface_priv_t *Unifi_netdev_instances[MAX_UNIFI_DEVS * CSR_WIFI_NUM_INTERFACES];
/*
* Array to hold the status of each unifi device in each slot.
* We only process an insert event when In_use[] for the slot is
* UNIFI_DEV_NOT_IN_USE. Otherwise, it means that the slot is in use or
* we are in the middle of a cleanup (the action on unplug).
*/
#define UNIFI_DEV_NOT_IN_USE 0
#define UNIFI_DEV_IN_USE 1
#define UNIFI_DEV_CLEANUP 2
static int In_use[MAX_UNIFI_DEVS];
/*
* Mutex to prevent UDI clients to open the character device before the priv
* is created and initialised.
*/
DEFINE_SEMAPHORE(Unifi_instance_mutex);
/*
* When the device is removed, unregister waits on Unifi_cleanup_wq
* until all the UDI clients release the character device.
*/
DECLARE_WAIT_QUEUE_HEAD(Unifi_cleanup_wq);
static int uf_read_proc(char *page, char **start, off_t offset, int count,
int *eof, void *data);
#ifdef CSR_WIFI_RX_PATH_SPLIT
static CsrResult signal_buffer_init(unifi_priv_t * priv, int size)
{
int i;
priv->rxSignalBuffer.writePointer =
priv->rxSignalBuffer.readPointer = 0;
priv->rxSignalBuffer.size = size;
/* Allocating Memory for Signal primitive pointer */
for(i=0; i<size; i++)
{
priv->rxSignalBuffer.rx_buff[i].sig_len=0;
priv->rxSignalBuffer.rx_buff[i].bufptr = kmalloc(UNIFI_PACKED_SIGBUF_SIZE, GFP_KERNEL);
if (priv->rxSignalBuffer.rx_buff[i].bufptr == NULL)
{
int j;
unifi_error(priv,"signal_buffer_init:Failed to Allocate shared memory for T-H signals \n");
for(j=0;j<i;j++)
{
priv->rxSignalBuffer.rx_buff[j].sig_len=0;
kfree(priv->rxSignalBuffer.rx_buff[j].bufptr);
priv->rxSignalBuffer.rx_buff[j].bufptr = NULL;
}
return -1;
}
}
return 0;
}
static void signal_buffer_free(unifi_priv_t * priv, int size)
{
int i;
for(i=0; i<size; i++)
{
priv->rxSignalBuffer.rx_buff[i].sig_len=0;
kfree(priv->rxSignalBuffer.rx_buff[i].bufptr);
priv->rxSignalBuffer.rx_buff[i].bufptr = NULL;
}
}
#endif
/*
* ---------------------------------------------------------------------------
* uf_register_netdev
*
* Registers the network interface, installes the qdisc,
* and registers the inet handler.
* In the porting exercise, register the driver to the network
* stack if necessary.
*
* Arguments:
* priv Pointer to driver context.
*
* Returns:
* O on success, non-zero otherwise.
*
* Notes:
* We will only unregister when the card is ejected, so we must
* only do it once.
* ---------------------------------------------------------------------------
*/
int
uf_register_netdev(unifi_priv_t *priv, int interfaceTag)
{
int r;
netInterface_priv_t *interfacePriv = priv->interfacePriv[interfaceTag];
if (interfaceTag >= CSR_WIFI_NUM_INTERFACES) {
unifi_error(priv, "uf_register_netdev bad interfaceTag\n");
return -EINVAL;
}
/*
* Allocates a device number and registers device with the network
* stack.
*/
unifi_trace(priv, UDBG5, "uf_register_netdev: netdev %d - 0x%p\n",
interfaceTag, priv->netdev[