/**
  * This file contains ioctl functions
  */

#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>

#include <net/iw_handler.h>
#include <net/ieee80211.h>

#include "host.h"
#include "radiotap.h"
#include "decl.h"
#include "defs.h"
#include "dev.h"
#include "join.h"
#include "wext.h"

#define MAX_SCAN_CELL_SIZE      (IW_EV_ADDR_LEN + \
				IW_ESSID_MAX_SIZE + \
				IW_EV_UINT_LEN + IW_EV_FREQ_LEN + \
				IW_EV_QUAL_LEN + IW_ESSID_MAX_SIZE + \
				IW_EV_PARAM_LEN + 40)	/* 40 for WPAIE */

#define WAIT_FOR_SCAN_RRESULT_MAX_TIME (10 * HZ)

static int setrxantenna(wlan_private * priv, int mode)
{
	int ret = 0;
	wlan_adapter *adapter = priv->adapter;

	if (mode != RF_ANTENNA_1 && mode != RF_ANTENNA_2
	    && mode != RF_ANTENNA_AUTO) {
		return -EINVAL;
	}

	adapter->rxantennamode = mode;

	lbs_pr_debug(1, "SET RX Antenna mode to 0x%04x\n", adapter->rxantennamode);

	ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
				    cmd_act_set_rx,
				    cmd_option_waitforrsp, 0,
				    &adapter->rxantennamode);
	return ret;
}

static int settxantenna(wlan_private * priv, int mode)
{
	int ret = 0;
	wlan_adapter *adapter = priv->adapter;

	if ((mode != RF_ANTENNA_1) && (mode != RF_ANTENNA_2)
	    && (mode != RF_ANTENNA_AUTO)) {
		return -EINVAL;
	}

	adapter->txantennamode = mode;

	lbs_pr_debug(1, "SET TX Antenna mode to 0x%04x\n", adapter->txantennamode);

	ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
				    cmd_act_set_tx,
				    cmd_option_waitforrsp, 0,
				    &adapter->txantennamode);

	return ret;
}

static int getrxantenna(wlan_private * priv, char *buf)
{
	int ret = 0;
	wlan_adapter *adapter = priv->adapter;

	// clear it, so we will know if the value
	// returned below is correct or not.
	adapter->rxantennamode = 0;

	ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
				    cmd_act_get_rx,
				    cmd_option_waitforrsp, 0, NULL);

	if (ret) {
		LEAVE();
		return ret;
	}

	lbs_pr_debug(1, "Get Rx Antenna mode:0x%04x\n", adapter->rxantennamode);

	return sprintf(buf, "0x%04x", adapter->rxantennamode) + 1;
}

static int gettxantenna(wlan_private * priv, char *buf)
{
	int ret = 0;
	wlan_adapter *adapter = priv->adapter;

	// clear it, so we will know if the value
	// returned below is correct or not.
	adapter->txantennamode = 0;

	ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
				    cmd_act_get_tx,
				    cmd_option_waitforrsp, 0, NULL);

	if (ret) {
		LEAVE();
		return ret;
	}

	lbs_pr_debug(1, "Get Tx Antenna mode:0x%04x\n", adapter->txantennamode);

	return sprintf(buf, "0x%04x", adapter->txantennamode) + 1;
}

static int wlan_set_region(wlan_private * priv, u16 region_code)
{
	int i;

	for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
		// use the region code to search for the index
		if (region_code == libertas_region_code_to_index[i]) {
			priv->adapter->regiontableindex = (u16) i;
			priv->adapter->regioncode = region_code;
			break;
		}
	}

	// if it's unidentified region code
	if (i >= MRVDRV_MAX_REGION_CODE) {
		lbs_pr_debug(1, "region Code not identified\n");
		LEAVE();
		return -1;
	}

	if (libertas_set_regiontable(priv, priv->adapter->regioncode, 0)) {
		LEAVE();
		return -EINVAL;
	}

	return 0;
}

/**
 *  @brief Get/Set Firmware wakeup method
 *
 *  @param priv		A pointer to wlan_private structure
 *  @param wrq	   	A pointer to user data
 *  @return 	   	0--success, otherwise fail
 */
static int wlan_txcontrol(wlan_private * priv, struct iwreq *wrq)
{
	wlan_adapter *adapter = priv->adapter;
	int data;
	ENTER();

	if ((int)wrq->u.data.length == 0) {
		if (copy_to_user
		    (wrq->u.data.pointer, &adapter->pkttxctrl, sizeof(u32))) {
			lbs_pr_alert("copy_to_user failed!\n");
			return -EFAULT;
		}
	} else {
		if ((int)wrq->u.data.length > 1) {
			lbs_pr_alert("ioctl too many args!\n");
			return -EFAULT;
		}
		if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
			lbs_pr_alert("Copy from user failed\n");
			return -EFAULT;
		}

		adapter->pkttxctrl = (u32) data;
	}

	wrq->u.data.length = 1;

	LEAVE();
	return 0;
}

/**
 *  @brief Get/Set NULL Package generation interval
 *
 *  @param priv		A pointer to wlan_private structure
 *  @param wrq	   	A pointer to user data
 *  @return 	   	0--success, otherwise fail
 */
static int wlan_null_pkt_interval(wlan_private * priv, struct iwreq *wrq)
{
	wlan_adapter *adapter = priv->adapter;
	int data;
	ENTER();

	if ((int)wrq->u.data.length == 0) {
		data = adapter->nullpktinterval;

		if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
			lbs_pr_alert( "copy_to_user failed!\n");
			return -EFAULT;
		}
	} else {
		if ((int)wrq->u.data.length > 1) {
			lbs_pr_alert( "ioctl too many args!\n");
			return -EFAULT;
		}
		if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
			lbs_pr_debug(1, "Copy from user failed\n");
			return -EFAULT;
		}

		adapter->nullpktinterval = data;
	}

	wrq->u.data.length = 1;

	LEAVE();
	return 0;
}

static int wlan_get_rxinfo(wlan_private * priv, struct iwreq *wrq)
{
	wlan_adapter *adapter = priv->adapter;
	int data[2];
	ENTER();
	data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG];
	data[1] = adapter->rxpd_rate;
	if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) {
		lbs_pr_debug(1, "Copy to user failed\n");
		return -EFAULT;
	}
	wrq->u.data.length = 2;
	LEAVE();
	return 0;
}

static int wlan_get_snr(wlan_private * priv, struct iwreq *wrq)
{
	int ret = 0;
	wlan_adapter *adapter = priv->adapter;
	int data[4];

	ENTER();
	memset(data, 0, sizeof(data));
	if (wrq->u.data.length) {
		if (copy_from_user(data, wrq->u.data.pointer,
		     min_t(size_t, wrq->u.data.length, 4) * sizeof(int)))
			return -EFAULT;
	}
	if ((wrq->u.data.length == 0) || (data[0] == 0) || (data[0] == 1)) {
		if (adapter->connect_status == libertas_connected) {
			ret = libertas_prepare_and_send_command(priv,
						    cmd_802_11_rssi,
						    0,
						    cmd_option_waitforrsp,
						    0, NULL);

			if (ret) {
				LEAVE();
				return ret;
			}
		}
	}

	if (wrq->u.data.length == 0) {
		data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG];
		data[1] = adapter->SNR[TYPE_BEACON][TYPE_AVG];
		data[2] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG];
		data[3] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 4))
			return -EFAULT;
		wrq->u.data.length = 4;
	} else if (data[0] == 0) {
		data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG];
		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
			return -EFAULT;
		wrq->u.data.length = 1;
	} else if (data[0] == 1) {
		data[0] = adapter->SNR[TYPE_BEACON][TYPE_AVG];
		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
			return -EFAULT;
		wrq->u.data.length = 1;
	} else if (data[0] == 2) {
		data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG];
		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
			return -EFAULT;
		wrq->u.data.length = 1;
	} else if (data[0] == 3) {
		data[0] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
			return -EFAULT;
		wrq->u.data.length = 1;
	} else
		return -ENOTSUPP;

	LEAVE();
	return 0;
}

static int wlan_beacon_interval(wlan_private * priv, struct iwreq *wrq)
{
	int data;
	wlan_adapter *adapter = priv->adapter;

	if (wrq->u.data.length > 0) {
		if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int)))
			return -EFAULT;

		lbs_pr_debug(1, "WLAN SET BEACON INTERVAL: %d\n", data);
		if ((data > MRVDRV_MAX_BEACON_INTERVAL)
		    || (data < MRVDRV_MIN_BEACON_INTERVAL))
			return -ENOTSUPP;
		adapter->beaconperiod = data;
	}
	data = adapter->beaconperiod;
	if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int)))
		return -EFAULT;

	wrq->u.data.length = 1;

	return 0;
}

static int wlan_get_rssi(wlan_private * priv, struct iwreq *wrq)
{
	int ret = 0;
	wlan_adapter *adapter = priv->adapter;
	int temp;
	int data = 0;
	int *val;

	ENTER();
	data = SUBCMD_DATA(wrq);
	if ((data == 0) || (data == 1)) {
		ret = libertas_prepare_and_send_command(priv,
					    cmd_802_11_rssi,
					    0, cmd_option_waitforrsp,
					    0, NULL);
		if (ret) {
			LEAVE();
			return ret;
		}
	}

	switch (data) {
	case 0:

		temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG],
				adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
		break;
	case 1:
		temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG],
				adapter->NF[TYPE_BEACON][TYPE_AVG]);
		break;
	case 2:
		temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG],
				adapter->NF[TYPE_RXPD][TYPE_NOAVG]);
		break;
	case 3:
		temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
				adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
		break;
	default:
		return -ENOTSUPP;
	}
	val = (int *)wrq->u.name;
	*val = temp;

	LEAVE();
	return 0;
}

static int wlan_get_nf(wlan_private * priv, struct iwreq *wrq)
{
	int ret = 0;
	wlan_adapter *adapter = priv->adapter;
	int temp;
	int data = 0;
	int *val;

	data = SUBCMD_DATA(wrq);
	if ((data == 0) || (data == 1)) {
		ret = libertas_prepare_and_send_command(priv,
					    cmd_802_11_rssi,
					    0, cmd_option_waitforrsp,
					    0, NULL);

		if (ret) {
			LEAVE();
			return ret;
		}
	}

	switch (data) {
	case 0:
		temp = adapter->NF[TYPE_BEACON][TYPE_NOAVG];
		break;
	case 1:
		temp = adapter->NF[TYPE_BEACON][TYPE_AVG];
		break;
	case 2:
		temp = adapter->NF[TYPE_RXPD][TYPE_NOAVG];
		break;
	case 3:
		temp = adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
		break;
	default:
		return -ENOTSUPP;
	}

	temp = CAL_NF(temp);

	lbs_pr_debug(1, "%s: temp = %d\n", __FUNCTION__, temp);
	val = (int *)wrq->u.name;
	*val = temp;
	return 0;
}

static int wlan_get_txrate_ioctl(wlan_private * priv, struct ifreq *req)
{
	wlan_adapter *adapter = priv->adapter;
	int *pdata;
	struct iwreq *wrq = (struct iwreq *)req;
	int ret = 0;
	adapter->txrate = 0;
	lbs_pr_debug(1, "wlan_get_txrate_ioctl\n");
	ret = libertas_prepare_and_send_command(priv, cmd_802_11_tx_rate_query,
				    cmd_act_get, cmd_option_waitforrsp,
				    0, NULL);
	if (ret)
		return ret;

	pdata = (int *)wrq->u.name;
	*pdata = (int)adapter->txrate;
	return 0;
}

static int wlan_get_adhoc_status_ioctl(wlan_private * priv, struct iwreq *wrq)
{
	char status[64];
	wlan_adapter *adapter = priv->adapter;

	memset(status, 0, sizeof(status));

	switch (adapter->inframode) {
	case wlan802_11ibss:
		if (adapter->connect_status == libertas_connected) {
			if (adapter->adhoccreate)
				memcpy(&status, "AdhocStarted", sizeof(status));
			else
				memcpy(&status, "AdhocJoined", sizeof(status));
		} else {
			memcpy(&status, "AdhocIdle", sizeof(status));
		}
		break;
	case wlan802_11infrastructure:
		memcpy(&status, "Inframode", sizeof(status));
		break;
	default:
		memcpy(&status, "AutoUnknownmode", sizeof(status));
		break;
	}

	lbs_pr_debug(1, "status = %s\n", status);
	wrq->u.data.length = strlen(status) + 1;

	if (wrq->u.data.pointer) {
		if (copy_to_user(wrq->u.data.pointer,
				 &status, wrq->u.data.length))
			return -EFAULT;
	}

	LEAVE();
	return 0;
}

/**
 *  @brief Set/Get WPA IE
 *  @param priv                 A pointer to wlan_private structure
 *  @param req			A pointer to ifreq structure
 *  @return 	   		0 --success, otherwise fail
 */
static int wlan_setwpaie_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct iwreq *wrq = (struct iwreq *)req;
	wlan_adapter *adapter = priv->adapter;
	int ret = 0;

	ENTER();

	if (wrq->u.data.length) {
		if (wrq->u.data.length > sizeof(adapter->wpa_ie)) {
			lbs_pr_debug(1, "failed to copy WPA IE, too big \n");
			return -EFAULT;
		}
		if (copy_from_user(adapter->wpa_ie, wrq->u.data.pointer,
				   wrq->u.data.length)) {
			lbs_pr_debug(1, "failed to copy WPA IE \n");
			return -EFAULT;
		}
		adapter->wpa_ie_len = wrq->u.data.length;
		lbs_pr_debug(1, "Set wpa_ie_len=%d IE=%#x\n", adapter->wpa_ie_len,
		       adapter->wpa_ie[0]);
		lbs_dbg_hex("wpa_ie", adapter->wpa_ie, adapter->wpa_ie_len);
		if (adapter->wpa_ie[0] == WPA_IE)
			adapter->secinfo.WPAenabled = 1;
		else if (adapter->wpa_ie[0] == WPA2_IE)
			adapter->secinfo.WPA2enabled = 1;
		else {
			adapter->secinfo.WPAenabled = 0;
			adapter->secinfo.WPA2enabled = 0;
		}
	} else {
		memset(adapter->wpa_ie, 0, sizeof(adapter->wpa_ie));
		adapter->wpa_ie_len = wrq->u.data.length;
		lbs_pr_debug(1, "Reset wpa_ie_len=%d IE=%#x\n",
		       adapter->wpa_ie_len, adapter->wpa_ie[0]);
		adapter->secinfo.WPAenabled = 0;
		adapter->secinfo.WPA2enabled = 0;
	}

	// enable/disable RSN in firmware if WPA is enabled/disabled
	// depending on variable adapter->secinfo.WPAenabled is set or not
	ret = libertas_prepare_and_send_command(priv, cmd_802_11_enable_rsn,
				    cmd_act_set, cmd_option_waitforrsp,
				    0, NULL);

	LEAVE();
	return ret;
}

/**
 *  @brief Set Auto prescan
 *  @param priv                 A pointer to wlan_private structure
 *  @param wrq			A pointer to iwreq structure
 *  @return 	   		0 --success, otherwise fail
 */
static int wlan_subcmd_setprescan_ioctl(wlan_private * priv, struct iwreq *wrq)
{
	int data;
	wlan_adapter *adapter = priv->adapter;
	int *val;

	data = SUBCMD_DATA(wrq);
	lbs_pr_debug(1, "WLAN_SUBCMD_SET_PRESCAN %d\n", data);
	adapter->prescan = data;

	val = (int *)wrq->u.name;
	*val = data;
	return 0;
}

static int wlan_set_multiple_dtim_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct iwreq *wrq = (struct iwreq *)req;
	u32 mdtim;
	int idata;
	int ret = -EINVAL;

	ENTER();

	idata = SUBCMD_DATA(wrq);
	mdtim = (u32) idata;
	if (((mdtim >= MRVDRV_MIN_MULTIPLE_DTIM)
	     && (mdtim <= MRVDRV_MAX_MULTIPLE_DTIM))
	    || (mdtim == MRVDRV_IGNORE_MULTIPLE_DTIM)) {
		priv->adapter->multipledtim = mdtim;
		ret = 0;
	}
	if (ret)
		lbs_pr_debug(1, "Invalid parameter, multipledtim not changed.\n");

	LEAVE();
	return ret;
}

/**
 *  @brief Set authentication mode
 *  @param priv                 A pointer to wlan_private structure
 *  @param req			A pointer to ifreq structure
 *  @return 	   		0 --success, otherwise fail
 */
static int wlan_setauthalg_ioctl(wlan_private * priv, struct ifreq *req)
{
	int alg;
	struct iwreq *wrq = (struct iwreq *)req;
	wlan_adapter *adapter = priv->adapter;

	if (wrq->u.data.flags == 0) {
		//from iwpriv subcmd
		alg = SUBCMD_DATA(wrq);
	} else {
		//from wpa_supplicant subcmd
		if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(alg))) {
			lbs_pr_debug(1, "Copy from user failed\n");
			return -EFAULT;
		}
	}

	lbs_pr_debug(1, "auth alg is %#x\n", alg);

	switch (alg) {
	case AUTH_ALG_SHARED_KEY:
		adapter->secinfo.authmode = wlan802_11authmodeshared;
		break;
	case AUTH_ALG_NETWORK_EAP:
		adapter->secinfo.authmode =
		    wlan802_11authmodenetworkEAP;
		break;
	case AUTH_ALG_OPEN_SYSTEM:
	default:
		adapter->secinfo.authmode = wlan802_11authmodeopen;
		break;
	}
	return 0;
}

static int wlan_setencryptionmode_ioctl(wlan_private * priv, struct ifreq *req)
{
	int mode;
	struct iwreq *wrq = (struct iwreq *)req;

	ENTER();

	if (wrq->u.data.flags == 0) {
		//from iwpriv subcmd
		mode = SUBCMD_DATA(wrq);
	} else {
		//from wpa_supplicant subcmd
		if (copy_from_user(&mode, wrq->u.data.pointer, sizeof(int))) {
			lbs_pr_debug(1, "Copy from user failed\n");
			return -EFAULT;
		}
	}
	lbs_pr_debug(1, "encryption mode is %#x\n", mode);
	priv->adapter->secinfo.Encryptionmode = mode;

	LEAVE();
	return 0;
}

static void adjust_mtu(wlan_private * priv)
{
	int mtu_increment = 0;

	if (priv->adapter->linkmode == WLAN_LINKMODE_802_11)
		mtu_increment += sizeof(struct ieee80211_hdr_4addr);

	if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP)
		mtu_increment += max(sizeof(struct tx_radiotap_hdr),
				     sizeof(struct rx_radiotap_hdr));
	priv->wlan_dev.netdev->mtu = ETH_FRAME_LEN
	    - sizeof(struct ethhdr)
	    + mtu_increment;
}

/**
 *  @brief Set Link-Layer Layer mode
 *  @param priv                 A pointer to wlan_private structure
 *  @param req			A pointer to ifreq structure
 *  @return 	   		0 --success, otherwise fail
 */
static int wlan_set_linkmode_ioctl(wlan_private * priv, struct ifreq *req)
{
	int mode;

	mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data;

	switch (mode) {
	case WLAN_LINKMODE_802_3:
		priv->adapter->linkmode = mode;
		break;
	case WLAN_LINKMODE_802_11:
		priv->adapter->linkmode = mode;
		break;
	default:
		lbs_pr_info("usb8388-5: invalid link-layer mode (%#x)\n",
		       mode);
		return -EINVAL;
		break;
	}
	lbs_pr_debug(1, "usb8388-5: link-layer mode is %#x\n", mode);

	adjust_mtu(priv);

	return 0;
}

/**
 *  @brief Set Radio header mode
 *  @param priv                 A pointer to wlan_private structure
 *  @param req			A pointer to ifreq structure
 *  @return 	   		0 --success, otherwise fail
 */
static int wlan_set_radiomode_ioctl(wlan_private * priv, struct ifreq *req)
{
	int mode;

	mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data;

	switch (mode) {
	case WLAN_RADIOMODE_NONE:
		priv->adapter->radiomode = mode;
		break;
	case WLAN_RADIOMODE_RADIOTAP:
		priv->adapter->radiomode = mode;
		break;
	default:
		lbs_pr_debug(1, "usb8388-5: invalid radio header mode (%#x)\n",
		       mode);
		return -EINVAL;
	}
	lbs_pr_debug(1, "usb8388-5: radio-header mode is %#x\n", mode);

	adjust_mtu(priv);
	return 0;
}

/**
 *  @brief Set Debug header mode
 *  @param priv                 A pointer to wlan_private structure
 *  @param req			A pointer to ifreq structure
 *  @return 	   		0 --success, otherwise fail
 */
static int wlan_set_debugmode_ioctl(wlan_private * priv, struct ifreq *req)
{
	priv->adapter->debugmode = (int)((struct ifreq *)
					 ((u8 *) req + 4))->ifr_data;
	return 0;
}

static int wlan_subcmd_getrxantenna_ioctl(wlan_private * priv,
					  struct ifreq *req)
{
	int len;
	char buf[8];
	struct iwreq *wrq = (struct iwreq *)req;

	lbs_pr_debug(1, "WLAN_SUBCMD_GETRXANTENNA\n");
	len = getrxantenna(priv, buf);

	wrq->u.data.length = len;
	if (wrq->u.data.pointer) {
		if (copy_to_user(wrq->u.data.pointer, &buf, len)) {
			lbs_pr_debug(1, "CopyToUser failed\n");
			return -EFAULT;
		}
	}

	return 0;
}

static int wlan_subcmd_gettxantenna_ioctl(wlan_private * priv,
					  struct ifreq *req)
{
	int len;
	char buf[8];
	struct iwreq *wrq = (struct iwreq *)req;

	lbs_pr_debug(1, "WLAN_SUBCMD_GETTXANTENNA\n");
	len = gettxantenna(priv, buf);

	wrq->u.data.length = len;
	if (wrq->u.data.pointer) {
		if (copy_to_user(wrq->u.data.pointer, &buf, len)) {
			lbs_pr_debug(1, "CopyToUser failed\n");
			return -EFAULT;
		}
	}
	return 0;
}

/**
 *  @brief Get the MAC TSF value from the firmware
 *
 *  @param priv         A pointer to wlan_private structure
 *  @param wrq          A pointer to iwreq structure containing buffer
 *                      space to store a TSF value retrieved from the firmware
 *
 *  @return             0 if successful; IOCTL error code otherwise
 */
static int wlan_get_tsf_ioctl(wlan_private * priv, struct iwreq *wrq)
{
	u64 tsfval;
	int ret;

	ret = libertas_prepare_and_send_command(priv,
				    cmd_get_tsf,
				    0, cmd_option_waitforrsp, 0, &tsfval);

	lbs_pr_debug(1, "IOCTL: Get TSF = 0x%016llx\n", tsfval);

	if (ret != 0) {
		lbs_pr_debug(1, "IOCTL: Get TSF; command exec failed\n");
		ret = -EFAULT;
	} else {
		if (copy_to_user(wrq->u.data.pointer,
				 &tsfval,
				 min_t(size_t, wrq->u.data.length,
				     sizeof(tsfval))) != 0) {

			lbs_pr_debug(1, "IOCTL: Get TSF; Copy to user failed\n");
			ret = -EFAULT;
		} else {
			ret = 0;
		}
	}
	return ret;
}

/**
 *  @brief Get/Set adapt rate
 *  @param priv                 A pointer to wlan_private structure
 *  @param wrq			A pointer to iwreq structure
 *  @return 	   		0 --success, otherwise fail
 */
static int wlan_adapt_rateset(wlan_private * priv, struct iwreq *wrq)
{
	int ret;
	wlan_adapter *adapter = priv->adapter;
	int data[2];

	memset(data, 0, sizeof(data));
	if (!wrq->u.data.length) {
		lbs_pr_debug(1, "Get ADAPT RATE SET\n");
		ret = libertas_prepare_and_send_command(priv,
					    cmd_802_11_rate_adapt_rateset,
					    cmd_act_get,
					    cmd_option_waitforrsp, 0, NULL);
		data[0] = adapter->enablehwauto;
		data[1] = adapter->ratebitmap;
		if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) {
			lbs_pr_debug(1, "Copy to user failed\n");
			return -EFAULT;
		}
#define GET_TWO_INT	2
		wrq->u.data.length = GET_TWO_INT;
	} else {
		lbs_pr_debug(1, "Set ADAPT RATE SET\n");
		if (wrq->u.data.length > 2)
			return -EINVAL;
		if (copy_from_user
		    (data, wrq->u.data.pointer,
		     sizeof(int) * wrq->u.data.length)) {
			lbs_pr_debug(1, "Copy from user failed\n");
			return -EFAULT;
		}

		adapter->enablehwauto = data[0];
		adapter->ratebitmap = data[1];
		ret = libertas_prepare_and_send_command(priv,
					    cmd_802_11_rate_adapt_rateset,
					    cmd_act_set,
					    cmd_option_waitforrsp, 0, NULL);
	}
	return ret;
}

/**
 *  @brief Get/Set inactivity timeout
 *  @param priv                 A pointer to wlan_private structure
 *  @param wrq			A pointer to iwreq structure
 *  @return 	   		0 --success, otherwise fail
 */
static int wlan_inactivity_timeout(wlan_private * priv, struct iwreq *wrq)
{
	int ret;
	int data = 0;
	u16 timeout = 0;

	ENTER();
	if (wrq->u.data.length > 1)
		return -ENOTSUPP;

	if (wrq->u.data.length == 0) {
		/* Get */
		ret = libertas_prepare_and_send_command(priv,
					    cmd_802_11_inactivity_timeout,
					    cmd_act_get,
					    cmd_option_waitforrsp, 0,
					    &timeout);
		data = timeout;
		if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
			lbs_pr_debug(1, "Copy to user failed\n");
			return -EFAULT;
		}
	} else {
		/* Set */
		if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
			lbs_pr_debug(1, "Copy from user failed\n");
			return -EFAULT;
		}

		timeout = data;
		ret = libertas_prepare_and_send_command(priv,
					    cmd_802_11_inactivity_timeout,
					    cmd_act_set,
					    cmd_option_waitforrsp, 0,
					    &timeout);
	}

	wrq->u.data.length = 1;

	LEAVE();
	return ret;
}

static int wlan_do_getlog_ioctl(wlan_private * priv, struct iwreq *wrq)
{
	int ret;
	char buf[GETLOG_BUFSIZE - 1];
	wlan_adapter *adapter = priv->adapter;

	lbs_pr_debug(1, " GET STATS\n");

	ret = libertas_prepare_and_send_command(priv, cmd_802_11_get_log,
				    0, cmd_option_waitforrsp, 0, NULL);

	if (ret) {
		return ret;
	}

	if (wrq->u.data.pointer) {
		sprintf(buf, "\n  mcasttxframe %u failed %u retry %u "
			"multiretry %u framedup %u "
			"rtssuccess %u rtsfailure %u ackfailure %u\n"
			"rxfrag %u mcastrxframe %u fcserror %u "
			"txframe %u wepundecryptable %u ",
			adapter->logmsg.mcasttxframe,
			adapter->logmsg.failed,
			adapter->logmsg.retry,
			adapter->logmsg.multiretry,
			adapter->logmsg.framedup,
			adapter->logmsg.rtssuccess,
			adapter->logmsg.rtsfailure,
			adapter->logmsg.ackfailure,
			adapter->logmsg.rxfrag,
			adapter->logmsg.mcastrxframe,
			adapter->logmsg.fcserror,
			adapter->logmsg.txframe,
			adapter->logmsg.wepundecryptable);
		wrq->u.data.length = strlen(buf) + 1;
		if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) {
			lbs_pr_debug(1, "Copy to user failed\n");
			return -EFAULT;
		}
	}

	return 0;
}

static int wlan_scan_type_ioctl(wlan_private * priv, struct iwreq *wrq)
{
	u8 buf[12];
	u8 *option[] = { "active", "passive", "get", };
	int i, max_options = (sizeof(option) / sizeof(option[0]));
	int ret = 0;
	wlan_adapter *adapter = priv->adapter;

	if (priv->adapter->enable11d) {
		lbs_pr_debug(1, "11D: Cannot set scantype when 11D enabled\n");
		return -EFAULT;
	}

	memset(buf, 0, sizeof(buf));

	if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf),
							 wrq->u.data.length)))
		return -EFAULT;

	lbs_pr_debug(1, "Scan type Option = %s\n", buf);

	buf[sizeof(buf) - 1] = '\0';

	for (i = 0; i < max_options; i++) {
		if (!strcmp(buf, option[i]))
			break;
	}

	switch (i) {
	case 0:
		adapter->scantype = cmd_scan_type_active;
		break;
	case 1:
		adapter->scantype = cmd_scan_type_passive;
		break;
	case 2:
		wrq->u.data.length = strlen(option[adapter->scantype]) + 1;

		if (copy_to_user(wrq->u.data.pointer,
				 option[adapter->scantype],
				 wrq->u.data.length)) {
			lbs_pr_debug(1, "Copy to user failed\n");
			ret = -EFAULT;
		}

		break;
	default:
		lbs_pr_debug(1, "Invalid Scan type Ioctl Option\n");
		ret = -EINVAL;
		break;
	}

	return ret;
}

static int wlan_scan_mode_ioctl(wlan_private * priv, struct iwreq *wrq)
{
	wlan_adapter *adapter = priv->adapter;
	u8 buf[12];
	u8 *option[] = { "bss", "ibss", "any", "get" };
	int i, max_options = (sizeof(option) / sizeof(option[0]));
	int ret = 0;

	ENTER();

	memset(buf, 0, sizeof(buf));

	if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf),
							 wrq->u.data.length))) {
		lbs_pr_debug(1, "Copy from user failed\n");
		return -EFAULT;
	}

	lbs_pr_debug(1, "Scan mode Option = %s\n", buf);

	buf[sizeof(buf) - 1] = '\0';

	for (i = 0; i < max_options; i++) {
		if (!strcmp(buf, option[i]))
			break;
	}

	switch (i) {

	case 0:
		adapter->scanmode = cmd_bss_type_bss;
		break;
	case 1:
		adapter->scanmode = cmd_bss_type_ibss;
		break;
	case 2:
		adapter->scanmode = cmd_bss_type_any;
		break;
	case 3:

		wrq->u.data.length = strlen(option[adapter->scanmode - 1]) + 1;

		lbs_pr_debug(1, "Get Scan mode Option = %s\n",
		       option[adapter->scanmode - 1]);

		lbs_pr_debug(1, "Scan mode length %d\n", wrq->u.data.length);

		if (copy_to_user(wrq->u.data.pointer,
				 option[adapter->scanmode - 1],
				 wrq->u.data.length)) {
			lbs_pr_debug(1, "Copy to user failed\n");
			ret = -EFAULT;
		}
		lbs_pr_debug(1, "GET Scan type Option after copy = %s\n",
		       (char *)wrq->u.data.pointer);

		break;

	default:
		lbs_pr_debug(1, "Invalid Scan mode Ioctl Option\n");
		ret = -EINVAL;
		break;
	}

	LEAVE();
	return ret;
}

/**
 *  @brief Get/Set Adhoc G Rate
 *
 *  @param priv		A pointer to wlan_private structure
 *  @param wrq	   	A pointer to user data
 *  @return 	   	0--success, otherwise fail
 */
static int wlan_do_set_grate_ioctl(wlan_private * priv, struct iwreq *wrq)
{
	wlan_adapter *adapter = priv->adapter;
	int data, data1;
	int *val;

	ENTER();

	data1 = SUBCMD_DATA(wrq);
	switch (data1) {
	case 0:
		adapter->adhoc_grate_enabled = 0;
		break;
	case 1:
		adapter->adhoc_grate_enabled = 1;
		break;
	case 2:
		break;
	default:
		return -EINVAL;
	}
	data = adapter->adhoc_grate_enabled;
	val = (int *)wrq->u.name;
	*val = data;
	LEAVE();
	return 0;
}

static inline int hex2int(char c)
{
	if (c >= '0' && c <= '9')
		return (c - '0');
	if (c >= 'a' && c <= 'f')
		return (c - 'a' + 10);
	if (c >= 'A' && c <= 'F')
		return (c - 'A' + 10);
	return -1;
}

/* Convert a string representation of a MAC address ("xx:xx:xx:xx:xx:xx")
   into binary format (6 bytes).

   This function expects that each byte is represented with 2 characters
   (e.g., 11:2:11:11:11:11 is invalid)

 */
static char *eth_str2addr(char *ethstr, u8 * addr)
{
	int i, val, val2;
	char *pos = ethstr;

	/* get rid of initial blanks */
	while (*pos == ' ' || *pos == '\t')
		++pos;

	for (i = 0; i < 6; i++) {
		val = hex2int(*pos++);
		if (val < 0)
			return NULL;
		val2 = hex2int(*pos++);
		if (val2 < 0)
			return NULL;
		addr[i] = (val * 16 + val2) & 0xff;

		if (i < 5 && *pos++ != ':')
			return NULL;
	}
	return pos;
}

/* this writes xx:xx:xx:xx:xx:xx into ethstr
   (ethstr must have space for 18 chars) */
static int eth_addr2str(u8 * addr, char *ethstr)
{
	int i;
	char *pos = ethstr;

	for (i = 0; i < 6; i++) {
		sprintf(pos, "%02x", addr[i] & 0xff);
		pos += 2;
		if (i < 5)
			*pos++ = ':';
	}
	return 17;
}

/**
 *  @brief          Add an entry to the BT table
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_bt_add_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct iwreq *wrq = (struct iwreq *)req;
	char ethaddrs_str[18];
	char *pos;
	u8 ethaddr[ETH_ALEN];

	ENTER();
	if (copy_from_user(ethaddrs_str, wrq->u.data.pointer,
			   sizeof(ethaddrs_str)))
		return -EFAULT;

	if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) {
		lbs_pr_info("BT_ADD: Invalid MAC address\n");
		return -EINVAL;
	}

	lbs_pr_debug(1, "BT: adding %s\n", ethaddrs_str);
	LEAVE();
	return (libertas_prepare_and_send_command(priv, cmd_bt_access,
				      cmd_act_bt_access_add,
				      cmd_option_waitforrsp, 0, ethaddr));
}

/**
 *  @brief          Delete an entry from the BT table
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_bt_del_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct iwreq *wrq = (struct iwreq *)req;
	char ethaddrs_str[18];
	u8 ethaddr[ETH_ALEN];
	char *pos;

	ENTER();
	if (copy_from_user(ethaddrs_str, wrq->u.data.pointer,
			   sizeof(ethaddrs_str)))
		return -EFAULT;

	if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) {
		lbs_pr_info("Invalid MAC address\n");
		return -EINVAL;
	}

	lbs_pr_debug(1, "BT: deleting %s\n", ethaddrs_str);

	return (libertas_prepare_and_send_command(priv,
				      cmd_bt_access,
				      cmd_act_bt_access_del,
				      cmd_option_waitforrsp, 0, ethaddr));
	LEAVE();
	return 0;
}

/**
 *  @brief          Reset all entries from the BT table
 *  @param priv     A pointer to wlan_private structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_bt_reset_ioctl(wlan_private * priv)
{
	ENTER();

	lbs_pr_alert( "BT: resetting\n");

	return (libertas_prepare_and_send_command(priv,
				      cmd_bt_access,
				      cmd_act_bt_access_reset,
				      cmd_option_waitforrsp, 0, NULL));

	LEAVE();
	return 0;
}

/**
 *  @brief          List an entry from the BT table
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_bt_list_ioctl(wlan_private * priv, struct ifreq *req)
{
	int pos;
	char *addr1;
	struct iwreq *wrq = (struct iwreq *)req;
	/* used to pass id and store the bt entry returned by the FW */
	union {
		int id;
		char addr1addr2[2 * ETH_ALEN];
	} param;
	static char outstr[64];
	char *pbuf = outstr;
	int ret;

	ENTER();

	if (copy_from_user(outstr, wrq->u.data.pointer, sizeof(outstr))) {
		lbs_pr_debug(1, "Copy from user failed\n");
		return -1;
	}
	param.id = simple_strtoul(outstr, NULL, 10);
	pos = sprintf(pbuf, "%d: ", param.id);
	pbuf += pos;

	ret = libertas_prepare_and_send_command(priv, cmd_bt_access,
				    cmd_act_bt_access_list,
				    cmd_option_waitforrsp, 0,
				    (char *)&param);

	if (ret == 0) {
		addr1 = param.addr1addr2;

		pos = sprintf(pbuf, "ignoring traffic from ");
		pbuf += pos;
		pos = eth_addr2str(addr1, pbuf);
		pbuf += pos;
	} else {
		sprintf(pbuf, "(null)");
		pbuf += pos;
	}

	wrq->u.data.length = strlen(outstr);
	if (copy_to_user(wrq->u.data.pointer, (char *)outstr,
			 wrq->u.data.length)) {
		lbs_pr_debug(1, "BT_LIST: Copy to user failed!\n");
		return -EFAULT;
	}

	LEAVE();
	return 0;
}

/**
 *  @brief          Find the next parameter in an input string
 *  @param ptr      A pointer to the input parameter string
 *  @return         A pointer to the next parameter, or 0 if no parameters left.
 */
static char * next_param(char * ptr)
{
	if (!ptr) return NULL;
	while (*ptr == ' ' || *ptr == '\t') ++ptr;
	return (*ptr == '\0') ? NULL : ptr;
}

/**
 *  @brief          Add an entry to the FWT table
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_fwt_add_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct iwreq *wrq = (struct iwreq *)req;
	char in_str[128];
	static struct cmd_ds_fwt_access fwt_access;
	char *ptr;

	ENTER();
	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
		return -EFAULT;

	if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
		lbs_pr_alert( "FWT_ADD: Invalid MAC address 1\n");
		return -EINVAL;
	}

	if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) {
		lbs_pr_alert( "FWT_ADD: Invalid MAC address 2\n");
		return -EINVAL;
	}

	if ((ptr = next_param(ptr)))
		fwt_access.metric =
			cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
	else
		fwt_access.metric = FWT_DEFAULT_METRIC;

	if ((ptr = next_param(ptr)))
		fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10);
	else
		fwt_access.dir = FWT_DEFAULT_DIR;

	if ((ptr = next_param(ptr)))
		fwt_access.ssn =
			cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
	else
		fwt_access.ssn = FWT_DEFAULT_SSN;

	if ((ptr = next_param(ptr)))
		fwt_access.dsn =
			cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
	else
		fwt_access.dsn = FWT_DEFAULT_DSN;

	if ((ptr = next_param(ptr)))
		fwt_access.hopcount = simple_strtoul(ptr, &ptr, 10);
	else
		fwt_access.hopcount = FWT_DEFAULT_HOPCOUNT;

	if ((ptr = next_param(ptr)))
		fwt_access.ttl = simple_strtoul(ptr, &ptr, 10);
	else
		fwt_access.ttl = FWT_DEFAULT_TTL;

	if ((ptr = next_param(ptr)))
		fwt_access.expiration =
			cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
	else
		fwt_access.expiration = FWT_DEFAULT_EXPIRATION;

	if ((ptr = next_param(ptr)))
		fwt_access.sleepmode = (u8)simple_strtoul(ptr, &ptr, 10);
	else
		fwt_access.sleepmode = FWT_DEFAULT_SLEEPMODE;

	if ((ptr = next_param(ptr)))
		fwt_access.snr =
			cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
	else
		fwt_access.snr = FWT_DEFAULT_SNR;

#ifdef DEBUG
	{
		char ethaddr1_str[18], ethaddr2_str[18];
		eth_addr2str(fwt_access.da, ethaddr1_str);
		eth_addr2str(fwt_access.ra, ethaddr2_str);
		lbs_pr_debug(1, "FWT_ADD: adding (da:%s,%i,ra:%s)\n", ethaddr1_str,
		       fwt_access.dir, ethaddr2_str);
		lbs_pr_debug(1, "FWT_ADD: ssn:%u dsn:%u met:%u hop:%u ttl:%u exp:%u slp:%u snr:%u\n",
		       fwt_access.ssn, fwt_access.dsn, fwt_access.metric,
		       fwt_access.hopcount, fwt_access.ttl, fwt_access.expiration,
		       fwt_access.sleepmode, fwt_access.snr);
	}
#endif

	LEAVE();
	return (libertas_prepare_and_send_command(priv, cmd_fwt_access,
						  cmd_act_fwt_access_add,
						  cmd_option_waitforrsp, 0,
						  (void *)&fwt_access));
}

/**
 *  @brief          Delete an entry from the FWT table
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_fwt_del_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct iwreq *wrq = (struct iwreq *)req;
	char in_str[64];
	static struct cmd_ds_fwt_access fwt_access;
	char *ptr;

	ENTER();
	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
		return -EFAULT;

	if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
		lbs_pr_alert( "FWT_DEL: Invalid MAC address 1\n");
		return -EINVAL;
	}

	if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) {
		lbs_pr_alert( "FWT_DEL: Invalid MAC address 2\n");
		return -EINVAL;
	}

	if ((ptr = next_param(ptr)))
		fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10);
	else
		fwt_access.dir = FWT_DEFAULT_DIR;

#ifdef DEBUG
	{
		char ethaddr1_str[18], ethaddr2_str[18];
		lbs_pr_debug(1, "FWT_DEL: line is %s\n", in_str);
		eth_addr2str(fwt_access.da, ethaddr1_str);
		eth_addr2str(fwt_access.ra, ethaddr2_str);
		lbs_pr_debug(1, "FWT_DEL: removing (da:%s,ra:%s,dir:%d)\n", ethaddr1_str,
		       ethaddr2_str, fwt_access.dir);
	}
#endif

	LEAVE();
	return (libertas_prepare_and_send_command(priv,
						  cmd_fwt_access,
						  cmd_act_fwt_access_del,
						  cmd_option_waitforrsp, 0,
						  (void *)&fwt_access));
}


/**
 *  @brief             Print route parameters
 *  @param fwt_access  struct cmd_ds_fwt_access with route info
 *  @param buf         destination buffer for route info
 */
static void print_route(struct cmd_ds_fwt_access fwt_access, char *buf)
{
	buf += sprintf(buf, " ");
	buf += eth_addr2str(fwt_access.da, buf);
	buf += sprintf(buf, " ");
	buf += eth_addr2str(fwt_access.ra, buf);
	buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.metric));
	buf += sprintf(buf, " %u", fwt_access.dir);
	buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.ssn));
	buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.dsn));
	buf += sprintf(buf, " %u", fwt_access.hopcount);
	buf += sprintf(buf, " %u", fwt_access.ttl);
	buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.expiration));
	buf += sprintf(buf, " %u", fwt_access.sleepmode);
	buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.snr));
}

/**
 *  @brief          Lookup an entry in the FWT table
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_fwt_lookup_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct iwreq *wrq = (struct iwreq *)req;
	char in_str[64];
	char *ptr;
	static struct cmd_ds_fwt_access fwt_access;
	static char out_str[128];
	int ret;

	ENTER();
	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
		return -EFAULT;

	if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
		lbs_pr_alert( "FWT_LOOKUP: Invalid MAC address\n");
		return -EINVAL;
	}

#ifdef DEBUG
	{
		char ethaddr1_str[18];
		lbs_pr_debug(1, "FWT_LOOKUP: line is %s\n", in_str);
		eth_addr2str(fwt_access.da, ethaddr1_str);
		lbs_pr_debug(1, "FWT_LOOKUP: looking for (da:%s)\n", ethaddr1_str);
	}
#endif

	ret = libertas_prepare_and_send_command(priv,
						cmd_fwt_access,
						cmd_act_fwt_access_lookup,
						cmd_option_waitforrsp, 0,
						(void *)&fwt_access);

	if (ret == 0)
		print_route(fwt_access, out_str);
	else
		sprintf(out_str, "(null)");

	wrq->u.data.length = strlen(out_str);
	if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
			 wrq->u.data.length)) {
		lbs_pr_debug(1, "FWT_LOOKUP: Copy to user failed!\n");
		return -EFAULT;
	}

	LEAVE();
	return 0;
}

/**
 *  @brief          Reset all entries from the FWT table
 *  @param priv     A pointer to wlan_private structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_fwt_reset_ioctl(wlan_private * priv)
{
	lbs_pr_debug(1, "FWT: resetting\n");

	return (libertas_prepare_and_send_command(priv,
				      cmd_fwt_access,
				      cmd_act_fwt_access_reset,
				      cmd_option_waitforrsp, 0, NULL));
}

/**
 *  @brief          List an entry from the FWT table
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_fwt_list_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct iwreq *wrq = (struct iwreq *)req;
	char in_str[8];
	static struct cmd_ds_fwt_access fwt_access;
	char *ptr = in_str;
	static char out_str[128];
	char *pbuf = out_str;
	int ret;

	ENTER();
	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
		return -EFAULT;

	fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));

#ifdef DEBUG
	{
		lbs_pr_debug(1, "FWT_LIST: line is %s\n", in_str);
		lbs_pr_debug(1, "FWT_LIST: listing id:%i\n", le32_to_cpu(fwt_access.id));
	}
#endif

	ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
				    cmd_act_fwt_access_list,
				    cmd_option_waitforrsp, 0, (void *)&fwt_access);

	if (ret == 0)
		print_route(fwt_access, pbuf);
	else
		pbuf += sprintf(pbuf, " (null)");

	wrq->u.data.length = strlen(out_str);
	if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
			 wrq->u.data.length)) {
		lbs_pr_debug(1, "FWT_LIST: Copy to user failed!\n");
		return -EFAULT;
	}

	LEAVE();
	return 0;
}

/**
 *  @brief          List an entry from the FRT table
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_fwt_list_route_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct iwreq *wrq = (struct iwreq *)req;
	char in_str[64];
	static struct cmd_ds_fwt_access fwt_access;
	char *ptr = in_str;
	static char out_str[128];
	char *pbuf = out_str;
	int ret;

	ENTER();
	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
		return -EFAULT;

	fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));

#ifdef DEBUG
	{
		lbs_pr_debug(1, "FWT_LIST_ROUTE: line is %s\n", in_str);
		lbs_pr_debug(1, "FWT_LIST_ROUTE: listing id:%i\n", le32_to_cpu(fwt_access.id));
	}
#endif

	ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
				    cmd_act_fwt_access_list_route,
				    cmd_option_waitforrsp, 0, (void *)&fwt_access);

	if (ret == 0) {
		pbuf += sprintf(pbuf, " ");
		pbuf += eth_addr2str(fwt_access.da, pbuf);
		pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.metric));
		pbuf += sprintf(pbuf, " %u", fwt_access.dir);
		/* note that the firmware returns the nid in the id field */
		pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.id));
		pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.ssn));
		pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.dsn));
		pbuf += sprintf(pbuf, "  hop %u", fwt_access.hopcount);
		pbuf += sprintf(pbuf, "  ttl %u", fwt_access.ttl);
		pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.expiration));
	} else
		pbuf += sprintf(pbuf, " (null)");

	wrq->u.data.length = strlen(out_str);
	if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
			 wrq->u.data.length)) {
		lbs_pr_debug(1, "FWT_LIST_ROUTE: Copy to user failed!\n");
		return -EFAULT;
	}

	LEAVE();
	return 0;
}

/**
 *  @brief          List an entry from the FNT table
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_fwt_list_neighbor_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct iwreq *wrq = (struct iwreq *)req;
	char in_str[8];
	static struct cmd_ds_fwt_access fwt_access;
	char *ptr = in_str;
	static char out_str[128];
	char *pbuf = out_str;
	int ret;

	ENTER();
	if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
		return -EFAULT;

	memset(&fwt_access, 0, sizeof(fwt_access));
	fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));

#ifdef DEBUG
	{
		lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: line is %s\n", in_str);
		lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: listing id:%i\n", le32_to_cpu(fwt_access.id));
	}
#endif

	ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
				    cmd_act_fwt_access_list_neighbor,
				    cmd_option_waitforrsp, 0,
				    (void *)&fwt_access);

	if (ret == 0) {
		pbuf += sprintf(pbuf, " ra ");
		pbuf += eth_addr2str(fwt_access.ra, pbuf);
		pbuf += sprintf(pbuf, "  slp %u", fwt_access.sleepmode);
		pbuf += sprintf(pbuf, "  snr %u", le32_to_cpu(fwt_access.snr));
		pbuf += sprintf(pbuf, "  ref %u", le32_to_cpu(fwt_access.references));
	} else
		pbuf += sprintf(pbuf, " (null)");

	wrq->u.data.length = strlen(out_str);
	if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
			 wrq->u.data.length)) {
		lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: Copy to user failed!\n");
		return -EFAULT;
	}

	LEAVE();
	return 0;
}

/**
 *  @brief          Cleans up the route (FRT) and neighbor (FNT) tables
 *                  (Garbage Collection)
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_fwt_cleanup_ioctl(wlan_private * priv, struct ifreq *req)
{
	static struct cmd_ds_fwt_access fwt_access;
	int ret;

	ENTER();

	lbs_pr_debug(1, "FWT: cleaning up\n");

	memset(&fwt_access, 0, sizeof(fwt_access));

	ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
				    cmd_act_fwt_access_cleanup,
				    cmd_option_waitforrsp, 0,
				    (void *)&fwt_access);

	if (ret == 0)
		req->ifr_data = (char *)(le32_to_cpu(fwt_access.references));
	else
		return -EFAULT;

	LEAVE();
	return 0;
}

/**
 *  @brief          Gets firmware internal time (debug purposes)
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_fwt_time_ioctl(wlan_private * priv, struct ifreq *req)
{
	static struct cmd_ds_fwt_access fwt_access;
	int ret;

	ENTER();

	lbs_pr_debug(1, "FWT: getting time\n");

	memset(&fwt_access, 0, sizeof(fwt_access));

	ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
				    cmd_act_fwt_access_time,
				    cmd_option_waitforrsp, 0,
				    (void *)&fwt_access);

	if (ret == 0)
		req->ifr_data = (char *)(le32_to_cpu(fwt_access.references));
	else
		return -EFAULT;

	LEAVE();
	return 0;
}

/**
 *  @brief          Gets mesh ttl from firmware
 *  @param priv     A pointer to wlan_private structure
 *  @param req      A pointer to ifreq structure
 *  @return         0 --success, otherwise fail
 */
static int wlan_mesh_get_ttl_ioctl(wlan_private * priv, struct ifreq *req)
{
	struct cmd_ds_mesh_access mesh_access;
	int ret;

	ENTER();

	memset(&mesh_access, 0, sizeof(mesh_access));

	ret = libertas_prepare_and_send_command(priv, cmd_mesh_access,
				    cmd_act_mesh_get_ttl,
				    cmd_option_waitforrsp, 0,
				    (void *)&mesh_access);

	if (ret == 0) {
		req->ifr_data = (char *)(le32_to_cpu(mesh_access.data[0]));
	}
	else
		return -EFAULT;

	LEAVE();
	return 0;
}

/**
 *  @brief          Gets mesh ttl from firmware
 *  @param priv     A pointer to wlan_private structure
 *  @param ttl      New ttl value
 *  @return         0 --success, otherwise fail
 */
static int wlan_mesh_set_ttl_ioctl(wlan_private * priv, int ttl)
{
	struct cmd_ds_mesh_access mesh_access;
	int ret;

	ENTER();

	if( (ttl > 0xff) || (ttl < 0) )
		return -EINVAL;

	memset(&mesh_access, 0, sizeof(mesh_access));
	mesh_access.data[0] = ttl;

	ret = libertas_prepare_and_send_command(priv, cmd_mesh_access,
						cmd_act_mesh_set_ttl,
						cmd_option_waitforrsp, 0,
						(void *)&mesh_access);

	if (ret != 0)
		ret = -EFAULT;

	LEAVE();
	return ret;
}

/**
 *  @brief ioctl function - entry point
 *
 *  @param dev		A pointer to net_device structure
 *  @param req	   	A pointer to ifreq structure
 *  @param cmd 		command
 *  @return 	   	0--success, otherwise fail
 */
int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{
	int subcmd = 0;
	int idata = 0;
	int *pdata;
	int ret = 0;
	wlan_private *priv = dev->priv;
	wlan_adapter *adapter = priv->adapter;
	struct iwreq *wrq = (struct iwreq *)req;

	ENTER();

	lbs_pr_debug(1, "libertas_do_ioctl: ioctl cmd = 0x%x\n", cmd);
	switch (cmd) {
	case WLANSCAN_TYPE:
		lbs_pr_debug(1, "Scan type Ioctl\n");
		ret = wlan_scan_type_ioctl(priv, wrq);
		break;

	case WLAN_SETNONE_GETNONE:	/* set WPA mode on/off ioctl #20 */
		switch (wrq->u.data.flags) {
		case WLANDEAUTH:
			lbs_pr_debug(1, "Deauth\n");
			libertas_send_deauth(priv);
			break;

		case WLANADHOCSTOP:
			lbs_pr_debug(1, "Adhoc stop\n");
			ret = libertas_do_adhocstop_ioctl(priv);
			break;

		case WLANRADIOON:
			wlan_radio_ioctl(priv, 1);
			break;

		case WLANRADIOOFF:
			wlan_radio_ioctl(priv, 0);
			break;
		case WLANWLANIDLEON:
			libertas_idle_on(priv);
			break;
		case WLANWLANIDLEOFF:
			libertas_idle_off(priv);
			break;
		case WLAN_SUBCMD_BT_RESET:	/* bt_reset */
			wlan_bt_reset_ioctl(priv);
			break;
		case WLAN_SUBCMD_FWT_RESET:	/* fwt_reset */
			wlan_fwt_reset_ioctl(priv);
			break;
		}		/* End of switch */
		break;

	case WLANSETWPAIE:
		ret = wlan_setwpaie_ioctl(priv, req);
		break;
	case WLAN_SETINT_GETINT:
		/* The first 4 bytes of req->ifr_data is sub-ioctl number
		 * after 4 bytes sits the payload.
		 */
		subcmd = (int)req->ifr_data;	//from iwpriv subcmd
		switch (subcmd) {
		case WLANNF:
			ret = wlan_get_nf(priv, wrq);
			break;
		case WLANRSSI:
			ret = wlan_get_rssi(priv, wrq);
			break;
		case WLANENABLE11D:
			ret = libertas_cmd_enable_11d(priv, wrq);
			break;
		case WLANADHOCGRATE:
			ret = wlan_do_set_grate_ioctl(priv, wrq);
			break;
		case WLAN_SUBCMD_SET_PRESCAN:
			ret = wlan_subcmd_setprescan_ioctl(priv, wrq);
			break;
		}
		break;

	case WLAN_SETONEINT_GETONEINT:
		switch (wrq->u.data.flags) {
		case WLAN_BEACON_INTERVAL:
			ret = wlan_beacon_interval(priv, wrq);
			break;

		case WLAN_LISTENINTRVL:
			if (!wrq->u.data.length) {
				int data;
				lbs_pr_debug(1, "Get locallisteninterval value\n");
#define GET_ONE_INT	1
				data = adapter->locallisteninterval;
				if (copy_to_user(wrq->u.data.pointer,
						 &data, sizeof(int))) {
					lbs_pr_debug(1, "Copy to user failed\n");
					return -EFAULT;
				}

				wrq->u.data.length = GET_ONE_INT;
			} else {
				int data;
				if (copy_from_user
				    (&data, wrq->u.data.pointer, sizeof(int))) {
					lbs_pr_debug(1, "Copy from user failed\n");
					return -EFAULT;
				}

				lbs_pr_debug(1, "Set locallisteninterval = %d\n",
				       data);
#define MAX_U16_VAL	65535
				if (data > MAX_U16_VAL) {
					lbs_pr_debug(1, "Exceeds U16 value\n");
					return -EINVAL;
				}
				adapter->locallisteninterval = data;
			}
			break;
		case WLAN_TXCONTROL:
			ret = wlan_txcontrol(priv, wrq);	//adds for txcontrol ioctl
			break;

		case WLAN_NULLPKTINTERVAL:
			ret = wlan_null_pkt_interval(priv, wrq);
			break;

		default:
			ret = -EOPNOTSUPP;
			break;
		}
		break;

	case WLAN_SETONEINT_GETNONE:
		/* The first 4 bytes of req->ifr_data is sub-ioctl number
		 * after 4 bytes sits the payload.
		 */
		subcmd = wrq->u.data.flags;	//from wpa_supplicant subcmd

		if (!subcmd)
			subcmd = (int)req->ifr_data;	//from iwpriv subcmd

		switch (subcmd) {
		case WLAN_SUBCMD_SETRXANTENNA:	/* SETRXANTENNA */
			idata = SUBCMD_DATA(wrq);
			ret = setrxantenna(priv, idata);
			break;
		case WLAN_SUBCMD_SETTXANTENNA:	/* SETTXANTENNA */
			idata = SUBCMD_DATA(wrq);
			ret = settxantenna(priv, idata);
			break;
		case WLAN_SET_ATIM_WINDOW:
			adapter->atimwindow = SUBCMD_DATA(wrq);
			adapter->atimwindow = min_t(__u16, adapter->atimwindow, 50);
			break;
		case WLANSETBCNAVG:
			adapter->bcn_avg_factor = SUBCMD_DATA(wrq);
			if (adapter->bcn_avg_factor == 0)
				adapter->bcn_avg_factor =
				    DEFAULT_BCN_AVG_FACTOR;
			if (adapter->bcn_avg_factor > DEFAULT_BCN_AVG_FACTOR)
				adapter->bcn_avg_factor =
				    DEFAULT_BCN_AVG_FACTOR;
			break;
		case WLANSETDATAAVG:
			adapter->data_avg_factor = SUBCMD_DATA(wrq);
			if (adapter->data_avg_factor == 0)
				adapter->data_avg_factor =
				    DEFAULT_DATA_AVG_FACTOR;
			if (adapter->data_avg_factor > DEFAULT_DATA_AVG_FACTOR)
				adapter->data_avg_factor =
				    DEFAULT_DATA_AVG_FACTOR;
			break;
		case WLANSETREGION:
			idata = SUBCMD_DATA(wrq);
			ret = wlan_set_region(priv, (u16) idata);
			break;

		case WLAN_SET_LISTEN_INTERVAL:
			idata = SUBCMD_DATA(wrq);
			adapter->listeninterval = (u16) idata;
			break;

		case WLAN_SET_MULTIPLE_DTIM:
			ret = wlan_set_multiple_dtim_ioctl(priv, req);
			break;

		case WLANSETAUTHALG:
			ret = wlan_setauthalg_ioctl(priv, req);
			break;

		case WLANSETENCRYPTIONMODE:
			ret = wlan_setencryptionmode_ioctl(priv, req);
			break;

		case WLAN_SET_LINKMODE:
			ret = wlan_set_linkmode_ioctl(priv, req);
			break;

		case WLAN_SET_RADIOMODE:
			ret = wlan_set_radiomode_ioctl(priv, req);
			break;

		case WLAN_SET_DEBUGMODE:
			ret = wlan_set_debugmode_ioctl(priv, req);
			break;

		case WLAN_SUBCMD_MESH_SET_TTL:
			idata = SUBCMD_DATA(wrq);
			ret = wlan_mesh_set_ttl_ioctl(priv, idata);
			break;

		default:
			ret = -EOPNOTSUPP;
			break;
		}

		break;

	case WLAN_SETNONE_GETTWELVE_CHAR:	/* Get Antenna settings */
		/*
		 * We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is
		 * in flags of iwreq structure, otherwise it will be in
		 * mode member of iwreq structure.
		 */
		switch ((int)wrq->u.data.flags) {
		case WLAN_SUBCMD_GETRXANTENNA:	/* Get Rx Antenna */
			ret = wlan_subcmd_getrxantenna_ioctl(priv, req);
			break;

		case WLAN_SUBCMD_GETTXANTENNA:	/* Get Tx Antenna */
			ret = wlan_subcmd_gettxantenna_ioctl(priv, req);
			break;

		case WLAN_GET_TSF:
			ret = wlan_get_tsf_ioctl(priv, wrq);
			break;
		}
		break;

	case WLAN_SET128CHAR_GET128CHAR:
		switch ((int)wrq->u.data.flags) {

		case WLANSCAN_MODE:
			lbs_pr_debug(1, "Scan mode Ioctl\n");
			ret = wlan_scan_mode_ioctl(priv, wrq);
			break;

		case WLAN_GET_ADHOC_STATUS:
			ret = wlan_get_adhoc_status_ioctl(priv, wrq);
			break;
		case WLAN_SUBCMD_BT_ADD:
			ret = wlan_bt_add_ioctl(priv, req);
			break;
		case WLAN_SUBCMD_BT_DEL:
			ret = wlan_bt_del_ioctl(priv, req);
			break;
		case WLAN_SUBCMD_BT_LIST:
			ret = wlan_bt_list_ioctl(priv, req);
			break;
		case WLAN_SUBCMD_FWT_ADD:
			ret = wlan_fwt_add_ioctl(priv, req);
			break;
		case WLAN_SUBCMD_FWT_DEL:
			ret = wlan_fwt_del_ioctl(priv, req);
			break;
		case WLAN_SUBCMD_FWT_LOOKUP:
			ret = wlan_fwt_lookup_ioctl(priv, req);
			break;
		case WLAN_SUBCMD_FWT_LIST_NEIGHBOR:
			ret = wlan_fwt_list_neighbor_ioctl(priv, req);
			break;
		case WLAN_SUBCMD_FWT_LIST:
			ret = wlan_fwt_list_ioctl(priv, req);
			break;
		case WLAN_SUBCMD_FWT_LIST_ROUTE:
			ret = wlan_fwt_list_route_ioctl(priv, req);
			break;
		}
		break;

	case WLAN_SETNONE_GETONEINT:
		switch ((int)req->ifr_data) {
		case WLANGETBCNAVG:
			pdata = (int *)wrq->u.name;
			*pdata = (int)adapter->bcn_avg_factor;
			break;

		case WLANGETREGION:
			pdata = (int *)wrq->u.name;
			*pdata = (int)adapter->regioncode;
			break;

		case WLAN_GET_LISTEN_INTERVAL:
			pdata = (int *)wrq->u.name;
			*pdata = (int)adapter->listeninterval;
			break;

		case WLAN_GET_LINKMODE:
			req->ifr_data = (char *)((u32) adapter->linkmode);
			break;

		case WLAN_GET_RADIOMODE:
			req->ifr_data = (char *)((u32) adapter->radiomode);
			break;

		case WLAN_GET_DEBUGMODE:
			req->ifr_data = (char *)((u32) adapter->debugmode);
			break;

		case WLAN_GET_MULTIPLE_DTIM:
			pdata = (int *)wrq->u.name;
			*pdata = (int)adapter->multipledtim;
			break;
		case WLAN_GET_TX_RATE:
			ret = wlan_get_txrate_ioctl(priv, req);
			break;
		case WLAN_SUBCMD_FWT_CLEANUP:	/* fwt_cleanup */
			ret = wlan_fwt_cleanup_ioctl(priv, req);
			break;

		case WLAN_SUBCMD_FWT_TIME:	/* fwt_time */
			ret = wlan_fwt_time_ioctl(priv, req);
			break;

		case WLAN_SUBCMD_MESH_GET_TTL:
			ret = wlan_mesh_get_ttl_ioctl(priv, req);
			break;

		default:
			ret = -EOPNOTSUPP;

		}

		break;

	case WLANGETLOG:
		ret = wlan_do_getlog_ioctl(priv, wrq);
		break;

	case WLAN_SET_GET_SIXTEEN_INT:
		switch ((int)wrq->u.data.flags) {
		case WLAN_TPCCFG:
			{
				int data[5];
				struct cmd_ds_802_11_tpc_cfg cfg;
				memset(&cfg, 0, sizeof(cfg));
				if ((wrq->u.data.length > 1)
				    && (wrq->u.data.length != 5))
					return -1;

				if (wrq->u.data.length == 0) {
					cfg.action =
					    cpu_to_le16
					    (cmd_act_get);
				} else {
					if (copy_from_user
					    (data, wrq->u.data.pointer,
					     sizeof(int) * 5)) {
						lbs_pr_debug(1,
						       "Copy from user failed\n");
						return -EFAULT;
					}

					cfg.action =
					    cpu_to_le16
					    (cmd_act_set);
					cfg.enable = data[0];
					cfg.usesnr = data[1];
					cfg.P0 = data[2];
					cfg.P1 = data[3];
					cfg.P2 = data[4];
				}

				ret =
				    libertas_prepare_and_send_command(priv,
							  cmd_802_11_tpc_cfg,
							  0,
							  cmd_option_waitforrsp,
							  0, (void *)&cfg);

				data[0] = cfg.enable;
				data[1] = cfg.usesnr;
				data[2] = cfg.P0;
				data[3] = cfg.P1;
				data[4] = cfg.P2;
				if (copy_to_user
				    (wrq->u.data.pointer, data,
				     sizeof(int) * 5)) {
					lbs_pr_debug(1, "Copy to user failed\n");
					return -EFAULT;
				}

				wrq->u.data.length = 5;
			}
			break;

		case WLAN_POWERCFG:
			{
				int data[4];
				struct cmd_ds_802_11_pwr_cfg cfg;
				memset(&cfg, 0, sizeof(cfg));
				if ((wrq->u.data.length > 1)
				    && (wrq->u.data.length != 4))
					return -1;
				if (wrq->u.data.length == 0) {
					cfg.action =
					    cpu_to_le16
					    (cmd_act_get);
				} else {
					if (copy_from_user
					    (data, wrq->u.data.pointer,
					     sizeof(int) * 4)) {
						lbs_pr_debug(1,
						       "Copy from user failed\n");
						return -EFAULT;
					}

					cfg.action =
					    cpu_to_le16
					    (cmd_act_set);
					cfg.enable = data[0];
					cfg.PA_P0 = data[1];
					cfg.PA_P1 = data[2];
					cfg.PA_P2 = data[3];
				}
				ret =
				    libertas_prepare_and_send_command(priv,
							  cmd_802_11_pwr_cfg,
							  0,
							  cmd_option_waitforrsp,
							  0, (void *)&cfg);
				data[0] = cfg.enable;
				data[1] = cfg.PA_P0;
				data[2] = cfg.PA_P1;
				data[3] = cfg.PA_P2;
				if (copy_to_user
				    (wrq->u.data.pointer, data,
				     sizeof(int) * 4)) {
					lbs_pr_debug(1, "Copy to user failed\n");
					return -EFAULT;
				}

				wrq->u.data.length = 4;
			}
			break;
		case WLAN_AUTO_FREQ_SET:
			{
				int data[3];
				struct cmd_ds_802_11_afc afc;
				memset(&afc, 0, sizeof(afc));
				if (wrq->u.data.length != 3)
					return -1;
				if (copy_from_user
				    (data, wrq->u.data.pointer,
				     sizeof(int) * 3)) {
					lbs_pr_debug(1, "Copy from user failed\n");
					return -EFAULT;
				}
				afc.afc_auto = data[0];

				if (afc.afc_auto != 0) {
					afc.threshold = data[1];
					afc.period = data[2];
				} else {
					afc.timing_offset = data[1];
					afc.carrier_offset = data[2];
				}
				ret =
				    libertas_prepare_and_send_command(priv,
							  cmd_802_11_set_afc,
							  0,
							  cmd_option_waitforrsp,
							  0, (void *)&afc);
			}
			break;
		case WLAN_AUTO_FREQ_GET:
			{
				int data[3];
				struct cmd_ds_802_11_afc afc;
				memset(&afc, 0, sizeof(afc));
				ret =
				    libertas_prepare_and_send_command(priv,
							  cmd_802_11_get_afc,
							  0,
							  cmd_option_waitforrsp,
							  0, (void *)&afc);
				data[0] = afc.afc_auto;
				data[1] = afc.timing_offset;
				data[2] = afc.carrier_offset;
				if (copy_to_user
				    (wrq->u.data.pointer, data,
				     sizeof(int) * 3)) {
					lbs_pr_debug(1, "Copy to user failed\n");
					return -EFAULT;
				}

				wrq->u.data.length = 3;
			}
			break;
		case WLAN_SCANPROBES:
			{
				int data;
				if (wrq->u.data.length > 0) {
					if (copy_from_user
					    (&data, wrq->u.data.pointer,
					     sizeof(int))) {
						lbs_pr_debug(1,
						       "Copy from user failed\n");
						return -EFAULT;
					}

					adapter->scanprobes = data;
				} else {
					data = adapter->scanprobes;
					if (copy_to_user
					    (wrq->u.data.pointer, &data,
					     sizeof(int))) {
						lbs_pr_debug(1,
						       "Copy to user failed\n");
						return -EFAULT;
					}
				}
				wrq->u.data.length = 1;
			}
			break;
		case WLAN_LED_GPIO_CTRL:
			{
				int i;
				int data[16];

				struct cmd_ds_802_11_led_ctrl ctrl;
				struct mrvlietypes_ledgpio *gpio =
				    (struct mrvlietypes_ledgpio *) ctrl.data;

				memset(&ctrl, 0, sizeof(ctrl));
				if (wrq->u.data.length > MAX_LEDS * 2)
					return -ENOTSUPP;
				if ((wrq->u.data.length % 2) != 0)
					return -ENOTSUPP;
				if (wrq->u.data.length == 0) {
					ctrl.action =
					    cpu_to_le16
					    (cmd_act_get);
				} else {
					if (copy_from_user
					    (data, wrq->u.data.pointer,
					     sizeof(int) *
					     wrq->u.data.length)) {
						lbs_pr_debug(1,
						       "Copy from user failed\n");
						return -EFAULT;
					}

					ctrl.action =
					    cpu_to_le16
					    (cmd_act_set);
					ctrl.numled = cpu_to_le16(0);
					gpio->header.type =
					    cpu_to_le16(TLV_TYPE_LED_GPIO);
					gpio->header.len = wrq->u.data.length;
					for (i = 0; i < wrq->u.data.length;
					     i += 2) {
						gpio->ledpin[i / 2].led =
						    data[i];
						gpio->ledpin[i / 2].pin =
						    data[i + 1];
					}
				}
				ret =
				    libertas_prepare_and_send_command(priv,
							  cmd_802_11_led_gpio_ctrl,
							  0,
							  cmd_option_waitforrsp,
							  0, (void *)&ctrl);
				for (i = 0; i < gpio->header.len; i += 2) {
					data[i] = gpio->ledpin[i / 2].led;
					data[i + 1] = gpio->ledpin[i / 2].pin;
				}
				if (copy_to_user(wrq->u.data.pointer, data,
						 sizeof(int) *
						 gpio->header.len)) {
					lbs_pr_debug(1, "Copy to user failed\n");
					return -EFAULT;
				}

				wrq->u.data.length = gpio->header.len;
			}
			break;
		case WLAN_ADAPT_RATESET:
			ret = wlan_adapt_rateset(priv, wrq);
			break;
		case WLAN_INACTIVITY_TIMEOUT:
			ret = wlan_inactivity_timeout(priv, wrq);
			break;
		case WLANSNR:
			ret = wlan_get_snr(priv, wrq);
			break;
		case WLAN_GET_RXINFO:
			ret = wlan_get_rxinfo(priv, wrq);
		}
		break;

	default:
		ret = -EINVAL;
		break;
	}
	LEAVE();
	return ret;
}