From: Kalle Valo Date: Tue, 17 Nov 2015 19:07:19 +0000 (+0200) Subject: libertas: move under marvell vendor directory X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=f988d64010ec058a206dce083b5c791ce09caa48;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git libertas: move under marvell vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- diff --git a/MAINTAINERS b/MAINTAINERS index d72be8c88138..75ff7434db0e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6708,7 +6708,7 @@ F: drivers/net/ethernet/marvell/sk* MARVELL LIBERTAS WIRELESS DRIVER L: libertas-dev@lists.infradead.org S: Orphan -F: drivers/net/wireless/libertas/ +F: drivers/net/wireless/marvell/libertas/ MARVELL MV643XX ETHERNET DRIVER M: Sebastian Hesselbarth diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index d71efe89970b..b11a2d364200 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -22,6 +22,7 @@ source "drivers/net/wireless/atmel/Kconfig" source "drivers/net/wireless/broadcom/Kconfig" source "drivers/net/wireless/cisco/Kconfig" source "drivers/net/wireless/intel/Kconfig" +source "drivers/net/wireless/marvell/Kconfig" source "drivers/net/wireless/st/Kconfig" config PCMCIA_RAYCS @@ -162,7 +163,6 @@ config MWL8K source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/hostap/Kconfig" -source "drivers/net/wireless/libertas/Kconfig" source "drivers/net/wireless/orinoco/Kconfig" source "drivers/net/wireless/p54/Kconfig" source "drivers/net/wireless/rt2x00/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 6bcb2925a6ce..a974a6edb4b6 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ +obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ obj-$(CONFIG_HERMES) += orinoco/ @@ -24,7 +25,6 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o obj-$(CONFIG_USB_ZD1201) += zd1201.o -obj-$(CONFIG_LIBERTAS) += libertas/ obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/libertas/Kconfig deleted file mode 100644 index e6268ceacbf1..000000000000 --- a/drivers/net/wireless/libertas/Kconfig +++ /dev/null @@ -1,45 +0,0 @@ -config LIBERTAS - tristate "Marvell 8xxx Libertas WLAN driver support" - depends on CFG80211 - select WIRELESS_EXT - select WEXT_SPY - select LIB80211 - select FW_LOADER - ---help--- - A library for Marvell Libertas 8xxx devices. - -config LIBERTAS_USB - tristate "Marvell Libertas 8388 USB 802.11b/g cards" - depends on LIBERTAS && USB - ---help--- - A driver for Marvell Libertas 8388 USB devices. - -config LIBERTAS_CS - tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards" - depends on LIBERTAS && PCMCIA && HAS_IOPORT_MAP - ---help--- - A driver for Marvell Libertas 8385 CompactFlash devices. - -config LIBERTAS_SDIO - tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards" - depends on LIBERTAS && MMC - ---help--- - A driver for Marvell Libertas 8385/8686/8688 SDIO devices. - -config LIBERTAS_SPI - tristate "Marvell Libertas 8686 SPI 802.11b/g cards" - depends on LIBERTAS && SPI - ---help--- - A driver for Marvell Libertas 8686 SPI devices. - -config LIBERTAS_DEBUG - bool "Enable full debugging output in the Libertas module." - depends on LIBERTAS - ---help--- - Debugging support. - -config LIBERTAS_MESH - bool "Enable mesh support" - depends on LIBERTAS - help - This enables Libertas' MESH support, used by e.g. the OLPC people. diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/libertas/LICENSE deleted file mode 100644 index 8862742213b9..000000000000 --- a/drivers/net/wireless/libertas/LICENSE +++ /dev/null @@ -1,16 +0,0 @@ - Copyright (c) 2003-2006, Marvell International Ltd. - All Rights Reserved - - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - more details. - - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. - diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile deleted file mode 100644 index eac72f7bd341..000000000000 --- a/drivers/net/wireless/libertas/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -libertas-y += cfg.o -libertas-y += cmd.o -libertas-y += cmdresp.o -libertas-y += debugfs.o -libertas-y += ethtool.o -libertas-y += main.o -libertas-y += rx.o -libertas-y += tx.o -libertas-y += firmware.o -libertas-$(CONFIG_LIBERTAS_MESH) += mesh.o - -usb8xxx-objs += if_usb.o -libertas_cs-objs += if_cs.o -libertas_sdio-objs += if_sdio.o -libertas_spi-objs += if_spi.o - -obj-$(CONFIG_LIBERTAS) += libertas.o -obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o -obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o -obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o -obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README deleted file mode 100644 index 1a554a685e91..000000000000 --- a/drivers/net/wireless/libertas/README +++ /dev/null @@ -1,239 +0,0 @@ -================================================================================ - README for Libertas - - (c) Copyright © 2003-2006, Marvell International Ltd. - All Rights Reserved - - This software file (the "File") is distributed by Marvell International - Ltd. under the terms of the GNU General Public License Version 2, June 1991 - (the "License"). You may use, redistribute and/or modify this File in - accordance with the terms and conditions of the License, a copy of which - is available along with the File in the license.txt file or on the worldwide - web at http://www.gnu.org/licenses/gpl.txt. - - THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - ARE EXPRESSLY DISCLAIMED. The License provides additional details about - this warranty disclaimer. -================================================================================ - -===================== -DRIVER LOADING -===================== - - o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/ - - o. Load driver by using the following command: - - insmod usb8388.ko [fw_name=usb8388.bin] - -========================= -ETHTOOL -========================= - - -Use the -i option to retrieve version information from the driver. - -# ethtool -i eth0 -driver: libertas -version: COMM-USB8388-318.p4 -firmware-version: 5.110.7 -bus-info: - -Use the -e option to read the EEPROM contents of the card. - - Usage: - ethtool -e ethX [raw on|off] [offset N] [length N] - - -e retrieves and prints an EEPROM dump for the specified ethernet - device. When raw is enabled, then it dumps the raw EEPROM data - to stdout. The length and offset parameters allow dumping cer- - tain portions of the EEPROM. Default is to dump the entire EEP- - ROM. - -# ethtool -e eth0 offset 0 length 16 -Offset Values ------- ------ -0x0000 38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00 - -======================== -DEBUGFS COMMANDS -======================== - -those commands are used via debugfs interface - -=========== -rdmac -rdbbp -rdrf - These commands are used to read the MAC, BBP and RF registers from the - card. These commands take one parameter that specifies the offset - location that is to be read. This parameter must be specified in - hexadecimal (its possible to precede preceding the number with a "0x"). - - Path: /sys/kernel/debug/libertas_wireless/ethX/registers/ - - Usage: - echo "0xa123" > rdmac ; cat rdmac - echo "0xa123" > rdbbp ; cat rdbbp - echo "0xa123" > rdrf ; cat rdrf -wrmac -wrbbp -wrrf - These commands are used to write the MAC, BBP and RF registers in the - card. These commands take two parameters that specify the offset - location and the value that is to be written. This parameters must - be specified in hexadecimal (its possible to precede the number - with a "0x"). - - Usage: - echo "0xa123 0xaa" > wrmac - echo "0xa123 0xaa" > wrbbp - echo "0xa123 0xaa" > wrrf - -sleepparams - This command is used to set the sleepclock configurations - - Path: /sys/kernel/debug/libertas_wireless/ethX/ - - Usage: - cat sleepparams: reads the current sleepclock configuration - - echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration. - - where: - p1 is Sleep clock error in ppm (0-65535) - p2 is Wakeup offset in usec (0-65535) - p3 is Clock stabilization time in usec (0-65535) - p4 is Control periodic calibration (0-2) - p5 is Control the use of external sleep clock (0-2) - p6 is reserved for debug (0-65535) - -subscribed_events - - The subscribed_events directory contains the interface for the - subscribed events API. - - Path: /sys/kernel/debug/libertas_wireless/ethX/subscribed_events/ - - Each event is represented by a filename. Each filename consists of the - following three fields: - Value Frequency Subscribed - - To read the current values for a given event, do: - cat event - To set the current values, do: - echo "60 2 1" > event - - Frequency field specifies the reporting frequency for this event. - If it is set to 0, then the event is reported only once, and then - automatically unsubscribed. If it is set to 1, then the event is - reported every time it occurs. If it is set to N, then the event is - reported every Nth time it occurs. - - beacon_missed - Value field specifies the number of consecutive missing beacons which - triggers the LINK_LOSS event. This event is generated only once after - which the firmware resets its state. At initialization, the LINK_LOSS - event is subscribed by default. The default value of MissedBeacons is - 60. - - failure_count - Value field specifies the consecutive failure count threshold which - triggers the generation of the MAX_FAIL event. Once this event is - generated, the consecutive failure count is reset to 0. - At initialization, the MAX_FAIL event is NOT subscribed by - default. - - high_rssi - This event is generated when the average received RSSI in beacons goes - above a threshold, specified by Value. - - low_rssi - This event is generated when the average received RSSI in beacons goes - below a threshold, specified by Value. - - high_snr - This event is generated when the average received SNR in beacons goes - above a threshold, specified by Value. - - low_snr - This event is generated when the average received SNR in beacons goes - below a threshold, specified by Value. - -extscan - This command is used to do a specific scan. - - Path: /sys/kernel/debug/libertas_wireless/ethX/ - - Usage: echo "SSID" > extscan - - Example: - echo "LINKSYS-AP" > extscan - - To see the results of use getscantable command. - -getscantable - - Display the current contents of the driver scan table (ie. get the - scan results). - - Path: /sys/kernel/debug/libertas_wireless/ethX/ - - Usage: - cat getscantable - -setuserscan - Initiate a customized scan and retrieve the results - - - Path: /sys/kernel/debug/libertas_wireless/ethX/ - - Usage: - echo "[ARGS]" > setuserscan - - where [ARGS]: - - bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan - ssid="[SSID]" specify a SSID filter for the scan - keep=[0 or 1] keep the previous scan results (1), discard (0) - dur=[scan time] time to scan for each channel in milliseconds - type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) - - Any combination of the above arguments can be supplied on the command - line. If dur tokens are absent, the driver default setting will be used. - The bssid and ssid fields, if blank, will produce an unfiltered scan. - The type field will default to 3 (Any) and the keep field will default - to 0 (Discard). - - Examples: - 1) Perform a passive scan on all channels for 20 ms per channel: - echo "dur=20" > setuserscan - - 2) Perform an active scan for a specific SSID: - echo "ssid="TestAP"" > setuserscan - - 3) Scan all available channels (B/G, A bands) for a specific BSSID, keep - the current scan table intact, update existing or append new scan data: - echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan - - 4) Scan for all infrastructure networks. - Keep the previous scan table intact. Update any duplicate BSSID/SSID - matches with the new scan data: - echo "type=1 keep=1" > setuserscan - - All entries in the scan table (not just the new scan data when keep=1) - will be displayed upon completion by use of the getscantable ioctl. - -hostsleep - This command is used to enable/disable host sleep. - Note: Host sleep parameters should be configured using - "ethtool -s ethX wol X" command before enabling host sleep. - - Path: /sys/kernel/debug/libertas_wireless/ethX/ - - Usage: - cat hostsleep: reads the current hostsleep state - echo "1" > hostsleep : enable host sleep. - echo "0" > hostsleep : disable host sleep - diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c deleted file mode 100644 index 8317afd065b4..000000000000 --- a/drivers/net/wireless/libertas/cfg.c +++ /dev/null @@ -1,2215 +0,0 @@ -/* - * Implement cfg80211 ("iw") support. - * - * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany - * Holger Schurig - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#include "decl.h" -#include "cfg.h" -#include "cmd.h" -#include "mesh.h" - - -#define CHAN2G(_channel, _freq, _flags) { \ - .band = IEEE80211_BAND_2GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} - -static struct ieee80211_channel lbs_2ghz_channels[] = { - CHAN2G(1, 2412, 0), - CHAN2G(2, 2417, 0), - CHAN2G(3, 2422, 0), - CHAN2G(4, 2427, 0), - CHAN2G(5, 2432, 0), - CHAN2G(6, 2437, 0), - CHAN2G(7, 2442, 0), - CHAN2G(8, 2447, 0), - CHAN2G(9, 2452, 0), - CHAN2G(10, 2457, 0), - CHAN2G(11, 2462, 0), - CHAN2G(12, 2467, 0), - CHAN2G(13, 2472, 0), - CHAN2G(14, 2484, 0), -}; - -#define RATETAB_ENT(_rate, _hw_value, _flags) { \ - .bitrate = (_rate), \ - .hw_value = (_hw_value), \ - .flags = (_flags), \ -} - - -/* Table 6 in section 3.2.1.1 */ -static struct ieee80211_rate lbs_rates[] = { - RATETAB_ENT(10, 0, 0), - RATETAB_ENT(20, 1, 0), - RATETAB_ENT(55, 2, 0), - RATETAB_ENT(110, 3, 0), - RATETAB_ENT(60, 9, 0), - RATETAB_ENT(90, 6, 0), - RATETAB_ENT(120, 7, 0), - RATETAB_ENT(180, 8, 0), - RATETAB_ENT(240, 9, 0), - RATETAB_ENT(360, 10, 0), - RATETAB_ENT(480, 11, 0), - RATETAB_ENT(540, 12, 0), -}; - -static struct ieee80211_supported_band lbs_band_2ghz = { - .channels = lbs_2ghz_channels, - .n_channels = ARRAY_SIZE(lbs_2ghz_channels), - .bitrates = lbs_rates, - .n_bitrates = ARRAY_SIZE(lbs_rates), -}; - - -static const u32 cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, -}; - -/* Time to stay on the channel */ -#define LBS_DWELL_PASSIVE 100 -#define LBS_DWELL_ACTIVE 40 - - -/*************************************************************************** - * Misc utility functions - * - * TLVs are Marvell specific. They are very similar to IEs, they have the - * same structure: type, length, data*. The only difference: for IEs, the - * type and length are u8, but for TLVs they're __le16. - */ - -/* - * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1 - * in the firmware spec - */ -static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type) -{ - int ret = -ENOTSUPP; - - switch (auth_type) { - case NL80211_AUTHTYPE_OPEN_SYSTEM: - case NL80211_AUTHTYPE_SHARED_KEY: - ret = auth_type; - break; - case NL80211_AUTHTYPE_AUTOMATIC: - ret = NL80211_AUTHTYPE_OPEN_SYSTEM; - break; - case NL80211_AUTHTYPE_NETWORK_EAP: - ret = 0x80; - break; - default: - /* silence compiler */ - break; - } - return ret; -} - - -/* - * Various firmware commands need the list of supported rates, but with - * the hight-bit set for basic rates - */ -static int lbs_add_rates(u8 *rates) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { - u8 rate = lbs_rates[i].bitrate / 5; - if (rate == 0x02 || rate == 0x04 || - rate == 0x0b || rate == 0x16) - rate |= 0x80; - rates[i] = rate; - } - return ARRAY_SIZE(lbs_rates); -} - - -/*************************************************************************** - * TLV utility functions - * - * TLVs are Marvell specific. They are very similar to IEs, they have the - * same structure: type, length, data*. The only difference: for IEs, the - * type and length are u8, but for TLVs they're __le16. - */ - - -/* - * Add ssid TLV - */ -#define LBS_MAX_SSID_TLV_SIZE \ - (sizeof(struct mrvl_ie_header) \ - + IEEE80211_MAX_SSID_LEN) - -static int lbs_add_ssid_tlv(u8 *tlv, const u8 *ssid, int ssid_len) -{ - struct mrvl_ie_ssid_param_set *ssid_tlv = (void *)tlv; - - /* - * TLV-ID SSID 00 00 - * length 06 00 - * ssid 4d 4e 54 45 53 54 - */ - ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID); - ssid_tlv->header.len = cpu_to_le16(ssid_len); - memcpy(ssid_tlv->ssid, ssid, ssid_len); - return sizeof(ssid_tlv->header) + ssid_len; -} - - -/* - * Add channel list TLV (section 8.4.2) - * - * Actual channel data comes from priv->wdev->wiphy->channels. - */ -#define LBS_MAX_CHANNEL_LIST_TLV_SIZE \ - (sizeof(struct mrvl_ie_header) \ - + (LBS_SCAN_BEFORE_NAP * sizeof(struct chanscanparamset))) - -static int lbs_add_channel_list_tlv(struct lbs_private *priv, u8 *tlv, - int last_channel, int active_scan) -{ - int chanscanparamsize = sizeof(struct chanscanparamset) * - (last_channel - priv->scan_channel); - - struct mrvl_ie_header *header = (void *) tlv; - - /* - * TLV-ID CHANLIST 01 01 - * length 0e 00 - * channel 00 01 00 00 00 64 00 - * radio type 00 - * channel 01 - * scan type 00 - * min scan time 00 00 - * max scan time 64 00 - * channel 2 00 02 00 00 00 64 00 - * - */ - - header->type = cpu_to_le16(TLV_TYPE_CHANLIST); - header->len = cpu_to_le16(chanscanparamsize); - tlv += sizeof(struct mrvl_ie_header); - - /* lbs_deb_scan("scan: channels %d to %d\n", priv->scan_channel, - last_channel); */ - memset(tlv, 0, chanscanparamsize); - - while (priv->scan_channel < last_channel) { - struct chanscanparamset *param = (void *) tlv; - - param->radiotype = CMD_SCAN_RADIO_TYPE_BG; - param->channumber = - priv->scan_req->channels[priv->scan_channel]->hw_value; - if (active_scan) { - param->maxscantime = cpu_to_le16(LBS_DWELL_ACTIVE); - } else { - param->chanscanmode.passivescan = 1; - param->maxscantime = cpu_to_le16(LBS_DWELL_PASSIVE); - } - tlv += sizeof(struct chanscanparamset); - priv->scan_channel++; - } - return sizeof(struct mrvl_ie_header) + chanscanparamsize; -} - - -/* - * Add rates TLV - * - * The rates are in lbs_bg_rates[], but for the 802.11b - * rates the high bit is set. We add this TLV only because - * there's a firmware which otherwise doesn't report all - * APs in range. - */ -#define LBS_MAX_RATES_TLV_SIZE \ - (sizeof(struct mrvl_ie_header) \ - + (ARRAY_SIZE(lbs_rates))) - -/* Adds a TLV with all rates the hardware supports */ -static int lbs_add_supported_rates_tlv(u8 *tlv) -{ - size_t i; - struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; - - /* - * TLV-ID RATES 01 00 - * length 0e 00 - * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c - */ - rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); - tlv += sizeof(rate_tlv->header); - i = lbs_add_rates(tlv); - tlv += i; - rate_tlv->header.len = cpu_to_le16(i); - return sizeof(rate_tlv->header) + i; -} - -/* Add common rates from a TLV and return the new end of the TLV */ -static u8 * -add_ie_rates(u8 *tlv, const u8 *ie, int *nrates) -{ - int hw, ap, ap_max = ie[1]; - u8 hw_rate; - - /* Advance past IE header */ - ie += 2; - - lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max); - - for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { - hw_rate = lbs_rates[hw].bitrate / 5; - for (ap = 0; ap < ap_max; ap++) { - if (hw_rate == (ie[ap] & 0x7f)) { - *tlv++ = ie[ap]; - *nrates = *nrates + 1; - } - } - } - return tlv; -} - -/* - * Adds a TLV with all rates the hardware *and* BSS supports. - */ -static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) -{ - struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; - const u8 *rates_eid, *ext_rates_eid; - int n = 0; - - rcu_read_lock(); - rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); - ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); - - /* - * 01 00 TLV_TYPE_RATES - * 04 00 len - * 82 84 8b 96 rates - */ - rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); - tlv += sizeof(rate_tlv->header); - - /* Add basic rates */ - if (rates_eid) { - tlv = add_ie_rates(tlv, rates_eid, &n); - - /* Add extended rates, if any */ - if (ext_rates_eid) - tlv = add_ie_rates(tlv, ext_rates_eid, &n); - } else { - lbs_deb_assoc("assoc: bss had no basic rate IE\n"); - /* Fallback: add basic 802.11b rates */ - *tlv++ = 0x82; - *tlv++ = 0x84; - *tlv++ = 0x8b; - *tlv++ = 0x96; - n = 4; - } - rcu_read_unlock(); - - rate_tlv->header.len = cpu_to_le16(n); - return sizeof(rate_tlv->header) + n; -} - - -/* - * Add auth type TLV. - * - * This is only needed for newer firmware (V9 and up). - */ -#define LBS_MAX_AUTH_TYPE_TLV_SIZE \ - sizeof(struct mrvl_ie_auth_type) - -static int lbs_add_auth_type_tlv(u8 *tlv, enum nl80211_auth_type auth_type) -{ - struct mrvl_ie_auth_type *auth = (void *) tlv; - - /* - * 1f 01 TLV_TYPE_AUTH_TYPE - * 01 00 len - * 01 auth type - */ - auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); - auth->header.len = cpu_to_le16(sizeof(*auth)-sizeof(auth->header)); - auth->auth = cpu_to_le16(lbs_auth_to_authtype(auth_type)); - return sizeof(*auth); -} - - -/* - * Add channel (phy ds) TLV - */ -#define LBS_MAX_CHANNEL_TLV_SIZE \ - sizeof(struct mrvl_ie_header) - -static int lbs_add_channel_tlv(u8 *tlv, u8 channel) -{ - struct mrvl_ie_ds_param_set *ds = (void *) tlv; - - /* - * 03 00 TLV_TYPE_PHY_DS - * 01 00 len - * 06 channel - */ - ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); - ds->header.len = cpu_to_le16(sizeof(*ds)-sizeof(ds->header)); - ds->channel = channel; - return sizeof(*ds); -} - - -/* - * Add (empty) CF param TLV of the form: - */ -#define LBS_MAX_CF_PARAM_TLV_SIZE \ - sizeof(struct mrvl_ie_header) - -static int lbs_add_cf_param_tlv(u8 *tlv) -{ - struct mrvl_ie_cf_param_set *cf = (void *)tlv; - - /* - * 04 00 TLV_TYPE_CF - * 06 00 len - * 00 cfpcnt - * 00 cfpperiod - * 00 00 cfpmaxduration - * 00 00 cfpdurationremaining - */ - cf->header.type = cpu_to_le16(TLV_TYPE_CF); - cf->header.len = cpu_to_le16(sizeof(*cf)-sizeof(cf->header)); - return sizeof(*cf); -} - -/* - * Add WPA TLV - */ -#define LBS_MAX_WPA_TLV_SIZE \ - (sizeof(struct mrvl_ie_header) \ - + 128 /* TODO: I guessed the size */) - -static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) -{ - size_t tlv_len; - - /* - * We need just convert an IE to an TLV. IEs use u8 for the header, - * u8 type - * u8 len - * u8[] data - * but TLVs use __le16 instead: - * __le16 type - * __le16 len - * u8[] data - */ - *tlv++ = *ie++; - *tlv++ = 0; - tlv_len = *tlv++ = *ie++; - *tlv++ = 0; - while (tlv_len--) - *tlv++ = *ie++; - /* the TLV is two bytes larger than the IE */ - return ie_len + 2; -} - -/* - * Set Channel - */ - -static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, - struct cfg80211_chan_def *chandef) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = -ENOTSUPP; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", - chandef->chan->center_freq, - cfg80211_get_chandef_type(chandef)); - - if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) - goto out; - - ret = lbs_set_channel(priv, chandef->chan->hw_value); - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - -static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy, - struct net_device *netdev, - struct ieee80211_channel *channel) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = -ENOTSUPP; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d", - netdev_name(netdev), channel->center_freq); - - if (netdev != priv->mesh_dev) - goto out; - - ret = lbs_mesh_set_channel(priv, channel->hw_value); - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - -/* - * Scanning - */ - -/* - * When scanning, the firmware doesn't send a nul packet with the power-safe - * bit to the AP. So we cannot stay away from our current channel too long, - * otherwise we loose data. So take a "nap" while scanning every other - * while. - */ -#define LBS_SCAN_BEFORE_NAP 4 - - -/* - * When the firmware reports back a scan-result, it gives us an "u8 rssi", - * which isn't really an RSSI, as it becomes larger when moving away from - * the AP. Anyway, we need to convert that into mBm. - */ -#define LBS_SCAN_RSSI_TO_MBM(rssi) \ - ((-(int)rssi + 3)*100) - -static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, - struct cmd_header *resp) -{ - struct cfg80211_bss *bss; - struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp; - int bsssize; - const u8 *pos; - const u8 *tsfdesc; - int tsfsize; - int i; - int ret = -EILSEQ; - - lbs_deb_enter(LBS_DEB_CFG80211); - - bsssize = get_unaligned_le16(&scanresp->bssdescriptsize); - - lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n", - scanresp->nr_sets, bsssize, le16_to_cpu(resp->size)); - - if (scanresp->nr_sets == 0) { - ret = 0; - goto done; - } - - /* - * The general layout of the scan response is described in chapter - * 5.7.1. Basically we have a common part, then any number of BSS - * descriptor sections. Finally we have section with the same number - * of TSFs. - * - * cmd_ds_802_11_scan_rsp - * cmd_header - * pos_size - * nr_sets - * bssdesc 1 - * bssid - * rssi - * timestamp - * intvl - * capa - * IEs - * bssdesc 2 - * bssdesc n - * MrvlIEtypes_TsfFimestamp_t - * TSF for BSS 1 - * TSF for BSS 2 - * TSF for BSS n - */ - - pos = scanresp->bssdesc_and_tlvbuffer; - - lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer, - scanresp->bssdescriptsize); - - tsfdesc = pos + bsssize; - tsfsize = 4 + 8 * scanresp->nr_sets; - lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize); - - /* Validity check: we expect a Marvell-Local TLV */ - i = get_unaligned_le16(tsfdesc); - tsfdesc += 2; - if (i != TLV_TYPE_TSFTIMESTAMP) { - lbs_deb_scan("scan response: invalid TSF Timestamp %d\n", i); - goto done; - } - - /* - * Validity check: the TLV holds TSF values with 8 bytes each, so - * the size in the TLV must match the nr_sets value - */ - i = get_unaligned_le16(tsfdesc); - tsfdesc += 2; - if (i / 8 != scanresp->nr_sets) { - lbs_deb_scan("scan response: invalid number of TSF timestamp " - "sets (expected %d got %d)\n", scanresp->nr_sets, - i / 8); - goto done; - } - - for (i = 0; i < scanresp->nr_sets; i++) { - const u8 *bssid; - const u8 *ie; - int left; - int ielen; - int rssi; - u16 intvl; - u16 capa; - int chan_no = -1; - const u8 *ssid = NULL; - u8 ssid_len = 0; - - int len = get_unaligned_le16(pos); - pos += 2; - - /* BSSID */ - bssid = pos; - pos += ETH_ALEN; - /* RSSI */ - rssi = *pos++; - /* Packet time stamp */ - pos += 8; - /* Beacon interval */ - intvl = get_unaligned_le16(pos); - pos += 2; - /* Capabilities */ - capa = get_unaligned_le16(pos); - pos += 2; - - /* To find out the channel, we must parse the IEs */ - ie = pos; - /* - * 6+1+8+2+2: size of BSSID, RSSI, time stamp, beacon - * interval, capabilities - */ - ielen = left = len - (6 + 1 + 8 + 2 + 2); - while (left >= 2) { - u8 id, elen; - id = *pos++; - elen = *pos++; - left -= 2; - if (elen > left) { - lbs_deb_scan("scan response: invalid IE fmt\n"); - goto done; - } - - if (id == WLAN_EID_DS_PARAMS) - chan_no = *pos; - if (id == WLAN_EID_SSID) { - ssid = pos; - ssid_len = elen; - } - left -= elen; - pos += elen; - } - - /* No channel, no luck */ - if (chan_no != -1) { - struct wiphy *wiphy = priv->wdev->wiphy; - int freq = ieee80211_channel_to_frequency(chan_no, - IEEE80211_BAND_2GHZ); - struct ieee80211_channel *channel = - ieee80211_get_channel(wiphy, freq); - - lbs_deb_scan("scan: %pM, capa %04x, chan %2d, %*pE, %d dBm\n", - bssid, capa, chan_no, ssid_len, ssid, - LBS_SCAN_RSSI_TO_MBM(rssi)/100); - - if (channel && - !(channel->flags & IEEE80211_CHAN_DISABLED)) { - bss = cfg80211_inform_bss(wiphy, channel, - CFG80211_BSS_FTYPE_UNKNOWN, - bssid, get_unaligned_le64(tsfdesc), - capa, intvl, ie, ielen, - LBS_SCAN_RSSI_TO_MBM(rssi), - GFP_KERNEL); - cfg80211_put_bss(wiphy, bss); - } - } else - lbs_deb_scan("scan response: missing BSS channel IE\n"); - - tsfdesc += 8; - } - ret = 0; - - done: - lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); - return ret; -} - - -/* - * Our scan command contains a TLV, consting of a SSID TLV, a channel list - * TLV and a rates TLV. Determine the maximum size of them: - */ -#define LBS_SCAN_MAX_CMD_SIZE \ - (sizeof(struct cmd_ds_802_11_scan) \ - + LBS_MAX_SSID_TLV_SIZE \ - + LBS_MAX_CHANNEL_LIST_TLV_SIZE \ - + LBS_MAX_RATES_TLV_SIZE) - -/* - * Assumes priv->scan_req is initialized and valid - * Assumes priv->scan_channel is initialized - */ -static void lbs_scan_worker(struct work_struct *work) -{ - struct lbs_private *priv = - container_of(work, struct lbs_private, scan_work.work); - struct cmd_ds_802_11_scan *scan_cmd; - u8 *tlv; /* pointer into our current, growing TLV storage area */ - int last_channel; - int running, carrier; - - lbs_deb_enter(LBS_DEB_SCAN); - - scan_cmd = kzalloc(LBS_SCAN_MAX_CMD_SIZE, GFP_KERNEL); - if (scan_cmd == NULL) - goto out_no_scan_cmd; - - /* prepare fixed part of scan command */ - scan_cmd->bsstype = CMD_BSS_TYPE_ANY; - - /* stop network while we're away from our main channel */ - running = !netif_queue_stopped(priv->dev); - carrier = netif_carrier_ok(priv->dev); - if (running) - netif_stop_queue(priv->dev); - if (carrier) - netif_carrier_off(priv->dev); - - /* prepare fixed part of scan command */ - tlv = scan_cmd->tlvbuffer; - - /* add SSID TLV */ - if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0) - tlv += lbs_add_ssid_tlv(tlv, - priv->scan_req->ssids[0].ssid, - priv->scan_req->ssids[0].ssid_len); - - /* add channel TLVs */ - last_channel = priv->scan_channel + LBS_SCAN_BEFORE_NAP; - if (last_channel > priv->scan_req->n_channels) - last_channel = priv->scan_req->n_channels; - tlv += lbs_add_channel_list_tlv(priv, tlv, last_channel, - priv->scan_req->n_ssids); - - /* add rates TLV */ - tlv += lbs_add_supported_rates_tlv(tlv); - - if (priv->scan_channel < priv->scan_req->n_channels) { - cancel_delayed_work(&priv->scan_work); - if (netif_running(priv->dev)) - queue_delayed_work(priv->work_thread, &priv->scan_work, - msecs_to_jiffies(300)); - } - - /* This is the final data we are about to send */ - scan_cmd->hdr.size = cpu_to_le16(tlv - (u8 *)scan_cmd); - lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd, - sizeof(*scan_cmd)); - lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer, - tlv - scan_cmd->tlvbuffer); - - __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr, - le16_to_cpu(scan_cmd->hdr.size), - lbs_ret_scan, 0); - - if (priv->scan_channel >= priv->scan_req->n_channels) { - /* Mark scan done */ - cancel_delayed_work(&priv->scan_work); - lbs_scan_done(priv); - } - - /* Restart network */ - if (carrier) - netif_carrier_on(priv->dev); - if (running && !priv->tx_pending_len) - netif_wake_queue(priv->dev); - - kfree(scan_cmd); - - /* Wake up anything waiting on scan completion */ - if (priv->scan_req == NULL) { - lbs_deb_scan("scan: waking up waiters\n"); - wake_up_all(&priv->scan_q); - } - - out_no_scan_cmd: - lbs_deb_leave(LBS_DEB_SCAN); -} - -static void _internal_start_scan(struct lbs_private *priv, bool internal, - struct cfg80211_scan_request *request) -{ - lbs_deb_enter(LBS_DEB_CFG80211); - - lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n", - request->n_ssids, request->n_channels, request->ie_len); - - priv->scan_channel = 0; - priv->scan_req = request; - priv->internal_scan = internal; - - queue_delayed_work(priv->work_thread, &priv->scan_work, - msecs_to_jiffies(50)); - - lbs_deb_leave(LBS_DEB_CFG80211); -} - -/* - * Clean up priv->scan_req. Should be used to handle the allocation details. - */ -void lbs_scan_done(struct lbs_private *priv) -{ - WARN_ON(!priv->scan_req); - - if (priv->internal_scan) - kfree(priv->scan_req); - else - cfg80211_scan_done(priv->scan_req, false); - - priv->scan_req = NULL; -} - -static int lbs_cfg_scan(struct wiphy *wiphy, - struct cfg80211_scan_request *request) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = 0; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (priv->scan_req || delayed_work_pending(&priv->scan_work)) { - /* old scan request not yet processed */ - ret = -EAGAIN; - goto out; - } - - _internal_start_scan(priv, false, request); - - if (priv->surpriseremoved) - ret = -EIO; - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - - -/* - * Events - */ - -void lbs_send_disconnect_notification(struct lbs_private *priv, - bool locally_generated) -{ - lbs_deb_enter(LBS_DEB_CFG80211); - - cfg80211_disconnected(priv->dev, 0, NULL, 0, locally_generated, - GFP_KERNEL); - - lbs_deb_leave(LBS_DEB_CFG80211); -} - -void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event) -{ - lbs_deb_enter(LBS_DEB_CFG80211); - - cfg80211_michael_mic_failure(priv->dev, - priv->assoc_bss, - event == MACREG_INT_CODE_MIC_ERR_MULTICAST ? - NL80211_KEYTYPE_GROUP : - NL80211_KEYTYPE_PAIRWISE, - -1, - NULL, - GFP_KERNEL); - - lbs_deb_leave(LBS_DEB_CFG80211); -} - - - - -/* - * Connect/disconnect - */ - - -/* - * This removes all WEP keys - */ -static int lbs_remove_wep_keys(struct lbs_private *priv) -{ - struct cmd_ds_802_11_set_wep cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CFG80211); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.keyindex = cpu_to_le16(priv->wep_tx_key); - cmd.action = cpu_to_le16(CMD_ACT_REMOVE); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); - - lbs_deb_leave(LBS_DEB_CFG80211); - return ret; -} - -/* - * Set WEP keys - */ -static int lbs_set_wep_keys(struct lbs_private *priv) -{ - struct cmd_ds_802_11_set_wep cmd; - int i; - int ret; - - lbs_deb_enter(LBS_DEB_CFG80211); - - /* - * command 13 00 - * size 50 00 - * sequence xx xx - * result 00 00 - * action 02 00 ACT_ADD - * transmit key 00 00 - * type for key 1 01 WEP40 - * type for key 2 00 - * type for key 3 00 - * type for key 4 00 - * key 1 39 39 39 39 39 00 00 00 - * 00 00 00 00 00 00 00 00 - * key 2 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * key 3 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * key 4 00 00 00 00 00 00 00 00 - */ - if (priv->wep_key_len[0] || priv->wep_key_len[1] || - priv->wep_key_len[2] || priv->wep_key_len[3]) { - /* Only set wep keys if we have at least one of them */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.keyindex = cpu_to_le16(priv->wep_tx_key); - cmd.action = cpu_to_le16(CMD_ACT_ADD); - - for (i = 0; i < 4; i++) { - switch (priv->wep_key_len[i]) { - case WLAN_KEY_LEN_WEP40: - cmd.keytype[i] = CMD_TYPE_WEP_40_BIT; - break; - case WLAN_KEY_LEN_WEP104: - cmd.keytype[i] = CMD_TYPE_WEP_104_BIT; - break; - default: - cmd.keytype[i] = 0; - break; - } - memcpy(cmd.keymaterial[i], priv->wep_key[i], - priv->wep_key_len[i]); - } - - ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); - } else { - /* Otherwise remove all wep keys */ - ret = lbs_remove_wep_keys(priv); - } - - lbs_deb_leave(LBS_DEB_CFG80211); - return ret; -} - - -/* - * Enable/Disable RSN status - */ -static int lbs_enable_rsn(struct lbs_private *priv, int enable) -{ - struct cmd_ds_802_11_enable_rsn cmd; - int ret; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", enable); - - /* - * cmd 2f 00 - * size 0c 00 - * sequence xx xx - * result 00 00 - * action 01 00 ACT_SET - * enable 01 00 - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.enable = cpu_to_le16(enable); - - ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd); - - lbs_deb_leave(LBS_DEB_CFG80211); - return ret; -} - - -/* - * Set WPA/WPA key material - */ - -/* - * like "struct cmd_ds_802_11_key_material", but with cmd_header. Once we - * get rid of WEXT, this should go into host.h - */ - -struct cmd_key_material { - struct cmd_header hdr; - - __le16 action; - struct MrvlIEtype_keyParamSet param; -} __packed; - -static int lbs_set_key_material(struct lbs_private *priv, - int key_type, int key_info, - const u8 *key, u16 key_len) -{ - struct cmd_key_material cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CFG80211); - - /* - * Example for WPA (TKIP): - * - * cmd 5e 00 - * size 34 00 - * sequence xx xx - * result 00 00 - * action 01 00 - * TLV type 00 01 key param - * length 00 26 - * key type 01 00 TKIP - * key info 06 00 UNICAST | ENABLED - * key len 20 00 - * key 32 bytes - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.param.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); - cmd.param.length = cpu_to_le16(sizeof(cmd.param) - 4); - cmd.param.keytypeid = cpu_to_le16(key_type); - cmd.param.keyinfo = cpu_to_le16(key_info); - cmd.param.keylen = cpu_to_le16(key_len); - if (key && key_len) - memcpy(cmd.param.key, key, key_len); - - ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd); - - lbs_deb_leave(LBS_DEB_CFG80211); - return ret; -} - - -/* - * Sets the auth type (open, shared, etc) in the firmware. That - * we use CMD_802_11_AUTHENTICATE is misleading, this firmware - * command doesn't send an authentication frame at all, it just - * stores the auth_type. - */ -static int lbs_set_authtype(struct lbs_private *priv, - struct cfg80211_connect_params *sme) -{ - struct cmd_ds_802_11_authenticate cmd; - int ret; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", sme->auth_type); - - /* - * cmd 11 00 - * size 19 00 - * sequence xx xx - * result 00 00 - * BSS id 00 13 19 80 da 30 - * auth type 00 - * reserved 00 00 00 00 00 00 00 00 00 00 - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - if (sme->bssid) - memcpy(cmd.bssid, sme->bssid, ETH_ALEN); - /* convert auth_type */ - ret = lbs_auth_to_authtype(sme->auth_type); - if (ret < 0) - goto done; - - cmd.authtype = ret; - ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd); - - done: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - -/* - * Create association request - */ -#define LBS_ASSOC_MAX_CMD_SIZE \ - (sizeof(struct cmd_ds_802_11_associate) \ - - 512 /* cmd_ds_802_11_associate.iebuf */ \ - + LBS_MAX_SSID_TLV_SIZE \ - + LBS_MAX_CHANNEL_TLV_SIZE \ - + LBS_MAX_CF_PARAM_TLV_SIZE \ - + LBS_MAX_AUTH_TYPE_TLV_SIZE \ - + LBS_MAX_WPA_TLV_SIZE) - -static int lbs_associate(struct lbs_private *priv, - struct cfg80211_bss *bss, - struct cfg80211_connect_params *sme) -{ - struct cmd_ds_802_11_associate_response *resp; - struct cmd_ds_802_11_associate *cmd = kzalloc(LBS_ASSOC_MAX_CMD_SIZE, - GFP_KERNEL); - const u8 *ssid_eid; - size_t len, resp_ie_len; - int status; - int ret; - u8 *pos = &(cmd->iebuf[0]); - u8 *tmp; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (!cmd) { - ret = -ENOMEM; - goto done; - } - - /* - * cmd 50 00 - * length 34 00 - * sequence xx xx - * result 00 00 - * BSS id 00 13 19 80 da 30 - * capabilities 11 00 - * listen interval 0a 00 - * beacon interval 00 00 - * DTIM period 00 - * TLVs xx (up to 512 bytes) - */ - cmd->hdr.command = cpu_to_le16(CMD_802_11_ASSOCIATE); - - /* Fill in static fields */ - memcpy(cmd->bssid, bss->bssid, ETH_ALEN); - cmd->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL); - cmd->capability = cpu_to_le16(bss->capability); - - /* add SSID TLV */ - rcu_read_lock(); - ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); - if (ssid_eid) - pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); - else - lbs_deb_assoc("no SSID\n"); - rcu_read_unlock(); - - /* add DS param TLV */ - if (bss->channel) - pos += lbs_add_channel_tlv(pos, bss->channel->hw_value); - else - lbs_deb_assoc("no channel\n"); - - /* add (empty) CF param TLV */ - pos += lbs_add_cf_param_tlv(pos); - - /* add rates TLV */ - tmp = pos + 4; /* skip Marvell IE header */ - pos += lbs_add_common_rates_tlv(pos, bss); - lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp); - - /* add auth type TLV */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) >= 9) - pos += lbs_add_auth_type_tlv(pos, sme->auth_type); - - /* add WPA/WPA2 TLV */ - if (sme->ie && sme->ie_len) - pos += lbs_add_wpa_tlv(pos, sme->ie, sme->ie_len); - - len = (sizeof(*cmd) - sizeof(cmd->iebuf)) + - (u16)(pos - (u8 *) &cmd->iebuf); - cmd->hdr.size = cpu_to_le16(len); - - lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_CMD", (u8 *) cmd, - le16_to_cpu(cmd->hdr.size)); - - /* store for later use */ - memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN); - - ret = lbs_cmd_with_response(priv, CMD_802_11_ASSOCIATE, cmd); - if (ret) - goto done; - - /* generate connect message to cfg80211 */ - - resp = (void *) cmd; /* recast for easier field access */ - status = le16_to_cpu(resp->statuscode); - - /* Older FW versions map the IEEE 802.11 Status Code in the association - * response to the following values returned in resp->statuscode: - * - * IEEE Status Code Marvell Status Code - * 0 -> 0x0000 ASSOC_RESULT_SUCCESS - * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED - * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED - * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED - * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED - * others -> 0x0003 ASSOC_RESULT_REFUSED - * - * Other response codes: - * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused) - * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for - * association response from the AP) - */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { - switch (status) { - case 0: - break; - case 1: - lbs_deb_assoc("invalid association parameters\n"); - status = WLAN_STATUS_CAPS_UNSUPPORTED; - break; - case 2: - lbs_deb_assoc("timer expired while waiting for AP\n"); - status = WLAN_STATUS_AUTH_TIMEOUT; - break; - case 3: - lbs_deb_assoc("association refused by AP\n"); - status = WLAN_STATUS_ASSOC_DENIED_UNSPEC; - break; - case 4: - lbs_deb_assoc("authentication refused by AP\n"); - status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; - break; - default: - lbs_deb_assoc("association failure %d\n", status); - /* v5 OLPC firmware does return the AP status code if - * it's not one of the values above. Let that through. - */ - break; - } - } - - lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, " - "aid 0x%04x\n", status, le16_to_cpu(resp->statuscode), - le16_to_cpu(resp->capability), le16_to_cpu(resp->aid)); - - resp_ie_len = le16_to_cpu(resp->hdr.size) - - sizeof(resp->hdr) - - 6; - cfg80211_connect_result(priv->dev, - priv->assoc_bss, - sme->ie, sme->ie_len, - resp->iebuf, resp_ie_len, - status, - GFP_KERNEL); - - if (status == 0) { - /* TODO: get rid of priv->connect_status */ - priv->connect_status = LBS_CONNECTED; - netif_carrier_on(priv->dev); - if (!priv->tx_pending_len) - netif_tx_wake_all_queues(priv->dev); - } - - kfree(cmd); -done: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - -static struct cfg80211_scan_request * -_new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme) -{ - struct cfg80211_scan_request *creq = NULL; - int i, n_channels = ieee80211_get_num_supported_channels(wiphy); - enum ieee80211_band band; - - creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + - n_channels * sizeof(void *), - GFP_ATOMIC); - if (!creq) - return NULL; - - /* SSIDs come after channels */ - creq->ssids = (void *)&creq->channels[n_channels]; - creq->n_channels = n_channels; - creq->n_ssids = 1; - - /* Scan all available channels */ - i = 0; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - int j; - - if (!wiphy->bands[band]) - continue; - - for (j = 0; j < wiphy->bands[band]->n_channels; j++) { - /* ignore disabled channels */ - if (wiphy->bands[band]->channels[j].flags & - IEEE80211_CHAN_DISABLED) - continue; - - creq->channels[i] = &wiphy->bands[band]->channels[j]; - i++; - } - } - if (i) { - /* Set real number of channels specified in creq->channels[] */ - creq->n_channels = i; - - /* Scan for the SSID we're going to connect to */ - memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len); - creq->ssids[0].ssid_len = sme->ssid_len; - } else { - /* No channels found... */ - kfree(creq); - creq = NULL; - } - - return creq; -} - -static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_connect_params *sme) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - struct cfg80211_bss *bss = NULL; - int ret = 0; - u8 preamble = RADIO_PREAMBLE_SHORT; - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (!sme->bssid) { - struct cfg80211_scan_request *creq; - - /* - * Scan for the requested network after waiting for existing - * scans to finish. - */ - lbs_deb_assoc("assoc: waiting for existing scans\n"); - wait_event_interruptible_timeout(priv->scan_q, - (priv->scan_req == NULL), - (15 * HZ)); - - creq = _new_connect_scan_req(wiphy, sme); - if (!creq) { - ret = -EINVAL; - goto done; - } - - lbs_deb_assoc("assoc: scanning for compatible AP\n"); - _internal_start_scan(priv, true, creq); - - lbs_deb_assoc("assoc: waiting for scan to complete\n"); - wait_event_interruptible_timeout(priv->scan_q, - (priv->scan_req == NULL), - (15 * HZ)); - lbs_deb_assoc("assoc: scanning completed\n"); - } - - /* Find the BSS we want using available scan results */ - bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, - sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS, - IEEE80211_PRIVACY_ANY); - if (!bss) { - wiphy_err(wiphy, "assoc: bss %pM not in scan results\n", - sme->bssid); - ret = -ENOENT; - goto done; - } - lbs_deb_assoc("trying %pM\n", bss->bssid); - lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n", - sme->crypto.cipher_group, - sme->key_idx, sme->key_len); - - /* As this is a new connection, clear locally stored WEP keys */ - priv->wep_tx_key = 0; - memset(priv->wep_key, 0, sizeof(priv->wep_key)); - memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); - - /* set/remove WEP keys */ - switch (sme->crypto.cipher_group) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - /* Store provided WEP keys in priv-> */ - priv->wep_tx_key = sme->key_idx; - priv->wep_key_len[sme->key_idx] = sme->key_len; - memcpy(priv->wep_key[sme->key_idx], sme->key, sme->key_len); - /* Set WEP keys and WEP mode */ - lbs_set_wep_keys(priv); - priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE; - lbs_set_mac_control(priv); - /* No RSN mode for WEP */ - lbs_enable_rsn(priv, 0); - break; - case 0: /* there's no WLAN_CIPHER_SUITE_NONE definition */ - /* - * If we don't have no WEP, no WPA and no WPA2, - * we remove all keys like in the WPA/WPA2 setup, - * we just don't set RSN. - * - * Therefore: fall-through - */ - case WLAN_CIPHER_SUITE_TKIP: - case WLAN_CIPHER_SUITE_CCMP: - /* Remove WEP keys and WEP mode */ - lbs_remove_wep_keys(priv); - priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE; - lbs_set_mac_control(priv); - - /* clear the WPA/WPA2 keys */ - lbs_set_key_material(priv, - KEY_TYPE_ID_WEP, /* doesn't matter */ - KEY_INFO_WPA_UNICAST, - NULL, 0); - lbs_set_key_material(priv, - KEY_TYPE_ID_WEP, /* doesn't matter */ - KEY_INFO_WPA_MCAST, - NULL, 0); - /* RSN mode for WPA/WPA2 */ - lbs_enable_rsn(priv, sme->crypto.cipher_group != 0); - break; - default: - wiphy_err(wiphy, "unsupported cipher group 0x%x\n", - sme->crypto.cipher_group); - ret = -ENOTSUPP; - goto done; - } - - ret = lbs_set_authtype(priv, sme); - if (ret == -ENOTSUPP) { - wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type); - goto done; - } - - lbs_set_radio(priv, preamble, 1); - - /* Do the actual association */ - ret = lbs_associate(priv, bss, sme); - - done: - if (bss) - cfg80211_put_bss(wiphy, bss); - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - -int lbs_disconnect(struct lbs_private *priv, u16 reason) -{ - struct cmd_ds_802_11_deauthenticate cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - /* Mildly ugly to use a locally store my own BSSID ... */ - memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN); - cmd.reasoncode = cpu_to_le16(reason); - - ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd); - if (ret) - return ret; - - cfg80211_disconnected(priv->dev, - reason, - NULL, 0, true, - GFP_KERNEL); - priv->connect_status = LBS_DISCONNECTED; - - return 0; -} - -static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, - u16 reason_code) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code); - - /* store for lbs_cfg_ret_disconnect() */ - priv->disassoc_reason = reason_code; - - return lbs_disconnect(priv, reason_code); -} - -static int lbs_cfg_set_default_key(struct wiphy *wiphy, - struct net_device *netdev, - u8 key_index, bool unicast, - bool multicast) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - - if (netdev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (key_index != priv->wep_tx_key) { - lbs_deb_assoc("set_default_key: to %d\n", key_index); - priv->wep_tx_key = key_index; - lbs_set_wep_keys(priv); - } - - return 0; -} - - -static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev, - u8 idx, bool pairwise, const u8 *mac_addr, - struct key_params *params) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - u16 key_info; - u16 key_type; - int ret = 0; - - if (netdev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter(LBS_DEB_CFG80211); - - lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n", - params->cipher, mac_addr); - lbs_deb_assoc("add_key: key index %d, key len %d\n", - idx, params->key_len); - if (params->key_len) - lbs_deb_hex(LBS_DEB_CFG80211, "KEY", - params->key, params->key_len); - - lbs_deb_assoc("add_key: seq len %d\n", params->seq_len); - if (params->seq_len) - lbs_deb_hex(LBS_DEB_CFG80211, "SEQ", - params->seq, params->seq_len); - - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - /* actually compare if something has changed ... */ - if ((priv->wep_key_len[idx] != params->key_len) || - memcmp(priv->wep_key[idx], - params->key, params->key_len) != 0) { - priv->wep_key_len[idx] = params->key_len; - memcpy(priv->wep_key[idx], - params->key, params->key_len); - lbs_set_wep_keys(priv); - } - break; - case WLAN_CIPHER_SUITE_TKIP: - case WLAN_CIPHER_SUITE_CCMP: - key_info = KEY_INFO_WPA_ENABLED | ((idx == 0) - ? KEY_INFO_WPA_UNICAST - : KEY_INFO_WPA_MCAST); - key_type = (params->cipher == WLAN_CIPHER_SUITE_TKIP) - ? KEY_TYPE_ID_TKIP - : KEY_TYPE_ID_AES; - lbs_set_key_material(priv, - key_type, - key_info, - params->key, params->key_len); - break; - default: - wiphy_err(wiphy, "unhandled cipher 0x%x\n", params->cipher); - ret = -ENOTSUPP; - break; - } - - return ret; -} - - -static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, - u8 key_index, bool pairwise, const u8 *mac_addr) -{ - - lbs_deb_enter(LBS_DEB_CFG80211); - - lbs_deb_assoc("del_key: key_idx %d, mac_addr %pM\n", - key_index, mac_addr); - -#ifdef TODO - struct lbs_private *priv = wiphy_priv(wiphy); - /* - * I think can keep this a NO-OP, because: - - * - we clear all keys whenever we do lbs_cfg_connect() anyway - * - neither "iw" nor "wpa_supplicant" won't call this during - * an ongoing connection - * - TODO: but I have to check if this is still true when - * I set the AP to periodic re-keying - * - we've not kzallec() something when we've added a key at - * lbs_cfg_connect() or lbs_cfg_add_key(). - * - * This causes lbs_cfg_del_key() only called at disconnect time, - * where we'd just waste time deleting a key that is not going - * to be used anyway. - */ - if (key_index < 3 && priv->wep_key_len[key_index]) { - priv->wep_key_len[key_index] = 0; - lbs_set_wep_keys(priv); - } -#endif - - return 0; -} - - -/* - * Get station - */ - -static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, struct station_info *sinfo) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - s8 signal, noise; - int ret; - size_t i; - - lbs_deb_enter(LBS_DEB_CFG80211); - - sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES) | - BIT(NL80211_STA_INFO_TX_PACKETS) | - BIT(NL80211_STA_INFO_RX_BYTES) | - BIT(NL80211_STA_INFO_RX_PACKETS); - sinfo->tx_bytes = priv->dev->stats.tx_bytes; - sinfo->tx_packets = priv->dev->stats.tx_packets; - sinfo->rx_bytes = priv->dev->stats.rx_bytes; - sinfo->rx_packets = priv->dev->stats.rx_packets; - - /* Get current RSSI */ - ret = lbs_get_rssi(priv, &signal, &noise); - if (ret == 0) { - sinfo->signal = signal; - sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); - } - - /* Convert priv->cur_rate from hw_value to NL80211 value */ - for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { - if (priv->cur_rate == lbs_rates[i].hw_value) { - sinfo->txrate.legacy = lbs_rates[i].bitrate; - sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); - break; - } - } - - return 0; -} - - - - -/* - * Change interface - */ - -static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = 0; - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - switch (type) { - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - break; - default: - return -EOPNOTSUPP; - } - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (priv->iface_running) - ret = lbs_set_iface_type(priv, type); - - if (!ret) - priv->wdev->iftype = type; - - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - -/* - * IBSS (Ad-Hoc) - */ - -/* - * The firmware needs the following bits masked out of the beacon-derived - * capability field when associating/joining to a BSS: - * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused) - */ -#define CAPINFO_MASK (~(0xda00)) - - -static void lbs_join_post(struct lbs_private *priv, - struct cfg80211_ibss_params *params, - u8 *bssid, u16 capability) -{ - u8 fake_ie[2 + IEEE80211_MAX_SSID_LEN + /* ssid */ - 2 + 4 + /* basic rates */ - 2 + 1 + /* DS parameter */ - 2 + 2 + /* atim */ - 2 + 8]; /* extended rates */ - u8 *fake = fake_ie; - struct cfg80211_bss *bss; - - lbs_deb_enter(LBS_DEB_CFG80211); - - /* - * For cfg80211_inform_bss, we'll need a fake IE, as we can't get - * the real IE from the firmware. So we fabricate a fake IE based on - * what the firmware actually sends (sniffed with wireshark). - */ - /* Fake SSID IE */ - *fake++ = WLAN_EID_SSID; - *fake++ = params->ssid_len; - memcpy(fake, params->ssid, params->ssid_len); - fake += params->ssid_len; - /* Fake supported basic rates IE */ - *fake++ = WLAN_EID_SUPP_RATES; - *fake++ = 4; - *fake++ = 0x82; - *fake++ = 0x84; - *fake++ = 0x8b; - *fake++ = 0x96; - /* Fake DS channel IE */ - *fake++ = WLAN_EID_DS_PARAMS; - *fake++ = 1; - *fake++ = params->chandef.chan->hw_value; - /* Fake IBSS params IE */ - *fake++ = WLAN_EID_IBSS_PARAMS; - *fake++ = 2; - *fake++ = 0; /* ATIM=0 */ - *fake++ = 0; - /* Fake extended rates IE, TODO: don't add this for 802.11b only, - * but I don't know how this could be checked */ - *fake++ = WLAN_EID_EXT_SUPP_RATES; - *fake++ = 8; - *fake++ = 0x0c; - *fake++ = 0x12; - *fake++ = 0x18; - *fake++ = 0x24; - *fake++ = 0x30; - *fake++ = 0x48; - *fake++ = 0x60; - *fake++ = 0x6c; - lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie); - - bss = cfg80211_inform_bss(priv->wdev->wiphy, - params->chandef.chan, - CFG80211_BSS_FTYPE_UNKNOWN, - bssid, - 0, - capability, - params->beacon_interval, - fake_ie, fake - fake_ie, - 0, GFP_KERNEL); - cfg80211_put_bss(priv->wdev->wiphy, bss); - - memcpy(priv->wdev->ssid, params->ssid, params->ssid_len); - priv->wdev->ssid_len = params->ssid_len; - - cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan, - GFP_KERNEL); - - /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */ - priv->connect_status = LBS_CONNECTED; - netif_carrier_on(priv->dev); - if (!priv->tx_pending_len) - netif_wake_queue(priv->dev); - - lbs_deb_leave(LBS_DEB_CFG80211); -} - -static int lbs_ibss_join_existing(struct lbs_private *priv, - struct cfg80211_ibss_params *params, - struct cfg80211_bss *bss) -{ - const u8 *rates_eid; - struct cmd_ds_802_11_ad_hoc_join cmd; - u8 preamble = RADIO_PREAMBLE_SHORT; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CFG80211); - - /* TODO: set preamble based on scan result */ - ret = lbs_set_radio(priv, preamble, 1); - if (ret) - goto out; - - /* - * Example CMD_802_11_AD_HOC_JOIN command: - * - * command 2c 00 CMD_802_11_AD_HOC_JOIN - * size 65 00 - * sequence xx xx - * result 00 00 - * bssid 02 27 27 97 2f 96 - * ssid 49 42 53 53 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * type 02 CMD_BSS_TYPE_IBSS - * beacon period 64 00 - * dtim period 00 - * timestamp 00 00 00 00 00 00 00 00 - * localtime 00 00 00 00 00 00 00 00 - * IE DS 03 - * IE DS len 01 - * IE DS channel 01 - * reserveed 00 00 00 00 - * IE IBSS 06 - * IE IBSS len 02 - * IE IBSS atim 00 00 - * reserved 00 00 00 00 - * capability 02 00 - * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c 00 - * fail timeout ff 00 - * probe delay 00 00 - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - - memcpy(cmd.bss.bssid, bss->bssid, ETH_ALEN); - memcpy(cmd.bss.ssid, params->ssid, params->ssid_len); - cmd.bss.type = CMD_BSS_TYPE_IBSS; - cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval); - cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS; - cmd.bss.ds.header.len = 1; - cmd.bss.ds.channel = params->chandef.chan->hw_value; - cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS; - cmd.bss.ibss.header.len = 2; - cmd.bss.ibss.atimwindow = 0; - cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK); - - /* set rates to the intersection of our rates and the rates in the - bss */ - rcu_read_lock(); - rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); - if (!rates_eid) { - lbs_add_rates(cmd.bss.rates); - } else { - int hw, i; - u8 rates_max = rates_eid[1]; - u8 *rates = cmd.bss.rates; - for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { - u8 hw_rate = lbs_rates[hw].bitrate / 5; - for (i = 0; i < rates_max; i++) { - if (hw_rate == (rates_eid[i+2] & 0x7f)) { - u8 rate = rates_eid[i+2]; - if (rate == 0x02 || rate == 0x04 || - rate == 0x0b || rate == 0x16) - rate |= 0x80; - *rates++ = rate; - } - } - } - } - rcu_read_unlock(); - - /* Only v8 and below support setting this */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { - cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); - cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); - } - ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd); - if (ret) - goto out; - - /* - * This is a sample response to CMD_802_11_AD_HOC_JOIN: - * - * response 2c 80 - * size 09 00 - * sequence xx xx - * result 00 00 - * reserved 00 - */ - lbs_join_post(priv, params, bss->bssid, bss->capability); - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - -static int lbs_ibss_start_new(struct lbs_private *priv, - struct cfg80211_ibss_params *params) -{ - struct cmd_ds_802_11_ad_hoc_start cmd; - struct cmd_ds_802_11_ad_hoc_result *resp = - (struct cmd_ds_802_11_ad_hoc_result *) &cmd; - u8 preamble = RADIO_PREAMBLE_SHORT; - int ret = 0; - u16 capability; - - lbs_deb_enter(LBS_DEB_CFG80211); - - ret = lbs_set_radio(priv, preamble, 1); - if (ret) - goto out; - - /* - * Example CMD_802_11_AD_HOC_START command: - * - * command 2b 00 CMD_802_11_AD_HOC_START - * size b1 00 - * sequence xx xx - * result 00 00 - * ssid 54 45 53 54 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 - * bss type 02 - * beacon period 64 00 - * dtim period 00 - * IE IBSS 06 - * IE IBSS len 02 - * IE IBSS atim 00 00 - * reserved 00 00 00 00 - * IE DS 03 - * IE DS len 01 - * IE DS channel 01 - * reserved 00 00 00 00 - * probe delay 00 00 - * capability 02 00 - * rates 82 84 8b 96 (basic rates with have bit 7 set) - * 0c 12 18 24 30 48 60 6c - * padding 100 bytes - */ - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - memcpy(cmd.ssid, params->ssid, params->ssid_len); - cmd.bsstype = CMD_BSS_TYPE_IBSS; - cmd.beaconperiod = cpu_to_le16(params->beacon_interval); - cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS; - cmd.ibss.header.len = 2; - cmd.ibss.atimwindow = 0; - cmd.ds.header.id = WLAN_EID_DS_PARAMS; - cmd.ds.header.len = 1; - cmd.ds.channel = params->chandef.chan->hw_value; - /* Only v8 and below support setting probe delay */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) - cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); - /* TODO: mix in WLAN_CAPABILITY_PRIVACY */ - capability = WLAN_CAPABILITY_IBSS; - cmd.capability = cpu_to_le16(capability); - lbs_add_rates(cmd.rates); - - - ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd); - if (ret) - goto out; - - /* - * This is a sample response to CMD_802_11_AD_HOC_JOIN: - * - * response 2b 80 - * size 14 00 - * sequence xx xx - * result 00 00 - * reserved 00 - * bssid 02 2b 7b 0f 86 0e - */ - lbs_join_post(priv, params, resp->bssid, capability); - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - -static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_ibss_params *params) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - int ret = 0; - struct cfg80211_bss *bss; - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (!params->chandef.chan) { - ret = -ENOTSUPP; - goto out; - } - - ret = lbs_set_channel(priv, params->chandef.chan->hw_value); - if (ret) - goto out; - - /* Search if someone is beaconing. This assumes that the - * bss list is populated already */ - bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid, - params->ssid, params->ssid_len, - IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY); - - if (bss) { - ret = lbs_ibss_join_existing(priv, params, bss); - cfg80211_put_bss(wiphy, bss); - } else - ret = lbs_ibss_start_new(priv, params); - - - out: - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - -static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - struct cmd_ds_802_11_ad_hoc_stop cmd; - int ret = 0; - - if (dev == priv->mesh_dev) - return -EOPNOTSUPP; - - lbs_deb_enter(LBS_DEB_CFG80211); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd); - - /* TODO: consider doing this at MACREG_INT_CODE_ADHOC_BCN_LOST time */ - lbs_mac_event_disconnected(priv, true); - - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - - - - -/* - * Initialization - */ - -static struct cfg80211_ops lbs_cfg80211_ops = { - .set_monitor_channel = lbs_cfg_set_monitor_channel, - .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel, - .scan = lbs_cfg_scan, - .connect = lbs_cfg_connect, - .disconnect = lbs_cfg_disconnect, - .add_key = lbs_cfg_add_key, - .del_key = lbs_cfg_del_key, - .set_default_key = lbs_cfg_set_default_key, - .get_station = lbs_cfg_get_station, - .change_virtual_intf = lbs_change_intf, - .join_ibss = lbs_join_ibss, - .leave_ibss = lbs_leave_ibss, -}; - - -/* - * At this time lbs_private *priv doesn't even exist, so we just allocate - * memory and don't initialize the wiphy further. This is postponed until we - * can talk to the firmware and happens at registration time in - * lbs_cfg_wiphy_register(). - */ -struct wireless_dev *lbs_cfg_alloc(struct device *dev) -{ - int ret = 0; - struct wireless_dev *wdev; - - lbs_deb_enter(LBS_DEB_CFG80211); - - wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); - if (!wdev) - return ERR_PTR(-ENOMEM); - - wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private)); - if (!wdev->wiphy) { - dev_err(dev, "cannot allocate wiphy\n"); - ret = -ENOMEM; - goto err_wiphy_new; - } - - lbs_deb_leave(LBS_DEB_CFG80211); - return wdev; - - err_wiphy_new: - kfree(wdev); - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ERR_PTR(ret); -} - - -static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv) -{ - struct region_code_mapping { - const char *cn; - int code; - }; - - /* Section 5.17.2 */ - static const struct region_code_mapping regmap[] = { - {"US ", 0x10}, /* US FCC */ - {"CA ", 0x20}, /* Canada */ - {"EU ", 0x30}, /* ETSI */ - {"ES ", 0x31}, /* Spain */ - {"FR ", 0x32}, /* France */ - {"JP ", 0x40}, /* Japan */ - }; - size_t i; - - lbs_deb_enter(LBS_DEB_CFG80211); - - for (i = 0; i < ARRAY_SIZE(regmap); i++) - if (regmap[i].code == priv->regioncode) { - regulatory_hint(priv->wdev->wiphy, regmap[i].cn); - break; - } - - lbs_deb_leave(LBS_DEB_CFG80211); -} - -static void lbs_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct lbs_private *priv = wiphy_priv(wiphy); - - lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain " - "callback for domain %c%c\n", request->alpha2[0], - request->alpha2[1]); - - memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); - if (lbs_iface_active(priv)) - lbs_set_11d_domain_info(priv); - - lbs_deb_leave(LBS_DEB_CFG80211); -} - -/* - * This function get's called after lbs_setup_firmware() determined the - * firmware capabities. So we can setup the wiphy according to our - * hardware/firmware. - */ -int lbs_cfg_register(struct lbs_private *priv) -{ - struct wireless_dev *wdev = priv->wdev; - int ret; - - lbs_deb_enter(LBS_DEB_CFG80211); - - wdev->wiphy->max_scan_ssids = 1; - wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - - wdev->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC); - if (lbs_rtap_supported(priv)) - wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); - if (lbs_mesh_activated(priv)) - wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT); - - wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz; - - /* - * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have - * never seen a firmware without WPA - */ - wdev->wiphy->cipher_suites = cipher_suites; - wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); - wdev->wiphy->reg_notifier = lbs_reg_notifier; - - ret = wiphy_register(wdev->wiphy); - if (ret < 0) - pr_err("cannot register wiphy device\n"); - - priv->wiphy_registered = true; - - ret = register_netdev(priv->dev); - if (ret) - pr_err("cannot register network device\n"); - - INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker); - - lbs_cfg_set_regulatory_hint(priv); - - lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); - return ret; -} - -void lbs_scan_deinit(struct lbs_private *priv) -{ - lbs_deb_enter(LBS_DEB_CFG80211); - cancel_delayed_work_sync(&priv->scan_work); -} - - -void lbs_cfg_free(struct lbs_private *priv) -{ - struct wireless_dev *wdev = priv->wdev; - - lbs_deb_enter(LBS_DEB_CFG80211); - - if (!wdev) - return; - - if (priv->wiphy_registered) - wiphy_unregister(wdev->wiphy); - - if (wdev->wiphy) - wiphy_free(wdev->wiphy); - - kfree(wdev); -} diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h deleted file mode 100644 index acccc2922401..000000000000 --- a/drivers/net/wireless/libertas/cfg.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __LBS_CFG80211_H__ -#define __LBS_CFG80211_H__ - -struct device; -struct lbs_private; -struct regulatory_request; -struct wiphy; - -struct wireless_dev *lbs_cfg_alloc(struct device *dev); -int lbs_cfg_register(struct lbs_private *priv); -void lbs_cfg_free(struct lbs_private *priv); - -void lbs_send_disconnect_notification(struct lbs_private *priv, - bool locally_generated); -void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event); - -void lbs_scan_done(struct lbs_private *priv); -void lbs_scan_deinit(struct lbs_private *priv); -int lbs_disconnect(struct lbs_private *priv, u16 reason); - -#endif diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c deleted file mode 100644 index 0387a5b380c8..000000000000 --- a/drivers/net/wireless/libertas/cmd.c +++ /dev/null @@ -1,1725 +0,0 @@ -/* - * This file contains the handling of command. - * It prepares command and sends it to firmware when it is ready. - */ - -#include -#include -#include -#include -#include -#include - -#include "decl.h" -#include "cfg.h" -#include "cmd.h" - -#define CAL_NF(nf) ((s32)(-(s32)(nf))) -#define CAL_RSSI(snr, nf) ((s32)((s32)(snr) + CAL_NF(nf))) - -/** - * lbs_cmd_copyback - Simple callback that copies response back into command - * - * @priv: A pointer to &struct lbs_private structure - * @extra: A pointer to the original command structure for which - * 'resp' is a response - * @resp: A pointer to the command response - * - * returns: 0 on success, error on failure - */ -int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, - struct cmd_header *resp) -{ - struct cmd_header *buf = (void *)extra; - uint16_t copy_len; - - copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); - memcpy(buf, resp, copy_len); - return 0; -} -EXPORT_SYMBOL_GPL(lbs_cmd_copyback); - -/** - * lbs_cmd_async_callback - Simple callback that ignores the result. - * Use this if you just want to send a command to the hardware, but don't - * care for the result. - * - * @priv: ignored - * @extra: ignored - * @resp: ignored - * - * returns: 0 for success - */ -static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra, - struct cmd_header *resp) -{ - return 0; -} - - -/** - * is_command_allowed_in_ps - tests if a command is allowed in Power Save mode - * - * @cmd: the command ID - * - * returns: 1 if allowed, 0 if not allowed - */ -static u8 is_command_allowed_in_ps(u16 cmd) -{ - switch (cmd) { - case CMD_802_11_RSSI: - return 1; - case CMD_802_11_HOST_SLEEP_CFG: - return 1; - default: - break; - } - return 0; -} - -/** - * lbs_update_hw_spec - Updates the hardware details like MAC address - * and regulatory region - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: 0 on success, error on failure - */ -int lbs_update_hw_spec(struct lbs_private *priv) -{ - struct cmd_ds_get_hw_spec cmd; - int ret = -1; - u32 i; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); - ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); - if (ret) - goto out; - - priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); - - /* The firmware release is in an interesting format: the patch - * level is in the most significant nibble ... so fix that: */ - priv->fwrelease = le32_to_cpu(cmd.fwrelease); - priv->fwrelease = (priv->fwrelease << 8) | - (priv->fwrelease >> 24 & 0xff); - - /* Some firmware capabilities: - * CF card firmware 5.0.16p0: cap 0x00000303 - * USB dongle firmware 5.110.17p2: cap 0x00000303 - */ - netdev_info(priv->dev, "%pM, fw %u.%u.%up%u, cap 0x%08x\n", - cmd.permanentaddr, - priv->fwrelease >> 24 & 0xff, - priv->fwrelease >> 16 & 0xff, - priv->fwrelease >> 8 & 0xff, - priv->fwrelease & 0xff, - priv->fwcapinfo); - lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", - cmd.hwifversion, cmd.version); - - /* Clamp region code to 8-bit since FW spec indicates that it should - * only ever be 8-bit, even though the field size is 16-bit. Some firmware - * returns non-zero high 8 bits here. - * - * Firmware version 4.0.102 used in CF8381 has region code shifted. We - * need to check for this problem and handle it properly. - */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4) - priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF; - else - priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; - - for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { - /* use the region code to search for the index */ - if (priv->regioncode == lbs_region_code_to_index[i]) - break; - } - - /* if it's unidentified region code, use the default (USA) */ - if (i >= MRVDRV_MAX_REGION_CODE) { - priv->regioncode = 0x10; - netdev_info(priv->dev, - "unidentified region code; using the default (USA)\n"); - } - - if (priv->current_addr[0] == 0xff) - memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); - - if (!priv->copied_hwaddr) { - memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN); - if (priv->mesh_dev) - memcpy(priv->mesh_dev->dev_addr, - priv->current_addr, ETH_ALEN); - priv->copied_hwaddr = 1; - } - -out: - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy, - struct cmd_header *resp) -{ - lbs_deb_enter(LBS_DEB_CMD); - if (priv->is_host_sleep_activated) { - priv->is_host_sleep_configured = 0; - if (priv->psstate == PS_STATE_FULL_POWER) { - priv->is_host_sleep_activated = 0; - wake_up_interruptible(&priv->host_sleep_q); - } - } else { - priv->is_host_sleep_configured = 1; - } - lbs_deb_leave(LBS_DEB_CMD); - return 0; -} - -int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, - struct wol_config *p_wol_config) -{ - struct cmd_ds_host_sleep cmd_config; - int ret; - - /* - * Certain firmware versions do not support EHS_REMOVE_WAKEUP command - * and the card will return a failure. Since we need to be - * able to reset the mask, in those cases we set a 0 mask instead. - */ - if (criteria == EHS_REMOVE_WAKEUP && !priv->ehs_remove_supported) - criteria = 0; - - cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config)); - cmd_config.criteria = cpu_to_le32(criteria); - cmd_config.gpio = priv->wol_gpio; - cmd_config.gap = priv->wol_gap; - - if (p_wol_config != NULL) - memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config, - sizeof(struct wol_config)); - else - cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE; - - ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr, - le16_to_cpu(cmd_config.hdr.size), - lbs_ret_host_sleep_cfg, 0); - if (!ret) { - if (p_wol_config) - memcpy((uint8_t *) p_wol_config, - (uint8_t *)&cmd_config.wol_conf, - sizeof(struct wol_config)); - } else { - netdev_info(priv->dev, "HOST_SLEEP_CFG failed %d\n", ret); - } - - return ret; -} -EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg); - -/** - * lbs_set_ps_mode - Sets the Power Save mode - * - * @priv: A pointer to &struct lbs_private structure - * @cmd_action: The Power Save operation (PS_MODE_ACTION_ENTER_PS or - * PS_MODE_ACTION_EXIT_PS) - * @block: Whether to block on a response or not - * - * returns: 0 on success, error on failure - */ -int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block) -{ - struct cmd_ds_802_11_ps_mode cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(cmd_action); - - if (cmd_action == PS_MODE_ACTION_ENTER_PS) { - lbs_deb_cmd("PS_MODE: action ENTER_PS\n"); - cmd.multipledtim = cpu_to_le16(1); /* Default DTIM multiple */ - } else if (cmd_action == PS_MODE_ACTION_EXIT_PS) { - lbs_deb_cmd("PS_MODE: action EXIT_PS\n"); - } else { - /* We don't handle CONFIRM_SLEEP here because it needs to - * be fastpathed to the firmware. - */ - lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action); - ret = -EOPNOTSUPP; - goto out; - } - - if (block) - ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd); - else - lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd)); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, - struct sleep_params *sp) -{ - struct cmd_ds_802_11_sleep_params cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - if (cmd_action == CMD_ACT_GET) { - memset(&cmd, 0, sizeof(cmd)); - } else { - cmd.error = cpu_to_le16(sp->sp_error); - cmd.offset = cpu_to_le16(sp->sp_offset); - cmd.stabletime = cpu_to_le16(sp->sp_stabletime); - cmd.calcontrol = sp->sp_calcontrol; - cmd.externalsleepclk = sp->sp_extsleepclk; - cmd.reserved = cpu_to_le16(sp->sp_reserved); - } - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(cmd_action); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SLEEP_PARAMS, &cmd); - - if (!ret) { - lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, " - "calcontrol 0x%x extsleepclk 0x%x\n", - le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset), - le16_to_cpu(cmd.stabletime), cmd.calcontrol, - cmd.externalsleepclk); - - sp->sp_error = le16_to_cpu(cmd.error); - sp->sp_offset = le16_to_cpu(cmd.offset); - sp->sp_stabletime = le16_to_cpu(cmd.stabletime); - sp->sp_calcontrol = cmd.calcontrol; - sp->sp_extsleepclk = cmd.externalsleepclk; - sp->sp_reserved = le16_to_cpu(cmd.reserved); - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return 0; -} - -static int lbs_wait_for_ds_awake(struct lbs_private *priv) -{ - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - if (priv->is_deep_sleep) { - if (!wait_event_interruptible_timeout(priv->ds_awake_q, - !priv->is_deep_sleep, (10 * HZ))) { - netdev_err(priv->dev, "ds_awake_q: timer expired\n"); - ret = -1; - } - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep) -{ - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - if (deep_sleep) { - if (priv->is_deep_sleep != 1) { - lbs_deb_cmd("deep sleep: sleep\n"); - BUG_ON(!priv->enter_deep_sleep); - ret = priv->enter_deep_sleep(priv); - if (!ret) { - netif_stop_queue(priv->dev); - netif_carrier_off(priv->dev); - } - } else { - netdev_err(priv->dev, "deep sleep: already enabled\n"); - } - } else { - if (priv->is_deep_sleep) { - lbs_deb_cmd("deep sleep: wakeup\n"); - BUG_ON(!priv->exit_deep_sleep); - ret = priv->exit_deep_sleep(priv); - if (!ret) { - ret = lbs_wait_for_ds_awake(priv); - if (ret) - netdev_err(priv->dev, - "deep sleep: wakeup failed\n"); - } - } - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -static int lbs_ret_host_sleep_activate(struct lbs_private *priv, - unsigned long dummy, - struct cmd_header *cmd) -{ - lbs_deb_enter(LBS_DEB_FW); - priv->is_host_sleep_activated = 1; - wake_up_interruptible(&priv->host_sleep_q); - lbs_deb_leave(LBS_DEB_FW); - return 0; -} - -int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep) -{ - struct cmd_header cmd; - int ret = 0; - uint32_t criteria = EHS_REMOVE_WAKEUP; - - lbs_deb_enter(LBS_DEB_CMD); - - if (host_sleep) { - if (priv->is_host_sleep_activated != 1) { - memset(&cmd, 0, sizeof(cmd)); - ret = lbs_host_sleep_cfg(priv, priv->wol_criteria, - (struct wol_config *)NULL); - if (ret) { - netdev_info(priv->dev, - "Host sleep configuration failed: %d\n", - ret); - return ret; - } - if (priv->psstate == PS_STATE_FULL_POWER) { - ret = __lbs_cmd(priv, - CMD_802_11_HOST_SLEEP_ACTIVATE, - &cmd, - sizeof(cmd), - lbs_ret_host_sleep_activate, 0); - if (ret) - netdev_info(priv->dev, - "HOST_SLEEP_ACTIVATE failed: %d\n", - ret); - } - - if (!wait_event_interruptible_timeout( - priv->host_sleep_q, - priv->is_host_sleep_activated, - (10 * HZ))) { - netdev_err(priv->dev, - "host_sleep_q: timer expired\n"); - ret = -1; - } - } else { - netdev_err(priv->dev, "host sleep: already enabled\n"); - } - } else { - if (priv->is_host_sleep_activated) - ret = lbs_host_sleep_cfg(priv, criteria, - (struct wol_config *)NULL); - } - - return ret; -} - -/** - * lbs_set_snmp_mib - Set an SNMP MIB value - * - * @priv: A pointer to &struct lbs_private structure - * @oid: The OID to set in the firmware - * @val: Value to set the OID to - * - * returns: 0 on success, error on failure - */ -int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val) -{ - struct cmd_ds_802_11_snmp_mib cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof (cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.oid = cpu_to_le16((u16) oid); - - switch (oid) { - case SNMP_MIB_OID_BSS_TYPE: - cmd.bufsize = cpu_to_le16(sizeof(u8)); - cmd.value[0] = val; - break; - case SNMP_MIB_OID_11D_ENABLE: - case SNMP_MIB_OID_FRAG_THRESHOLD: - case SNMP_MIB_OID_RTS_THRESHOLD: - case SNMP_MIB_OID_SHORT_RETRY_LIMIT: - case SNMP_MIB_OID_LONG_RETRY_LIMIT: - cmd.bufsize = cpu_to_le16(sizeof(u16)); - *((__le16 *)(&cmd.value)) = cpu_to_le16(val); - break; - default: - lbs_deb_cmd("SNMP_CMD: (set) unhandled OID 0x%x\n", oid); - ret = -EINVAL; - goto out; - } - - lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n", - le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_get_snmp_mib - Get an SNMP MIB value - * - * @priv: A pointer to &struct lbs_private structure - * @oid: The OID to retrieve from the firmware - * @out_val: Location for the returned value - * - * returns: 0 on success, error on failure - */ -int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val) -{ - struct cmd_ds_802_11_snmp_mib cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof (cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_GET); - cmd.oid = cpu_to_le16(oid); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); - if (ret) - goto out; - - switch (le16_to_cpu(cmd.bufsize)) { - case sizeof(u8): - *out_val = cmd.value[0]; - break; - case sizeof(u16): - *out_val = le16_to_cpu(*((__le16 *)(&cmd.value))); - break; - default: - lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n", - oid, le16_to_cpu(cmd.bufsize)); - break; - } - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_get_tx_power - Get the min, max, and current TX power - * - * @priv: A pointer to &struct lbs_private structure - * @curlevel: Current power level in dBm - * @minlevel: Minimum supported power level in dBm (optional) - * @maxlevel: Maximum supported power level in dBm (optional) - * - * returns: 0 on success, error on failure - */ -int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, - s16 *maxlevel) -{ - struct cmd_ds_802_11_rf_tx_power cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_GET); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); - if (ret == 0) { - *curlevel = le16_to_cpu(cmd.curlevel); - if (minlevel) - *minlevel = cmd.minlevel; - if (maxlevel) - *maxlevel = cmd.maxlevel; - } - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -/** - * lbs_set_tx_power - Set the TX power - * - * @priv: A pointer to &struct lbs_private structure - * @dbm: The desired power level in dBm - * - * returns: 0 on success, error on failure - */ -int lbs_set_tx_power(struct lbs_private *priv, s16 dbm) -{ - struct cmd_ds_802_11_rf_tx_power cmd; - int ret; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.curlevel = cpu_to_le16(dbm); - - lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -/** - * lbs_set_monitor_mode - Enable or disable monitor mode - * (only implemented on OLPC usb8388 FW) - * - * @priv: A pointer to &struct lbs_private structure - * @enable: 1 to enable monitor mode, 0 to disable - * - * returns: 0 on success, error on failure - */ -int lbs_set_monitor_mode(struct lbs_private *priv, int enable) -{ - struct cmd_ds_802_11_monitor_mode cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - if (enable) - cmd.mode = cpu_to_le16(0x1); - - lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable); - - ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd); - if (ret == 0) { - priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP : - ARPHRD_ETHER; - } - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -/** - * lbs_get_channel - Get the radio channel - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: The channel on success, error on failure - */ -static int lbs_get_channel(struct lbs_private *priv) -{ - struct cmd_ds_802_11_rf_channel cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); - if (ret) - goto out; - - ret = le16_to_cpu(cmd.channel); - lbs_deb_cmd("current radio channel is %d\n", ret); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -int lbs_update_channel(struct lbs_private *priv) -{ - int ret; - - /* the channel in f/w could be out of sync; get the current channel */ - lbs_deb_enter(LBS_DEB_ASSOC); - - ret = lbs_get_channel(priv); - if (ret > 0) { - priv->channel = ret; - ret = 0; - } - lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); - return ret; -} - -/** - * lbs_set_channel - Set the radio channel - * - * @priv: A pointer to &struct lbs_private structure - * @channel: The desired channel, or 0 to clear a locked channel - * - * returns: 0 on success, error on failure - */ -int lbs_set_channel(struct lbs_private *priv, u8 channel) -{ - struct cmd_ds_802_11_rf_channel cmd; -#ifdef DEBUG - u8 old_channel = priv->channel; -#endif - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); - cmd.channel = cpu_to_le16(channel); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); - if (ret) - goto out; - - priv->channel = (uint8_t) le16_to_cpu(cmd.channel); - lbs_deb_cmd("channel switch from %d to %d\n", old_channel, - priv->channel); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_get_rssi - Get current RSSI and noise floor - * - * @priv: A pointer to &struct lbs_private structure - * @rssi: On successful return, signal level in mBm - * @nf: On successful return, Noise floor - * - * returns: The channel on success, error on failure - */ -int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf) -{ - struct cmd_ds_802_11_rssi cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - BUG_ON(rssi == NULL); - BUG_ON(nf == NULL); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - /* Average SNR over last 8 beacons */ - cmd.n_or_snr = cpu_to_le16(8); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd); - if (ret == 0) { - *nf = CAL_NF(le16_to_cpu(cmd.nf)); - *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf)); - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_set_11d_domain_info - Send regulatory and 802.11d domain information - * to the firmware - * - * @priv: pointer to &struct lbs_private - * - * returns: 0 on success, error code on failure -*/ -int lbs_set_11d_domain_info(struct lbs_private *priv) -{ - struct wiphy *wiphy = priv->wdev->wiphy; - struct ieee80211_supported_band **bands = wiphy->bands; - struct cmd_ds_802_11d_domain_info cmd; - struct mrvl_ie_domain_param_set *domain = &cmd.domain; - struct ieee80211_country_ie_triplet *t; - enum ieee80211_band band; - struct ieee80211_channel *ch; - u8 num_triplet = 0; - u8 num_parsed_chan = 0; - u8 first_channel = 0, next_chan = 0, max_pwr = 0; - u8 i, flag = 0; - size_t triplet_size; - int ret = 0; - - lbs_deb_enter(LBS_DEB_11D); - if (!priv->country_code[0]) - goto out; - - memset(&cmd, 0, sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - - lbs_deb_11d("Setting country code '%c%c'\n", - priv->country_code[0], priv->country_code[1]); - - domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); - - /* Set country code */ - domain->country_code[0] = priv->country_code[0]; - domain->country_code[1] = priv->country_code[1]; - domain->country_code[2] = ' '; - - /* Now set up the channel triplets; firmware is somewhat picky here - * and doesn't validate channel numbers and spans; hence it would - * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since - * the last 3 aren't valid channels, the driver is responsible for - * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20) - * etc. - */ - for (band = 0; - (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS); - band++) { - - if (!bands[band]) - continue; - - for (i = 0; - (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS); - i++) { - ch = &bands[band]->channels[i]; - if (ch->flags & IEEE80211_CHAN_DISABLED) - continue; - - if (!flag) { - flag = 1; - next_chan = first_channel = (u32) ch->hw_value; - max_pwr = ch->max_power; - num_parsed_chan = 1; - continue; - } - - if ((ch->hw_value == next_chan + 1) && - (ch->max_power == max_pwr)) { - /* Consolidate adjacent channels */ - next_chan++; - num_parsed_chan++; - } else { - /* Add this triplet */ - lbs_deb_11d("11D triplet (%d, %d, %d)\n", - first_channel, num_parsed_chan, - max_pwr); - t = &domain->triplet[num_triplet]; - t->chans.first_channel = first_channel; - t->chans.num_channels = num_parsed_chan; - t->chans.max_power = max_pwr; - num_triplet++; - flag = 0; - } - } - - if (flag) { - /* Add last triplet */ - lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel, - num_parsed_chan, max_pwr); - t = &domain->triplet[num_triplet]; - t->chans.first_channel = first_channel; - t->chans.num_channels = num_parsed_chan; - t->chans.max_power = max_pwr; - num_triplet++; - } - } - - lbs_deb_11d("# triplets %d\n", num_triplet); - - /* Set command header sizes */ - triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet); - domain->header.len = cpu_to_le16(sizeof(domain->country_code) + - triplet_size); - - lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set", - (u8 *) &cmd.domain.country_code, - le16_to_cpu(domain->header.len)); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) + - sizeof(cmd.action) + - sizeof(cmd.domain.header) + - sizeof(cmd.domain.country_code) + - triplet_size); - - ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd); - -out: - lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); - return ret; -} - -/** - * lbs_get_reg - Read a MAC, Baseband, or RF register - * - * @priv: pointer to &struct lbs_private - * @reg: register command, one of CMD_MAC_REG_ACCESS, - * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS - * @offset: byte offset of the register to get - * @value: on success, the value of the register at 'offset' - * - * returns: 0 on success, error code on failure -*/ -int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) -{ - struct cmd_ds_reg_access cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - BUG_ON(value == NULL); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_GET); - cmd.offset = cpu_to_le16(offset); - - if (reg != CMD_MAC_REG_ACCESS && - reg != CMD_BBP_REG_ACCESS && - reg != CMD_RF_REG_ACCESS) { - ret = -EINVAL; - goto out; - } - - ret = lbs_cmd_with_response(priv, reg, &cmd); - if (!ret) { - if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) - *value = cmd.value.bbp_rf; - else if (reg == CMD_MAC_REG_ACCESS) - *value = le32_to_cpu(cmd.value.mac); - } - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -/** - * lbs_set_reg - Write a MAC, Baseband, or RF register - * - * @priv: pointer to &struct lbs_private - * @reg: register command, one of CMD_MAC_REG_ACCESS, - * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS - * @offset: byte offset of the register to set - * @value: the value to write to the register at 'offset' - * - * returns: 0 on success, error code on failure -*/ -int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value) -{ - struct cmd_ds_reg_access cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.offset = cpu_to_le16(offset); - - if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) - cmd.value.bbp_rf = (u8) (value & 0xFF); - else if (reg == CMD_MAC_REG_ACCESS) - cmd.value.mac = cpu_to_le32(value); - else { - ret = -EINVAL; - goto out; - } - - ret = lbs_cmd_with_response(priv, reg, &cmd); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -static void lbs_queue_cmd(struct lbs_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - unsigned long flags; - int addtail = 1; - - lbs_deb_enter(LBS_DEB_HOST); - - if (!cmdnode) { - lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n"); - goto done; - } - if (!cmdnode->cmdbuf->size) { - lbs_deb_host("DNLD_CMD: cmd size is zero\n"); - goto done; - } - cmdnode->result = 0; - - /* Exit_PS command needs to be queued in the header always. */ - if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) { - struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf; - - if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { - if (priv->psstate != PS_STATE_FULL_POWER) - addtail = 0; - } - } - - if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM) - addtail = 0; - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (addtail) - list_add_tail(&cmdnode->list, &priv->cmdpendingq); - else - list_add(&cmdnode->list, &priv->cmdpendingq); - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", - le16_to_cpu(cmdnode->cmdbuf->command)); - -done: - lbs_deb_leave(LBS_DEB_HOST); -} - -static void lbs_submit_command(struct lbs_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - unsigned long flags; - struct cmd_header *cmd; - uint16_t cmdsize; - uint16_t command; - int timeo = 3 * HZ; - int ret; - - lbs_deb_enter(LBS_DEB_HOST); - - cmd = cmdnode->cmdbuf; - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->seqnum++; - cmd->seqnum = cpu_to_le16(priv->seqnum); - priv->cur_cmd = cmdnode; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - cmdsize = le16_to_cpu(cmd->size); - command = le16_to_cpu(cmd->command); - - /* These commands take longer */ - if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE) - timeo = 5 * HZ; - - lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", - command, le16_to_cpu(cmd->seqnum), cmdsize); - lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); - - ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); - - if (ret) { - netdev_info(priv->dev, "DNLD_CMD: hw_host_to_card failed: %d\n", - ret); - /* Reset dnld state machine, report failure */ - priv->dnld_sent = DNLD_RES_RECEIVED; - lbs_complete_command(priv, cmdnode, ret); - } - - if (command == CMD_802_11_DEEP_SLEEP) { - if (priv->is_auto_deep_sleep_enabled) { - priv->wakeup_dev_required = 1; - priv->dnld_sent = 0; - } - priv->is_deep_sleep = 1; - lbs_complete_command(priv, cmdnode, 0); - } else { - /* Setup the timer after transmit command */ - mod_timer(&priv->command_timer, jiffies + timeo); - } - - lbs_deb_leave(LBS_DEB_HOST); -} - -/* - * This function inserts command node to cmdfreeq - * after cleans it. Requires priv->driver_lock held. - */ -static void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - lbs_deb_enter(LBS_DEB_HOST); - - if (!cmdnode) - goto out; - - cmdnode->callback = NULL; - cmdnode->callback_arg = 0; - - memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); - - list_add_tail(&cmdnode->list, &priv->cmdfreeq); - out: - lbs_deb_leave(LBS_DEB_HOST); -} - -static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv, - struct cmd_ctrl_node *ptempcmd) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->driver_lock, flags); - __lbs_cleanup_and_insert_cmd(priv, ptempcmd); - spin_unlock_irqrestore(&priv->driver_lock, flags); -} - -void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result) -{ - /* - * Normally, commands are removed from cmdpendingq before being - * submitted. However, we can arrive here on alternative codepaths - * where the command is still pending. Make sure the command really - * isn't part of a list at this point. - */ - list_del_init(&cmd->list); - - cmd->result = result; - cmd->cmdwaitqwoken = 1; - wake_up(&cmd->cmdwait_q); - - if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) - __lbs_cleanup_and_insert_cmd(priv, cmd); - priv->cur_cmd = NULL; - wake_up(&priv->waitq); -} - -void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result) -{ - unsigned long flags; - spin_lock_irqsave(&priv->driver_lock, flags); - __lbs_complete_command(priv, cmd, result); - spin_unlock_irqrestore(&priv->driver_lock, flags); -} - -int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) -{ - struct cmd_ds_802_11_radio_control cmd; - int ret = -EINVAL; - - lbs_deb_enter(LBS_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.control = 0; - - /* Only v8 and below support setting the preamble */ - if (priv->fwrelease < 0x09000000) { - switch (preamble) { - case RADIO_PREAMBLE_SHORT: - case RADIO_PREAMBLE_AUTO: - case RADIO_PREAMBLE_LONG: - cmd.control = cpu_to_le16(preamble); - break; - default: - goto out; - } - } - - if (radio_on) - cmd.control |= cpu_to_le16(0x1); - else { - cmd.control &= cpu_to_le16(~0x1); - priv->txpower_cur = 0; - } - - lbs_deb_cmd("RADIO_CONTROL: radio %s, preamble %d\n", - radio_on ? "ON" : "OFF", preamble); - - priv->radio_on = radio_on; - - ret = lbs_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); - -out: - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} - -void lbs_set_mac_control(struct lbs_private *priv) -{ - struct cmd_ds_mac_control cmd; - - lbs_deb_enter(LBS_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(priv->mac_control); - cmd.reserved = 0; - - lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd)); - - lbs_deb_leave(LBS_DEB_CMD); -} - -int lbs_set_mac_control_sync(struct lbs_private *priv) -{ - struct cmd_ds_mac_control cmd; - int ret = 0; - - lbs_deb_enter(LBS_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(priv->mac_control); - cmd.reserved = 0; - ret = lbs_cmd_with_response(priv, CMD_MAC_CONTROL, &cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -/** - * lbs_allocate_cmd_buffer - allocates the command buffer and links - * it to command free queue - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: 0 for success or -1 on error - */ -int lbs_allocate_cmd_buffer(struct lbs_private *priv) -{ - int ret = 0; - u32 bufsize; - u32 i; - struct cmd_ctrl_node *cmdarray; - - lbs_deb_enter(LBS_DEB_HOST); - - /* Allocate and initialize the command array */ - bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; - if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) { - lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); - ret = -1; - goto done; - } - priv->cmd_array = cmdarray; - - /* Allocate and initialize each command buffer in the command array */ - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); - if (!cmdarray[i].cmdbuf) { - lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); - ret = -1; - goto done; - } - } - - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - init_waitqueue_head(&cmdarray[i].cmdwait_q); - lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]); - } - ret = 0; - -done: - lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); - return ret; -} - -/** - * lbs_free_cmd_buffer - free the command buffer - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: 0 for success - */ -int lbs_free_cmd_buffer(struct lbs_private *priv) -{ - struct cmd_ctrl_node *cmdarray; - unsigned int i; - - lbs_deb_enter(LBS_DEB_HOST); - - /* need to check if cmd array is allocated or not */ - if (priv->cmd_array == NULL) { - lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); - goto done; - } - - cmdarray = priv->cmd_array; - - /* Release shared memory buffers */ - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - if (cmdarray[i].cmdbuf) { - kfree(cmdarray[i].cmdbuf); - cmdarray[i].cmdbuf = NULL; - } - } - - /* Release cmd_ctrl_node */ - if (priv->cmd_array) { - kfree(priv->cmd_array); - priv->cmd_array = NULL; - } - -done: - lbs_deb_leave(LBS_DEB_HOST); - return 0; -} - -/** - * lbs_get_free_cmd_node - gets a free command node if available in - * command free queue - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: A pointer to &cmd_ctrl_node structure on success - * or %NULL on error - */ -static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv) -{ - struct cmd_ctrl_node *tempnode; - unsigned long flags; - - lbs_deb_enter(LBS_DEB_HOST); - - if (!priv) - return NULL; - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!list_empty(&priv->cmdfreeq)) { - tempnode = list_first_entry(&priv->cmdfreeq, - struct cmd_ctrl_node, list); - list_del_init(&tempnode->list); - } else { - lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); - tempnode = NULL; - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbs_deb_leave(LBS_DEB_HOST); - return tempnode; -} - -/** - * lbs_execute_next_command - execute next command in command - * pending queue. Will put firmware back to PS mode if applicable. - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: 0 on success or -1 on error - */ -int lbs_execute_next_command(struct lbs_private *priv) -{ - struct cmd_ctrl_node *cmdnode = NULL; - struct cmd_header *cmd; - unsigned long flags; - int ret = 0; - - /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the - * only caller to us is lbs_thread() and we get even when a - * data packet is received */ - lbs_deb_enter(LBS_DEB_THREAD); - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->cur_cmd) { - netdev_alert(priv->dev, - "EXEC_NEXT_CMD: already processing command!\n"); - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - if (!list_empty(&priv->cmdpendingq)) { - cmdnode = list_first_entry(&priv->cmdpendingq, - struct cmd_ctrl_node, list); - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - if (cmdnode) { - cmd = cmdnode->cmdbuf; - - if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) { - if ((priv->psstate == PS_STATE_SLEEP) || - (priv->psstate == PS_STATE_PRE_SLEEP)) { - lbs_deb_host( - "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n", - le16_to_cpu(cmd->command), - priv->psstate); - ret = -1; - goto done; - } - lbs_deb_host("EXEC_NEXT_CMD: OK to send command " - "0x%04x in psstate %d\n", - le16_to_cpu(cmd->command), priv->psstate); - } else if (priv->psstate != PS_STATE_FULL_POWER) { - /* - * 1. Non-PS command: - * Queue it. set needtowakeup to TRUE if current state - * is SLEEP, otherwise call send EXIT_PS. - * 2. PS command but not EXIT_PS: - * Ignore it. - * 3. PS command EXIT_PS: - * Set needtowakeup to TRUE if current state is SLEEP, - * otherwise send this command down to firmware - * immediately. - */ - if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) { - /* Prepare to send Exit PS, - * this non PS command will be sent later */ - if ((priv->psstate == PS_STATE_SLEEP) - || (priv->psstate == PS_STATE_PRE_SLEEP) - ) { - /* w/ new scheme, it will not reach here. - since it is blocked in main_thread. */ - priv->needtowakeup = 1; - } else { - lbs_set_ps_mode(priv, - PS_MODE_ACTION_EXIT_PS, - false); - } - - ret = 0; - goto done; - } else { - /* - * PS command. Ignore it if it is not Exit_PS. - * otherwise send it down immediately. - */ - struct cmd_ds_802_11_ps_mode *psm = (void *)&cmd[1]; - - lbs_deb_host( - "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n", - psm->action); - if (psm->action != - cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { - lbs_deb_host( - "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); - lbs_complete_command(priv, cmdnode, 0); - - ret = 0; - goto done; - } - - if ((priv->psstate == PS_STATE_SLEEP) || - (priv->psstate == PS_STATE_PRE_SLEEP)) { - lbs_deb_host( - "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); - lbs_complete_command(priv, cmdnode, 0); - priv->needtowakeup = 1; - - ret = 0; - goto done; - } - - lbs_deb_host( - "EXEC_NEXT_CMD: sending EXIT_PS\n"); - } - } - spin_lock_irqsave(&priv->driver_lock, flags); - list_del_init(&cmdnode->list); - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", - le16_to_cpu(cmd->command)); - lbs_submit_command(priv, cmdnode); - } else { - /* - * check if in power save mode, if yes, put the device back - * to PS mode - */ -#ifdef TODO - /* - * This was the old code for libertas+wext. Someone that - * understands this beast should re-code it in a sane way. - * - * I actually don't understand why this is related to WPA - * and to connection status, shouldn't powering should be - * independ of such things? - */ - if ((priv->psmode != LBS802_11POWERMODECAM) && - (priv->psstate == PS_STATE_FULL_POWER) && - ((priv->connect_status == LBS_CONNECTED) || - lbs_mesh_connected(priv))) { - if (priv->secinfo.WPAenabled || - priv->secinfo.WPA2enabled) { - /* check for valid WPA group keys */ - if (priv->wpa_mcast_key.len || - priv->wpa_unicast_key.len) { - lbs_deb_host( - "EXEC_NEXT_CMD: WPA enabled and GTK_SET" - " go back to PS_SLEEP"); - lbs_set_ps_mode(priv, - PS_MODE_ACTION_ENTER_PS, - false); - } - } else { - lbs_deb_host( - "EXEC_NEXT_CMD: cmdpendingq empty, " - "go back to PS_SLEEP"); - lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, - false); - } - } -#endif - } - - ret = 0; -done: - lbs_deb_leave(LBS_DEB_THREAD); - return ret; -} - -static void lbs_send_confirmsleep(struct lbs_private *priv) -{ - unsigned long flags; - int ret; - - lbs_deb_enter(LBS_DEB_HOST); - lbs_deb_hex(LBS_DEB_HOST, "sleep confirm", (u8 *) &confirm_sleep, - sizeof(confirm_sleep)); - - ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep, - sizeof(confirm_sleep)); - if (ret) { - netdev_alert(priv->dev, "confirm_sleep failed\n"); - goto out; - } - - spin_lock_irqsave(&priv->driver_lock, flags); - - /* We don't get a response on the sleep-confirmation */ - priv->dnld_sent = DNLD_RES_RECEIVED; - - if (priv->is_host_sleep_configured) { - priv->is_host_sleep_activated = 1; - wake_up_interruptible(&priv->host_sleep_q); - } - - /* If nothing to do, go back to sleep (?) */ - if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx]) - priv->psstate = PS_STATE_SLEEP; - - spin_unlock_irqrestore(&priv->driver_lock, flags); - -out: - lbs_deb_leave(LBS_DEB_HOST); -} - -/** - * lbs_ps_confirm_sleep - checks condition and prepares to - * send sleep confirm command to firmware if ok - * - * @priv: A pointer to &struct lbs_private structure - * - * returns: n/a - */ -void lbs_ps_confirm_sleep(struct lbs_private *priv) -{ - unsigned long flags =0; - int allowed = 1; - - lbs_deb_enter(LBS_DEB_HOST); - - spin_lock_irqsave(&priv->driver_lock, flags); - if (priv->dnld_sent) { - allowed = 0; - lbs_deb_host("dnld_sent was set\n"); - } - - /* In-progress command? */ - if (priv->cur_cmd) { - allowed = 0; - lbs_deb_host("cur_cmd was set\n"); - } - - /* Pending events or command responses? */ - if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) { - allowed = 0; - lbs_deb_host("pending events or command responses\n"); - } - spin_unlock_irqrestore(&priv->driver_lock, flags); - - if (allowed) { - lbs_deb_host("sending lbs_ps_confirm_sleep\n"); - lbs_send_confirmsleep(priv); - } else { - lbs_deb_host("sleep confirm has been delayed\n"); - } - - lbs_deb_leave(LBS_DEB_HOST); -} - - -/** - * lbs_set_tpc_cfg - Configures the transmission power control functionality - * - * @priv: A pointer to &struct lbs_private structure - * @enable: Transmission power control enable - * @p0: Power level when link quality is good (dBm). - * @p1: Power level when link quality is fair (dBm). - * @p2: Power level when link quality is poor (dBm). - * @usesnr: Use Signal to Noise Ratio in TPC - * - * returns: 0 on success - */ -int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, - int8_t p2, int usesnr) -{ - struct cmd_ds_802_11_tpc_cfg cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.enable = !!enable; - cmd.usesnr = !!usesnr; - cmd.P0 = p0; - cmd.P1 = p1; - cmd.P2 = p2; - - ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd); - - return ret; -} - -/** - * lbs_set_power_adapt_cfg - Configures the power adaptation settings - * - * @priv: A pointer to &struct lbs_private structure - * @enable: Power adaptation enable - * @p0: Power level for 1, 2, 5.5 and 11 Mbps (dBm). - * @p1: Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm). - * @p2: Power level for 48 and 54 Mbps (dBm). - * - * returns: 0 on Success - */ - -int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, - int8_t p1, int8_t p2) -{ - struct cmd_ds_802_11_pa_cfg cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.enable = !!enable; - cmd.P0 = p0; - cmd.P1 = p1; - cmd.P2 = p2; - - ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd); - - return ret; -} - - -struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, - uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), - unsigned long callback_arg) -{ - struct cmd_ctrl_node *cmdnode; - - lbs_deb_enter(LBS_DEB_HOST); - - if (priv->surpriseremoved) { - lbs_deb_host("PREP_CMD: card removed\n"); - cmdnode = ERR_PTR(-ENOENT); - goto done; - } - - /* No commands are allowed in Deep Sleep until we toggle the GPIO - * to wake up the card and it has signaled that it's ready. - */ - if (!priv->is_auto_deep_sleep_enabled) { - if (priv->is_deep_sleep) { - lbs_deb_cmd("command not allowed in deep sleep\n"); - cmdnode = ERR_PTR(-EBUSY); - goto done; - } - } - - cmdnode = lbs_get_free_cmd_node(priv); - if (cmdnode == NULL) { - lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); - - /* Wake up main thread to execute next command */ - wake_up(&priv->waitq); - cmdnode = ERR_PTR(-ENOBUFS); - goto done; - } - - cmdnode->callback = callback; - cmdnode->callback_arg = callback_arg; - - /* Copy the incoming command to the buffer */ - memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); - - /* Set command, clean result, move to buffer */ - cmdnode->cmdbuf->command = cpu_to_le16(command); - cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); - cmdnode->cmdbuf->result = 0; - - lbs_deb_host("PREP_CMD: command 0x%04x\n", command); - - cmdnode->cmdwaitqwoken = 0; - lbs_queue_cmd(priv, cmdnode); - wake_up(&priv->waitq); - - done: - lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode); - return cmdnode; -} - -void lbs_cmd_async(struct lbs_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size) -{ - lbs_deb_enter(LBS_DEB_CMD); - __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, - lbs_cmd_async_callback, 0); - lbs_deb_leave(LBS_DEB_CMD); -} - -int __lbs_cmd(struct lbs_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), - unsigned long callback_arg) -{ - struct cmd_ctrl_node *cmdnode; - unsigned long flags; - int ret = 0; - - lbs_deb_enter(LBS_DEB_HOST); - - cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, - callback, callback_arg); - if (IS_ERR(cmdnode)) { - ret = PTR_ERR(cmdnode); - goto done; - } - - might_sleep(); - - /* - * Be careful with signals here. A signal may be received as the system - * goes into suspend or resume. We do not want this to interrupt the - * command, so we perform an uninterruptible sleep. - */ - wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); - - spin_lock_irqsave(&priv->driver_lock, flags); - ret = cmdnode->result; - if (ret) - netdev_info(priv->dev, "PREP_CMD: command 0x%04x failed: %d\n", - command, ret); - - __lbs_cleanup_and_insert_cmd(priv, cmdnode); - spin_unlock_irqrestore(&priv->driver_lock, flags); - -done: - lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(__lbs_cmd); diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h deleted file mode 100644 index 0c5444b02c64..000000000000 --- a/drivers/net/wireless/libertas/cmd.h +++ /dev/null @@ -1,141 +0,0 @@ -/* Copyright (C) 2007, Red Hat, Inc. */ - -#ifndef _LBS_CMD_H_ -#define _LBS_CMD_H_ - -#include - -#include "host.h" -#include "dev.h" - - -/* Command & response transfer between host and card */ - -struct cmd_ctrl_node { - struct list_head list; - int result; - /* command response */ - int (*callback)(struct lbs_private *, - unsigned long, - struct cmd_header *); - unsigned long callback_arg; - /* command data */ - struct cmd_header *cmdbuf; - /* wait queue */ - u16 cmdwaitqwoken; - wait_queue_head_t cmdwait_q; -}; - - -/* lbs_cmd() infers the size of the buffer to copy data back into, from - the size of the target of the pointer. Since the command to be sent - may often be smaller, that size is set in cmd->size by the caller.*/ -#define lbs_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ - uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ - (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \ - __lbs_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \ -}) - -#define lbs_cmd_with_response(priv, cmdnr, cmd) \ - lbs_cmd(priv, cmdnr, cmd, lbs_cmd_copyback, (unsigned long) (cmd)) - -void lbs_cmd_async(struct lbs_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size); - -int __lbs_cmd(struct lbs_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), - unsigned long callback_arg); - -struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, - uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), - unsigned long callback_arg); - -int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, - struct cmd_header *resp); - -int lbs_allocate_cmd_buffer(struct lbs_private *priv); -int lbs_free_cmd_buffer(struct lbs_private *priv); - -int lbs_execute_next_command(struct lbs_private *priv); -void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result); -void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result); -int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); - - -/* From cmdresp.c */ - -void lbs_mac_event_disconnected(struct lbs_private *priv, - bool locally_generated); - - - -/* Events */ - -int lbs_process_event(struct lbs_private *priv, u32 event); - - -/* Actual commands */ - -int lbs_update_hw_spec(struct lbs_private *priv); - -int lbs_set_channel(struct lbs_private *priv, u8 channel); - -int lbs_update_channel(struct lbs_private *priv); - -int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, - struct wol_config *p_wol_config); - -int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, - struct sleep_params *sp); - -void lbs_ps_confirm_sleep(struct lbs_private *priv); - -int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on); - -void lbs_set_mac_control(struct lbs_private *priv); -int lbs_set_mac_control_sync(struct lbs_private *priv); - -int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, - s16 *maxlevel); - -int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val); - -int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val); - - -/* Commands only used in wext.c, assoc. and scan.c */ - -int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, - int8_t p1, int8_t p2); - -int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, - int8_t p2, int usesnr); - -int lbs_set_data_rate(struct lbs_private *priv, u8 rate); - -int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, - uint16_t cmd_action); - -int lbs_set_tx_power(struct lbs_private *priv, s16 dbm); - -int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep); - -int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep); - -int lbs_set_monitor_mode(struct lbs_private *priv, int enable); - -int lbs_get_rssi(struct lbs_private *priv, s8 *snr, s8 *nf); - -int lbs_set_11d_domain_info(struct lbs_private *priv); - -int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value); - -int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value); - -int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block); - -#endif /* _LBS_CMD_H */ diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c deleted file mode 100644 index e5442e8956f7..000000000000 --- a/drivers/net/wireless/libertas/cmdresp.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * This file contains the handling of command - * responses as well as events generated by firmware. - */ - -#include -#include -#include -#include -#include -#include - -#include "cfg.h" -#include "cmd.h" - -/** - * lbs_mac_event_disconnected - handles disconnect event. It - * reports disconnect to upper layer, clean tx/rx packets, - * reset link state etc. - * - * @priv: A pointer to struct lbs_private structure - * @locally_generated: indicates disconnect was requested locally - * (usually by userspace) - * - * returns: n/a - */ -void lbs_mac_event_disconnected(struct lbs_private *priv, - bool locally_generated) -{ - if (priv->connect_status != LBS_CONNECTED) - return; - - lbs_deb_enter(LBS_DEB_ASSOC); - - /* - * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. - * It causes problem in the Supplicant - */ - msleep_interruptible(1000); - - if (priv->wdev->iftype == NL80211_IFTYPE_STATION) - lbs_send_disconnect_notification(priv, locally_generated); - - /* report disconnect to upper layer */ - netif_stop_queue(priv->dev); - netif_carrier_off(priv->dev); - - /* Free Tx and Rx packets */ - kfree_skb(priv->currenttxskb); - priv->currenttxskb = NULL; - priv->tx_pending_len = 0; - - priv->connect_status = LBS_DISCONNECTED; - - if (priv->psstate != PS_STATE_FULL_POWER) { - /* make firmware to exit PS mode */ - lbs_deb_cmd("disconnected, so exit PS mode\n"); - lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); - } - lbs_deb_leave(LBS_DEB_ASSOC); -} - -int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) -{ - uint16_t respcmd, curcmd; - struct cmd_header *resp; - int ret = 0; - unsigned long flags; - uint16_t result; - - lbs_deb_enter(LBS_DEB_HOST); - - mutex_lock(&priv->lock); - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!priv->cur_cmd) { - lbs_deb_host("CMD_RESP: cur_cmd is NULL\n"); - ret = -1; - spin_unlock_irqrestore(&priv->driver_lock, flags); - goto done; - } - - resp = (void *)data; - curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); - respcmd = le16_to_cpu(resp->command); - result = le16_to_cpu(resp->result); - - lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n", - respcmd, le16_to_cpu(resp->seqnum), len); - lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len); - - if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { - netdev_info(priv->dev, - "Received CMD_RESP with invalid sequence %d (expected %d)\n", - le16_to_cpu(resp->seqnum), - le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum)); - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - if (respcmd != CMD_RET(curcmd) && - respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) { - netdev_info(priv->dev, "Invalid CMD_RESP %x to command %x!\n", - respcmd, curcmd); - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - if (resp->result == cpu_to_le16(0x0004)) { - /* 0x0004 means -EAGAIN. Drop the response, let it time out - and be resubmitted */ - netdev_info(priv->dev, - "Firmware returns DEFER to command %x. Will let it time out...\n", - le16_to_cpu(resp->command)); - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - /* Now we got response from FW, cancel the command timer */ - del_timer(&priv->command_timer); - priv->cmd_timed_out = 0; - - if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) { - struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1]; - u16 action = le16_to_cpu(psmode->action); - - lbs_deb_host( - "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n", - result, action); - - if (result) { - lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n", - result); - /* - * We should not re-try enter-ps command in - * ad-hoc mode. It takes place in - * lbs_execute_next_command(). - */ - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR && - action == PS_MODE_ACTION_ENTER_PS) - priv->psmode = LBS802_11POWERMODECAM; - } else if (action == PS_MODE_ACTION_ENTER_PS) { - priv->needtowakeup = 0; - priv->psstate = PS_STATE_AWAKE; - - lbs_deb_host("CMD_RESP: ENTER_PS command response\n"); - if (priv->connect_status != LBS_CONNECTED) { - /* - * When Deauth Event received before Enter_PS command - * response, We need to wake up the firmware. - */ - lbs_deb_host( - "disconnected, invoking lbs_ps_wakeup\n"); - - spin_unlock_irqrestore(&priv->driver_lock, flags); - mutex_unlock(&priv->lock); - lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, - false); - mutex_lock(&priv->lock); - spin_lock_irqsave(&priv->driver_lock, flags); - } - } else if (action == PS_MODE_ACTION_EXIT_PS) { - priv->needtowakeup = 0; - priv->psstate = PS_STATE_FULL_POWER; - lbs_deb_host("CMD_RESP: EXIT_PS command response\n"); - } else { - lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); - } - - __lbs_complete_command(priv, priv->cur_cmd, result); - spin_unlock_irqrestore(&priv->driver_lock, flags); - - ret = 0; - goto done; - } - - /* If the command is not successful, cleanup and return failure */ - if ((result != 0 || !(respcmd & 0x8000))) { - lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n", - result, respcmd); - /* - * Handling errors here - */ - switch (respcmd) { - case CMD_RET(CMD_GET_HW_SPEC): - case CMD_RET(CMD_802_11_RESET): - lbs_deb_host("CMD_RESP: reset failed\n"); - break; - - } - __lbs_complete_command(priv, priv->cur_cmd, result); - spin_unlock_irqrestore(&priv->driver_lock, flags); - - ret = -1; - goto done; - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - if (priv->cur_cmd && priv->cur_cmd->callback) { - ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, - resp); - } - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->cur_cmd) { - /* Clean up and Put current command back to cmdfreeq */ - __lbs_complete_command(priv, priv->cur_cmd, result); - } - spin_unlock_irqrestore(&priv->driver_lock, flags); - -done: - mutex_unlock(&priv->lock); - lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); - return ret; -} - -int lbs_process_event(struct lbs_private *priv, u32 event) -{ - int ret = 0; - struct cmd_header cmd; - - lbs_deb_enter(LBS_DEB_CMD); - - switch (event) { - case MACREG_INT_CODE_LINK_SENSED: - lbs_deb_cmd("EVENT: link sensed\n"); - break; - - case MACREG_INT_CODE_DEAUTHENTICATED: - lbs_deb_cmd("EVENT: deauthenticated\n"); - lbs_mac_event_disconnected(priv, false); - break; - - case MACREG_INT_CODE_DISASSOCIATED: - lbs_deb_cmd("EVENT: disassociated\n"); - lbs_mac_event_disconnected(priv, false); - break; - - case MACREG_INT_CODE_LINK_LOST_NO_SCAN: - lbs_deb_cmd("EVENT: link lost\n"); - lbs_mac_event_disconnected(priv, true); - break; - - case MACREG_INT_CODE_PS_SLEEP: - lbs_deb_cmd("EVENT: ps sleep\n"); - - /* handle unexpected PS SLEEP event */ - if (priv->psstate == PS_STATE_FULL_POWER) { - lbs_deb_cmd( - "EVENT: in FULL POWER mode, ignoring PS_SLEEP\n"); - break; - } - priv->psstate = PS_STATE_PRE_SLEEP; - - lbs_ps_confirm_sleep(priv); - - break; - - case MACREG_INT_CODE_HOST_AWAKE: - lbs_deb_cmd("EVENT: host awake\n"); - if (priv->reset_deep_sleep_wakeup) - priv->reset_deep_sleep_wakeup(priv); - priv->is_deep_sleep = 0; - lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd, - sizeof(cmd)); - priv->is_host_sleep_activated = 0; - wake_up_interruptible(&priv->host_sleep_q); - break; - - case MACREG_INT_CODE_DEEP_SLEEP_AWAKE: - if (priv->reset_deep_sleep_wakeup) - priv->reset_deep_sleep_wakeup(priv); - lbs_deb_cmd("EVENT: ds awake\n"); - priv->is_deep_sleep = 0; - priv->wakeup_dev_required = 0; - wake_up_interruptible(&priv->ds_awake_q); - break; - - case MACREG_INT_CODE_PS_AWAKE: - lbs_deb_cmd("EVENT: ps awake\n"); - /* handle unexpected PS AWAKE event */ - if (priv->psstate == PS_STATE_FULL_POWER) { - lbs_deb_cmd( - "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); - break; - } - - priv->psstate = PS_STATE_AWAKE; - - if (priv->needtowakeup) { - /* - * wait for the command processing to finish - * before resuming sending - * priv->needtowakeup will be set to FALSE - * in lbs_ps_wakeup() - */ - lbs_deb_cmd("waking up ...\n"); - lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); - } - break; - - case MACREG_INT_CODE_MIC_ERR_UNICAST: - lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n"); - lbs_send_mic_failureevent(priv, event); - break; - - case MACREG_INT_CODE_MIC_ERR_MULTICAST: - lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n"); - lbs_send_mic_failureevent(priv, event); - break; - - case MACREG_INT_CODE_MIB_CHANGED: - lbs_deb_cmd("EVENT: MIB CHANGED\n"); - break; - case MACREG_INT_CODE_INIT_DONE: - lbs_deb_cmd("EVENT: INIT DONE\n"); - break; - case MACREG_INT_CODE_ADHOC_BCN_LOST: - lbs_deb_cmd("EVENT: ADHOC beacon lost\n"); - break; - case MACREG_INT_CODE_RSSI_LOW: - netdev_alert(priv->dev, "EVENT: rssi low\n"); - break; - case MACREG_INT_CODE_SNR_LOW: - netdev_alert(priv->dev, "EVENT: snr low\n"); - break; - case MACREG_INT_CODE_MAX_FAIL: - netdev_alert(priv->dev, "EVENT: max fail\n"); - break; - case MACREG_INT_CODE_RSSI_HIGH: - netdev_alert(priv->dev, "EVENT: rssi high\n"); - break; - case MACREG_INT_CODE_SNR_HIGH: - netdev_alert(priv->dev, "EVENT: snr high\n"); - break; - - case MACREG_INT_CODE_MESH_AUTO_STARTED: - /* Ignore spurious autostart events */ - netdev_info(priv->dev, "EVENT: MESH_AUTO_STARTED (ignoring)\n"); - break; - - default: - netdev_alert(priv->dev, "EVENT: unknown event id %d\n", event); - break; - } - - lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); - return ret; -} diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c deleted file mode 100644 index 26cbf1dcc662..000000000000 --- a/drivers/net/wireless/libertas/debugfs.c +++ /dev/null @@ -1,989 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "decl.h" -#include "cmd.h" -#include "debugfs.h" - -static struct dentry *lbs_dir; -static char *szStates[] = { - "Connected", - "Disconnected" -}; - -#ifdef PROC_DEBUG -static void lbs_debug_init(struct lbs_private *priv); -#endif - -static ssize_t write_file_dummy(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static const size_t len = PAGE_SIZE; - -static ssize_t lbs_dev_info(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - size_t pos = 0; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - ssize_t res; - if (!buf) - return -ENOMEM; - - pos += snprintf(buf+pos, len-pos, "state = %s\n", - szStates[priv->connect_status]); - pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", - (u32) priv->regioncode); - - res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - - free_page(addr); - return res; -} - -static ssize_t lbs_sleepparams_write(struct file *file, - const char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t buf_size, ret; - struct sleep_params sp; - int p1, p2, p3, p4, p5, p6; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, user_buf, buf_size)) { - ret = -EFAULT; - goto out_unlock; - } - ret = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); - if (ret != 6) { - ret = -EINVAL; - goto out_unlock; - } - sp.sp_error = p1; - sp.sp_offset = p2; - sp.sp_stabletime = p3; - sp.sp_calcontrol = p4; - sp.sp_extsleepclk = p5; - sp.sp_reserved = p6; - - ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_SET, &sp); - if (!ret) - ret = count; - else if (ret > 0) - ret = -EINVAL; - -out_unlock: - free_page(addr); - return ret; -} - -static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t ret; - size_t pos = 0; - struct sleep_params sp; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp); - if (ret) - goto out_unlock; - - pos += snprintf(buf, len, "%d %d %d %d %d %d\n", sp.sp_error, - sp.sp_offset, sp.sp_stabletime, - sp.sp_calcontrol, sp.sp_extsleepclk, - sp.sp_reserved); - - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - -out_unlock: - free_page(addr); - return ret; -} - -static ssize_t lbs_host_sleep_write(struct file *file, - const char __user *user_buf, size_t count, - loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t buf_size, ret; - int host_sleep; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, user_buf, buf_size)) { - ret = -EFAULT; - goto out_unlock; - } - ret = sscanf(buf, "%d", &host_sleep); - if (ret != 1) { - ret = -EINVAL; - goto out_unlock; - } - - if (host_sleep == 0) - ret = lbs_set_host_sleep(priv, 0); - else if (host_sleep == 1) { - if (priv->wol_criteria == EHS_REMOVE_WAKEUP) { - netdev_info(priv->dev, - "wake parameters not configured\n"); - ret = -EINVAL; - goto out_unlock; - } - ret = lbs_set_host_sleep(priv, 1); - } else { - netdev_err(priv->dev, "invalid option\n"); - ret = -EINVAL; - } - - if (!ret) - ret = count; - -out_unlock: - free_page(addr); - return ret; -} - -static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t ret; - size_t pos = 0; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated); - - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - - free_page(addr); - return ret; -} - -/* - * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might - * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the - * firmware. Here's an example: - * 04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00 - * 00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * - * The 04 01 is the TLV type (here TLV_TYPE_RSSI_LOW), 02 00 is the length, - * 00 00 are the data bytes of this TLV. For this TLV, their meaning is - * defined in mrvlietypes_thresholds - * - * This function searches in this TLV data chunk for a given TLV type - * and returns a pointer to the first data byte of the TLV, or to NULL - * if the TLV hasn't been found. - */ -static void *lbs_tlv_find(uint16_t tlv_type, const uint8_t *tlv, uint16_t size) -{ - struct mrvl_ie_header *tlv_h; - uint16_t length; - ssize_t pos = 0; - - while (pos < size) { - tlv_h = (struct mrvl_ie_header *) tlv; - if (!tlv_h->len) - return NULL; - if (tlv_h->type == cpu_to_le16(tlv_type)) - return tlv_h; - length = le16_to_cpu(tlv_h->len) + sizeof(*tlv_h); - pos += length; - tlv += length; - } - return NULL; -} - - -static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask, - struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct cmd_ds_802_11_subscribe_event *subscribed; - struct mrvl_ie_thresholds *got; - struct lbs_private *priv = file->private_data; - ssize_t ret = 0; - size_t pos = 0; - char *buf; - u8 value; - u8 freq; - int events = 0; - - buf = (char *)get_zeroed_page(GFP_KERNEL); - if (!buf) - return -ENOMEM; - - subscribed = kzalloc(sizeof(*subscribed), GFP_KERNEL); - if (!subscribed) { - ret = -ENOMEM; - goto out_page; - } - - subscribed->hdr.size = cpu_to_le16(sizeof(*subscribed)); - subscribed->action = cpu_to_le16(CMD_ACT_GET); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, subscribed); - if (ret) - goto out_cmd; - - got = lbs_tlv_find(tlv_type, subscribed->tlv, sizeof(subscribed->tlv)); - if (got) { - value = got->value; - freq = got->freq; - events = le16_to_cpu(subscribed->events); - - pos += snprintf(buf, len, "%d %d %d\n", value, freq, - !!(events & event_mask)); - } - - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - - out_cmd: - kfree(subscribed); - - out_page: - free_page((unsigned long)buf); - return ret; -} - - -static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask, - struct file *file, - const char __user *userbuf, size_t count, - loff_t *ppos) -{ - struct cmd_ds_802_11_subscribe_event *events; - struct mrvl_ie_thresholds *tlv; - struct lbs_private *priv = file->private_data; - ssize_t buf_size; - int value, freq, new_mask; - uint16_t curr_mask; - char *buf; - int ret; - - buf = (char *)get_zeroed_page(GFP_KERNEL); - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - ret = -EFAULT; - goto out_page; - } - ret = sscanf(buf, "%d %d %d", &value, &freq, &new_mask); - if (ret != 3) { - ret = -EINVAL; - goto out_page; - } - events = kzalloc(sizeof(*events), GFP_KERNEL); - if (!events) { - ret = -ENOMEM; - goto out_page; - } - - events->hdr.size = cpu_to_le16(sizeof(*events)); - events->action = cpu_to_le16(CMD_ACT_GET); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events); - if (ret) - goto out_events; - - curr_mask = le16_to_cpu(events->events); - - if (new_mask) - new_mask = curr_mask | event_mask; - else - new_mask = curr_mask & ~event_mask; - - /* Now everything is set and we can send stuff down to the firmware */ - - tlv = (void *)events->tlv; - - events->action = cpu_to_le16(CMD_ACT_SET); - events->events = cpu_to_le16(new_mask); - tlv->header.type = cpu_to_le16(tlv_type); - tlv->header.len = cpu_to_le16(sizeof(*tlv) - sizeof(tlv->header)); - tlv->value = value; - if (tlv_type != TLV_TYPE_BCNMISS) - tlv->freq = freq; - - /* The command header, the action, the event mask, and one TLV */ - events->hdr.size = cpu_to_le16(sizeof(events->hdr) + 4 + sizeof(*tlv)); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events); - - if (!ret) - ret = count; - out_events: - kfree(events); - out_page: - free_page((unsigned long)buf); - return ret; -} - - -static ssize_t lbs_lowrssi_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_lowrssi_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_lowsnr_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_lowsnr_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_failcount_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_failcount_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_highrssi_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_highrssi_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_highsnr_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_highsnr_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, - file, userbuf, count, ppos); -} - -static ssize_t lbs_bcnmiss_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_read(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_bcnmiss_write(struct file *file, const char __user *userbuf, - size_t count, loff_t *ppos) -{ - return lbs_threshold_write(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, - file, userbuf, count, ppos); -} - - -static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t pos = 0; - int ret; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - u32 val = 0; - - if (!buf) - return -ENOMEM; - - ret = lbs_get_reg(priv, CMD_MAC_REG_ACCESS, priv->mac_offset, &val); - mdelay(10); - if (!ret) { - pos = snprintf(buf, len, "MAC[0x%x] = 0x%08x\n", - priv->mac_offset, val); - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - } - free_page(addr); - return ret; -} - -static ssize_t lbs_rdmac_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - priv->mac_offset = simple_strtoul(buf, NULL, 16); - res = count; -out_unlock: - free_page(addr); - return res; -} - -static ssize_t lbs_wrmac_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - u32 offset, value; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - res = sscanf(buf, "%x %x", &offset, &value); - if (res != 2) { - res = -EFAULT; - goto out_unlock; - } - - res = lbs_set_reg(priv, CMD_MAC_REG_ACCESS, offset, value); - mdelay(10); - - if (!res) - res = count; -out_unlock: - free_page(addr); - return res; -} - -static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t pos = 0; - int ret; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - u32 val; - - if (!buf) - return -ENOMEM; - - ret = lbs_get_reg(priv, CMD_BBP_REG_ACCESS, priv->bbp_offset, &val); - mdelay(10); - if (!ret) { - pos = snprintf(buf, len, "BBP[0x%x] = 0x%08x\n", - priv->bbp_offset, val); - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - } - free_page(addr); - - return ret; -} - -static ssize_t lbs_rdbbp_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - priv->bbp_offset = simple_strtoul(buf, NULL, 16); - res = count; -out_unlock: - free_page(addr); - return res; -} - -static ssize_t lbs_wrbbp_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - u32 offset, value; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - res = sscanf(buf, "%x %x", &offset, &value); - if (res != 2) { - res = -EFAULT; - goto out_unlock; - } - - res = lbs_set_reg(priv, CMD_BBP_REG_ACCESS, offset, value); - mdelay(10); - - if (!res) - res = count; -out_unlock: - free_page(addr); - return res; -} - -static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t pos = 0; - int ret; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - u32 val; - - if (!buf) - return -ENOMEM; - - ret = lbs_get_reg(priv, CMD_RF_REG_ACCESS, priv->rf_offset, &val); - mdelay(10); - if (!ret) { - pos = snprintf(buf, len, "RF[0x%x] = 0x%08x\n", - priv->rf_offset, val); - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - } - free_page(addr); - - return ret; -} - -static ssize_t lbs_rdrf_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - priv->rf_offset = simple_strtoul(buf, NULL, 16); - res = count; -out_unlock: - free_page(addr); - return res; -} - -static ssize_t lbs_wrrf_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - - struct lbs_private *priv = file->private_data; - ssize_t res, buf_size; - u32 offset, value; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - buf_size = min(count, len - 1); - if (copy_from_user(buf, userbuf, buf_size)) { - res = -EFAULT; - goto out_unlock; - } - res = sscanf(buf, "%x %x", &offset, &value); - if (res != 2) { - res = -EFAULT; - goto out_unlock; - } - - res = lbs_set_reg(priv, CMD_RF_REG_ACCESS, offset, value); - mdelay(10); - - if (!res) - res = count; -out_unlock: - free_page(addr); - return res; -} - -#define FOPS(fread, fwrite) { \ - .owner = THIS_MODULE, \ - .open = simple_open, \ - .read = (fread), \ - .write = (fwrite), \ - .llseek = generic_file_llseek, \ -} - -struct lbs_debugfs_files { - const char *name; - umode_t perm; - struct file_operations fops; -}; - -static const struct lbs_debugfs_files debugfs_files[] = { - { "info", 0444, FOPS(lbs_dev_info, write_file_dummy), }, - { "sleepparams", 0644, FOPS(lbs_sleepparams_read, - lbs_sleepparams_write), }, - { "hostsleep", 0644, FOPS(lbs_host_sleep_read, - lbs_host_sleep_write), }, -}; - -static const struct lbs_debugfs_files debugfs_events_files[] = { - {"low_rssi", 0644, FOPS(lbs_lowrssi_read, - lbs_lowrssi_write), }, - {"low_snr", 0644, FOPS(lbs_lowsnr_read, - lbs_lowsnr_write), }, - {"failure_count", 0644, FOPS(lbs_failcount_read, - lbs_failcount_write), }, - {"beacon_missed", 0644, FOPS(lbs_bcnmiss_read, - lbs_bcnmiss_write), }, - {"high_rssi", 0644, FOPS(lbs_highrssi_read, - lbs_highrssi_write), }, - {"high_snr", 0644, FOPS(lbs_highsnr_read, - lbs_highsnr_write), }, -}; - -static const struct lbs_debugfs_files debugfs_regs_files[] = { - {"rdmac", 0644, FOPS(lbs_rdmac_read, lbs_rdmac_write), }, - {"wrmac", 0600, FOPS(NULL, lbs_wrmac_write), }, - {"rdbbp", 0644, FOPS(lbs_rdbbp_read, lbs_rdbbp_write), }, - {"wrbbp", 0600, FOPS(NULL, lbs_wrbbp_write), }, - {"rdrf", 0644, FOPS(lbs_rdrf_read, lbs_rdrf_write), }, - {"wrrf", 0600, FOPS(NULL, lbs_wrrf_write), }, -}; - -void lbs_debugfs_init(void) -{ - if (!lbs_dir) - lbs_dir = debugfs_create_dir("lbs_wireless", NULL); -} - -void lbs_debugfs_remove(void) -{ - debugfs_remove(lbs_dir); -} - -void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev) -{ - int i; - const struct lbs_debugfs_files *files; - if (!lbs_dir) - goto exit; - - priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir); - if (!priv->debugfs_dir) - goto exit; - - for (i=0; idebugfs_files[i] = debugfs_create_file(files->name, - files->perm, - priv->debugfs_dir, - priv, - &files->fops); - } - - priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); - if (!priv->events_dir) - goto exit; - - for (i=0; idebugfs_events_files[i] = debugfs_create_file(files->name, - files->perm, - priv->events_dir, - priv, - &files->fops); - } - - priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); - if (!priv->regs_dir) - goto exit; - - for (i=0; idebugfs_regs_files[i] = debugfs_create_file(files->name, - files->perm, - priv->regs_dir, - priv, - &files->fops); - } - -#ifdef PROC_DEBUG - lbs_debug_init(priv); -#endif -exit: - return; -} - -void lbs_debugfs_remove_one(struct lbs_private *priv) -{ - int i; - - for(i=0; idebugfs_regs_files[i]); - - debugfs_remove(priv->regs_dir); - - for(i=0; idebugfs_events_files[i]); - - debugfs_remove(priv->events_dir); -#ifdef PROC_DEBUG - debugfs_remove(priv->debugfs_debug); -#endif - for(i=0; idebugfs_files[i]); - debugfs_remove(priv->debugfs_dir); -} - - - -/* debug entry */ - -#ifdef PROC_DEBUG - -#define item_size(n) (FIELD_SIZEOF(struct lbs_private, n)) -#define item_addr(n) (offsetof(struct lbs_private, n)) - - -struct debug_data { - char name[32]; - u32 size; - size_t addr; -}; - -/* To debug any member of struct lbs_private, simply add one line here. - */ -static struct debug_data items[] = { - {"psmode", item_size(psmode), item_addr(psmode)}, - {"psstate", item_size(psstate), item_addr(psstate)}, -}; - -static int num_of_items = ARRAY_SIZE(items); - -/** - * lbs_debugfs_read - proc read function - * - * @file: file to read - * @userbuf: pointer to buffer - * @count: number of bytes to read - * @ppos: read data starting position - * - * returns: amount of data read or negative error code - */ -static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf, - size_t count, loff_t *ppos) -{ - int val = 0; - size_t pos = 0; - ssize_t res; - char *p; - int i; - struct debug_data *d; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - if (!buf) - return -ENOMEM; - - p = buf; - - d = file->private_data; - - for (i = 0; i < num_of_items; i++) { - if (d[i].size == 1) - val = *((u8 *) d[i].addr); - else if (d[i].size == 2) - val = *((u16 *) d[i].addr); - else if (d[i].size == 4) - val = *((u32 *) d[i].addr); - else if (d[i].size == 8) - val = *((u64 *) d[i].addr); - - pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); - } - - res = simple_read_from_buffer(userbuf, count, ppos, p, pos); - - free_page(addr); - return res; -} - -/** - * lbs_debugfs_write - proc write function - * - * @f: file pointer - * @buf: pointer to data buffer - * @cnt: data number to write - * @ppos: file position - * - * returns: amount of data written - */ -static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, - size_t cnt, loff_t *ppos) -{ - int r, i; - char *pdata; - char *p; - char *p0; - char *p1; - char *p2; - struct debug_data *d = f->private_data; - - if (cnt == 0) - return 0; - - pdata = kmalloc(cnt + 1, GFP_KERNEL); - if (pdata == NULL) - return 0; - - if (copy_from_user(pdata, buf, cnt)) { - lbs_deb_debugfs("Copy from user failed\n"); - kfree(pdata); - return 0; - } - pdata[cnt] = '\0'; - - p0 = pdata; - for (i = 0; i < num_of_items; i++) { - do { - p = strstr(p0, d[i].name); - if (p == NULL) - break; - p1 = strchr(p, '\n'); - if (p1 == NULL) - break; - p0 = p1++; - p2 = strchr(p, '='); - if (!p2) - break; - p2++; - r = simple_strtoul(p2, NULL, 0); - if (d[i].size == 1) - *((u8 *) d[i].addr) = (u8) r; - else if (d[i].size == 2) - *((u16 *) d[i].addr) = (u16) r; - else if (d[i].size == 4) - *((u32 *) d[i].addr) = (u32) r; - else if (d[i].size == 8) - *((u64 *) d[i].addr) = (u64) r; - break; - } while (1); - } - kfree(pdata); - - return (ssize_t)cnt; -} - -static const struct file_operations lbs_debug_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .write = lbs_debugfs_write, - .read = lbs_debugfs_read, - .llseek = default_llseek, -}; - -/** - * lbs_debug_init - create debug proc file - * - * @priv: pointer to &struct lbs_private - * - * returns: N/A - */ -static void lbs_debug_init(struct lbs_private *priv) -{ - int i; - - if (!priv->debugfs_dir) - return; - - for (i = 0; i < num_of_items; i++) - items[i].addr += (size_t) priv; - - priv->debugfs_debug = debugfs_create_file("debug", 0644, - priv->debugfs_dir, &items[0], - &lbs_debug_fops); -} -#endif diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/libertas/debugfs.h deleted file mode 100644 index f2b9c7ffe0fd..000000000000 --- a/drivers/net/wireless/libertas/debugfs.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _LBS_DEBUGFS_H_ -#define _LBS_DEBUGFS_H_ - -void lbs_debugfs_init(void); -void lbs_debugfs_remove(void); - -void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev); -void lbs_debugfs_remove_one(struct lbs_private *priv); - -#endif diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h deleted file mode 100644 index 84a3aa7ac570..000000000000 --- a/drivers/net/wireless/libertas/decl.h +++ /dev/null @@ -1,82 +0,0 @@ - -/* - * This file contains declaration referring to - * functions defined in other source files - */ - -#ifndef _LBS_DECL_H_ -#define _LBS_DECL_H_ - -#include -#include -#include - -/* Should be terminated by a NULL entry */ -struct lbs_fw_table { - int model; - const char *helper; - const char *fwname; -}; - -struct lbs_private; -typedef void (*lbs_fw_cb)(struct lbs_private *priv, int ret, - const struct firmware *helper, const struct firmware *mainfw); - -struct lbs_private; -struct sk_buff; -struct net_device; -struct cmd_ds_command; - - -/* ethtool.c */ -extern const struct ethtool_ops lbs_ethtool_ops; - - -/* tx.c */ -void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count); -netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev); - -/* rx.c */ -int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *); - - -/* main.c */ -struct lbs_private *lbs_add_card(void *card, struct device *dmdev); -void lbs_remove_card(struct lbs_private *priv); -int lbs_start_card(struct lbs_private *priv); -void lbs_stop_card(struct lbs_private *priv); -void lbs_host_to_card_done(struct lbs_private *priv); - -int lbs_start_iface(struct lbs_private *priv); -int lbs_stop_iface(struct lbs_private *priv); -int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type); - -int lbs_rtap_supported(struct lbs_private *priv); - -int lbs_set_mac_address(struct net_device *dev, void *addr); -void lbs_set_multicast_list(struct net_device *dev); -void lbs_update_mcast(struct lbs_private *priv); - -int lbs_suspend(struct lbs_private *priv); -int lbs_resume(struct lbs_private *priv); - -void lbs_queue_event(struct lbs_private *priv, u32 event); -void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx); - -int lbs_enter_auto_deep_sleep(struct lbs_private *priv); -int lbs_exit_auto_deep_sleep(struct lbs_private *priv); - -u32 lbs_fw_index_to_data_rate(u8 index); -u8 lbs_data_rate_to_fw_index(u32 rate); - -int lbs_get_firmware(struct device *dev, u32 card_model, - const struct lbs_fw_table *fw_table, - const struct firmware **helper, - const struct firmware **mainfw); -int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, - u32 card_model, const struct lbs_fw_table *fw_table, - lbs_fw_cb callback); -void lbs_wait_for_firmware_load(struct lbs_private *priv); - -#endif diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h deleted file mode 100644 index 407784aca627..000000000000 --- a/drivers/net/wireless/libertas/defs.h +++ /dev/null @@ -1,394 +0,0 @@ -/* - * This header file contains global constant/enum definitions, - * global variable declaration. - */ -#ifndef _LBS_DEFS_H_ -#define _LBS_DEFS_H_ - -#include - -#ifdef CONFIG_LIBERTAS_DEBUG -#define DEBUG -#define PROC_DEBUG -#endif - -#ifndef DRV_NAME -#define DRV_NAME "libertas" -#endif - - -#define LBS_DEB_ENTER 0x00000001 -#define LBS_DEB_LEAVE 0x00000002 -#define LBS_DEB_MAIN 0x00000004 -#define LBS_DEB_NET 0x00000008 -#define LBS_DEB_MESH 0x00000010 -#define LBS_DEB_WEXT 0x00000020 -#define LBS_DEB_IOCTL 0x00000040 -#define LBS_DEB_SCAN 0x00000080 -#define LBS_DEB_ASSOC 0x00000100 -#define LBS_DEB_JOIN 0x00000200 -#define LBS_DEB_11D 0x00000400 -#define LBS_DEB_DEBUGFS 0x00000800 -#define LBS_DEB_ETHTOOL 0x00001000 -#define LBS_DEB_HOST 0x00002000 -#define LBS_DEB_CMD 0x00004000 -#define LBS_DEB_RX 0x00008000 -#define LBS_DEB_TX 0x00010000 -#define LBS_DEB_USB 0x00020000 -#define LBS_DEB_CS 0x00040000 -#define LBS_DEB_FW 0x00080000 -#define LBS_DEB_THREAD 0x00100000 -#define LBS_DEB_HEX 0x00200000 -#define LBS_DEB_SDIO 0x00400000 -#define LBS_DEB_SYSFS 0x00800000 -#define LBS_DEB_SPI 0x01000000 -#define LBS_DEB_CFG80211 0x02000000 - -extern unsigned int lbs_debug; - -#ifdef DEBUG -#define LBS_DEB_LL(grp, grpnam, fmt, args...) \ -do { if ((lbs_debug & (grp)) == (grp)) \ - printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \ - in_interrupt() ? " (INT)" : "", ## args); } while (0) -#else -#define LBS_DEB_LL(grp, grpnam, fmt, args...) do {} while (0) -#endif - -#define lbs_deb_enter(grp) \ - LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s()\n", __func__); -#define lbs_deb_enter_args(grp, fmt, args...) \ - LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args); -#define lbs_deb_leave(grp) \ - LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s()\n", __func__); -#define lbs_deb_leave_args(grp, fmt, args...) \ - LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s(), " fmt "\n", \ - __func__, ##args); -#define lbs_deb_main(fmt, args...) LBS_DEB_LL(LBS_DEB_MAIN, " main", fmt, ##args) -#define lbs_deb_net(fmt, args...) LBS_DEB_LL(LBS_DEB_NET, " net", fmt, ##args) -#define lbs_deb_mesh(fmt, args...) LBS_DEB_LL(LBS_DEB_MESH, " mesh", fmt, ##args) -#define lbs_deb_wext(fmt, args...) LBS_DEB_LL(LBS_DEB_WEXT, " wext", fmt, ##args) -#define lbs_deb_ioctl(fmt, args...) LBS_DEB_LL(LBS_DEB_IOCTL, " ioctl", fmt, ##args) -#define lbs_deb_scan(fmt, args...) LBS_DEB_LL(LBS_DEB_SCAN, " scan", fmt, ##args) -#define lbs_deb_assoc(fmt, args...) LBS_DEB_LL(LBS_DEB_ASSOC, " assoc", fmt, ##args) -#define lbs_deb_join(fmt, args...) LBS_DEB_LL(LBS_DEB_JOIN, " join", fmt, ##args) -#define lbs_deb_11d(fmt, args...) LBS_DEB_LL(LBS_DEB_11D, " 11d", fmt, ##args) -#define lbs_deb_debugfs(fmt, args...) LBS_DEB_LL(LBS_DEB_DEBUGFS, " debugfs", fmt, ##args) -#define lbs_deb_ethtool(fmt, args...) LBS_DEB_LL(LBS_DEB_ETHTOOL, " ethtool", fmt, ##args) -#define lbs_deb_host(fmt, args...) LBS_DEB_LL(LBS_DEB_HOST, " host", fmt, ##args) -#define lbs_deb_cmd(fmt, args...) LBS_DEB_LL(LBS_DEB_CMD, " cmd", fmt, ##args) -#define lbs_deb_rx(fmt, args...) LBS_DEB_LL(LBS_DEB_RX, " rx", fmt, ##args) -#define lbs_deb_tx(fmt, args...) LBS_DEB_LL(LBS_DEB_TX, " tx", fmt, ##args) -#define lbs_deb_fw(fmt, args...) LBS_DEB_LL(LBS_DEB_FW, " fw", fmt, ##args) -#define lbs_deb_usb(fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usb", fmt, ##args) -#define lbs_deb_usbd(dev, fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args) -#define lbs_deb_cs(fmt, args...) LBS_DEB_LL(LBS_DEB_CS, " cs", fmt, ##args) -#define lbs_deb_thread(fmt, args...) LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args) -#define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args) -#define lbs_deb_sysfs(fmt, args...) LBS_DEB_LL(LBS_DEB_SYSFS, " sysfs", fmt, ##args) -#define lbs_deb_spi(fmt, args...) LBS_DEB_LL(LBS_DEB_SPI, " spi", fmt, ##args) -#define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args) - -#ifdef DEBUG -static inline void lbs_deb_hex(unsigned int grp, const char *prompt, - const u8 *buf, int len) -{ - int i = 0; - - if (len && - (lbs_debug & LBS_DEB_HEX) && - (lbs_debug & grp)) - { - for (i = 1; i <= len; i++) { - if ((i & 0xf) == 1) { - if (i != 1) - printk("\n"); - printk(DRV_NAME " %s: ", prompt); - } - printk("%02x ", (u8) * buf); - buf++; - } - printk("\n"); - } -} -#else -#define lbs_deb_hex(grp,prompt,buf,len) do {} while (0) -#endif - - - -/* Buffer Constants */ - -/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical - * addresses of TxPD buffers. Station has only 8 TxPD available, Whereas - * driver has more local TxPDs. Each TxPD on the host memory is associated - * with a Tx control node. The driver maintains 8 RxPD descriptors for - * station firmware to store Rx packet information. - * - * Current version of MAC has a 32x6 multicast address buffer. - * - * 802.11b can have up to 14 channels, the driver keeps the - * BSSID(MAC address) of each APs or Ad hoc stations it has sensed. - */ - -#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 -#define LBS_NUM_CMD_BUFFERS 10 -#define LBS_CMD_BUFFER_SIZE (2 * 1024) -#define MRVDRV_MAX_CHANNEL_SIZE 14 -#define MRVDRV_ASSOCIATION_TIME_OUT 255 -#define MRVDRV_SNAP_HEADER_LEN 8 - -#define LBS_UPLD_SIZE 2312 -#define DEV_NAME_LEN 32 - -/* Wake criteria for HOST_SLEEP_CFG command */ -#define EHS_WAKE_ON_BROADCAST_DATA 0x0001 -#define EHS_WAKE_ON_UNICAST_DATA 0x0002 -#define EHS_WAKE_ON_MAC_EVENT 0x0004 -#define EHS_WAKE_ON_MULTICAST_DATA 0x0008 -#define EHS_REMOVE_WAKEUP 0xFFFFFFFF -/* Wake rules for Host_Sleep_CFG command */ -#define WOL_RULE_NET_TYPE_INFRA_OR_IBSS 0x00 -#define WOL_RULE_NET_TYPE_MESH 0x10 -#define WOL_RULE_ADDR_TYPE_BCAST 0x01 -#define WOL_RULE_ADDR_TYPE_MCAST 0x08 -#define WOL_RULE_ADDR_TYPE_UCAST 0x02 -#define WOL_RULE_OP_AND 0x01 -#define WOL_RULE_OP_OR 0x02 -#define WOL_RULE_OP_INVALID 0xFF -#define WOL_RESULT_VALID_CMD 0 -#define WOL_RESULT_NOSPC_ERR 1 -#define WOL_RESULT_EEXIST_ERR 2 - -/* Misc constants */ -/* This section defines 802.11 specific contants */ - -#define MRVDRV_MAX_BSS_DESCRIPTS 16 -#define MRVDRV_MAX_REGION_CODE 6 - -#define MRVDRV_DEFAULT_LISTEN_INTERVAL 10 - -#define MRVDRV_CHANNELS_PER_SCAN 4 -#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 - -#define MRVDRV_MIN_BEACON_INTERVAL 20 -#define MRVDRV_MAX_BEACON_INTERVAL 1000 -#define MRVDRV_BEACON_INTERVAL 100 - -#define MARVELL_MESH_IE_LENGTH 9 - -/* - * Values used to populate the struct mrvl_mesh_ie. The only time you need this - * is when enabling the mesh using CMD_MESH_CONFIG. - */ -#define MARVELL_MESH_IE_TYPE 4 -#define MARVELL_MESH_IE_SUBTYPE 0 -#define MARVELL_MESH_IE_VERSION 0 -#define MARVELL_MESH_PROTO_ID_HWMP 0 -#define MARVELL_MESH_METRIC_ID 0 -#define MARVELL_MESH_CAPABILITY 0 - -/* INT status Bit Definition */ -#define MRVDRV_TX_DNLD_RDY 0x0001 -#define MRVDRV_RX_UPLD_RDY 0x0002 -#define MRVDRV_CMD_DNLD_RDY 0x0004 -#define MRVDRV_CMD_UPLD_RDY 0x0008 -#define MRVDRV_CARDEVENT 0x0010 - -/* Automatic TX control default levels */ -#define POW_ADAPT_DEFAULT_P0 13 -#define POW_ADAPT_DEFAULT_P1 15 -#define POW_ADAPT_DEFAULT_P2 18 -#define TPC_DEFAULT_P0 5 -#define TPC_DEFAULT_P1 10 -#define TPC_DEFAULT_P2 13 - -/* TxPD status */ - -/* - * Station firmware use TxPD status field to report final Tx transmit - * result, Bit masks are used to present combined situations. - */ - -#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 -#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 - -/* Tx mesh flag */ -/* - * Currently we are using normal WDS flag as mesh flag. - * TODO: change to proper mesh flag when MAC understands it. - */ -#define TxPD_CONTROL_WDS_FRAME (1<<17) -#define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME - -/* Mesh interface ID */ -#define MESH_IFACE_ID 0x0001 -/* Mesh id should be in bits 14-13-12 */ -#define MESH_IFACE_BIT_OFFSET 0x000c -/* Mesh enable bit in FW capability */ -#define MESH_CAPINFO_ENABLE_MASK (1<<16) - -/* FW definition from Marvell v4 */ -#define MRVL_FW_V4 (0x04) -/* FW definition from Marvell v5 */ -#define MRVL_FW_V5 (0x05) -/* FW definition from Marvell v10 */ -#define MRVL_FW_V10 (0x0a) -/* FW major revision definition */ -#define MRVL_FW_MAJOR_REV(x) ((x)>>24) - -/* RxPD status */ - -#define MRVDRV_RXPD_STATUS_OK 0x0001 - -/* RxPD status - Received packet types */ -/* Rx mesh flag */ -/* - * Currently we are using normal WDS flag as mesh flag. - * TODO: change to proper mesh flag when MAC understands it. - */ -#define RxPD_CONTROL_WDS_FRAME (0x40) -#define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME - -/* RSSI-related defines */ -/* - * RSSI constants are used to implement 802.11 RSSI threshold - * indication. if the Rx packet signal got too weak for 5 consecutive - * times, miniport driver (driver) will report this event to wrapper - */ - -#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) - -/* RTS/FRAG related defines */ -#define MRVDRV_RTS_MIN_VALUE 0 -#define MRVDRV_RTS_MAX_VALUE 2347 -#define MRVDRV_FRAG_MIN_VALUE 256 -#define MRVDRV_FRAG_MAX_VALUE 2346 - -/* This is for firmware specific length */ -#define EXTRA_LEN 36 - -#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ - (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) - -#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ - (ETH_FRAME_LEN + sizeof(struct rxpd) \ - + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) - -#define CMD_F_HOSTCMD (1 << 0) -#define FW_CAPINFO_WPA (1 << 0) -#define FW_CAPINFO_PS (1 << 1) -#define FW_CAPINFO_FIRMWARE_UPGRADE (1 << 13) -#define FW_CAPINFO_BOOT2_UPGRADE (1<<14) -#define FW_CAPINFO_PERSISTENT_CONFIG (1<<15) - -#define KEY_LEN_WPA_AES 16 -#define KEY_LEN_WPA_TKIP 32 -#define KEY_LEN_WEP_104 13 -#define KEY_LEN_WEP_40 5 - -#define RF_ANTENNA_1 0x1 -#define RF_ANTENNA_2 0x2 -#define RF_ANTENNA_AUTO 0xFFFF - -#define BAND_B (0x01) -#define BAND_G (0x02) -#define ALL_802_11_BANDS (BAND_B | BAND_G) - -#define MAX_RATES 14 - -#define MAX_LEDS 8 - -/* Global Variable Declaration */ -extern const char lbs_driver_version[]; -extern u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE]; - - -/* ENUM definition */ -/* SNRNF_TYPE */ -enum SNRNF_TYPE { - TYPE_BEACON = 0, - TYPE_RXPD, - MAX_TYPE_B -}; - -/* SNRNF_DATA */ -enum SNRNF_DATA { - TYPE_NOAVG = 0, - TYPE_AVG, - MAX_TYPE_AVG -}; - -/* LBS_802_11_POWER_MODE */ -enum LBS_802_11_POWER_MODE { - LBS802_11POWERMODECAM, - LBS802_11POWERMODEMAX_PSP, - LBS802_11POWERMODEFAST_PSP, - /* not a real mode, defined as an upper bound */ - LBS802_11POWEMODEMAX -}; - -/* PS_STATE */ -enum PS_STATE { - PS_STATE_FULL_POWER, - PS_STATE_AWAKE, - PS_STATE_PRE_SLEEP, - PS_STATE_SLEEP -}; - -/* DNLD_STATE */ -enum DNLD_STATE { - DNLD_RES_RECEIVED, - DNLD_DATA_SENT, - DNLD_CMD_SENT, - DNLD_BOOTCMD_SENT, -}; - -/* LBS_MEDIA_STATE */ -enum LBS_MEDIA_STATE { - LBS_CONNECTED, - LBS_DISCONNECTED -}; - -/* LBS_802_11_PRIVACY_FILTER */ -enum LBS_802_11_PRIVACY_FILTER { - LBS802_11PRIVFILTERACCEPTALL, - LBS802_11PRIVFILTER8021XWEP -}; - -/* mv_ms_type */ -enum mv_ms_type { - MVMS_DAT = 0, - MVMS_CMD = 1, - MVMS_TXDONE = 2, - MVMS_EVENT -}; - -/* KEY_TYPE_ID */ -enum KEY_TYPE_ID { - KEY_TYPE_ID_WEP = 0, - KEY_TYPE_ID_TKIP, - KEY_TYPE_ID_AES -}; - -/* KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */ -enum KEY_INFO_WPA { - KEY_INFO_WPA_MCAST = 0x01, - KEY_INFO_WPA_UNICAST = 0x02, - KEY_INFO_WPA_ENABLED = 0x04 -}; - -/* Default values for fwt commands. */ -#define FWT_DEFAULT_METRIC 0 -#define FWT_DEFAULT_DIR 1 -/* Default Rate, 11Mbps */ -#define FWT_DEFAULT_RATE 3 -#define FWT_DEFAULT_SSN 0xffffffff -#define FWT_DEFAULT_DSN 0 -#define FWT_DEFAULT_HOPCOUNT 0 -#define FWT_DEFAULT_TTL 0 -#define FWT_DEFAULT_EXPIRATION 0 -#define FWT_DEFAULT_SLEEPMODE 0 -#define FWT_DEFAULT_SNR 0 - -#endif diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h deleted file mode 100644 index 6bd1608992b0..000000000000 --- a/drivers/net/wireless/libertas/dev.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * This file contains definitions and data structures specific - * to Marvell 802.11 NIC. It contains the Device Information - * structure struct lbs_private.. - */ -#ifndef _LBS_DEV_H_ -#define _LBS_DEV_H_ - -#include "defs.h" -#include "decl.h" -#include "host.h" - -#include - -/* sleep_params */ -struct sleep_params { - uint16_t sp_error; - uint16_t sp_offset; - uint16_t sp_stabletime; - uint8_t sp_calcontrol; - uint8_t sp_extsleepclk; - uint16_t sp_reserved; -}; - -/* Mesh statistics */ -struct lbs_mesh_stats { - u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ - u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ - u32 fwd_drop_ttl; /* Fwd: TTL zero */ - u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ - u32 fwd_drop_noroute; /* Fwd: No route to Destination */ - u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ - u32 drop_blind; /* Rx: Dropped by blinding table */ - u32 tx_failed_cnt; /* Tx: Failed transmissions */ -}; - -/* Private structure for the MV device */ -struct lbs_private { - - /* Basic networking */ - struct net_device *dev; - u32 connect_status; - struct work_struct mcast_work; - u32 nr_of_multicastmacaddr; - u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; - - /* CFG80211 */ - struct wireless_dev *wdev; - bool wiphy_registered; - struct cfg80211_scan_request *scan_req; - u8 assoc_bss[ETH_ALEN]; - u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - u8 disassoc_reason; - - /* Mesh */ - struct net_device *mesh_dev; /* Virtual device */ -#ifdef CONFIG_LIBERTAS_MESH - struct lbs_mesh_stats mstats; - uint16_t mesh_tlv; - u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1]; - u8 mesh_ssid_len; - u8 mesh_channel; -#endif - - /* Debugfs */ - struct dentry *debugfs_dir; - struct dentry *debugfs_debug; - struct dentry *debugfs_files[6]; - struct dentry *events_dir; - struct dentry *debugfs_events_files[6]; - struct dentry *regs_dir; - struct dentry *debugfs_regs_files[6]; - - /* Hardware debugging */ - u32 mac_offset; - u32 bbp_offset; - u32 rf_offset; - - /* Power management */ - u16 psmode; - u32 psstate; - u8 needtowakeup; - - /* Deep sleep */ - int is_deep_sleep; - int deep_sleep_required; - int is_auto_deep_sleep_enabled; - int wakeup_dev_required; - int is_activity_detected; - int auto_deep_sleep_timeout; /* in ms */ - wait_queue_head_t ds_awake_q; - struct timer_list auto_deepsleep_timer; - - /* Host sleep*/ - int is_host_sleep_configured; - int is_host_sleep_activated; - wait_queue_head_t host_sleep_q; - - /* Hardware access */ - void *card; - bool iface_running; - u8 fw_ready; - u8 surpriseremoved; - u8 setup_fw_on_resume; - int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb); - void (*reset_card) (struct lbs_private *priv); - int (*power_save) (struct lbs_private *priv); - int (*power_restore) (struct lbs_private *priv); - int (*enter_deep_sleep) (struct lbs_private *priv); - int (*exit_deep_sleep) (struct lbs_private *priv); - int (*reset_deep_sleep_wakeup) (struct lbs_private *priv); - - /* Adapter info (from EEPROM) */ - u32 fwrelease; - u32 fwcapinfo; - u16 regioncode; - u8 current_addr[ETH_ALEN]; - u8 copied_hwaddr; - - /* Command download */ - u8 dnld_sent; - /* bit0 1/0=data_sent/data_tx_done, - bit1 1/0=cmd_sent/cmd_tx_done, - all other bits reserved 0 */ - u16 seqnum; - struct cmd_ctrl_node *cmd_array; - struct cmd_ctrl_node *cur_cmd; - struct list_head cmdfreeq; /* free command buffers */ - struct list_head cmdpendingq; /* pending command buffers */ - struct timer_list command_timer; - int cmd_timed_out; - - /* Command responses sent from the hardware to the driver */ - u8 resp_idx; - u8 resp_buf[2][LBS_UPLD_SIZE]; - u32 resp_len[2]; - - /* Events sent from hardware to driver */ - struct kfifo event_fifo; - - /* thread to service interrupts */ - struct task_struct *main_thread; - wait_queue_head_t waitq; - struct workqueue_struct *work_thread; - - /* Encryption stuff */ - u8 authtype_auto; - u8 wep_tx_key; - u8 wep_key[4][WLAN_KEY_LEN_WEP104]; - u8 wep_key_len[4]; - - /* Wake On LAN */ - uint32_t wol_criteria; - uint8_t wol_gpio; - uint8_t wol_gap; - bool ehs_remove_supported; - - /* Transmitting */ - int tx_pending_len; /* -1 while building packet */ - u8 tx_pending_buf[LBS_UPLD_SIZE]; - /* protected by hard_start_xmit serialization */ - u8 txretrycount; - struct sk_buff *currenttxskb; - struct timer_list tx_lockup_timer; - - /* Locks */ - struct mutex lock; - spinlock_t driver_lock; - - /* NIC/link operation characteristics */ - u16 mac_control; - u8 radio_on; - u8 cur_rate; - u8 channel; - s16 txpower_cur; - s16 txpower_min; - s16 txpower_max; - - /* Scanning */ - struct delayed_work scan_work; - int scan_channel; - /* Queue of things waiting for scan completion */ - wait_queue_head_t scan_q; - /* Whether the scan was initiated internally and not by cfg80211 */ - bool internal_scan; - - /* Firmware load */ - u32 fw_model; - wait_queue_head_t fw_waitq; - struct device *fw_device; - const struct firmware *helper_fw; - const struct lbs_fw_table *fw_table; - const struct lbs_fw_table *fw_iter; - lbs_fw_cb fw_callback; -}; - -extern struct cmd_confirm_sleep confirm_sleep; - -/* Check if there is an interface active. */ -static inline int lbs_iface_active(struct lbs_private *priv) -{ - int r; - - r = netif_running(priv->dev); - if (priv->mesh_dev) - r |= netif_running(priv->mesh_dev); - - return r; -} - -#endif diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c deleted file mode 100644 index f955b2d66ed6..000000000000 --- a/drivers/net/wireless/libertas/ethtool.c +++ /dev/null @@ -1,120 +0,0 @@ -#include -#include -#include -#include - -#include "decl.h" -#include "cmd.h" -#include "mesh.h" - - -static void lbs_ethtool_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - struct lbs_private *priv = dev->ml_priv; - - snprintf(info->fw_version, sizeof(info->fw_version), - "%u.%u.%u.p%u", - priv->fwrelease >> 24 & 0xff, - priv->fwrelease >> 16 & 0xff, - priv->fwrelease >> 8 & 0xff, - priv->fwrelease & 0xff); - strlcpy(info->driver, "libertas", sizeof(info->driver)); - strlcpy(info->version, lbs_driver_version, sizeof(info->version)); -} - -/* - * All 8388 parts have 16KiB EEPROM size at the time of writing. - * In case that changes this needs fixing. - */ -#define LBS_EEPROM_LEN 16384 - -static int lbs_ethtool_get_eeprom_len(struct net_device *dev) -{ - return LBS_EEPROM_LEN; -} - -static int lbs_ethtool_get_eeprom(struct net_device *dev, - struct ethtool_eeprom *eeprom, u8 * bytes) -{ - struct lbs_private *priv = dev->ml_priv; - struct cmd_ds_802_11_eeprom_access cmd; - int ret; - - lbs_deb_enter(LBS_DEB_ETHTOOL); - - if (eeprom->offset + eeprom->len > LBS_EEPROM_LEN || - eeprom->len > LBS_EEPROM_READ_LEN) { - ret = -EINVAL; - goto out; - } - - cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) - - LBS_EEPROM_READ_LEN + eeprom->len); - cmd.action = cpu_to_le16(CMD_ACT_GET); - cmd.offset = cpu_to_le16(eeprom->offset); - cmd.len = cpu_to_le16(eeprom->len); - ret = lbs_cmd_with_response(priv, CMD_802_11_EEPROM_ACCESS, &cmd); - if (!ret) - memcpy(bytes, cmd.value, eeprom->len); - -out: - lbs_deb_leave_args(LBS_DEB_ETHTOOL, "ret %d", ret); - return ret; -} - -static void lbs_ethtool_get_wol(struct net_device *dev, - struct ethtool_wolinfo *wol) -{ - struct lbs_private *priv = dev->ml_priv; - - wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY; - - if (priv->wol_criteria == EHS_REMOVE_WAKEUP) - return; - - if (priv->wol_criteria & EHS_WAKE_ON_UNICAST_DATA) - wol->wolopts |= WAKE_UCAST; - if (priv->wol_criteria & EHS_WAKE_ON_MULTICAST_DATA) - wol->wolopts |= WAKE_MCAST; - if (priv->wol_criteria & EHS_WAKE_ON_BROADCAST_DATA) - wol->wolopts |= WAKE_BCAST; - if (priv->wol_criteria & EHS_WAKE_ON_MAC_EVENT) - wol->wolopts |= WAKE_PHY; -} - -static int lbs_ethtool_set_wol(struct net_device *dev, - struct ethtool_wolinfo *wol) -{ - struct lbs_private *priv = dev->ml_priv; - - if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY)) - return -EOPNOTSUPP; - - priv->wol_criteria = 0; - if (wol->wolopts & WAKE_UCAST) - priv->wol_criteria |= EHS_WAKE_ON_UNICAST_DATA; - if (wol->wolopts & WAKE_MCAST) - priv->wol_criteria |= EHS_WAKE_ON_MULTICAST_DATA; - if (wol->wolopts & WAKE_BCAST) - priv->wol_criteria |= EHS_WAKE_ON_BROADCAST_DATA; - if (wol->wolopts & WAKE_PHY) - priv->wol_criteria |= EHS_WAKE_ON_MAC_EVENT; - if (wol->wolopts == 0) - priv->wol_criteria |= EHS_REMOVE_WAKEUP; - return 0; -} - -const struct ethtool_ops lbs_ethtool_ops = { - .get_drvinfo = lbs_ethtool_get_drvinfo, - .get_eeprom = lbs_ethtool_get_eeprom, - .get_eeprom_len = lbs_ethtool_get_eeprom_len, -#ifdef CONFIG_LIBERTAS_MESH - .get_sset_count = lbs_mesh_ethtool_get_sset_count, - .get_ethtool_stats = lbs_mesh_ethtool_get_stats, - .get_strings = lbs_mesh_ethtool_get_strings, -#endif - .get_wol = lbs_ethtool_get_wol, - .set_wol = lbs_ethtool_set_wol, -}; - diff --git a/drivers/net/wireless/libertas/firmware.c b/drivers/net/wireless/libertas/firmware.c deleted file mode 100644 index 51b92b5df119..000000000000 --- a/drivers/net/wireless/libertas/firmware.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Firmware loading and handling functions. - */ - -#include -#include -#include - -#include "dev.h" -#include "decl.h" - -static void load_next_firmware_from_table(struct lbs_private *private); - -static void lbs_fw_loaded(struct lbs_private *priv, int ret, - const struct firmware *helper, const struct firmware *mainfw) -{ - unsigned long flags; - - lbs_deb_fw("firmware load complete, code %d\n", ret); - - /* User must free helper/mainfw */ - priv->fw_callback(priv, ret, helper, mainfw); - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->fw_callback = NULL; - wake_up(&priv->fw_waitq); - spin_unlock_irqrestore(&priv->driver_lock, flags); -} - -static void do_load_firmware(struct lbs_private *priv, const char *name, - void (*cb)(const struct firmware *fw, void *context)) -{ - int ret; - - lbs_deb_fw("Requesting %s\n", name); - ret = request_firmware_nowait(THIS_MODULE, true, name, - priv->fw_device, GFP_KERNEL, priv, cb); - if (ret) { - lbs_deb_fw("request_firmware_nowait error %d\n", ret); - lbs_fw_loaded(priv, ret, NULL, NULL); - } -} - -static void main_firmware_cb(const struct firmware *firmware, void *context) -{ - struct lbs_private *priv = context; - - if (!firmware) { - /* Failed to find firmware: try next table entry */ - load_next_firmware_from_table(priv); - return; - } - - /* Firmware found! */ - lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); - if (priv->helper_fw) { - release_firmware (priv->helper_fw); - priv->helper_fw = NULL; - } - release_firmware (firmware); -} - -static void helper_firmware_cb(const struct firmware *firmware, void *context) -{ - struct lbs_private *priv = context; - - if (!firmware) { - /* Failed to find firmware: try next table entry */ - load_next_firmware_from_table(priv); - return; - } - - /* Firmware found! */ - if (priv->fw_iter->fwname) { - priv->helper_fw = firmware; - do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb); - } else { - /* No main firmware needed for this helper --> success! */ - lbs_fw_loaded(priv, 0, firmware, NULL); - } -} - -static void load_next_firmware_from_table(struct lbs_private *priv) -{ - const struct lbs_fw_table *iter; - - if (!priv->fw_iter) - iter = priv->fw_table; - else - iter = ++priv->fw_iter; - - if (priv->helper_fw) { - release_firmware(priv->helper_fw); - priv->helper_fw = NULL; - } - -next: - if (!iter->helper) { - /* End of table hit. */ - lbs_fw_loaded(priv, -ENOENT, NULL, NULL); - return; - } - - if (iter->model != priv->fw_model) { - iter++; - goto next; - } - - priv->fw_iter = iter; - do_load_firmware(priv, iter->helper, helper_firmware_cb); -} - -void lbs_wait_for_firmware_load(struct lbs_private *priv) -{ - wait_event(priv->fw_waitq, priv->fw_callback == NULL); -} - -/** - * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load - * either a helper firmware and a main firmware (2-stage), or just the helper. - * - * @priv: Pointer to lbs_private instance - * @dev: A pointer to &device structure - * @card_model: Bus-specific card model ID used to filter firmware table - * elements - * @fw_table: Table of firmware file names and device model numbers - * terminated by an entry with a NULL helper name - * @callback: User callback to invoke when firmware load succeeds or fails. - */ -int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, - u32 card_model, const struct lbs_fw_table *fw_table, - lbs_fw_cb callback) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->driver_lock, flags); - if (priv->fw_callback) { - lbs_deb_fw("firmware load already in progress\n"); - spin_unlock_irqrestore(&priv->driver_lock, flags); - return -EBUSY; - } - - priv->fw_device = device; - priv->fw_callback = callback; - priv->fw_table = fw_table; - priv->fw_iter = NULL; - priv->fw_model = card_model; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbs_deb_fw("Starting async firmware load\n"); - load_next_firmware_from_table(priv); - return 0; -} -EXPORT_SYMBOL_GPL(lbs_get_firmware_async); - -/** - * lbs_get_firmware - Retrieves two-stage firmware - * - * @dev: A pointer to &device structure - * @card_model: Bus-specific card model ID used to filter firmware table - * elements - * @fw_table: Table of firmware file names and device model numbers - * terminated by an entry with a NULL helper name - * @helper: On success, the helper firmware; caller must free - * @mainfw: On success, the main firmware; caller must free - * - * Deprecated: use lbs_get_firmware_async() instead. - * - * returns: 0 on success, non-zero on failure - */ -int lbs_get_firmware(struct device *dev, u32 card_model, - const struct lbs_fw_table *fw_table, - const struct firmware **helper, - const struct firmware **mainfw) -{ - const struct lbs_fw_table *iter; - int ret; - - BUG_ON(helper == NULL); - BUG_ON(mainfw == NULL); - - /* Search for firmware to use from the table. */ - iter = fw_table; - while (iter && iter->helper) { - if (iter->model != card_model) - goto next; - - if (*helper == NULL) { - ret = request_firmware(helper, iter->helper, dev); - if (ret) - goto next; - - /* If the device has one-stage firmware (ie cf8305) and - * we've got it then we don't need to bother with the - * main firmware. - */ - if (iter->fwname == NULL) - return 0; - } - - if (*mainfw == NULL) { - ret = request_firmware(mainfw, iter->fwname, dev); - if (ret) { - /* Clear the helper to ensure we don't have - * mismatched firmware pairs. - */ - release_firmware(*helper); - *helper = NULL; - } - } - - if (*helper && *mainfw) - return 0; - - next: - iter++; - } - - /* Failed */ - release_firmware(*helper); - *helper = NULL; - release_firmware(*mainfw); - *mainfw = NULL; - - return -ENOENT; -} -EXPORT_SYMBOL_GPL(lbs_get_firmware); diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h deleted file mode 100644 index 96726f79a1dd..000000000000 --- a/drivers/net/wireless/libertas/host.h +++ /dev/null @@ -1,978 +0,0 @@ -/* - * This file function prototypes, data structure - * and definitions for all the host/station commands - */ - -#ifndef _LBS_HOST_H_ -#define _LBS_HOST_H_ - -#include "types.h" -#include "defs.h" - -#define DEFAULT_AD_HOC_CHANNEL 6 - -#define CMD_OPTION_WAITFORRSP 0x0002 - -/* Host command IDs */ - -/* - * Return command are almost always the same as the host command, but with - * bit 15 set high. There are a few exceptions, though... - */ -#define CMD_RET(cmd) (0x8000 | cmd) - -/* Return command convention exceptions: */ -#define CMD_RET_802_11_ASSOCIATE 0x8012 - -/* Command codes */ -#define CMD_GET_HW_SPEC 0x0003 -#define CMD_EEPROM_UPDATE 0x0004 -#define CMD_802_11_RESET 0x0005 -#define CMD_802_11_SCAN 0x0006 -#define CMD_802_11_GET_LOG 0x000b -#define CMD_MAC_MULTICAST_ADR 0x0010 -#define CMD_802_11_AUTHENTICATE 0x0011 -#define CMD_802_11_EEPROM_ACCESS 0x0059 -#define CMD_802_11_ASSOCIATE 0x0050 -#define CMD_802_11_SET_WEP 0x0013 -#define CMD_802_11_GET_STAT 0x0014 -#define CMD_802_3_GET_STAT 0x0015 -#define CMD_802_11_SNMP_MIB 0x0016 -#define CMD_MAC_REG_MAP 0x0017 -#define CMD_BBP_REG_MAP 0x0018 -#define CMD_MAC_REG_ACCESS 0x0019 -#define CMD_BBP_REG_ACCESS 0x001a -#define CMD_RF_REG_ACCESS 0x001b -#define CMD_802_11_RADIO_CONTROL 0x001c -#define CMD_802_11_RF_CHANNEL 0x001d -#define CMD_802_11_RF_TX_POWER 0x001e -#define CMD_802_11_RSSI 0x001f -#define CMD_802_11_RF_ANTENNA 0x0020 -#define CMD_802_11_PS_MODE 0x0021 -#define CMD_802_11_DATA_RATE 0x0022 -#define CMD_RF_REG_MAP 0x0023 -#define CMD_802_11_DEAUTHENTICATE 0x0024 -#define CMD_802_11_REASSOCIATE 0x0025 -#define CMD_MAC_CONTROL 0x0028 -#define CMD_802_11_AD_HOC_START 0x002b -#define CMD_802_11_AD_HOC_JOIN 0x002c -#define CMD_802_11_QUERY_TKIP_REPLY_CNTRS 0x002e -#define CMD_802_11_ENABLE_RSN 0x002f -#define CMD_802_11_SET_AFC 0x003c -#define CMD_802_11_GET_AFC 0x003d -#define CMD_802_11_DEEP_SLEEP 0x003e -#define CMD_802_11_AD_HOC_STOP 0x0040 -#define CMD_802_11_HOST_SLEEP_CFG 0x0043 -#define CMD_802_11_WAKEUP_CONFIRM 0x0044 -#define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045 -#define CMD_802_11_BEACON_STOP 0x0049 -#define CMD_802_11_MAC_ADDRESS 0x004d -#define CMD_802_11_LED_GPIO_CTRL 0x004e -#define CMD_802_11_BAND_CONFIG 0x0058 -#define CMD_GSPI_BUS_CONFIG 0x005a -#define CMD_802_11D_DOMAIN_INFO 0x005b -#define CMD_802_11_KEY_MATERIAL 0x005e -#define CMD_802_11_SLEEP_PARAMS 0x0066 -#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067 -#define CMD_802_11_SLEEP_PERIOD 0x0068 -#define CMD_802_11_TPC_CFG 0x0072 -#define CMD_802_11_PA_CFG 0x0073 -#define CMD_802_11_FW_WAKE_METHOD 0x0074 -#define CMD_802_11_SUBSCRIBE_EVENT 0x0075 -#define CMD_802_11_RATE_ADAPT_RATESET 0x0076 -#define CMD_802_11_TX_RATE_QUERY 0x007f -#define CMD_GET_TSF 0x0080 -#define CMD_BT_ACCESS 0x0087 -#define CMD_FWT_ACCESS 0x0095 -#define CMD_802_11_MONITOR_MODE 0x0098 -#define CMD_MESH_ACCESS 0x009b -#define CMD_MESH_CONFIG_OLD 0x00a3 -#define CMD_MESH_CONFIG 0x00ac -#define CMD_SET_BOOT2_VER 0x00a5 -#define CMD_FUNC_INIT 0x00a9 -#define CMD_FUNC_SHUTDOWN 0x00aa -#define CMD_802_11_BEACON_CTRL 0x00b0 - -/* For the IEEE Power Save */ -#define PS_MODE_ACTION_ENTER_PS 0x0030 -#define PS_MODE_ACTION_EXIT_PS 0x0031 -#define PS_MODE_ACTION_SLEEP_CONFIRMED 0x0034 - -#define CMD_ENABLE_RSN 0x0001 -#define CMD_DISABLE_RSN 0x0000 - -#define CMD_ACT_GET 0x0000 -#define CMD_ACT_SET 0x0001 - -/* Define action or option for CMD_802_11_SET_WEP */ -#define CMD_ACT_ADD 0x0002 -#define CMD_ACT_REMOVE 0x0004 - -#define CMD_TYPE_WEP_40_BIT 0x01 -#define CMD_TYPE_WEP_104_BIT 0x02 - -#define CMD_NUM_OF_WEP_KEYS 4 - -#define CMD_WEP_KEY_INDEX_MASK 0x3fff - -/* Define action or option for CMD_802_11_SCAN */ -#define CMD_BSS_TYPE_BSS 0x0001 -#define CMD_BSS_TYPE_IBSS 0x0002 -#define CMD_BSS_TYPE_ANY 0x0003 - -/* Define action or option for CMD_802_11_SCAN */ -#define CMD_SCAN_TYPE_ACTIVE 0x0000 -#define CMD_SCAN_TYPE_PASSIVE 0x0001 - -#define CMD_SCAN_RADIO_TYPE_BG 0 - -#define CMD_SCAN_PROBE_DELAY_TIME 0 - -/* Define action or option for CMD_MAC_CONTROL */ -#define CMD_ACT_MAC_RX_ON 0x0001 -#define CMD_ACT_MAC_TX_ON 0x0002 -#define CMD_ACT_MAC_LOOPBACK_ON 0x0004 -#define CMD_ACT_MAC_WEP_ENABLE 0x0008 -#define CMD_ACT_MAC_INT_ENABLE 0x0010 -#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 -#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 -#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 -#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 -#define CMD_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400 - -/* Event flags for CMD_802_11_SUBSCRIBE_EVENT */ -#define CMD_SUBSCRIBE_RSSI_LOW 0x0001 -#define CMD_SUBSCRIBE_SNR_LOW 0x0002 -#define CMD_SUBSCRIBE_FAILCOUNT 0x0004 -#define CMD_SUBSCRIBE_BCNMISS 0x0008 -#define CMD_SUBSCRIBE_RSSI_HIGH 0x0010 -#define CMD_SUBSCRIBE_SNR_HIGH 0x0020 - -#define RADIO_PREAMBLE_LONG 0x00 -#define RADIO_PREAMBLE_SHORT 0x02 -#define RADIO_PREAMBLE_AUTO 0x04 - -/* Define action or option for CMD_802_11_RF_CHANNEL */ -#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 -#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 - -/* Define action or option for CMD_802_11_DATA_RATE */ -#define CMD_ACT_SET_TX_AUTO 0x0000 -#define CMD_ACT_SET_TX_FIX_RATE 0x0001 -#define CMD_ACT_GET_TX_RATE 0x0002 - -/* Options for CMD_802_11_FW_WAKE_METHOD */ -#define CMD_WAKE_METHOD_UNCHANGED 0x0000 -#define CMD_WAKE_METHOD_COMMAND_INT 0x0001 -#define CMD_WAKE_METHOD_GPIO 0x0002 - -/* Object IDs for CMD_802_11_SNMP_MIB */ -#define SNMP_MIB_OID_BSS_TYPE 0x0000 -#define SNMP_MIB_OID_OP_RATE_SET 0x0001 -#define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */ -#define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */ -#define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */ -#define SNMP_MIB_OID_RTS_THRESHOLD 0x0005 -#define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006 -#define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007 -#define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008 -#define SNMP_MIB_OID_11D_ENABLE 0x0009 -#define SNMP_MIB_OID_11H_ENABLE 0x000A - -/* Define action or option for CMD_BT_ACCESS */ -enum cmd_bt_access_opts { - /* The bt commands start at 5 instead of 1 because the old dft commands - * are mapped to 1-4. These old commands are no longer maintained and - * should not be called. - */ - CMD_ACT_BT_ACCESS_ADD = 5, - CMD_ACT_BT_ACCESS_DEL, - CMD_ACT_BT_ACCESS_LIST, - CMD_ACT_BT_ACCESS_RESET, - CMD_ACT_BT_ACCESS_SET_INVERT, - CMD_ACT_BT_ACCESS_GET_INVERT -}; - -/* Define action or option for CMD_FWT_ACCESS */ -enum cmd_fwt_access_opts { - CMD_ACT_FWT_ACCESS_ADD = 1, - CMD_ACT_FWT_ACCESS_DEL, - CMD_ACT_FWT_ACCESS_LOOKUP, - CMD_ACT_FWT_ACCESS_LIST, - CMD_ACT_FWT_ACCESS_LIST_ROUTE, - CMD_ACT_FWT_ACCESS_LIST_NEIGHBOR, - CMD_ACT_FWT_ACCESS_RESET, - CMD_ACT_FWT_ACCESS_CLEANUP, - CMD_ACT_FWT_ACCESS_TIME, -}; - -/* Define action or option for CMD_802_11_HOST_SLEEP_CFG */ -enum cmd_wol_cfg_opts { - CMD_ACT_ACTION_NONE = 0, - CMD_ACT_SET_WOL_RULE, - CMD_ACT_GET_WOL_RULE, - CMD_ACT_RESET_WOL_RULE, -}; - -/* Define action or option for CMD_MESH_ACCESS */ -enum cmd_mesh_access_opts { - CMD_ACT_MESH_GET_TTL = 1, - CMD_ACT_MESH_SET_TTL, - CMD_ACT_MESH_GET_STATS, - CMD_ACT_MESH_GET_ANYCAST, - CMD_ACT_MESH_SET_ANYCAST, - CMD_ACT_MESH_SET_LINK_COSTS, - CMD_ACT_MESH_GET_LINK_COSTS, - CMD_ACT_MESH_SET_BCAST_RATE, - CMD_ACT_MESH_GET_BCAST_RATE, - CMD_ACT_MESH_SET_RREQ_DELAY, - CMD_ACT_MESH_GET_RREQ_DELAY, - CMD_ACT_MESH_SET_ROUTE_EXP, - CMD_ACT_MESH_GET_ROUTE_EXP, - CMD_ACT_MESH_SET_AUTOSTART_ENABLED, - CMD_ACT_MESH_GET_AUTOSTART_ENABLED, - CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT = 17, -}; - -/* Define actions and types for CMD_MESH_CONFIG */ -enum cmd_mesh_config_actions { - CMD_ACT_MESH_CONFIG_STOP = 0, - CMD_ACT_MESH_CONFIG_START, - CMD_ACT_MESH_CONFIG_SET, - CMD_ACT_MESH_CONFIG_GET, -}; - -enum cmd_mesh_config_types { - CMD_TYPE_MESH_SET_BOOTFLAG = 1, - CMD_TYPE_MESH_SET_BOOTTIME, - CMD_TYPE_MESH_SET_DEF_CHANNEL, - CMD_TYPE_MESH_SET_MESH_IE, - CMD_TYPE_MESH_GET_DEFAULTS, - CMD_TYPE_MESH_GET_MESH_IE, /* GET_DEFAULTS is superset of GET_MESHIE */ -}; - -/* Card Event definition */ -#define MACREG_INT_CODE_TX_PPA_FREE 0 -#define MACREG_INT_CODE_TX_DMA_DONE 1 -#define MACREG_INT_CODE_LINK_LOST_W_SCAN 2 -#define MACREG_INT_CODE_LINK_LOST_NO_SCAN 3 -#define MACREG_INT_CODE_LINK_SENSED 4 -#define MACREG_INT_CODE_CMD_FINISHED 5 -#define MACREG_INT_CODE_MIB_CHANGED 6 -#define MACREG_INT_CODE_INIT_DONE 7 -#define MACREG_INT_CODE_DEAUTHENTICATED 8 -#define MACREG_INT_CODE_DISASSOCIATED 9 -#define MACREG_INT_CODE_PS_AWAKE 10 -#define MACREG_INT_CODE_PS_SLEEP 11 -#define MACREG_INT_CODE_MIC_ERR_MULTICAST 13 -#define MACREG_INT_CODE_MIC_ERR_UNICAST 14 -#define MACREG_INT_CODE_WM_AWAKE 15 -#define MACREG_INT_CODE_DEEP_SLEEP_AWAKE 16 -#define MACREG_INT_CODE_ADHOC_BCN_LOST 17 -#define MACREG_INT_CODE_HOST_AWAKE 18 -#define MACREG_INT_CODE_STOP_TX 19 -#define MACREG_INT_CODE_START_TX 20 -#define MACREG_INT_CODE_CHANNEL_SWITCH 21 -#define MACREG_INT_CODE_MEASUREMENT_RDY 22 -#define MACREG_INT_CODE_WMM_CHANGE 23 -#define MACREG_INT_CODE_BG_SCAN_REPORT 24 -#define MACREG_INT_CODE_RSSI_LOW 25 -#define MACREG_INT_CODE_SNR_LOW 26 -#define MACREG_INT_CODE_MAX_FAIL 27 -#define MACREG_INT_CODE_RSSI_HIGH 28 -#define MACREG_INT_CODE_SNR_HIGH 29 -#define MACREG_INT_CODE_MESH_AUTO_STARTED 35 -#define MACREG_INT_CODE_FIRMWARE_READY 48 - - -/* 802.11-related definitions */ - -/* TxPD descriptor */ -struct txpd { - /* union to cope up with later FW revisions */ - union { - /* Current Tx packet status */ - __le32 tx_status; - struct { - /* BSS type: client, AP, etc. */ - u8 bss_type; - /* BSS number */ - u8 bss_num; - /* Reserved */ - __le16 reserved; - } bss; - } u; - /* Tx control */ - __le32 tx_control; - __le32 tx_packet_location; - /* Tx packet length */ - __le16 tx_packet_length; - /* First 2 byte of destination MAC address */ - u8 tx_dest_addr_high[2]; - /* Last 4 byte of destination MAC address */ - u8 tx_dest_addr_low[4]; - /* Pkt Priority */ - u8 priority; - /* Pkt Trasnit Power control */ - u8 powermgmt; - /* Amount of time the packet has been queued (units = 2ms) */ - u8 pktdelay_2ms; - /* reserved */ - u8 reserved1; -} __packed; - -/* RxPD Descriptor */ -struct rxpd { - /* union to cope up with later FW revisions */ - union { - /* Current Rx packet status */ - __le16 status; - struct { - /* BSS type: client, AP, etc. */ - u8 bss_type; - /* BSS number */ - u8 bss_num; - } __packed bss; - } __packed u; - - /* SNR */ - u8 snr; - - /* Tx control */ - u8 rx_control; - - /* Pkt length */ - __le16 pkt_len; - - /* Noise Floor */ - u8 nf; - - /* Rx Packet Rate */ - u8 rx_rate; - - /* Pkt addr */ - __le32 pkt_ptr; - - /* Next Rx RxPD addr */ - __le32 next_rxpd_ptr; - - /* Pkt Priority */ - u8 priority; - u8 reserved[3]; -} __packed; - -struct cmd_header { - __le16 command; - __le16 size; - __le16 seqnum; - __le16 result; -} __packed; - -/* Generic structure to hold all key types. */ -struct enc_key { - u16 len; - u16 flags; /* KEY_INFO_* from defs.h */ - u16 type; /* KEY_TYPE_* from defs.h */ - u8 key[32]; -}; - -/* lbs_offset_value */ -struct lbs_offset_value { - u32 offset; - u32 value; -} __packed; - -#define MAX_11D_TRIPLETS 83 - -struct mrvl_ie_domain_param_set { - struct mrvl_ie_header header; - - u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - struct ieee80211_country_ie_triplet triplet[MAX_11D_TRIPLETS]; -} __packed; - -struct cmd_ds_802_11d_domain_info { - struct cmd_header hdr; - - __le16 action; - struct mrvl_ie_domain_param_set domain; -} __packed; - -/* - * Define data structure for CMD_GET_HW_SPEC - * This structure defines the response for the GET_HW_SPEC command - */ -struct cmd_ds_get_hw_spec { - struct cmd_header hdr; - - /* HW Interface version number */ - __le16 hwifversion; - /* HW version number */ - __le16 version; - /* Max number of TxPD FW can handle */ - __le16 nr_txpd; - /* Max no of Multicast address */ - __le16 nr_mcast_adr; - /* MAC address */ - u8 permanentaddr[6]; - - /* region Code */ - __le16 regioncode; - - /* Number of antenna used */ - __le16 nr_antenna; - - /* FW release number, example 0x01030304 = 2.3.4p1 */ - __le32 fwrelease; - - /* Base Address of TxPD queue */ - __le32 wcb_base; - /* Read Pointer of RxPd queue */ - __le32 rxpd_rdptr; - - /* Write Pointer of RxPd queue */ - __le32 rxpd_wrptr; - - /*FW/HW capability */ - __le32 fwcapinfo; -} __packed; - -struct cmd_ds_802_11_subscribe_event { - struct cmd_header hdr; - - __le16 action; - __le16 events; - - /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a - * number of TLVs. From the v5.1 manual, those TLVs would add up to - * 40 bytes. However, future firmware might add additional TLVs, so I - * bump this up a bit. - */ - uint8_t tlv[128]; -} __packed; - -/* - * This scan handle Country Information IE(802.11d compliant) - * Define data structure for CMD_802_11_SCAN - */ -struct cmd_ds_802_11_scan { - struct cmd_header hdr; - - uint8_t bsstype; - uint8_t bssid[ETH_ALEN]; - uint8_t tlvbuffer[0]; -} __packed; - -struct cmd_ds_802_11_scan_rsp { - struct cmd_header hdr; - - __le16 bssdescriptsize; - uint8_t nr_sets; - uint8_t bssdesc_and_tlvbuffer[0]; -} __packed; - -struct cmd_ds_802_11_get_log { - struct cmd_header hdr; - - __le32 mcasttxframe; - __le32 failed; - __le32 retry; - __le32 multiretry; - __le32 framedup; - __le32 rtssuccess; - __le32 rtsfailure; - __le32 ackfailure; - __le32 rxfrag; - __le32 mcastrxframe; - __le32 fcserror; - __le32 txframe; - __le32 wepundecryptable; -} __packed; - -struct cmd_ds_mac_control { - struct cmd_header hdr; - __le16 action; - u16 reserved; -} __packed; - -struct cmd_ds_mac_multicast_adr { - struct cmd_header hdr; - __le16 action; - __le16 nr_of_adrs; - u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; -} __packed; - -struct cmd_ds_802_11_authenticate { - struct cmd_header hdr; - - u8 bssid[ETH_ALEN]; - u8 authtype; - u8 reserved[10]; -} __packed; - -struct cmd_ds_802_11_deauthenticate { - struct cmd_header hdr; - - u8 macaddr[ETH_ALEN]; - __le16 reasoncode; -} __packed; - -struct cmd_ds_802_11_associate { - struct cmd_header hdr; - - u8 bssid[6]; - __le16 capability; - __le16 listeninterval; - __le16 bcnperiod; - u8 dtimperiod; - u8 iebuf[512]; /* Enough for required and most optional IEs */ -} __packed; - -struct cmd_ds_802_11_associate_response { - struct cmd_header hdr; - - __le16 capability; - __le16 statuscode; - __le16 aid; - u8 iebuf[512]; -} __packed; - -struct cmd_ds_802_11_set_wep { - struct cmd_header hdr; - - /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ - __le16 action; - - /* key Index selected for Tx */ - __le16 keyindex; - - /* 40, 128bit or TXWEP */ - uint8_t keytype[4]; - uint8_t keymaterial[4][16]; -} __packed; - -struct cmd_ds_802_11_snmp_mib { - struct cmd_header hdr; - - __le16 action; - __le16 oid; - __le16 bufsize; - u8 value[128]; -} __packed; - -struct cmd_ds_reg_access { - struct cmd_header hdr; - - __le16 action; - __le16 offset; - union { - u8 bbp_rf; /* for BBP and RF registers */ - __le32 mac; /* for MAC registers */ - } value; -} __packed; - -struct cmd_ds_802_11_radio_control { - struct cmd_header hdr; - - __le16 action; - __le16 control; -} __packed; - -struct cmd_ds_802_11_beacon_control { - struct cmd_header hdr; - - __le16 action; - __le16 beacon_enable; - __le16 beacon_period; -} __packed; - -struct cmd_ds_802_11_sleep_params { - struct cmd_header hdr; - - /* ACT_GET/ACT_SET */ - __le16 action; - - /* Sleep clock error in ppm */ - __le16 error; - - /* Wakeup offset in usec */ - __le16 offset; - - /* Clock stabilization time in usec */ - __le16 stabletime; - - /* control periodic calibration */ - uint8_t calcontrol; - - /* control the use of external sleep clock */ - uint8_t externalsleepclk; - - /* reserved field, should be set to zero */ - __le16 reserved; -} __packed; - -struct cmd_ds_802_11_rf_channel { - struct cmd_header hdr; - - __le16 action; - __le16 channel; - __le16 rftype; /* unused */ - __le16 reserved; /* unused */ - u8 channellist[32]; /* unused */ -} __packed; - -struct cmd_ds_802_11_rssi { - struct cmd_header hdr; - - /* - * request: number of beacons (N) to average the SNR and NF over - * response: SNR of most recent beacon - */ - __le16 n_or_snr; - - /* - * The following fields are only set in the response. - * In the request these are reserved and should be set to 0. - */ - __le16 nf; /* most recent beacon noise floor */ - __le16 avg_snr; /* average SNR weighted by N from request */ - __le16 avg_nf; /* average noise floor weighted by N from request */ -} __packed; - -struct cmd_ds_802_11_mac_address { - struct cmd_header hdr; - - __le16 action; - u8 macadd[ETH_ALEN]; -} __packed; - -struct cmd_ds_802_11_rf_tx_power { - struct cmd_header hdr; - - __le16 action; - __le16 curlevel; - s8 maxlevel; - s8 minlevel; -} __packed; - -/* MONITOR_MODE only exists in OLPC v5 firmware */ -struct cmd_ds_802_11_monitor_mode { - struct cmd_header hdr; - - __le16 action; - __le16 mode; -} __packed; - -struct cmd_ds_set_boot2_ver { - struct cmd_header hdr; - - __le16 action; - __le16 version; -} __packed; - -struct cmd_ds_802_11_fw_wake_method { - struct cmd_header hdr; - - __le16 action; - __le16 method; -} __packed; - -struct cmd_ds_802_11_ps_mode { - struct cmd_header hdr; - - __le16 action; - - /* - * Interval for keepalive in PS mode: - * 0x0000 = don't change - * 0x001E = firmware default - * 0xFFFF = disable - */ - __le16 nullpktinterval; - - /* - * Number of DTIM intervals to wake up for: - * 0 = don't change - * 1 = firmware default - * 5 = max - */ - __le16 multipledtim; - - __le16 reserved; - __le16 locallisteninterval; - - /* - * AdHoc awake period (FW v9+ only): - * 0 = don't change - * 1 = always awake (IEEE standard behavior) - * 2 - 31 = sleep for (n - 1) periods and awake for 1 period - * 32 - 254 = invalid - * 255 = sleep at each ATIM - */ - __le16 adhoc_awake_period; -} __packed; - -struct cmd_confirm_sleep { - struct cmd_header hdr; - - __le16 action; - __le16 nullpktinterval; - __le16 multipledtim; - __le16 reserved; - __le16 locallisteninterval; -} __packed; - -struct cmd_ds_802_11_data_rate { - struct cmd_header hdr; - - __le16 action; - __le16 reserved; - u8 rates[MAX_RATES]; -} __packed; - -struct cmd_ds_802_11_rate_adapt_rateset { - struct cmd_header hdr; - __le16 action; - __le16 enablehwauto; - __le16 bitmap; -} __packed; - -struct cmd_ds_802_11_ad_hoc_start { - struct cmd_header hdr; - - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 bsstype; - __le16 beaconperiod; - u8 dtimperiod; /* Reserved on v9 and later */ - struct ieee_ie_ibss_param_set ibss; - u8 reserved1[4]; - struct ieee_ie_ds_param_set ds; - u8 reserved2[4]; - __le16 probedelay; /* Reserved on v9 and later */ - __le16 capability; - u8 rates[MAX_RATES]; - u8 tlv_memory_size_pad[100]; -} __packed; - -struct cmd_ds_802_11_ad_hoc_result { - struct cmd_header hdr; - - u8 pad[3]; - u8 bssid[ETH_ALEN]; -} __packed; - -struct adhoc_bssdesc { - u8 bssid[ETH_ALEN]; - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 type; - __le16 beaconperiod; - u8 dtimperiod; - __le64 timestamp; - __le64 localtime; - struct ieee_ie_ds_param_set ds; - u8 reserved1[4]; - struct ieee_ie_ibss_param_set ibss; - u8 reserved2[4]; - __le16 capability; - u8 rates[MAX_RATES]; - - /* - * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the - * Adhoc join command and will cause a binary layout mismatch with - * the firmware - */ -} __packed; - -struct cmd_ds_802_11_ad_hoc_join { - struct cmd_header hdr; - - struct adhoc_bssdesc bss; - __le16 failtimeout; /* Reserved on v9 and later */ - __le16 probedelay; /* Reserved on v9 and later */ -} __packed; - -struct cmd_ds_802_11_ad_hoc_stop { - struct cmd_header hdr; -} __packed; - -struct cmd_ds_802_11_enable_rsn { - struct cmd_header hdr; - - __le16 action; - __le16 enable; -} __packed; - -struct MrvlIEtype_keyParamSet { - /* type ID */ - __le16 type; - - /* length of Payload */ - __le16 length; - - /* type of key: WEP=0, TKIP=1, AES=2 */ - __le16 keytypeid; - - /* key control Info specific to a keytypeid */ - __le16 keyinfo; - - /* length of key */ - __le16 keylen; - - /* key material of size keylen */ - u8 key[32]; -} __packed; - -#define MAX_WOL_RULES 16 - -struct host_wol_rule { - uint8_t rule_no; - uint8_t rule_ops; - __le16 sig_offset; - __le16 sig_length; - __le16 reserve; - __be32 sig_mask; - __be32 signature; -} __packed; - -struct wol_config { - uint8_t action; - uint8_t pattern; - uint8_t no_rules_in_cmd; - uint8_t result; - struct host_wol_rule rule[MAX_WOL_RULES]; -} __packed; - -struct cmd_ds_host_sleep { - struct cmd_header hdr; - __le32 criteria; - uint8_t gpio; - uint16_t gap; - struct wol_config wol_conf; -} __packed; - - - -struct cmd_ds_802_11_key_material { - struct cmd_header hdr; - - __le16 action; - struct MrvlIEtype_keyParamSet keyParamSet[2]; -} __packed; - -struct cmd_ds_802_11_eeprom_access { - struct cmd_header hdr; - __le16 action; - __le16 offset; - __le16 len; - /* firmware says it returns a maximum of 20 bytes */ -#define LBS_EEPROM_READ_LEN 20 - u8 value[LBS_EEPROM_READ_LEN]; -} __packed; - -struct cmd_ds_802_11_tpc_cfg { - struct cmd_header hdr; - - __le16 action; - uint8_t enable; - int8_t P0; - int8_t P1; - int8_t P2; - uint8_t usesnr; -} __packed; - - -struct cmd_ds_802_11_pa_cfg { - struct cmd_header hdr; - - __le16 action; - uint8_t enable; - int8_t P0; - int8_t P1; - int8_t P2; -} __packed; - - -struct cmd_ds_802_11_led_ctrl { - struct cmd_header hdr; - - __le16 action; - __le16 numled; - u8 data[256]; -} __packed; - -/* Automatic Frequency Control */ -struct cmd_ds_802_11_afc { - struct cmd_header hdr; - - __le16 afc_auto; - union { - struct { - __le16 threshold; - __le16 period; - }; - struct { - __le16 timing_offset; /* signed */ - __le16 carrier_offset; /* signed */ - }; - }; -} __packed; - -struct cmd_tx_rate_query { - __le16 txrate; -} __packed; - -struct cmd_ds_get_tsf { - __le64 tsfvalue; -} __packed; - -struct cmd_ds_bt_access { - struct cmd_header hdr; - - __le16 action; - __le32 id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; -} __packed; - -struct cmd_ds_fwt_access { - struct cmd_header hdr; - - __le16 action; - __le32 id; - u8 valid; - u8 da[ETH_ALEN]; - u8 dir; - u8 ra[ETH_ALEN]; - __le32 ssn; - __le32 dsn; - __le32 metric; - u8 rate; - u8 hopcount; - u8 ttl; - __le32 expiration; - u8 sleepmode; - __le32 snr; - __le32 references; - u8 prec[ETH_ALEN]; -} __packed; - -struct cmd_ds_mesh_config { - struct cmd_header hdr; - - __le16 action; - __le16 channel; - __le16 type; - __le16 length; - u8 data[128]; /* last position reserved */ -} __packed; - -struct cmd_ds_mesh_access { - struct cmd_header hdr; - - __le16 action; - __le32 data[32]; /* last position reserved */ -} __packed; - -/* Number of stats counters returned by the firmware */ -#define MESH_STATS_NUM 8 -#endif diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c deleted file mode 100644 index f499efc6abcf..000000000000 --- a/drivers/net/wireless/libertas/if_cs.c +++ /dev/null @@ -1,1006 +0,0 @@ -/* - - Driver for the Marvell 8385 based compact flash WLAN cards. - - (C) 2007 by Holger Schurig - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; see the file COPYING. If not, write to - the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#define DRV_NAME "libertas_cs" - -#include "decl.h" -#include "defs.h" -#include "dev.h" - - -/********************************************************************/ -/* Module stuff */ -/********************************************************************/ - -MODULE_AUTHOR("Holger Schurig "); -MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards"); -MODULE_LICENSE("GPL"); - - - -/********************************************************************/ -/* Data structures */ -/********************************************************************/ - -struct if_cs_card { - struct pcmcia_device *p_dev; - struct lbs_private *priv; - void __iomem *iobase; - bool align_regs; - u32 model; -}; - - -enum { - MODEL_UNKNOWN = 0x00, - MODEL_8305 = 0x01, - MODEL_8381 = 0x02, - MODEL_8385 = 0x03 -}; - -static const struct lbs_fw_table fw_table[] = { - { MODEL_8305, "libertas/cf8305.bin", NULL }, - { MODEL_8305, "libertas_cs_helper.fw", NULL }, - { MODEL_8381, "libertas/cf8381_helper.bin", "libertas/cf8381.bin" }, - { MODEL_8381, "libertas_cs_helper.fw", "libertas_cs.fw" }, - { MODEL_8385, "libertas/cf8385_helper.bin", "libertas/cf8385.bin" }, - { MODEL_8385, "libertas_cs_helper.fw", "libertas_cs.fw" }, - { 0, NULL, NULL } -}; -MODULE_FIRMWARE("libertas/cf8305.bin"); -MODULE_FIRMWARE("libertas/cf8381_helper.bin"); -MODULE_FIRMWARE("libertas/cf8381.bin"); -MODULE_FIRMWARE("libertas/cf8385_helper.bin"); -MODULE_FIRMWARE("libertas/cf8385.bin"); -MODULE_FIRMWARE("libertas_cs_helper.fw"); -MODULE_FIRMWARE("libertas_cs.fw"); - - -/********************************************************************/ -/* Hardware access */ -/********************************************************************/ - -/* This define enables wrapper functions which allow you - to dump all register accesses. You normally won't this, - except for development */ -/* #define DEBUG_IO */ - -#ifdef DEBUG_IO -static int debug_output = 0; -#else -/* This way the compiler optimizes the printk's away */ -#define debug_output 0 -#endif - -static inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg) -{ - unsigned int val = ioread8(card->iobase + reg); - if (debug_output) - printk(KERN_INFO "inb %08x<%02x\n", reg, val); - return val; -} -static inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg) -{ - unsigned int val = ioread16(card->iobase + reg); - if (debug_output) - printk(KERN_INFO "inw %08x<%04x\n", reg, val); - return val; -} -static inline void if_cs_read16_rep( - struct if_cs_card *card, - uint reg, - void *buf, - unsigned long count) -{ - if (debug_output) - printk(KERN_INFO "insw %08x<(0x%lx words)\n", - reg, count); - ioread16_rep(card->iobase + reg, buf, count); -} - -static inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val) -{ - if (debug_output) - printk(KERN_INFO "outb %08x>%02x\n", reg, val); - iowrite8(val, card->iobase + reg); -} - -static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val) -{ - if (debug_output) - printk(KERN_INFO "outw %08x>%04x\n", reg, val); - iowrite16(val, card->iobase + reg); -} - -static inline void if_cs_write16_rep( - struct if_cs_card *card, - uint reg, - const void *buf, - unsigned long count) -{ - if (debug_output) - printk(KERN_INFO "outsw %08x>(0x%lx words)\n", - reg, count); - iowrite16_rep(card->iobase + reg, buf, count); -} - - -/* - * I know that polling/delaying is frowned upon. However, this procedure - * with polling is needed while downloading the firmware. At this stage, - * the hardware does unfortunately not create any interrupts. - * - * Fortunately, this function is never used once the firmware is in - * the card. :-) - * - * As a reference, see the "Firmware Specification v5.1", page 18 - * and 19. I did not follow their suggested timing to the word, - * but this works nice & fast anyway. - */ -static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 reg) -{ - int i; - - for (i = 0; i < 100000; i++) { - u8 val = if_cs_read8(card, addr); - if (val == reg) - return 0; - udelay(5); - } - return -ETIME; -} - - - -/* - * First the bitmasks for the host/card interrupt/status registers: - */ -#define IF_CS_BIT_TX 0x0001 -#define IF_CS_BIT_RX 0x0002 -#define IF_CS_BIT_COMMAND 0x0004 -#define IF_CS_BIT_RESP 0x0008 -#define IF_CS_BIT_EVENT 0x0010 -#define IF_CS_BIT_MASK 0x001f - - - -/* - * It's not really clear to me what the host status register is for. It - * needs to be set almost in union with "host int cause". The following - * bits from above are used: - * - * IF_CS_BIT_TX driver downloaded a data packet - * IF_CS_BIT_RX driver got a data packet - * IF_CS_BIT_COMMAND driver downloaded a command - * IF_CS_BIT_RESP not used (has some meaning with powerdown) - * IF_CS_BIT_EVENT driver read a host event - */ -#define IF_CS_HOST_STATUS 0x00000000 - -/* - * With the host int cause register can the host (that is, Linux) cause - * an interrupt in the firmware, to tell the firmware about those events: - * - * IF_CS_BIT_TX a data packet has been downloaded - * IF_CS_BIT_RX a received data packet has retrieved - * IF_CS_BIT_COMMAND a firmware block or a command has been downloaded - * IF_CS_BIT_RESP not used (has some meaning with powerdown) - * IF_CS_BIT_EVENT a host event (link lost etc) has been retrieved - */ -#define IF_CS_HOST_INT_CAUSE 0x00000002 - -/* - * The host int mask register is used to enable/disable interrupt. However, - * I have the suspicion that disabled interrupts are lost. - */ -#define IF_CS_HOST_INT_MASK 0x00000004 - -/* - * Used to send or receive data packets: - */ -#define IF_CS_WRITE 0x00000016 -#define IF_CS_WRITE_LEN 0x00000014 -#define IF_CS_READ 0x00000010 -#define IF_CS_READ_LEN 0x00000024 - -/* - * Used to send commands (and to send firmware block) and to - * receive command responses: - */ -#define IF_CS_CMD 0x0000001A -#define IF_CS_CMD_LEN 0x00000018 -#define IF_CS_RESP 0x00000012 -#define IF_CS_RESP_LEN 0x00000030 - -/* - * The card status registers shows what the card/firmware actually - * accepts: - * - * IF_CS_BIT_TX you may send a data packet - * IF_CS_BIT_RX you may retrieve a data packet - * IF_CS_BIT_COMMAND you may send a command - * IF_CS_BIT_RESP you may retrieve a command response - * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) - * - * When reading this register several times, you will get back the same - * results --- with one exception: the IF_CS_BIT_EVENT clear itself - * automatically. - * - * Not that we don't rely on BIT_RX,_BIT_RESP or BIT_EVENT because - * we handle this via the card int cause register. - */ -#define IF_CS_CARD_STATUS 0x00000020 -#define IF_CS_CARD_STATUS_MASK 0x7f00 - -/* - * The card int cause register is used by the card/firmware to notify us - * about the following events: - * - * IF_CS_BIT_TX a data packet has successfully been sentx - * IF_CS_BIT_RX a data packet has been received and can be retrieved - * IF_CS_BIT_COMMAND not used - * IF_CS_BIT_RESP the firmware has a command response for us - * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) - */ -#define IF_CS_CARD_INT_CAUSE 0x00000022 - -/* - * This is used to for handshaking with the card's bootloader/helper image - * to synchronize downloading of firmware blocks. - */ -#define IF_CS_SQ_READ_LOW 0x00000028 -#define IF_CS_SQ_HELPER_OK 0x10 - -/* - * The scratch register tells us ... - * - * IF_CS_SCRATCH_BOOT_OK the bootloader runs - * IF_CS_SCRATCH_HELPER_OK the helper firmware already runs - */ -#define IF_CS_SCRATCH 0x0000003F -#define IF_CS_SCRATCH_BOOT_OK 0x00 -#define IF_CS_SCRATCH_HELPER_OK 0x5a - -/* - * Used to detect ancient chips: - */ -#define IF_CS_PRODUCT_ID 0x0000001C -#define IF_CS_CF8385_B1_REV 0x12 -#define IF_CS_CF8381_B3_REV 0x04 -#define IF_CS_CF8305_B1_REV 0x03 - -/* - * Used to detect other cards than CF8385 since their revisions of silicon - * doesn't match those from CF8385, eg. CF8381 B3 works with this driver. - */ -#define CF8305_MANFID 0x02db -#define CF8305_CARDID 0x8103 -#define CF8381_MANFID 0x02db -#define CF8381_CARDID 0x6064 -#define CF8385_MANFID 0x02df -#define CF8385_CARDID 0x8103 - -/* - * FIXME: just use the 'driver_info' field of 'struct pcmcia_device_id' when - * that gets fixed. Currently there's no way to access it from the probe hook. - */ -static inline u32 get_model(u16 manf_id, u16 card_id) -{ - /* NOTE: keep in sync with if_cs_ids */ - if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID) - return MODEL_8305; - else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID) - return MODEL_8381; - else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID) - return MODEL_8385; - return MODEL_UNKNOWN; -} - -/********************************************************************/ -/* I/O and interrupt handling */ -/********************************************************************/ - -static inline void if_cs_enable_ints(struct if_cs_card *card) -{ - lbs_deb_enter(LBS_DEB_CS); - if_cs_write16(card, IF_CS_HOST_INT_MASK, 0); -} - -static inline void if_cs_disable_ints(struct if_cs_card *card) -{ - lbs_deb_enter(LBS_DEB_CS); - if_cs_write16(card, IF_CS_HOST_INT_MASK, IF_CS_BIT_MASK); -} - -/* - * Called from if_cs_host_to_card to send a command to the hardware - */ -static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb) -{ - struct if_cs_card *card = (struct if_cs_card *)priv->card; - int ret = -1; - int loops = 0; - - lbs_deb_enter(LBS_DEB_CS); - if_cs_disable_ints(card); - - /* Is hardware ready? */ - while (1) { - u16 status = if_cs_read16(card, IF_CS_CARD_STATUS); - if (status & IF_CS_BIT_COMMAND) - break; - if (++loops > 100) { - netdev_err(priv->dev, "card not ready for commands\n"); - goto done; - } - mdelay(1); - } - - if_cs_write16(card, IF_CS_CMD_LEN, nb); - - if_cs_write16_rep(card, IF_CS_CMD, buf, nb / 2); - /* Are we supposed to transfer an odd amount of bytes? */ - if (nb & 1) - if_cs_write8(card, IF_CS_CMD, buf[nb-1]); - - /* "Assert the download over interrupt command in the Host - * status register" */ - if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); - - /* "Assert the download over interrupt command in the Card - * interrupt case register" */ - if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); - ret = 0; - -done: - if_cs_enable_ints(card); - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); - return ret; -} - -/* - * Called from if_cs_host_to_card to send a data to the hardware - */ -static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) -{ - struct if_cs_card *card = (struct if_cs_card *)priv->card; - u16 status; - - lbs_deb_enter(LBS_DEB_CS); - if_cs_disable_ints(card); - - status = if_cs_read16(card, IF_CS_CARD_STATUS); - BUG_ON((status & IF_CS_BIT_TX) == 0); - - if_cs_write16(card, IF_CS_WRITE_LEN, nb); - - /* write even number of bytes, then odd byte if necessary */ - if_cs_write16_rep(card, IF_CS_WRITE, buf, nb / 2); - if (nb & 1) - if_cs_write8(card, IF_CS_WRITE, buf[nb-1]); - - if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_TX); - if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_TX); - if_cs_enable_ints(card); - - lbs_deb_leave(LBS_DEB_CS); -} - -/* - * Get the command result out of the card. - */ -static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len) -{ - unsigned long flags; - int ret = -1; - u16 status; - - lbs_deb_enter(LBS_DEB_CS); - - /* is hardware ready? */ - status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); - if ((status & IF_CS_BIT_RESP) == 0) { - netdev_err(priv->dev, "no cmd response in card\n"); - *len = 0; - goto out; - } - - *len = if_cs_read16(priv->card, IF_CS_RESP_LEN); - if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) { - netdev_err(priv->dev, - "card cmd buffer has invalid # of bytes (%d)\n", - *len); - goto out; - } - - /* read even number of bytes, then odd byte if necessary */ - if_cs_read16_rep(priv->card, IF_CS_RESP, data, *len/sizeof(u16)); - if (*len & 1) - data[*len-1] = if_cs_read8(priv->card, IF_CS_RESP); - - /* This is a workaround for a firmware that reports too much - * bytes */ - *len -= 8; - ret = 0; - - /* Clear this flag again */ - spin_lock_irqsave(&priv->driver_lock, flags); - priv->dnld_sent = DNLD_RES_RECEIVED; - spin_unlock_irqrestore(&priv->driver_lock, flags); - -out: - lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len); - return ret; -} - -static struct sk_buff *if_cs_receive_data(struct lbs_private *priv) -{ - struct sk_buff *skb = NULL; - u16 len; - u8 *data; - - lbs_deb_enter(LBS_DEB_CS); - - len = if_cs_read16(priv->card, IF_CS_READ_LEN); - if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { - netdev_err(priv->dev, - "card data buffer has invalid # of bytes (%d)\n", - len); - priv->dev->stats.rx_dropped++; - goto dat_err; - } - - skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2); - if (!skb) - goto out; - skb_put(skb, len); - skb_reserve(skb, 2);/* 16 byte align */ - data = skb->data; - - /* read even number of bytes, then odd byte if necessary */ - if_cs_read16_rep(priv->card, IF_CS_READ, data, len/sizeof(u16)); - if (len & 1) - data[len-1] = if_cs_read8(priv->card, IF_CS_READ); - -dat_err: - if_cs_write16(priv->card, IF_CS_HOST_STATUS, IF_CS_BIT_RX); - if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_RX); - -out: - lbs_deb_leave_args(LBS_DEB_CS, "ret %p", skb); - return skb; -} - -static irqreturn_t if_cs_interrupt(int irq, void *data) -{ - struct if_cs_card *card = data; - struct lbs_private *priv = card->priv; - u16 cause; - - lbs_deb_enter(LBS_DEB_CS); - - /* Ask card interrupt cause register if there is something for us */ - cause = if_cs_read16(card, IF_CS_CARD_INT_CAUSE); - lbs_deb_cs("cause 0x%04x\n", cause); - - if (cause == 0) { - /* Not for us */ - return IRQ_NONE; - } - - if (cause == 0xffff) { - /* Read in junk, the card has probably been removed */ - card->priv->surpriseremoved = 1; - return IRQ_HANDLED; - } - - if (cause & IF_CS_BIT_RX) { - struct sk_buff *skb; - lbs_deb_cs("rx packet\n"); - skb = if_cs_receive_data(priv); - if (skb) - lbs_process_rxed_packet(priv, skb); - } - - if (cause & IF_CS_BIT_TX) { - lbs_deb_cs("tx done\n"); - lbs_host_to_card_done(priv); - } - - if (cause & IF_CS_BIT_RESP) { - unsigned long flags; - u8 i; - - lbs_deb_cs("cmd resp\n"); - spin_lock_irqsave(&priv->driver_lock, flags); - i = (priv->resp_idx == 0) ? 1 : 0; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - BUG_ON(priv->resp_len[i]); - if_cs_receive_cmdres(priv, priv->resp_buf[i], - &priv->resp_len[i]); - - spin_lock_irqsave(&priv->driver_lock, flags); - lbs_notify_command_response(priv, i); - spin_unlock_irqrestore(&priv->driver_lock, flags); - } - - if (cause & IF_CS_BIT_EVENT) { - u16 status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); - if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, - IF_CS_BIT_EVENT); - lbs_queue_event(priv, (status & IF_CS_CARD_STATUS_MASK) >> 8); - } - - /* Clear interrupt cause */ - if_cs_write16(card, IF_CS_CARD_INT_CAUSE, cause & IF_CS_BIT_MASK); - - lbs_deb_leave(LBS_DEB_CS); - return IRQ_HANDLED; -} - - - - -/********************************************************************/ -/* Firmware */ -/********************************************************************/ - -/* - * Tries to program the helper firmware. - * - * Return 0 on success - */ -static int if_cs_prog_helper(struct if_cs_card *card, const struct firmware *fw) -{ - int ret = 0; - int sent = 0; - u8 scratch; - - lbs_deb_enter(LBS_DEB_CS); - - /* - * This is the only place where an unaligned register access happens on - * the CF8305 card, therefore for the sake of speed of the driver, we do - * the alignment correction here. - */ - if (card->align_regs) - scratch = if_cs_read16(card, IF_CS_SCRATCH) >> 8; - else - scratch = if_cs_read8(card, IF_CS_SCRATCH); - - /* "If the value is 0x5a, the firmware is already - * downloaded successfully" - */ - if (scratch == IF_CS_SCRATCH_HELPER_OK) - goto done; - - /* "If the value is != 00, it is invalid value of register */ - if (scratch != IF_CS_SCRATCH_BOOT_OK) { - ret = -ENODEV; - goto done; - } - - lbs_deb_cs("helper size %td\n", fw->size); - - /* "Set the 5 bytes of the helper image to 0" */ - /* Not needed, this contains an ARM branch instruction */ - - for (;;) { - /* "the number of bytes to send is 256" */ - int count = 256; - int remain = fw->size - sent; - - if (remain < count) - count = remain; - - /* - * "write the number of bytes to be sent to the I/O Command - * write length register" - */ - if_cs_write16(card, IF_CS_CMD_LEN, count); - - /* "write this to I/O Command port register as 16 bit writes */ - if (count) - if_cs_write16_rep(card, IF_CS_CMD, - &fw->data[sent], - count >> 1); - - /* - * "Assert the download over interrupt command in the Host - * status register" - */ - if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); - - /* - * "Assert the download over interrupt command in the Card - * interrupt case register" - */ - if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); - - /* - * "The host polls the Card Status register ... for 50 ms before - * declaring a failure" - */ - ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, - IF_CS_BIT_COMMAND); - if (ret < 0) { - pr_err("can't download helper at 0x%x, ret %d\n", - sent, ret); - goto done; - } - - if (count == 0) - break; - - sent += count; - } - -done: - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); - return ret; -} - - -static int if_cs_prog_real(struct if_cs_card *card, const struct firmware *fw) -{ - int ret = 0; - int retry = 0; - int len = 0; - int sent; - - lbs_deb_enter(LBS_DEB_CS); - - lbs_deb_cs("fw size %td\n", fw->size); - - ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW, - IF_CS_SQ_HELPER_OK); - if (ret < 0) { - pr_err("helper firmware doesn't answer\n"); - goto done; - } - - for (sent = 0; sent < fw->size; sent += len) { - len = if_cs_read16(card, IF_CS_SQ_READ_LOW); - if (len & 1) { - retry++; - pr_info("odd, need to retry this firmware block\n"); - } else { - retry = 0; - } - - if (retry > 20) { - pr_err("could not download firmware\n"); - ret = -ENODEV; - goto done; - } - if (retry) { - sent -= len; - } - - - if_cs_write16(card, IF_CS_CMD_LEN, len); - - if_cs_write16_rep(card, IF_CS_CMD, - &fw->data[sent], - (len+1) >> 1); - if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); - if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); - - ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, - IF_CS_BIT_COMMAND); - if (ret < 0) { - pr_err("can't download firmware at 0x%x\n", sent); - goto done; - } - } - - ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a); - if (ret < 0) - pr_err("firmware download failed\n"); - -done: - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); - return ret; -} - -static void if_cs_prog_firmware(struct lbs_private *priv, int ret, - const struct firmware *helper, - const struct firmware *mainfw) -{ - struct if_cs_card *card = priv->card; - - if (ret) { - pr_err("failed to find firmware (%d)\n", ret); - return; - } - - /* Load the firmware */ - ret = if_cs_prog_helper(card, helper); - if (ret == 0 && (card->model != MODEL_8305)) - ret = if_cs_prog_real(card, mainfw); - if (ret) - return; - - /* Now actually get the IRQ */ - ret = request_irq(card->p_dev->irq, if_cs_interrupt, - IRQF_SHARED, DRV_NAME, card); - if (ret) { - pr_err("error in request_irq\n"); - return; - } - - /* - * Clear any interrupt cause that happened while sending - * firmware/initializing card - */ - if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK); - if_cs_enable_ints(card); - - /* And finally bring the card up */ - priv->fw_ready = 1; - if (lbs_start_card(priv) != 0) { - pr_err("could not activate card\n"); - free_irq(card->p_dev->irq, card); - } -} - - -/********************************************************************/ -/* Callback functions for libertas.ko */ -/********************************************************************/ - -/* Send commands or data packets to the card */ -static int if_cs_host_to_card(struct lbs_private *priv, - u8 type, - u8 *buf, - u16 nb) -{ - int ret = -1; - - lbs_deb_enter_args(LBS_DEB_CS, "type %d, bytes %d", type, nb); - - switch (type) { - case MVMS_DAT: - priv->dnld_sent = DNLD_DATA_SENT; - if_cs_send_data(priv, buf, nb); - ret = 0; - break; - case MVMS_CMD: - priv->dnld_sent = DNLD_CMD_SENT; - ret = if_cs_send_cmd(priv, buf, nb); - break; - default: - netdev_err(priv->dev, "%s: unsupported type %d\n", - __func__, type); - } - - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); - return ret; -} - - -static void if_cs_release(struct pcmcia_device *p_dev) -{ - struct if_cs_card *card = p_dev->priv; - - lbs_deb_enter(LBS_DEB_CS); - - free_irq(p_dev->irq, card); - pcmcia_disable_device(p_dev); - if (card->iobase) - ioport_unmap(card->iobase); - - lbs_deb_leave(LBS_DEB_CS); -} - - -static int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data) -{ - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - - if (p_dev->resource[1]->end) { - pr_err("wrong CIS (check number of IO windows)\n"); - return -ENODEV; - } - - /* This reserves IO space but doesn't actually enable it */ - return pcmcia_request_io(p_dev); -} - -static int if_cs_probe(struct pcmcia_device *p_dev) -{ - int ret = -ENOMEM; - unsigned int prod_id; - struct lbs_private *priv; - struct if_cs_card *card; - - lbs_deb_enter(LBS_DEB_CS); - - card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL); - if (!card) - goto out; - - card->p_dev = p_dev; - p_dev->priv = card; - - p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - - if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) { - pr_err("error in pcmcia_loop_config\n"); - goto out1; - } - - /* - * Allocate an interrupt line. Note that this does not assign - * a handler to the interrupt, unless the 'Handler' member of - * the irq structure is initialized. - */ - if (!p_dev->irq) - goto out1; - - /* Initialize io access */ - card->iobase = ioport_map(p_dev->resource[0]->start, - resource_size(p_dev->resource[0])); - if (!card->iobase) { - pr_err("error in ioport_map\n"); - ret = -EIO; - goto out1; - } - - ret = pcmcia_enable_device(p_dev); - if (ret) { - pr_err("error in pcmcia_enable_device\n"); - goto out2; - } - - /* Finally, report what we've done */ - lbs_deb_cs("irq %d, io %pR", p_dev->irq, p_dev->resource[0]); - - /* - * Most of the libertas cards can do unaligned register access, but some - * weird ones cannot. That's especially true for the CF8305 card. - */ - card->align_regs = false; - - card->model = get_model(p_dev->manf_id, p_dev->card_id); - if (card->model == MODEL_UNKNOWN) { - pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n", - p_dev->manf_id, p_dev->card_id); - ret = -ENODEV; - goto out2; - } - - /* Check if we have a current silicon */ - prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID); - if (card->model == MODEL_8305) { - card->align_regs = true; - if (prod_id < IF_CS_CF8305_B1_REV) { - pr_err("8305 rev B0 and older are not supported\n"); - ret = -ENODEV; - goto out2; - } - } - - if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) { - pr_err("8381 rev B2 and older are not supported\n"); - ret = -ENODEV; - goto out2; - } - - if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) { - pr_err("8385 rev B0 and older are not supported\n"); - ret = -ENODEV; - goto out2; - } - - /* Make this card known to the libertas driver */ - priv = lbs_add_card(card, &p_dev->dev); - if (!priv) { - ret = -ENOMEM; - goto out2; - } - - /* Set up fields in lbs_private */ - card->priv = priv; - priv->card = card; - priv->hw_host_to_card = if_cs_host_to_card; - priv->enter_deep_sleep = NULL; - priv->exit_deep_sleep = NULL; - priv->reset_deep_sleep_wakeup = NULL; - - /* Get firmware */ - ret = lbs_get_firmware_async(priv, &p_dev->dev, card->model, fw_table, - if_cs_prog_firmware); - if (ret) { - pr_err("failed to find firmware (%d)\n", ret); - goto out3; - } - - goto out; - -out3: - lbs_remove_card(priv); -out2: - ioport_unmap(card->iobase); -out1: - pcmcia_disable_device(p_dev); -out: - lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); - return ret; -} - - -static void if_cs_detach(struct pcmcia_device *p_dev) -{ - struct if_cs_card *card = p_dev->priv; - - lbs_deb_enter(LBS_DEB_CS); - - lbs_stop_card(card->priv); - lbs_remove_card(card->priv); - if_cs_disable_ints(card); - if_cs_release(p_dev); - kfree(card); - - lbs_deb_leave(LBS_DEB_CS); -} - - - -/********************************************************************/ -/* Module initialization */ -/********************************************************************/ - -static const struct pcmcia_device_id if_cs_ids[] = { - PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID), - PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID), - PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID), - /* NOTE: keep in sync with get_model() */ - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); - -static struct pcmcia_driver lbs_driver = { - .owner = THIS_MODULE, - .name = DRV_NAME, - .probe = if_cs_probe, - .remove = if_cs_detach, - .id_table = if_cs_ids, -}; -module_pcmcia_driver(lbs_driver); diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c deleted file mode 100644 index 33ceda296c9c..000000000000 --- a/drivers/net/wireless/libertas/if_sdio.c +++ /dev/null @@ -1,1453 +0,0 @@ -/* - * linux/drivers/net/wireless/libertas/if_sdio.c - * - * Copyright 2007-2008 Pierre Ossman - * - * Inspired by if_cs.c, Copyright 2007 Holger Schurig - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This hardware has more or less no CMD53 support, so all registers - * must be accessed using sdio_readb()/sdio_writeb(). - * - * Transfers must be in one transaction or the firmware goes bonkers. - * This means that the transfer must either be small enough to do a - * byte based transfer or it must be padded to a multiple of the - * current block size. - * - * As SDIO is still new to the kernel, it is unfortunately common with - * bugs in the host controllers related to that. One such bug is that - * controllers cannot do transfers that aren't a multiple of 4 bytes. - * If you don't have time to fix the host controller driver, you can - * work around the problem by modifying if_sdio_host_to_card() and - * if_sdio_card_to_host() to pad the data. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "host.h" -#include "decl.h" -#include "defs.h" -#include "dev.h" -#include "cmd.h" -#include "if_sdio.h" - -static void if_sdio_interrupt(struct sdio_func *func); - -/* The if_sdio_remove() callback function is called when - * user removes this module from kernel space or ejects - * the card from the slot. The driver handles these 2 cases - * differently for SD8688 combo chip. - * If the user is removing the module, the FUNC_SHUTDOWN - * command for SD8688 is sent to the firmware. - * If the card is removed, there is no need to send this command. - * - * The variable 'user_rmmod' is used to distinguish these two - * scenarios. This flag is initialized as FALSE in case the card - * is removed, and will be set to TRUE for module removal when - * module_exit function is called. - */ -static u8 user_rmmod; - -static const struct sdio_device_id if_sdio_ids[] = { - { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, - SDIO_DEVICE_ID_MARVELL_LIBERTAS) }, - { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, - SDIO_DEVICE_ID_MARVELL_8688WLAN) }, - { /* end: all zeroes */ }, -}; - -MODULE_DEVICE_TABLE(sdio, if_sdio_ids); - -#define MODEL_8385 0x04 -#define MODEL_8686 0x0b -#define MODEL_8688 0x10 - -static const struct lbs_fw_table fw_table[] = { - { MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" }, - { MODEL_8385, "sd8385_helper.bin", "sd8385.bin" }, - { MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" }, - { MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" }, - { MODEL_8686, "sd8686_helper.bin", "sd8686.bin" }, - { MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" }, - { MODEL_8688, "sd8688_helper.bin", "sd8688.bin" }, - { 0, NULL, NULL } -}; -MODULE_FIRMWARE("libertas/sd8385_helper.bin"); -MODULE_FIRMWARE("libertas/sd8385.bin"); -MODULE_FIRMWARE("sd8385_helper.bin"); -MODULE_FIRMWARE("sd8385.bin"); -MODULE_FIRMWARE("libertas/sd8686_v9_helper.bin"); -MODULE_FIRMWARE("libertas/sd8686_v9.bin"); -MODULE_FIRMWARE("libertas/sd8686_v8_helper.bin"); -MODULE_FIRMWARE("libertas/sd8686_v8.bin"); -MODULE_FIRMWARE("sd8686_helper.bin"); -MODULE_FIRMWARE("sd8686.bin"); -MODULE_FIRMWARE("libertas/sd8688_helper.bin"); -MODULE_FIRMWARE("libertas/sd8688.bin"); -MODULE_FIRMWARE("sd8688_helper.bin"); -MODULE_FIRMWARE("sd8688.bin"); - -struct if_sdio_packet { - struct if_sdio_packet *next; - u16 nb; - u8 buffer[0] __attribute__((aligned(4))); -}; - -struct if_sdio_card { - struct sdio_func *func; - struct lbs_private *priv; - - int model; - unsigned long ioport; - unsigned int scratch_reg; - bool started; - wait_queue_head_t pwron_waitq; - - u8 buffer[65536] __attribute__((aligned(4))); - - spinlock_t lock; - struct if_sdio_packet *packets; - - struct workqueue_struct *workqueue; - struct work_struct packet_worker; - - u8 rx_unit; -}; - -static void if_sdio_finish_power_on(struct if_sdio_card *card); -static int if_sdio_power_off(struct if_sdio_card *card); - -/********************************************************************/ -/* I/O */ -/********************************************************************/ - -/* - * For SD8385/SD8686, this function reads firmware status after - * the image is downloaded, or reads RX packet length when - * interrupt (with IF_SDIO_H_INT_UPLD bit set) is received. - * For SD8688, this function reads firmware status only. - */ -static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err) -{ - int ret; - u16 scratch; - - scratch = sdio_readb(card->func, card->scratch_reg, &ret); - if (!ret) - scratch |= sdio_readb(card->func, card->scratch_reg + 1, - &ret) << 8; - - if (err) - *err = ret; - - if (ret) - return 0xffff; - - return scratch; -} - -static u8 if_sdio_read_rx_unit(struct if_sdio_card *card) -{ - int ret; - u8 rx_unit; - - rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret); - - if (ret) - rx_unit = 0; - - return rx_unit; -} - -static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err) -{ - int ret; - u16 rx_len; - - switch (card->model) { - case MODEL_8385: - case MODEL_8686: - rx_len = if_sdio_read_scratch(card, &ret); - break; - case MODEL_8688: - default: /* for newer chipsets */ - rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret); - if (!ret) - rx_len <<= card->rx_unit; - else - rx_len = 0xffff; /* invalid length */ - - break; - } - - if (err) - *err = ret; - - return rx_len; -} - -static int if_sdio_handle_cmd(struct if_sdio_card *card, - u8 *buffer, unsigned size) -{ - struct lbs_private *priv = card->priv; - int ret; - unsigned long flags; - u8 i; - - lbs_deb_enter(LBS_DEB_SDIO); - - if (size > LBS_CMD_BUFFER_SIZE) { - lbs_deb_sdio("response packet too large (%d bytes)\n", - (int)size); - ret = -E2BIG; - goto out; - } - - spin_lock_irqsave(&priv->driver_lock, flags); - - i = (priv->resp_idx == 0) ? 1 : 0; - BUG_ON(priv->resp_len[i]); - priv->resp_len[i] = size; - memcpy(priv->resp_buf[i], buffer, size); - lbs_notify_command_response(priv, i); - - spin_unlock_irqrestore(&card->priv->driver_lock, flags); - - ret = 0; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; -} - -static int if_sdio_handle_data(struct if_sdio_card *card, - u8 *buffer, unsigned size) -{ - int ret; - struct sk_buff *skb; - char *data; - - lbs_deb_enter(LBS_DEB_SDIO); - - if (size > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { - lbs_deb_sdio("response packet too large (%d bytes)\n", - (int)size); - ret = -E2BIG; - goto out; - } - - skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN); - if (!skb) { - ret = -ENOMEM; - goto out; - } - - skb_reserve(skb, NET_IP_ALIGN); - - data = skb_put(skb, size); - - memcpy(data, buffer, size); - - lbs_process_rxed_packet(card->priv, skb); - - ret = 0; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; -} - -static int if_sdio_handle_event(struct if_sdio_card *card, - u8 *buffer, unsigned size) -{ - int ret; - u32 event; - - lbs_deb_enter(LBS_DEB_SDIO); - - if (card->model == MODEL_8385) { - event = sdio_readb(card->func, IF_SDIO_EVENT, &ret); - if (ret) - goto out; - - /* right shift 3 bits to get the event id */ - event >>= 3; - } else { - if (size < 4) { - lbs_deb_sdio("event packet too small (%d bytes)\n", - (int)size); - ret = -EINVAL; - goto out; - } - event = buffer[3] << 24; - event |= buffer[2] << 16; - event |= buffer[1] << 8; - event |= buffer[0] << 0; - } - - lbs_queue_event(card->priv, event & 0xFF); - ret = 0; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; -} - -static int if_sdio_wait_status(struct if_sdio_card *card, const u8 condition) -{ - u8 status; - unsigned long timeout; - int ret = 0; - - timeout = jiffies + HZ; - while (1) { - status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); - if (ret) - return ret; - if ((status & condition) == condition) - break; - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - mdelay(1); - } - return ret; -} - -static int if_sdio_card_to_host(struct if_sdio_card *card) -{ - int ret; - u16 size, type, chunk; - - lbs_deb_enter(LBS_DEB_SDIO); - - size = if_sdio_read_rx_len(card, &ret); - if (ret) - goto out; - - if (size < 4) { - lbs_deb_sdio("invalid packet size (%d bytes) from firmware\n", - (int)size); - ret = -EINVAL; - goto out; - } - - ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); - if (ret) - goto out; - - /* - * The transfer must be in one transaction or the firmware - * goes suicidal. There's no way to guarantee that for all - * controllers, but we can at least try. - */ - chunk = sdio_align_size(card->func, size); - - ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk); - if (ret) - goto out; - - chunk = card->buffer[0] | (card->buffer[1] << 8); - type = card->buffer[2] | (card->buffer[3] << 8); - - lbs_deb_sdio("packet of type %d and size %d bytes\n", - (int)type, (int)chunk); - - if (chunk > size) { - lbs_deb_sdio("packet fragment (%d > %d)\n", - (int)chunk, (int)size); - ret = -EINVAL; - goto out; - } - - if (chunk < size) { - lbs_deb_sdio("packet fragment (%d < %d)\n", - (int)chunk, (int)size); - } - - switch (type) { - case MVMS_CMD: - ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4); - if (ret) - goto out; - break; - case MVMS_DAT: - ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4); - if (ret) - goto out; - break; - case MVMS_EVENT: - ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4); - if (ret) - goto out; - break; - default: - lbs_deb_sdio("invalid type (%d) from firmware\n", - (int)type); - ret = -EINVAL; - goto out; - } - -out: - if (ret) - pr_err("problem fetching packet from firmware\n"); - - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; -} - -static void if_sdio_host_to_card_worker(struct work_struct *work) -{ - struct if_sdio_card *card; - struct if_sdio_packet *packet; - int ret; - unsigned long flags; - - lbs_deb_enter(LBS_DEB_SDIO); - - card = container_of(work, struct if_sdio_card, packet_worker); - - while (1) { - spin_lock_irqsave(&card->lock, flags); - packet = card->packets; - if (packet) - card->packets = packet->next; - spin_unlock_irqrestore(&card->lock, flags); - - if (!packet) - break; - - sdio_claim_host(card->func); - - ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); - if (ret == 0) { - ret = sdio_writesb(card->func, card->ioport, - packet->buffer, packet->nb); - } - - if (ret) - pr_err("error %d sending packet to firmware\n", ret); - - sdio_release_host(card->func); - - kfree(packet); - } - - lbs_deb_leave(LBS_DEB_SDIO); -} - -/********************************************************************/ -/* Firmware */ -/********************************************************************/ - -#define FW_DL_READY_STATUS (IF_SDIO_IO_RDY | IF_SDIO_DL_RDY) - -static int if_sdio_prog_helper(struct if_sdio_card *card, - const struct firmware *fw) -{ - int ret; - unsigned long timeout; - u8 *chunk_buffer; - u32 chunk_size; - const u8 *firmware; - size_t size; - - lbs_deb_enter(LBS_DEB_SDIO); - - chunk_buffer = kzalloc(64, GFP_KERNEL); - if (!chunk_buffer) { - ret = -ENOMEM; - goto out; - } - - sdio_claim_host(card->func); - - ret = sdio_set_block_size(card->func, 32); - if (ret) - goto release; - - firmware = fw->data; - size = fw->size; - - while (size) { - ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); - if (ret) - goto release; - - /* On some platforms (like Davinci) the chip needs more time - * between helper blocks. - */ - mdelay(2); - - chunk_size = min_t(size_t, size, 60); - - *((__le32*)chunk_buffer) = cpu_to_le32(chunk_size); - memcpy(chunk_buffer + 4, firmware, chunk_size); -/* - lbs_deb_sdio("sending %d bytes chunk\n", chunk_size); -*/ - ret = sdio_writesb(card->func, card->ioport, - chunk_buffer, 64); - if (ret) - goto release; - - firmware += chunk_size; - size -= chunk_size; - } - - /* an empty block marks the end of the transfer */ - memset(chunk_buffer, 0, 4); - ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64); - if (ret) - goto release; - - lbs_deb_sdio("waiting for helper to boot...\n"); - - /* wait for the helper to boot by looking at the size register */ - timeout = jiffies + HZ; - while (1) { - u16 req_size; - - req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); - if (ret) - goto release; - - req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; - if (ret) - goto release; - - if (req_size != 0) - break; - - if (time_after(jiffies, timeout)) { - ret = -ETIMEDOUT; - goto release; - } - - msleep(10); - } - - ret = 0; - -release: - sdio_release_host(card->func); - kfree(chunk_buffer); - -out: - if (ret) - pr_err("failed to load helper firmware\n"); - - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; -} - -static int if_sdio_prog_real(struct if_sdio_card *card, - const struct firmware *fw) -{ - int ret; - unsigned long timeout; - u8 *chunk_buffer; - u32 chunk_size; - const u8 *firmware; - size_t size, req_size; - - lbs_deb_enter(LBS_DEB_SDIO); - - chunk_buffer = kzalloc(512, GFP_KERNEL); - if (!chunk_buffer) { - ret = -ENOMEM; - goto out; - } - - sdio_claim_host(card->func); - - ret = sdio_set_block_size(card->func, 32); - if (ret) - goto release; - - firmware = fw->data; - size = fw->size; - - while (size) { - timeout = jiffies + HZ; - while (1) { - ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); - if (ret) - goto release; - - req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, - &ret); - if (ret) - goto release; - - req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, - &ret) << 8; - if (ret) - goto release; - - /* - * For SD8688 wait until the length is not 0, 1 or 2 - * before downloading the first FW block, - * since BOOT code writes the register to indicate the - * helper/FW download winner, - * the value could be 1 or 2 (Func1 or Func2). - */ - if ((size != fw->size) || (req_size > 2)) - break; - if (time_after(jiffies, timeout)) { - ret = -ETIMEDOUT; - goto release; - } - mdelay(1); - } - -/* - lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size); -*/ - if (req_size == 0) { - lbs_deb_sdio("firmware helper gave up early\n"); - ret = -EIO; - goto release; - } - - if (req_size & 0x01) { - lbs_deb_sdio("firmware helper signalled error\n"); - ret = -EIO; - goto release; - } - - if (req_size > size) - req_size = size; - - while (req_size) { - chunk_size = min_t(size_t, req_size, 512); - - memcpy(chunk_buffer, firmware, chunk_size); -/* - lbs_deb_sdio("sending %d bytes (%d bytes) chunk\n", - chunk_size, (chunk_size + 31) / 32 * 32); -*/ - ret = sdio_writesb(card->func, card->ioport, - chunk_buffer, roundup(chunk_size, 32)); - if (ret) - goto release; - - firmware += chunk_size; - size -= chunk_size; - req_size -= chunk_size; - } - } - - ret = 0; - - lbs_deb_sdio("waiting for firmware to boot...\n"); - - /* wait for the firmware to boot */ - timeout = jiffies + HZ; - while (1) { - u16 scratch; - - scratch = if_sdio_read_scratch(card, &ret); - if (ret) - goto release; - - if (scratch == IF_SDIO_FIRMWARE_OK) - break; - - if (time_after(jiffies, timeout)) { - ret = -ETIMEDOUT; - goto release; - } - - msleep(10); - } - - ret = 0; - -release: - sdio_release_host(card->func); - kfree(chunk_buffer); - -out: - if (ret) - pr_err("failed to load firmware\n"); - - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; -} - -static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret, - const struct firmware *helper, - const struct firmware *mainfw) -{ - struct if_sdio_card *card = priv->card; - - if (ret) { - pr_err("failed to find firmware (%d)\n", ret); - return; - } - - ret = if_sdio_prog_helper(card, helper); - if (ret) - return; - - lbs_deb_sdio("Helper firmware loaded\n"); - - ret = if_sdio_prog_real(card, mainfw); - if (ret) - return; - - lbs_deb_sdio("Firmware loaded\n"); - if_sdio_finish_power_on(card); -} - -static int if_sdio_prog_firmware(struct if_sdio_card *card) -{ - int ret; - u16 scratch; - - lbs_deb_enter(LBS_DEB_SDIO); - - /* - * Disable interrupts - */ - sdio_claim_host(card->func); - sdio_writeb(card->func, 0x00, IF_SDIO_H_INT_MASK, &ret); - sdio_release_host(card->func); - - sdio_claim_host(card->func); - scratch = if_sdio_read_scratch(card, &ret); - sdio_release_host(card->func); - - lbs_deb_sdio("firmware status = %#x\n", scratch); - lbs_deb_sdio("scratch ret = %d\n", ret); - - if (ret) - goto out; - - - /* - * The manual clearly describes that FEDC is the right code to use - * to detect firmware presence, but for SD8686 it is not that simple. - * Scratch is also used to store the RX packet length, so we lose - * the FEDC value early on. So we use a non-zero check in order - * to validate firmware presence. - * Additionally, the SD8686 in the Gumstix always has the high scratch - * bit set, even when the firmware is not loaded. So we have to - * exclude that from the test. - */ - if (scratch == IF_SDIO_FIRMWARE_OK) { - lbs_deb_sdio("firmware already loaded\n"); - if_sdio_finish_power_on(card); - return 0; - } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) { - lbs_deb_sdio("firmware may be running\n"); - if_sdio_finish_power_on(card); - return 0; - } - - ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model, - fw_table, if_sdio_do_prog_firmware); - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; -} - -/********************************************************************/ -/* Power management */ -/********************************************************************/ - -/* Finish power on sequence (after firmware is loaded) */ -static void if_sdio_finish_power_on(struct if_sdio_card *card) -{ - struct sdio_func *func = card->func; - struct lbs_private *priv = card->priv; - int ret; - - sdio_claim_host(func); - sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); - - /* - * Get rx_unit if the chip is SD8688 or newer. - * SD8385 & SD8686 do not have rx_unit. - */ - if ((card->model != MODEL_8385) - && (card->model != MODEL_8686)) - card->rx_unit = if_sdio_read_rx_unit(card); - else - card->rx_unit = 0; - - /* - * Set up the interrupt handler late. - * - * If we set it up earlier, the (buggy) hardware generates a spurious - * interrupt, even before the interrupt has been enabled, with - * CCCR_INTx = 0. - * - * We register the interrupt handler late so that we can handle any - * spurious interrupts, and also to avoid generation of that known - * spurious interrupt in the first place. - */ - ret = sdio_claim_irq(func, if_sdio_interrupt); - if (ret) - goto release; - - /* - * Enable interrupts now that everything is set up - */ - sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); - if (ret) - goto release_irq; - - sdio_release_host(func); - - /* Set fw_ready before queuing any commands so that - * lbs_thread won't block from sending them to firmware. - */ - priv->fw_ready = 1; - - /* - * FUNC_INIT is required for SD8688 WLAN/BT multiple functions - */ - if (card->model == MODEL_8688) { - struct cmd_header cmd; - - memset(&cmd, 0, sizeof(cmd)); - - lbs_deb_sdio("send function INIT command\n"); - if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd), - lbs_cmd_copyback, (unsigned long) &cmd)) - netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n"); - } - - wake_up(&card->pwron_waitq); - - if (!card->started) { - ret = lbs_start_card(priv); - if_sdio_power_off(card); - if (ret == 0) { - card->started = true; - /* Tell PM core that we don't need the card to be - * powered now */ - pm_runtime_put(&func->dev); - } - } - - return; - -release_irq: - sdio_release_irq(func); -release: - sdio_release_host(func); -} - -static int if_sdio_power_on(struct if_sdio_card *card) -{ - struct sdio_func *func = card->func; - struct mmc_host *host = func->card->host; - int ret; - - sdio_claim_host(func); - - ret = sdio_enable_func(func); - if (ret) - goto release; - - /* For 1-bit transfers to the 8686 model, we need to enable the - * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 - * bit to allow access to non-vendor registers. */ - if ((card->model == MODEL_8686) && - (host->caps & MMC_CAP_SDIO_IRQ) && - (host->ios.bus_width == MMC_BUS_WIDTH_1)) { - u8 reg; - - func->card->quirks |= MMC_QUIRK_LENIENT_FN0; - reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret); - if (ret) - goto disable; - - reg |= SDIO_BUS_ECSI; - sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret); - if (ret) - goto disable; - } - - card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); - if (ret) - goto disable; - - card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; - if (ret) - goto disable; - - card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; - if (ret) - goto disable; - - sdio_release_host(func); - ret = if_sdio_prog_firmware(card); - if (ret) { - sdio_claim_host(func); - goto disable; - } - - return 0; - -disable: - sdio_disable_func(func); -release: - sdio_release_host(func); - return ret; -} - -static int if_sdio_power_off(struct if_sdio_card *card) -{ - struct sdio_func *func = card->func; - struct lbs_private *priv = card->priv; - - priv->fw_ready = 0; - - sdio_claim_host(func); - sdio_release_irq(func); - sdio_disable_func(func); - sdio_release_host(func); - return 0; -} - - -/*******************************************************************/ -/* Libertas callbacks */ -/*******************************************************************/ - -static int if_sdio_host_to_card(struct lbs_private *priv, - u8 type, u8 *buf, u16 nb) -{ - int ret; - struct if_sdio_card *card; - struct if_sdio_packet *packet, *cur; - u16 size; - unsigned long flags; - - lbs_deb_enter_args(LBS_DEB_SDIO, "type %d, bytes %d", type, nb); - - card = priv->card; - - if (nb > (65536 - sizeof(struct if_sdio_packet) - 4)) { - ret = -EINVAL; - goto out; - } - - /* - * The transfer must be in one transaction or the firmware - * goes suicidal. There's no way to guarantee that for all - * controllers, but we can at least try. - */ - size = sdio_align_size(card->func, nb + 4); - - packet = kzalloc(sizeof(struct if_sdio_packet) + size, - GFP_ATOMIC); - if (!packet) { - ret = -ENOMEM; - goto out; - } - - packet->next = NULL; - packet->nb = size; - - /* - * SDIO specific header. - */ - packet->buffer[0] = (nb + 4) & 0xff; - packet->buffer[1] = ((nb + 4) >> 8) & 0xff; - packet->buffer[2] = type; - packet->buffer[3] = 0; - - memcpy(packet->buffer + 4, buf, nb); - - spin_lock_irqsave(&card->lock, flags); - - if (!card->packets) - card->packets = packet; - else { - cur = card->packets; - while (cur->next) - cur = cur->next; - cur->next = packet; - } - - switch (type) { - case MVMS_CMD: - priv->dnld_sent = DNLD_CMD_SENT; - break; - case MVMS_DAT: - priv->dnld_sent = DNLD_DATA_SENT; - break; - default: - lbs_deb_sdio("unknown packet type %d\n", (int)type); - } - - spin_unlock_irqrestore(&card->lock, flags); - - queue_work(card->workqueue, &card->packet_worker); - - ret = 0; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; -} - -static int if_sdio_enter_deep_sleep(struct lbs_private *priv) -{ - int ret = -1; - struct cmd_header cmd; - - memset(&cmd, 0, sizeof(cmd)); - - lbs_deb_sdio("send DEEP_SLEEP command\n"); - ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd), - lbs_cmd_copyback, (unsigned long) &cmd); - if (ret) - netdev_err(priv->dev, "DEEP_SLEEP cmd failed\n"); - - mdelay(200); - return ret; -} - -static int if_sdio_exit_deep_sleep(struct lbs_private *priv) -{ - struct if_sdio_card *card = priv->card; - int ret = -1; - - lbs_deb_enter(LBS_DEB_SDIO); - sdio_claim_host(card->func); - - sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); - if (ret) - netdev_err(priv->dev, "sdio_writeb failed!\n"); - - sdio_release_host(card->func); - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; -} - -static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv) -{ - struct if_sdio_card *card = priv->card; - int ret = -1; - - lbs_deb_enter(LBS_DEB_SDIO); - sdio_claim_host(card->func); - - sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret); - if (ret) - netdev_err(priv->dev, "sdio_writeb failed!\n"); - - sdio_release_host(card->func); - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - return ret; - -} - -static struct mmc_host *reset_host; - -static void if_sdio_reset_card_worker(struct work_struct *work) -{ - /* - * The actual reset operation must be run outside of lbs_thread. This - * is because mmc_remove_host() will cause the device to be instantly - * destroyed, and the libertas driver then needs to end lbs_thread, - * leading to a deadlock. - * - * We run it in a workqueue totally independent from the if_sdio_card - * instance for that reason. - */ - - pr_info("Resetting card..."); - mmc_remove_host(reset_host); - mmc_add_host(reset_host); -} -static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker); - -static void if_sdio_reset_card(struct lbs_private *priv) -{ - struct if_sdio_card *card = priv->card; - - if (work_pending(&card_reset_work)) - return; - - reset_host = card->func->card->host; - schedule_work(&card_reset_work); -} - -static int if_sdio_power_save(struct lbs_private *priv) -{ - struct if_sdio_card *card = priv->card; - int ret; - - flush_workqueue(card->workqueue); - - ret = if_sdio_power_off(card); - - /* Let runtime PM know the card is powered off */ - pm_runtime_put_sync(&card->func->dev); - - return ret; -} - -static int if_sdio_power_restore(struct lbs_private *priv) -{ - struct if_sdio_card *card = priv->card; - int r; - - /* Make sure the card will not be powered off by runtime PM */ - pm_runtime_get_sync(&card->func->dev); - - r = if_sdio_power_on(card); - if (r) - return r; - - wait_event(card->pwron_waitq, priv->fw_ready); - return 0; -} - - -/*******************************************************************/ -/* SDIO callbacks */ -/*******************************************************************/ - -static void if_sdio_interrupt(struct sdio_func *func) -{ - int ret; - struct if_sdio_card *card; - u8 cause; - - lbs_deb_enter(LBS_DEB_SDIO); - - card = sdio_get_drvdata(func); - - cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret); - if (ret || !cause) - goto out; - - lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause); - - sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret); - if (ret) - goto out; - - /* - * Ignore the define name, this really means the card has - * successfully received the command. - */ - card->priv->is_activity_detected = 1; - if (cause & IF_SDIO_H_INT_DNLD) - lbs_host_to_card_done(card->priv); - - - if (cause & IF_SDIO_H_INT_UPLD) { - ret = if_sdio_card_to_host(card); - if (ret) - goto out; - } - - ret = 0; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); -} - -static int if_sdio_probe(struct sdio_func *func, - const struct sdio_device_id *id) -{ - struct if_sdio_card *card; - struct lbs_private *priv; - int ret, i; - unsigned int model; - struct if_sdio_packet *packet; - - lbs_deb_enter(LBS_DEB_SDIO); - - for (i = 0;i < func->card->num_info;i++) { - if (sscanf(func->card->info[i], - "802.11 SDIO ID: %x", &model) == 1) - break; - if (sscanf(func->card->info[i], - "ID: %x", &model) == 1) - break; - if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) { - model = MODEL_8385; - break; - } - } - - if (i == func->card->num_info) { - pr_err("unable to identify card model\n"); - return -ENODEV; - } - - card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL); - if (!card) - return -ENOMEM; - - card->func = func; - card->model = model; - - switch (card->model) { - case MODEL_8385: - card->scratch_reg = IF_SDIO_SCRATCH_OLD; - break; - case MODEL_8686: - card->scratch_reg = IF_SDIO_SCRATCH; - break; - case MODEL_8688: - default: /* for newer chipsets */ - card->scratch_reg = IF_SDIO_FW_STATUS; - break; - } - - spin_lock_init(&card->lock); - card->workqueue = create_workqueue("libertas_sdio"); - INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); - init_waitqueue_head(&card->pwron_waitq); - - /* Check if we support this card */ - for (i = 0; i < ARRAY_SIZE(fw_table); i++) { - if (card->model == fw_table[i].model) - break; - } - if (i == ARRAY_SIZE(fw_table)) { - pr_err("unknown card model 0x%x\n", card->model); - ret = -ENODEV; - goto free; - } - - sdio_set_drvdata(func, card); - - lbs_deb_sdio("class = 0x%X, vendor = 0x%X, " - "device = 0x%X, model = 0x%X, ioport = 0x%X\n", - func->class, func->vendor, func->device, - model, (unsigned)card->ioport); - - - priv = lbs_add_card(card, &func->dev); - if (!priv) { - ret = -ENOMEM; - goto free; - } - - card->priv = priv; - - priv->card = card; - priv->hw_host_to_card = if_sdio_host_to_card; - priv->enter_deep_sleep = if_sdio_enter_deep_sleep; - priv->exit_deep_sleep = if_sdio_exit_deep_sleep; - priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; - priv->reset_card = if_sdio_reset_card; - priv->power_save = if_sdio_power_save; - priv->power_restore = if_sdio_power_restore; - - ret = if_sdio_power_on(card); - if (ret) - goto err_activate_card; - -out: - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; - -err_activate_card: - flush_workqueue(card->workqueue); - lbs_remove_card(priv); -free: - destroy_workqueue(card->workqueue); - while (card->packets) { - packet = card->packets; - card->packets = card->packets->next; - kfree(packet); - } - - kfree(card); - - goto out; -} - -static void if_sdio_remove(struct sdio_func *func) -{ - struct if_sdio_card *card; - struct if_sdio_packet *packet; - - lbs_deb_enter(LBS_DEB_SDIO); - - card = sdio_get_drvdata(func); - - /* Undo decrement done above in if_sdio_probe */ - pm_runtime_get_noresume(&func->dev); - - if (user_rmmod && (card->model == MODEL_8688)) { - /* - * FUNC_SHUTDOWN is required for SD8688 WLAN/BT - * multiple functions - */ - struct cmd_header cmd; - - memset(&cmd, 0, sizeof(cmd)); - - lbs_deb_sdio("send function SHUTDOWN command\n"); - if (__lbs_cmd(card->priv, CMD_FUNC_SHUTDOWN, - &cmd, sizeof(cmd), lbs_cmd_copyback, - (unsigned long) &cmd)) - pr_alert("CMD_FUNC_SHUTDOWN cmd failed\n"); - } - - - lbs_deb_sdio("call remove card\n"); - lbs_stop_card(card->priv); - lbs_remove_card(card->priv); - - flush_workqueue(card->workqueue); - destroy_workqueue(card->workqueue); - - while (card->packets) { - packet = card->packets; - card->packets = card->packets->next; - kfree(packet); - } - - kfree(card); - lbs_deb_leave(LBS_DEB_SDIO); -} - -static int if_sdio_suspend(struct device *dev) -{ - struct sdio_func *func = dev_to_sdio_func(dev); - int ret; - struct if_sdio_card *card = sdio_get_drvdata(func); - - mmc_pm_flag_t flags = sdio_get_host_pm_caps(func); - - /* If we're powered off anyway, just let the mmc layer remove the - * card. */ - if (!lbs_iface_active(card->priv)) - return -ENOSYS; - - dev_info(dev, "%s: suspend: PM flags = 0x%x\n", - sdio_func_id(func), flags); - - /* If we aren't being asked to wake on anything, we should bail out - * and let the SD stack power down the card. - */ - if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) { - dev_info(dev, "Suspend without wake params -- powering down card\n"); - return -ENOSYS; - } - - if (!(flags & MMC_PM_KEEP_POWER)) { - dev_err(dev, "%s: cannot remain alive while host is suspended\n", - sdio_func_id(func)); - return -ENOSYS; - } - - ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); - if (ret) - return ret; - - ret = lbs_suspend(card->priv); - if (ret) - return ret; - - return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); -} - -static int if_sdio_resume(struct device *dev) -{ - struct sdio_func *func = dev_to_sdio_func(dev); - struct if_sdio_card *card = sdio_get_drvdata(func); - int ret; - - dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func)); - - ret = lbs_resume(card->priv); - - return ret; -} - -static const struct dev_pm_ops if_sdio_pm_ops = { - .suspend = if_sdio_suspend, - .resume = if_sdio_resume, -}; - -static struct sdio_driver if_sdio_driver = { - .name = "libertas_sdio", - .id_table = if_sdio_ids, - .probe = if_sdio_probe, - .remove = if_sdio_remove, - .drv = { - .pm = &if_sdio_pm_ops, - }, -}; - -/*******************************************************************/ -/* Module functions */ -/*******************************************************************/ - -static int __init if_sdio_init_module(void) -{ - int ret = 0; - - lbs_deb_enter(LBS_DEB_SDIO); - - printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n"); - printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n"); - - ret = sdio_register_driver(&if_sdio_driver); - - /* Clear the flag in case user removes the card. */ - user_rmmod = 0; - - lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); - - return ret; -} - -static void __exit if_sdio_exit_module(void) -{ - lbs_deb_enter(LBS_DEB_SDIO); - - /* Set the flag as user is removing this module. */ - user_rmmod = 1; - - cancel_work_sync(&card_reset_work); - - sdio_unregister_driver(&if_sdio_driver); - - lbs_deb_leave(LBS_DEB_SDIO); -} - -module_init(if_sdio_init_module); -module_exit(if_sdio_exit_module); - -MODULE_DESCRIPTION("Libertas SDIO WLAN Driver"); -MODULE_AUTHOR("Pierre Ossman"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h deleted file mode 100644 index 62fda3592f67..000000000000 --- a/drivers/net/wireless/libertas/if_sdio.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * linux/drivers/net/wireless/libertas/if_sdio.h - * - * Copyright 2007 Pierre Ossman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ - -#ifndef _LBS_IF_SDIO_H -#define _LBS_IF_SDIO_H - -#define IF_SDIO_IOPORT 0x00 - -#define IF_SDIO_H_INT_MASK 0x04 -#define IF_SDIO_H_INT_OFLOW 0x08 -#define IF_SDIO_H_INT_UFLOW 0x04 -#define IF_SDIO_H_INT_DNLD 0x02 -#define IF_SDIO_H_INT_UPLD 0x01 - -#define IF_SDIO_H_INT_STATUS 0x05 -#define IF_SDIO_H_INT_RSR 0x06 -#define IF_SDIO_H_INT_STATUS2 0x07 - -#define IF_SDIO_RD_BASE 0x10 - -#define IF_SDIO_STATUS 0x20 -#define IF_SDIO_IO_RDY 0x08 -#define IF_SDIO_CIS_RDY 0x04 -#define IF_SDIO_UL_RDY 0x02 -#define IF_SDIO_DL_RDY 0x01 - -#define IF_SDIO_C_INT_MASK 0x24 -#define IF_SDIO_C_INT_STATUS 0x28 -#define IF_SDIO_C_INT_RSR 0x2C - -#define IF_SDIO_SCRATCH 0x34 -#define IF_SDIO_SCRATCH_OLD 0x80fe -#define IF_SDIO_FW_STATUS 0x40 -#define IF_SDIO_FIRMWARE_OK 0xfedc - -#define IF_SDIO_RX_LEN 0x42 -#define IF_SDIO_RX_UNIT 0x43 - -#define IF_SDIO_EVENT 0x80fc - -#define IF_SDIO_BLOCK_SIZE 256 -#define CONFIGURATION_REG 0x03 -#define HOST_POWER_UP (0x1U << 1) -#endif diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c deleted file mode 100644 index 82c0796377aa..000000000000 --- a/drivers/net/wireless/libertas/if_spi.c +++ /dev/null @@ -1,1318 +0,0 @@ -/* - * linux/drivers/net/wireless/libertas/if_spi.c - * - * Driver for Marvell SPI WLAN cards. - * - * Copyright 2008 Analog Devices Inc. - * - * Authors: - * Andrey Yurovsky - * Colin McCabe - * - * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "host.h" -#include "decl.h" -#include "defs.h" -#include "dev.h" -#include "if_spi.h" - -struct if_spi_packet { - struct list_head list; - u16 blen; - u8 buffer[0] __attribute__((aligned(4))); -}; - -struct if_spi_card { - struct spi_device *spi; - struct lbs_private *priv; - struct libertas_spi_platform_data *pdata; - - /* The card ID and card revision, as reported by the hardware. */ - u16 card_id; - u8 card_rev; - - /* The last time that we initiated an SPU operation */ - unsigned long prev_xfer_time; - - int use_dummy_writes; - unsigned long spu_port_delay; - unsigned long spu_reg_delay; - - /* Handles all SPI communication (except for FW load) */ - struct workqueue_struct *workqueue; - struct work_struct packet_work; - struct work_struct resume_work; - - u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE]; - - /* A buffer of incoming packets from libertas core. - * Since we can't sleep in hw_host_to_card, we have to buffer - * them. */ - struct list_head cmd_packet_list; - struct list_head data_packet_list; - - /* Protects cmd_packet_list and data_packet_list */ - spinlock_t buffer_lock; - - /* True is card suspended */ - u8 suspended; -}; - -static void free_if_spi_card(struct if_spi_card *card) -{ - struct list_head *cursor, *next; - struct if_spi_packet *packet; - - list_for_each_safe(cursor, next, &card->cmd_packet_list) { - packet = container_of(cursor, struct if_spi_packet, list); - list_del(&packet->list); - kfree(packet); - } - list_for_each_safe(cursor, next, &card->data_packet_list) { - packet = container_of(cursor, struct if_spi_packet, list); - list_del(&packet->list); - kfree(packet); - } - kfree(card); -} - -#define MODEL_8385 0x04 -#define MODEL_8686 0x0b -#define MODEL_8688 0x10 - -static const struct lbs_fw_table fw_table[] = { - { MODEL_8385, "libertas/gspi8385_helper.bin", "libertas/gspi8385.bin" }, - { MODEL_8385, "libertas/gspi8385_hlp.bin", "libertas/gspi8385.bin" }, - { MODEL_8686, "libertas/gspi8686_v9_helper.bin", "libertas/gspi8686_v9.bin" }, - { MODEL_8686, "libertas/gspi8686_hlp.bin", "libertas/gspi8686.bin" }, - { MODEL_8688, "libertas/gspi8688_helper.bin", "libertas/gspi8688.bin" }, - { 0, NULL, NULL } -}; -MODULE_FIRMWARE("libertas/gspi8385_helper.bin"); -MODULE_FIRMWARE("libertas/gspi8385_hlp.bin"); -MODULE_FIRMWARE("libertas/gspi8385.bin"); -MODULE_FIRMWARE("libertas/gspi8686_v9_helper.bin"); -MODULE_FIRMWARE("libertas/gspi8686_v9.bin"); -MODULE_FIRMWARE("libertas/gspi8686_hlp.bin"); -MODULE_FIRMWARE("libertas/gspi8686.bin"); -MODULE_FIRMWARE("libertas/gspi8688_helper.bin"); -MODULE_FIRMWARE("libertas/gspi8688.bin"); - - -/* - * SPI Interface Unit Routines - * - * The SPU sits between the host and the WLAN module. - * All communication with the firmware is through SPU transactions. - * - * First we have to put a SPU register name on the bus. Then we can - * either read from or write to that register. - * - */ - -static void spu_transaction_init(struct if_spi_card *card) -{ - if (!time_after(jiffies, card->prev_xfer_time + 1)) { - /* Unfortunately, the SPU requires a delay between successive - * transactions. If our last transaction was more than a jiffy - * ago, we have obviously already delayed enough. - * If not, we have to busy-wait to be on the safe side. */ - ndelay(400); - } -} - -static void spu_transaction_finish(struct if_spi_card *card) -{ - card->prev_xfer_time = jiffies; -} - -/* - * Write out a byte buffer to an SPI register, - * using a series of 16-bit transfers. - */ -static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len) -{ - int err = 0; - __le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK); - struct spi_message m; - struct spi_transfer reg_trans; - struct spi_transfer data_trans; - - spi_message_init(&m); - memset(®_trans, 0, sizeof(reg_trans)); - memset(&data_trans, 0, sizeof(data_trans)); - - /* You must give an even number of bytes to the SPU, even if it - * doesn't care about the last one. */ - BUG_ON(len & 0x1); - - spu_transaction_init(card); - - /* write SPU register index */ - reg_trans.tx_buf = ®_out; - reg_trans.len = sizeof(reg_out); - - data_trans.tx_buf = buf; - data_trans.len = len; - - spi_message_add_tail(®_trans, &m); - spi_message_add_tail(&data_trans, &m); - - err = spi_sync(card->spi, &m); - spu_transaction_finish(card); - return err; -} - -static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val) -{ - __le16 buff; - - buff = cpu_to_le16(val); - return spu_write(card, reg, (u8 *)&buff, sizeof(u16)); -} - -static inline int spu_reg_is_port_reg(u16 reg) -{ - switch (reg) { - case IF_SPI_IO_RDWRPORT_REG: - case IF_SPI_CMD_RDWRPORT_REG: - case IF_SPI_DATA_RDWRPORT_REG: - return 1; - default: - return 0; - } -} - -static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len) -{ - unsigned int delay; - int err = 0; - __le16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK); - struct spi_message m; - struct spi_transfer reg_trans; - struct spi_transfer dummy_trans; - struct spi_transfer data_trans; - - /* - * You must take an even number of bytes from the SPU, even if you - * don't care about the last one. - */ - BUG_ON(len & 0x1); - - spu_transaction_init(card); - - spi_message_init(&m); - memset(®_trans, 0, sizeof(reg_trans)); - memset(&dummy_trans, 0, sizeof(dummy_trans)); - memset(&data_trans, 0, sizeof(data_trans)); - - /* write SPU register index */ - reg_trans.tx_buf = ®_out; - reg_trans.len = sizeof(reg_out); - spi_message_add_tail(®_trans, &m); - - delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay : - card->spu_reg_delay; - if (card->use_dummy_writes) { - /* Clock in dummy cycles while the SPU fills the FIFO */ - dummy_trans.len = delay / 8; - spi_message_add_tail(&dummy_trans, &m); - } else { - /* Busy-wait while the SPU fills the FIFO */ - reg_trans.delay_usecs = - DIV_ROUND_UP((100 + (delay * 10)), 1000); - } - - /* read in data */ - data_trans.rx_buf = buf; - data_trans.len = len; - spi_message_add_tail(&data_trans, &m); - - err = spi_sync(card->spi, &m); - spu_transaction_finish(card); - return err; -} - -/* Read 16 bits from an SPI register */ -static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val) -{ - __le16 buf; - int ret; - - ret = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); - if (ret == 0) - *val = le16_to_cpup(&buf); - return ret; -} - -/* - * Read 32 bits from an SPI register. - * The low 16 bits are read first. - */ -static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val) -{ - __le32 buf; - int err; - - err = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); - if (!err) - *val = le32_to_cpup(&buf); - return err; -} - -/* - * Keep reading 16 bits from an SPI register until you get the correct result. - * - * If mask = 0, the correct result is any non-zero number. - * If mask != 0, the correct result is any number where - * number & target_mask == target - * - * Returns -ETIMEDOUT if a second passes without the correct result. - */ -static int spu_wait_for_u16(struct if_spi_card *card, u16 reg, - u16 target_mask, u16 target) -{ - int err; - unsigned long timeout = jiffies + 5*HZ; - while (1) { - u16 val; - err = spu_read_u16(card, reg, &val); - if (err) - return err; - if (target_mask) { - if ((val & target_mask) == target) - return 0; - } else { - if (val) - return 0; - } - udelay(100); - if (time_after(jiffies, timeout)) { - pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n", - __func__, val, target_mask, target); - return -ETIMEDOUT; - } - } -} - -/* - * Read 16 bits from an SPI register until you receive a specific value. - * Returns -ETIMEDOUT if a 4 tries pass without success. - */ -static int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target) -{ - int err, try; - for (try = 0; try < 4; ++try) { - u32 val = 0; - err = spu_read_u32(card, reg, &val); - if (err) - return err; - if (val == target) - return 0; - mdelay(100); - } - return -ETIMEDOUT; -} - -static int spu_set_interrupt_mode(struct if_spi_card *card, - int suppress_host_int, - int auto_int) -{ - int err = 0; - - /* - * We can suppress a host interrupt by clearing the appropriate - * bit in the "host interrupt status mask" register - */ - if (suppress_host_int) { - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); - if (err) - return err; - } else { - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, - IF_SPI_HISM_TX_DOWNLOAD_RDY | - IF_SPI_HISM_RX_UPLOAD_RDY | - IF_SPI_HISM_CMD_DOWNLOAD_RDY | - IF_SPI_HISM_CARDEVENT | - IF_SPI_HISM_CMD_UPLOAD_RDY); - if (err) - return err; - } - - /* - * If auto-interrupts are on, the completion of certain transactions - * will trigger an interrupt automatically. If auto-interrupts - * are off, we need to set the "Card Interrupt Cause" register to - * trigger a card interrupt. - */ - if (auto_int) { - err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG, - IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO | - IF_SPI_HICT_RX_UPLOAD_OVER_AUTO | - IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO | - IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO); - if (err) - return err; - } else { - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); - if (err) - return err; - } - return err; -} - -static int spu_get_chip_revision(struct if_spi_card *card, - u16 *card_id, u8 *card_rev) -{ - int err = 0; - u32 dev_ctrl; - err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl); - if (err) - return err; - *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl); - *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl); - return err; -} - -static int spu_set_bus_mode(struct if_spi_card *card, u16 mode) -{ - int err = 0; - u16 rval; - /* set bus mode */ - err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode); - if (err) - return err; - /* Check that we were able to read back what we just wrote. */ - err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval); - if (err) - return err; - if ((rval & 0xF) != mode) { - pr_err("Can't read bus mode register\n"); - return -EIO; - } - return 0; -} - -static int spu_init(struct if_spi_card *card, int use_dummy_writes) -{ - int err = 0; - u32 delay; - - /* - * We have to start up in timed delay mode so that we can safely - * read the Delay Read Register. - */ - card->use_dummy_writes = 0; - err = spu_set_bus_mode(card, - IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | - IF_SPI_BUS_MODE_DELAY_METHOD_TIMED | - IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); - if (err) - return err; - card->spu_port_delay = 1000; - card->spu_reg_delay = 1000; - err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay); - if (err) - return err; - card->spu_port_delay = delay & 0x0000ffff; - card->spu_reg_delay = (delay & 0xffff0000) >> 16; - - /* If dummy clock delay mode has been requested, switch to it now */ - if (use_dummy_writes) { - card->use_dummy_writes = 1; - err = spu_set_bus_mode(card, - IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | - IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK | - IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); - if (err) - return err; - } - - lbs_deb_spi("Initialized SPU unit. " - "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n", - card->spu_port_delay, card->spu_reg_delay); - return err; -} - -/* - * Firmware Loading - */ - -static int if_spi_prog_helper_firmware(struct if_spi_card *card, - const struct firmware *firmware) -{ - int err = 0; - int bytes_remaining; - const u8 *fw; - u8 temp[HELPER_FW_LOAD_CHUNK_SZ]; - - lbs_deb_enter(LBS_DEB_SPI); - - err = spu_set_interrupt_mode(card, 1, 0); - if (err) - goto out; - - bytes_remaining = firmware->size; - fw = firmware->data; - - /* Load helper firmware image */ - while (bytes_remaining > 0) { - /* - * Scratch pad 1 should contain the number of bytes we - * want to download to the firmware - */ - err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, - HELPER_FW_LOAD_CHUNK_SZ); - if (err) - goto out; - - err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, - IF_SPI_HIST_CMD_DOWNLOAD_RDY, - IF_SPI_HIST_CMD_DOWNLOAD_RDY); - if (err) - goto out; - - /* - * Feed the data into the command read/write port reg - * in chunks of 64 bytes - */ - memset(temp, 0, sizeof(temp)); - memcpy(temp, fw, - min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ)); - mdelay(10); - err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, - temp, HELPER_FW_LOAD_CHUNK_SZ); - if (err) - goto out; - - /* Interrupt the boot code */ - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); - if (err) - goto out; - err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, - IF_SPI_CIC_CMD_DOWNLOAD_OVER); - if (err) - goto out; - bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ; - fw += HELPER_FW_LOAD_CHUNK_SZ; - } - - /* - * Once the helper / single stage firmware download is complete, - * write 0 to scratch pad 1 and interrupt the - * bootloader. This completes the helper download. - */ - err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK); - if (err) - goto out; - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); - if (err) - goto out; - err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, - IF_SPI_CIC_CMD_DOWNLOAD_OVER); -out: - if (err) - pr_err("failed to load helper firmware (err=%d)\n", err); - lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); - return err; -} - -/* - * Returns the length of the next packet the firmware expects us to send. - * Sets crc_err if the previous transfer had a CRC error. - */ -static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card, - int *crc_err) -{ - u16 len; - int err = 0; - - /* - * wait until the host interrupt status register indicates - * that we are ready to download - */ - err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, - IF_SPI_HIST_CMD_DOWNLOAD_RDY, - IF_SPI_HIST_CMD_DOWNLOAD_RDY); - if (err) { - pr_err("timed out waiting for host_int_status\n"); - return err; - } - - /* Ask the device how many bytes of firmware it wants. */ - err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); - if (err) - return err; - - if (len > IF_SPI_CMD_BUF_SIZE) { - pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n", - len); - return -EIO; - } - if (len & 0x1) { - lbs_deb_spi("%s: crc error\n", __func__); - len &= ~0x1; - *crc_err = 1; - } else - *crc_err = 0; - - return len; -} - -static int if_spi_prog_main_firmware(struct if_spi_card *card, - const struct firmware *firmware) -{ - struct lbs_private *priv = card->priv; - int len, prev_len; - int bytes, crc_err = 0, err = 0; - const u8 *fw; - u16 num_crc_errs; - - lbs_deb_enter(LBS_DEB_SPI); - - err = spu_set_interrupt_mode(card, 1, 0); - if (err) - goto out; - - err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0); - if (err) { - netdev_err(priv->dev, - "%s: timed out waiting for initial scratch reg = 0\n", - __func__); - goto out; - } - - num_crc_errs = 0; - prev_len = 0; - bytes = firmware->size; - fw = firmware->data; - while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) { - if (len < 0) { - err = len; - goto out; - } - if (bytes < 0) { - /* - * If there are no more bytes left, we would normally - * expect to have terminated with len = 0 - */ - netdev_err(priv->dev, - "Firmware load wants more bytes than we have to offer.\n"); - break; - } - if (crc_err) { - /* Previous transfer failed. */ - if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) { - pr_err("Too many CRC errors encountered in firmware load.\n"); - err = -EIO; - goto out; - } - } else { - /* Previous transfer succeeded. Advance counters. */ - bytes -= prev_len; - fw += prev_len; - } - if (bytes < len) { - memset(card->cmd_buffer, 0, len); - memcpy(card->cmd_buffer, fw, bytes); - } else - memcpy(card->cmd_buffer, fw, len); - - err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); - if (err) - goto out; - err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, - card->cmd_buffer, len); - if (err) - goto out; - err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG , - IF_SPI_CIC_CMD_DOWNLOAD_OVER); - if (err) - goto out; - prev_len = len; - } - if (bytes > prev_len) { - pr_err("firmware load wants fewer bytes than we have to offer\n"); - } - - /* Confirm firmware download */ - err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG, - SUCCESSFUL_FW_DOWNLOAD_MAGIC); - if (err) { - pr_err("failed to confirm the firmware download\n"); - goto out; - } - -out: - if (err) - pr_err("failed to load firmware (err=%d)\n", err); - lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); - return err; -} - -/* - * SPI Transfer Thread - * - * The SPI worker handles all SPI transfers, so there is no need for a lock. - */ - -/* Move a command from the card to the host */ -static int if_spi_c2h_cmd(struct if_spi_card *card) -{ - struct lbs_private *priv = card->priv; - unsigned long flags; - int err = 0; - u16 len; - u8 i; - - /* - * We need a buffer big enough to handle whatever people send to - * hw_host_to_card - */ - BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE); - BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE); - - /* - * It's just annoying if the buffer size isn't a multiple of 4, because - * then we might have len < IF_SPI_CMD_BUF_SIZE but - * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE - */ - BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0); - - lbs_deb_enter(LBS_DEB_SPI); - - /* How many bytes are there to read? */ - err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len); - if (err) - goto out; - if (!len) { - netdev_err(priv->dev, "%s: error: card has no data for host\n", - __func__); - err = -EINVAL; - goto out; - } else if (len > IF_SPI_CMD_BUF_SIZE) { - netdev_err(priv->dev, - "%s: error: response packet too large: %d bytes, but maximum is %d\n", - __func__, len, IF_SPI_CMD_BUF_SIZE); - err = -EINVAL; - goto out; - } - - /* Read the data from the WLAN module into our command buffer */ - err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG, - card->cmd_buffer, ALIGN(len, 4)); - if (err) - goto out; - - spin_lock_irqsave(&priv->driver_lock, flags); - i = (priv->resp_idx == 0) ? 1 : 0; - BUG_ON(priv->resp_len[i]); - priv->resp_len[i] = len; - memcpy(priv->resp_buf[i], card->cmd_buffer, len); - lbs_notify_command_response(priv, i); - spin_unlock_irqrestore(&priv->driver_lock, flags); - -out: - if (err) - netdev_err(priv->dev, "%s: err=%d\n", __func__, err); - lbs_deb_leave(LBS_DEB_SPI); - return err; -} - -/* Move data from the card to the host */ -static int if_spi_c2h_data(struct if_spi_card *card) -{ - struct lbs_private *priv = card->priv; - struct sk_buff *skb; - char *data; - u16 len; - int err = 0; - - lbs_deb_enter(LBS_DEB_SPI); - - /* How many bytes are there to read? */ - err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); - if (err) - goto out; - if (!len) { - netdev_err(priv->dev, "%s: error: card has no data for host\n", - __func__); - err = -EINVAL; - goto out; - } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { - netdev_err(priv->dev, - "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n", - __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); - err = -EINVAL; - goto out; - } - - /* TODO: should we allocate a smaller skb if we have less data? */ - skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); - if (!skb) { - err = -ENOBUFS; - goto out; - } - skb_reserve(skb, IPFIELD_ALIGN_OFFSET); - data = skb_put(skb, len); - - /* Read the data from the WLAN module into our skb... */ - err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 4)); - if (err) - goto free_skb; - - /* pass the SKB to libertas */ - err = lbs_process_rxed_packet(card->priv, skb); - if (err) - goto free_skb; - - /* success */ - goto out; - -free_skb: - dev_kfree_skb(skb); -out: - if (err) - netdev_err(priv->dev, "%s: err=%d\n", __func__, err); - lbs_deb_leave(LBS_DEB_SPI); - return err; -} - -/* Move data or a command from the host to the card. */ -static void if_spi_h2c(struct if_spi_card *card, - struct if_spi_packet *packet, int type) -{ - struct lbs_private *priv = card->priv; - int err = 0; - u16 int_type, port_reg; - - switch (type) { - case MVMS_DAT: - int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER; - port_reg = IF_SPI_DATA_RDWRPORT_REG; - break; - case MVMS_CMD: - int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER; - port_reg = IF_SPI_CMD_RDWRPORT_REG; - break; - default: - netdev_err(priv->dev, "can't transfer buffer of type %d\n", - type); - err = -EINVAL; - goto out; - } - - /* Write the data to the card */ - err = spu_write(card, port_reg, packet->buffer, packet->blen); - if (err) - goto out; - -out: - kfree(packet); - - if (err) - netdev_err(priv->dev, "%s: error %d\n", __func__, err); -} - -/* Inform the host about a card event */ -static void if_spi_e2h(struct if_spi_card *card) -{ - int err = 0; - u32 cause; - struct lbs_private *priv = card->priv; - - err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause); - if (err) - goto out; - - /* re-enable the card event interrupt */ - spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, - ~IF_SPI_HICU_CARD_EVENT); - - /* generate a card interrupt */ - spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT); - - lbs_queue_event(priv, cause & 0xff); -out: - if (err) - netdev_err(priv->dev, "%s: error %d\n", __func__, err); -} - -static void if_spi_host_to_card_worker(struct work_struct *work) -{ - int err; - struct if_spi_card *card; - u16 hiStatus; - unsigned long flags; - struct if_spi_packet *packet; - struct lbs_private *priv; - - card = container_of(work, struct if_spi_card, packet_work); - priv = card->priv; - - lbs_deb_enter(LBS_DEB_SPI); - - /* - * Read the host interrupt status register to see what we - * can do. - */ - err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, - &hiStatus); - if (err) { - netdev_err(priv->dev, "I/O error\n"); - goto err; - } - - if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) { - err = if_spi_c2h_cmd(card); - if (err) - goto err; - } - if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) { - err = if_spi_c2h_data(card); - if (err) - goto err; - } - - /* - * workaround: in PS mode, the card does not set the Command - * Download Ready bit, but it sets TX Download Ready. - */ - if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY || - (card->priv->psstate != PS_STATE_FULL_POWER && - (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { - /* - * This means two things. First of all, - * if there was a previous command sent, the card has - * successfully received it. - * Secondly, it is now ready to download another - * command. - */ - lbs_host_to_card_done(card->priv); - - /* Do we have any command packets from the host to send? */ - packet = NULL; - spin_lock_irqsave(&card->buffer_lock, flags); - if (!list_empty(&card->cmd_packet_list)) { - packet = (struct if_spi_packet *)(card-> - cmd_packet_list.next); - list_del(&packet->list); - } - spin_unlock_irqrestore(&card->buffer_lock, flags); - - if (packet) - if_spi_h2c(card, packet, MVMS_CMD); - } - if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) { - /* Do we have any data packets from the host to send? */ - packet = NULL; - spin_lock_irqsave(&card->buffer_lock, flags); - if (!list_empty(&card->data_packet_list)) { - packet = (struct if_spi_packet *)(card-> - data_packet_list.next); - list_del(&packet->list); - } - spin_unlock_irqrestore(&card->buffer_lock, flags); - - if (packet) - if_spi_h2c(card, packet, MVMS_DAT); - } - if (hiStatus & IF_SPI_HIST_CARD_EVENT) - if_spi_e2h(card); - -err: - if (err) - netdev_err(priv->dev, "%s: got error %d\n", __func__, err); - - lbs_deb_leave(LBS_DEB_SPI); -} - -/* - * Host to Card - * - * Called from Libertas to transfer some data to the WLAN device - * We can't sleep here. - */ -static int if_spi_host_to_card(struct lbs_private *priv, - u8 type, u8 *buf, u16 nb) -{ - int err = 0; - unsigned long flags; - struct if_spi_card *card = priv->card; - struct if_spi_packet *packet; - u16 blen; - - lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb); - - if (nb == 0) { - netdev_err(priv->dev, "%s: invalid size requested: %d\n", - __func__, nb); - err = -EINVAL; - goto out; - } - blen = ALIGN(nb, 4); - packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC); - if (!packet) { - err = -ENOMEM; - goto out; - } - packet->blen = blen; - memcpy(packet->buffer, buf, nb); - memset(packet->buffer + nb, 0, blen - nb); - - switch (type) { - case MVMS_CMD: - priv->dnld_sent = DNLD_CMD_SENT; - spin_lock_irqsave(&card->buffer_lock, flags); - list_add_tail(&packet->list, &card->cmd_packet_list); - spin_unlock_irqrestore(&card->buffer_lock, flags); - break; - case MVMS_DAT: - priv->dnld_sent = DNLD_DATA_SENT; - spin_lock_irqsave(&card->buffer_lock, flags); - list_add_tail(&packet->list, &card->data_packet_list); - spin_unlock_irqrestore(&card->buffer_lock, flags); - break; - default: - kfree(packet); - netdev_err(priv->dev, "can't transfer buffer of type %d\n", - type); - err = -EINVAL; - break; - } - - /* Queue spi xfer work */ - queue_work(card->workqueue, &card->packet_work); -out: - lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err); - return err; -} - -/* - * Host Interrupts - * - * Service incoming interrupts from the WLAN device. We can't sleep here, so - * don't try to talk on the SPI bus, just queue the SPI xfer work. - */ -static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id) -{ - struct if_spi_card *card = dev_id; - - queue_work(card->workqueue, &card->packet_work); - - return IRQ_HANDLED; -} - -/* - * SPI callbacks - */ - -static int if_spi_init_card(struct if_spi_card *card) -{ - struct lbs_private *priv = card->priv; - int err, i; - u32 scratch; - const struct firmware *helper = NULL; - const struct firmware *mainfw = NULL; - - lbs_deb_enter(LBS_DEB_SPI); - - err = spu_init(card, card->pdata->use_dummy_writes); - if (err) - goto out; - err = spu_get_chip_revision(card, &card->card_id, &card->card_rev); - if (err) - goto out; - - err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch); - if (err) - goto out; - if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC) - lbs_deb_spi("Firmware is already loaded for " - "Marvell WLAN 802.11 adapter\n"); - else { - /* Check if we support this card */ - for (i = 0; i < ARRAY_SIZE(fw_table); i++) { - if (card->card_id == fw_table[i].model) - break; - } - if (i == ARRAY_SIZE(fw_table)) { - netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n", - card->card_id); - err = -ENODEV; - goto out; - } - - err = lbs_get_firmware(&card->spi->dev, card->card_id, - &fw_table[0], &helper, &mainfw); - if (err) { - netdev_err(priv->dev, "failed to find firmware (%d)\n", - err); - goto out; - } - - lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter " - "(chip_id = 0x%04x, chip_rev = 0x%02x) " - "attached to SPI bus_num %d, chip_select %d. " - "spi->max_speed_hz=%d\n", - card->card_id, card->card_rev, - card->spi->master->bus_num, - card->spi->chip_select, - card->spi->max_speed_hz); - err = if_spi_prog_helper_firmware(card, helper); - if (err) - goto out; - err = if_spi_prog_main_firmware(card, mainfw); - if (err) - goto out; - lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n"); - } - - err = spu_set_interrupt_mode(card, 0, 1); - if (err) - goto out; - -out: - lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); - return err; -} - -static void if_spi_resume_worker(struct work_struct *work) -{ - struct if_spi_card *card; - - card = container_of(work, struct if_spi_card, resume_work); - - if (card->suspended) { - if (card->pdata->setup) - card->pdata->setup(card->spi); - - /* Init card ... */ - if_spi_init_card(card); - - enable_irq(card->spi->irq); - - /* And resume it ... */ - lbs_resume(card->priv); - - card->suspended = 0; - } -} - -static int if_spi_probe(struct spi_device *spi) -{ - struct if_spi_card *card; - struct lbs_private *priv = NULL; - struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev); - int err = 0; - - lbs_deb_enter(LBS_DEB_SPI); - - if (!pdata) { - err = -EINVAL; - goto out; - } - - if (pdata->setup) { - err = pdata->setup(spi); - if (err) - goto out; - } - - /* Allocate card structure to represent this specific device */ - card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL); - if (!card) { - err = -ENOMEM; - goto teardown; - } - spi_set_drvdata(spi, card); - card->pdata = pdata; - card->spi = spi; - card->prev_xfer_time = jiffies; - - INIT_LIST_HEAD(&card->cmd_packet_list); - INIT_LIST_HEAD(&card->data_packet_list); - spin_lock_init(&card->buffer_lock); - - /* Initialize the SPI Interface Unit */ - - /* Firmware load */ - err = if_spi_init_card(card); - if (err) - goto free_card; - - /* - * Register our card with libertas. - * This will call alloc_etherdev. - */ - priv = lbs_add_card(card, &spi->dev); - if (!priv) { - err = -ENOMEM; - goto free_card; - } - card->priv = priv; - priv->setup_fw_on_resume = 1; - priv->card = card; - priv->hw_host_to_card = if_spi_host_to_card; - priv->enter_deep_sleep = NULL; - priv->exit_deep_sleep = NULL; - priv->reset_deep_sleep_wakeup = NULL; - priv->fw_ready = 1; - - /* Initialize interrupt handling stuff. */ - card->workqueue = create_workqueue("libertas_spi"); - INIT_WORK(&card->packet_work, if_spi_host_to_card_worker); - INIT_WORK(&card->resume_work, if_spi_resume_worker); - - err = request_irq(spi->irq, if_spi_host_interrupt, - IRQF_TRIGGER_FALLING, "libertas_spi", card); - if (err) { - pr_err("can't get host irq line-- request_irq failed\n"); - goto terminate_workqueue; - } - - /* - * Start the card. - * This will call register_netdev, and we'll start - * getting interrupts... - */ - err = lbs_start_card(priv); - if (err) - goto release_irq; - - lbs_deb_spi("Finished initializing WLAN module.\n"); - - /* successful exit */ - goto out; - -release_irq: - free_irq(spi->irq, card); -terminate_workqueue: - flush_workqueue(card->workqueue); - destroy_workqueue(card->workqueue); - lbs_remove_card(priv); /* will call free_netdev */ -free_card: - free_if_spi_card(card); -teardown: - if (pdata->teardown) - pdata->teardown(spi); -out: - lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); - return err; -} - -static int libertas_spi_remove(struct spi_device *spi) -{ - struct if_spi_card *card = spi_get_drvdata(spi); - struct lbs_private *priv = card->priv; - - lbs_deb_spi("libertas_spi_remove\n"); - lbs_deb_enter(LBS_DEB_SPI); - - cancel_work_sync(&card->resume_work); - - lbs_stop_card(priv); - lbs_remove_card(priv); /* will call free_netdev */ - - free_irq(spi->irq, card); - flush_workqueue(card->workqueue); - destroy_workqueue(card->workqueue); - if (card->pdata->teardown) - card->pdata->teardown(spi); - free_if_spi_card(card); - lbs_deb_leave(LBS_DEB_SPI); - return 0; -} - -static int if_spi_suspend(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - struct if_spi_card *card = spi_get_drvdata(spi); - - if (!card->suspended) { - lbs_suspend(card->priv); - flush_workqueue(card->workqueue); - disable_irq(spi->irq); - - if (card->pdata->teardown) - card->pdata->teardown(spi); - card->suspended = 1; - } - - return 0; -} - -static int if_spi_resume(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - struct if_spi_card *card = spi_get_drvdata(spi); - - /* Schedule delayed work */ - schedule_work(&card->resume_work); - - return 0; -} - -static const struct dev_pm_ops if_spi_pm_ops = { - .suspend = if_spi_suspend, - .resume = if_spi_resume, -}; - -static struct spi_driver libertas_spi_driver = { - .probe = if_spi_probe, - .remove = libertas_spi_remove, - .driver = { - .name = "libertas_spi", - .pm = &if_spi_pm_ops, - }, -}; - -/* - * Module functions - */ - -static int __init if_spi_init_module(void) -{ - int ret = 0; - lbs_deb_enter(LBS_DEB_SPI); - printk(KERN_INFO "libertas_spi: Libertas SPI driver\n"); - ret = spi_register_driver(&libertas_spi_driver); - lbs_deb_leave(LBS_DEB_SPI); - return ret; -} - -static void __exit if_spi_exit_module(void) -{ - lbs_deb_enter(LBS_DEB_SPI); - spi_unregister_driver(&libertas_spi_driver); - lbs_deb_leave(LBS_DEB_SPI); -} - -module_init(if_spi_init_module); -module_exit(if_spi_exit_module); - -MODULE_DESCRIPTION("Libertas SPI WLAN Driver"); -MODULE_AUTHOR("Andrey Yurovsky , " - "Colin McCabe "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:libertas_spi"); diff --git a/drivers/net/wireless/libertas/if_spi.h b/drivers/net/wireless/libertas/if_spi.h deleted file mode 100644 index e450e31fd11d..000000000000 --- a/drivers/net/wireless/libertas/if_spi.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * linux/drivers/net/wireless/libertas/if_spi.c - * - * Driver for Marvell SPI WLAN cards. - * - * Copyright 2008 Analog Devices Inc. - * - * Authors: - * Andrey Yurovsky - * Colin McCabe - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ - -#ifndef _LBS_IF_SPI_H_ -#define _LBS_IF_SPI_H_ - -#define IPFIELD_ALIGN_OFFSET 2 -#define IF_SPI_CMD_BUF_SIZE 2400 - -/***************** Firmware *****************/ - -#define IF_SPI_FW_NAME_MAX 30 - -#define MAX_MAIN_FW_LOAD_CRC_ERR 10 - -/* Chunk size when loading the helper firmware */ -#define HELPER_FW_LOAD_CHUNK_SZ 64 - -/* Value to write to indicate end of helper firmware dnld */ -#define FIRMWARE_DNLD_OK 0x0000 - -/* Value to check once the main firmware is downloaded */ -#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888 - -/***************** SPI Interface Unit *****************/ -/* Masks used in SPI register read/write operations */ -#define IF_SPI_READ_OPERATION_MASK 0x0 -#define IF_SPI_WRITE_OPERATION_MASK 0x8000 - -/* SPI register offsets. 4-byte aligned. */ -#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */ -#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */ -#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */ -#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */ - -#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */ -#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */ -#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */ - -#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */ -#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */ -#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */ - -#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */ -#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */ -#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */ -#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */ - -#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */ -#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */ - -#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */ - -#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */ -#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interrupt status reg */ -#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */ -#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */ - -#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */ - -#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */ -#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */ -#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */ -#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */ -#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */ - -#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */ -#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */ - -/***************** IF_SPI_DEVICEID_CTRL_REG *****************/ -#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16) -#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff) - -/***************** IF_SPI_HOST_INT_CTRL_REG *****************/ -/* Host Interrupt Control bit : Wake up */ -#define IF_SPI_HICT_WAKE_UP (1<<0) -/* Host Interrupt Control bit : WLAN ready */ -#define IF_SPI_HICT_WLAN_READY (1<<1) -/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */ -/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */ -/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */ -/* Host Interrupt Control bit : Tx auto download */ -#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5) -/* Host Interrupt Control bit : Rx auto upload */ -#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6) -/* Host Interrupt Control bit : Command auto download */ -#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7) -/* Host Interrupt Control bit : Command auto upload */ -#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8) - -/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/ -/* Card Interrupt Case bit : Tx download over */ -#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0) -/* Card Interrupt Case bit : Rx upload over */ -#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1) -/* Card Interrupt Case bit : Command download over */ -#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2) -/* Card Interrupt Case bit : Host event */ -#define IF_SPI_CIC_HOST_EVENT (1<<3) -/* Card Interrupt Case bit : Command upload over */ -#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4) -/* Card Interrupt Case bit : Power down */ -#define IF_SPI_CIC_POWER_DOWN (1<<5) - -/***************** IF_SPI_CARD_INT_STATUS_REG *****************/ -#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0) -#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1) -#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2) -#define IF_SPI_CIS_HOST_EVENT (1<<3) -#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4) -#define IF_SPI_CIS_POWER_DOWN (1<<5) - -/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/ -#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0) -#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1) -#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2) -#define IF_SPI_HICU_CARD_EVENT (1<<3) -#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4) -#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5) -#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6) -#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7) -#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8) -#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9) -#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10) - -/***************** IF_SPI_HOST_INT_STATUS_REG *****************/ -/* Host Interrupt Status bit : Tx download ready */ -#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0) -/* Host Interrupt Status bit : Rx upload ready */ -#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1) -/* Host Interrupt Status bit : Command download ready */ -#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2) -/* Host Interrupt Status bit : Card event */ -#define IF_SPI_HIST_CARD_EVENT (1<<3) -/* Host Interrupt Status bit : Command upload ready */ -#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4) -/* Host Interrupt Status bit : I/O write FIFO overflow */ -#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5) -/* Host Interrupt Status bit : I/O read FIFO underflow */ -#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6) -/* Host Interrupt Status bit : Data write FIFO overflow */ -#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7) -/* Host Interrupt Status bit : Data read FIFO underflow */ -#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8) -/* Host Interrupt Status bit : Command write FIFO overflow */ -#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9) -/* Host Interrupt Status bit : Command read FIFO underflow */ -#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10) - -/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/ -/* Host Interrupt Status Mask bit : Tx download ready */ -#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0) -/* Host Interrupt Status Mask bit : Rx upload ready */ -#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1) -/* Host Interrupt Status Mask bit : Command download ready */ -#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2) -/* Host Interrupt Status Mask bit : Card event */ -#define IF_SPI_HISM_CARDEVENT (1<<3) -/* Host Interrupt Status Mask bit : Command upload ready */ -#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4) -/* Host Interrupt Status Mask bit : I/O write FIFO overflow */ -#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5) -/* Host Interrupt Status Mask bit : I/O read FIFO underflow */ -#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6) -/* Host Interrupt Status Mask bit : Data write FIFO overflow */ -#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7) -/* Host Interrupt Status Mask bit : Data write FIFO underflow */ -#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8) -/* Host Interrupt Status Mask bit : Command write FIFO overflow */ -#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9) -/* Host Interrupt Status Mask bit : Command write FIFO underflow */ -#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10) - -/***************** IF_SPI_SPU_BUS_MODE_REG *****************/ -/* SCK edge on which the WLAN module outputs data on MISO */ -#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8 -#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0 - -/* In a SPU read operation, there is a delay between writing the SPU - * register name and getting back data from the WLAN module. - * This can be specified in terms of nanoseconds or in terms of dummy - * clock cycles which the master must output before receiving a response. */ -#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4 -#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0 - -/* Some different modes of SPI operation */ -#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00 -#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01 -#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02 -#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03 - -#endif diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c deleted file mode 100644 index dff08a2896a3..000000000000 --- a/drivers/net/wireless/libertas/if_usb.c +++ /dev/null @@ -1,1018 +0,0 @@ -/* - * This file contains functions used in USB interface module. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_OLPC -#include -#endif - -#define DRV_NAME "usb8xxx" - -#include "host.h" -#include "decl.h" -#include "defs.h" -#include "dev.h" -#include "cmd.h" -#include "if_usb.h" - -#define INSANEDEBUG 0 -#define lbs_deb_usb2(...) do { if (INSANEDEBUG) lbs_deb_usbd(__VA_ARGS__); } while (0) - -#define MESSAGE_HEADER_LEN 4 - -MODULE_FIRMWARE("libertas/usb8388_v9.bin"); -MODULE_FIRMWARE("libertas/usb8388_v5.bin"); -MODULE_FIRMWARE("libertas/usb8388.bin"); -MODULE_FIRMWARE("libertas/usb8682.bin"); -MODULE_FIRMWARE("usb8388.bin"); - -enum { - MODEL_UNKNOWN = 0x0, - MODEL_8388 = 0x1, - MODEL_8682 = 0x2 -}; - -/* table of firmware file names */ -static const struct lbs_fw_table fw_table[] = { - { MODEL_8388, "libertas/usb8388_olpc.bin", NULL }, - { MODEL_8388, "libertas/usb8388_v9.bin", NULL }, - { MODEL_8388, "libertas/usb8388_v5.bin", NULL }, - { MODEL_8388, "libertas/usb8388.bin", NULL }, - { MODEL_8388, "usb8388.bin", NULL }, - { MODEL_8682, "libertas/usb8682.bin", NULL } -}; - -static struct usb_device_id if_usb_table[] = { - /* Enter the device signature inside */ - { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 }, - { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 }, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, if_usb_table); - -static void if_usb_receive(struct urb *urb); -static void if_usb_receive_fwload(struct urb *urb); -static void if_usb_prog_firmware(struct lbs_private *priv, int ret, - const struct firmware *fw, - const struct firmware *unused); -static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, - uint8_t *payload, uint16_t nb); -static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, - uint16_t nb); -static void if_usb_free(struct if_usb_card *cardp); -static int if_usb_submit_rx_urb(struct if_usb_card *cardp); -static int if_usb_reset_device(struct if_usb_card *cardp); - -/** - * if_usb_write_bulk_callback - callback function to handle the status - * of the URB - * @urb: pointer to &urb structure - * returns: N/A - */ -static void if_usb_write_bulk_callback(struct urb *urb) -{ - struct if_usb_card *cardp = (struct if_usb_card *) urb->context; - - /* handle the transmission complete validations */ - - if (urb->status == 0) { - struct lbs_private *priv = cardp->priv; - - lbs_deb_usb2(&urb->dev->dev, "URB status is successful\n"); - lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", - urb->actual_length); - - /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not - * passed up to the lbs level. - */ - if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT) - lbs_host_to_card_done(priv); - } else { - /* print the failure status number for debug */ - pr_info("URB in failure status: %d\n", urb->status); - } -} - -/** - * if_usb_free - free tx/rx urb, skb and rx buffer - * @cardp: pointer to &if_usb_card - * returns: N/A - */ -static void if_usb_free(struct if_usb_card *cardp) -{ - lbs_deb_enter(LBS_DEB_USB); - - /* Unlink tx & rx urb */ - usb_kill_urb(cardp->tx_urb); - usb_kill_urb(cardp->rx_urb); - - usb_free_urb(cardp->tx_urb); - cardp->tx_urb = NULL; - - usb_free_urb(cardp->rx_urb); - cardp->rx_urb = NULL; - - kfree(cardp->ep_out_buf); - cardp->ep_out_buf = NULL; - - lbs_deb_leave(LBS_DEB_USB); -} - -static void if_usb_setup_firmware(struct lbs_private *priv) -{ - struct if_usb_card *cardp = priv->card; - struct cmd_ds_set_boot2_ver b2_cmd; - struct cmd_ds_802_11_fw_wake_method wake_method; - - b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); - b2_cmd.action = 0; - b2_cmd.version = cardp->boot2_version; - - if (lbs_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) - lbs_deb_usb("Setting boot2 version failed\n"); - - priv->wol_gpio = 2; /* Wake via GPIO2... */ - priv->wol_gap = 20; /* ... after 20ms */ - lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA, - (struct wol_config *) NULL); - - wake_method.hdr.size = cpu_to_le16(sizeof(wake_method)); - wake_method.action = cpu_to_le16(CMD_ACT_GET); - if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) { - netdev_info(priv->dev, "Firmware does not seem to support PS mode\n"); - priv->fwcapinfo &= ~FW_CAPINFO_PS; - } else { - if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) { - lbs_deb_usb("Firmware seems to support PS with wake-via-command\n"); - } else { - /* The versions which boot up this way don't seem to - work even if we set it to the command interrupt */ - priv->fwcapinfo &= ~FW_CAPINFO_PS; - netdev_info(priv->dev, - "Firmware doesn't wake via command interrupt; disabling PS mode\n"); - } - } -} - -static void if_usb_fw_timeo(unsigned long priv) -{ - struct if_usb_card *cardp = (void *)priv; - - if (cardp->fwdnldover) { - lbs_deb_usb("Download complete, no event. Assuming success\n"); - } else { - pr_err("Download timed out\n"); - cardp->surprise_removed = 1; - } - wake_up(&cardp->fw_wq); -} - -#ifdef CONFIG_OLPC -static void if_usb_reset_olpc_card(struct lbs_private *priv) -{ - printk(KERN_CRIT "Resetting OLPC wireless via EC...\n"); - olpc_ec_cmd(0x25, NULL, 0, NULL, 0); -} -#endif - -/** - * if_usb_probe - sets the configuration values - * @intf: &usb_interface pointer - * @id: pointer to usb_device_id - * returns: 0 on success, error code on failure - */ -static int if_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - struct lbs_private *priv; - struct if_usb_card *cardp; - int r = -ENOMEM; - int i; - - udev = interface_to_usbdev(intf); - - cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); - if (!cardp) - goto error; - - setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); - init_waitqueue_head(&cardp->fw_wq); - - cardp->udev = udev; - cardp->model = (uint32_t) id->driver_info; - iface_desc = intf->cur_altsetting; - - lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" - " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", - le16_to_cpu(udev->descriptor.bcdUSB), - udev->descriptor.bDeviceClass, - udev->descriptor.bDeviceSubClass, - udev->descriptor.bDeviceProtocol); - - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_bulk_in(endpoint)) { - cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); - cardp->ep_in = usb_endpoint_num(endpoint); - - lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); - lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); - - } else if (usb_endpoint_is_bulk_out(endpoint)) { - cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); - cardp->ep_out = usb_endpoint_num(endpoint); - - lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); - lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); - } - } - if (!cardp->ep_out_size || !cardp->ep_in_size) { - lbs_deb_usbd(&udev->dev, "Endpoints not found\n"); - goto dealloc; - } - if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) { - lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); - goto dealloc; - } - if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) { - lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n"); - goto dealloc; - } - cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL); - if (!cardp->ep_out_buf) { - lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n"); - goto dealloc; - } - - if (!(priv = lbs_add_card(cardp, &intf->dev))) - goto err_add_card; - - cardp->priv = priv; - - priv->hw_host_to_card = if_usb_host_to_card; - priv->enter_deep_sleep = NULL; - priv->exit_deep_sleep = NULL; - priv->reset_deep_sleep_wakeup = NULL; -#ifdef CONFIG_OLPC - if (machine_is_olpc()) - priv->reset_card = if_usb_reset_olpc_card; -#endif - - cardp->boot2_version = udev->descriptor.bcdDevice; - - usb_get_dev(udev); - usb_set_intfdata(intf, cardp); - - r = lbs_get_firmware_async(priv, &udev->dev, cardp->model, - fw_table, if_usb_prog_firmware); - if (r) - goto err_get_fw; - - return 0; - -err_get_fw: - lbs_remove_card(priv); -err_add_card: - if_usb_reset_device(cardp); -dealloc: - if_usb_free(cardp); - -error: - return r; -} - -/** - * if_usb_disconnect - free resource and cleanup - * @intf: USB interface structure - * returns: N/A - */ -static void if_usb_disconnect(struct usb_interface *intf) -{ - struct if_usb_card *cardp = usb_get_intfdata(intf); - struct lbs_private *priv = cardp->priv; - - lbs_deb_enter(LBS_DEB_MAIN); - - cardp->surprise_removed = 1; - - if (priv) { - lbs_stop_card(priv); - lbs_remove_card(priv); - } - - /* Unlink and free urb */ - if_usb_free(cardp); - - usb_set_intfdata(intf, NULL); - usb_put_dev(interface_to_usbdev(intf)); - - lbs_deb_leave(LBS_DEB_MAIN); -} - -/** - * if_usb_send_fw_pkt - download FW - * @cardp: pointer to &struct if_usb_card - * returns: 0 - */ -static int if_usb_send_fw_pkt(struct if_usb_card *cardp) -{ - struct fwdata *fwdata = cardp->ep_out_buf; - const uint8_t *firmware = cardp->fw->data; - - /* If we got a CRC failure on the last block, back - up and retry it */ - if (!cardp->CRC_OK) { - cardp->totalbytes = cardp->fwlastblksent; - cardp->fwseqnum--; - } - - lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n", - cardp->totalbytes); - - /* struct fwdata (which we sent to the card) has an - extra __le32 field in between the header and the data, - which is not in the struct fwheader in the actual - firmware binary. Insert the seqnum in the middle... */ - memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], - sizeof(struct fwheader)); - - cardp->fwlastblksent = cardp->totalbytes; - cardp->totalbytes += sizeof(struct fwheader); - - memcpy(fwdata->data, &firmware[cardp->totalbytes], - le32_to_cpu(fwdata->hdr.datalength)); - - lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n", - le32_to_cpu(fwdata->hdr.datalength)); - - fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); - cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); - - usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + - le32_to_cpu(fwdata->hdr.datalength)); - - if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) { - lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n"); - lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n", - cardp->fwseqnum, cardp->totalbytes); - } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { - lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n"); - lbs_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n"); - - cardp->fwfinalblk = 1; - } - - lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n", - cardp->totalbytes); - - return 0; -} - -static int if_usb_reset_device(struct if_usb_card *cardp) -{ - struct cmd_header *cmd = cardp->ep_out_buf + 4; - int ret; - - lbs_deb_enter(LBS_DEB_USB); - - *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); - - cmd->command = cpu_to_le16(CMD_802_11_RESET); - cmd->size = cpu_to_le16(sizeof(cmd)); - cmd->result = cpu_to_le16(0); - cmd->seqnum = cpu_to_le16(0x5a5a); - usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_header)); - - msleep(100); - ret = usb_reset_device(cardp->udev); - msleep(100); - -#ifdef CONFIG_OLPC - if (ret && machine_is_olpc()) - if_usb_reset_olpc_card(NULL); -#endif - - lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret); - - return ret; -} - -/** - * usb_tx_block - transfer the data to the device - * @cardp: pointer to &struct if_usb_card - * @payload: pointer to payload data - * @nb: data length - * returns: 0 for success or negative error code - */ -static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb) -{ - int ret; - - /* check if device is removed */ - if (cardp->surprise_removed) { - lbs_deb_usbd(&cardp->udev->dev, "Device removed\n"); - ret = -ENODEV; - goto tx_ret; - } - - usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, - usb_sndbulkpipe(cardp->udev, - cardp->ep_out), - payload, nb, if_usb_write_bulk_callback, cardp); - - cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; - - if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { - lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret); - } else { - lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n"); - ret = 0; - } - -tx_ret: - return ret; -} - -static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, - void (*callbackfn)(struct urb *urb)) -{ - struct sk_buff *skb; - int ret = -1; - - if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { - pr_err("No free skb\n"); - goto rx_ret; - } - - cardp->rx_skb = skb; - - /* Fill the receive configuration URB and initialise the Rx call back */ - usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, - usb_rcvbulkpipe(cardp->udev, cardp->ep_in), - skb->data + IPFIELD_ALIGN_OFFSET, - MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, - cardp); - - cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; - - lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); - if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { - lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret); - kfree_skb(skb); - cardp->rx_skb = NULL; - ret = -1; - } else { - lbs_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n"); - ret = 0; - } - -rx_ret: - return ret; -} - -static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) -{ - return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); -} - -static int if_usb_submit_rx_urb(struct if_usb_card *cardp) -{ - return __if_usb_submit_rx_urb(cardp, &if_usb_receive); -} - -static void if_usb_receive_fwload(struct urb *urb) -{ - struct if_usb_card *cardp = urb->context; - struct sk_buff *skb = cardp->rx_skb; - struct fwsyncheader *syncfwheader; - struct bootcmdresp bootcmdresp; - - if (urb->status) { - lbs_deb_usbd(&cardp->udev->dev, - "URB status is failed during fw load\n"); - kfree_skb(skb); - return; - } - - if (cardp->fwdnldover) { - __le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); - - if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && - tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) { - pr_info("Firmware ready event received\n"); - wake_up(&cardp->fw_wq); - } else { - lbs_deb_usb("Waiting for confirmation; got %x %x\n", - le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1])); - if_usb_submit_rx_urb_fwload(cardp); - } - kfree_skb(skb); - return; - } - if (cardp->bootcmdresp <= 0) { - memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, - sizeof(bootcmdresp)); - - if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { - kfree_skb(skb); - if_usb_submit_rx_urb_fwload(cardp); - cardp->bootcmdresp = BOOT_CMD_RESP_OK; - lbs_deb_usbd(&cardp->udev->dev, - "Received valid boot command response\n"); - return; - } - if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { - if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || - bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || - bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) { - if (!cardp->bootcmdresp) - pr_info("Firmware already seems alive; resetting\n"); - cardp->bootcmdresp = -1; - } else { - pr_info("boot cmd response wrong magic number (0x%x)\n", - le32_to_cpu(bootcmdresp.magic)); - } - } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) && - (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) && - (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) { - pr_info("boot cmd response cmd_tag error (%d)\n", - bootcmdresp.cmd); - } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { - pr_info("boot cmd response result error (%d)\n", - bootcmdresp.result); - } else { - cardp->bootcmdresp = 1; - lbs_deb_usbd(&cardp->udev->dev, - "Received valid boot command response\n"); - } - kfree_skb(skb); - if_usb_submit_rx_urb_fwload(cardp); - return; - } - - syncfwheader = kmemdup(skb->data + IPFIELD_ALIGN_OFFSET, - sizeof(struct fwsyncheader), GFP_ATOMIC); - if (!syncfwheader) { - lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n"); - kfree_skb(skb); - return; - } - - if (!syncfwheader->cmd) { - lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n"); - lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n", - le32_to_cpu(syncfwheader->seqnum)); - cardp->CRC_OK = 1; - } else { - lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n"); - cardp->CRC_OK = 0; - } - - kfree_skb(skb); - - /* Give device 5s to either write firmware to its RAM or eeprom */ - mod_timer(&cardp->fw_timeout, jiffies + (HZ*5)); - - if (cardp->fwfinalblk) { - cardp->fwdnldover = 1; - goto exit; - } - - if_usb_send_fw_pkt(cardp); - - exit: - if_usb_submit_rx_urb_fwload(cardp); - - kfree(syncfwheader); -} - -#define MRVDRV_MIN_PKT_LEN 30 - -static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, - struct if_usb_card *cardp, - struct lbs_private *priv) -{ - if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN - || recvlength < MRVDRV_MIN_PKT_LEN) { - lbs_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n"); - kfree_skb(skb); - return; - } - - skb_reserve(skb, IPFIELD_ALIGN_OFFSET); - skb_put(skb, recvlength); - skb_pull(skb, MESSAGE_HEADER_LEN); - - lbs_process_rxed_packet(priv, skb); -} - -static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, - struct sk_buff *skb, - struct if_usb_card *cardp, - struct lbs_private *priv) -{ - u8 i; - - if (recvlength > LBS_CMD_BUFFER_SIZE) { - lbs_deb_usbd(&cardp->udev->dev, - "The receive buffer is too large\n"); - kfree_skb(skb); - return; - } - - BUG_ON(!in_interrupt()); - - spin_lock(&priv->driver_lock); - - i = (priv->resp_idx == 0) ? 1 : 0; - BUG_ON(priv->resp_len[i]); - priv->resp_len[i] = (recvlength - MESSAGE_HEADER_LEN); - memcpy(priv->resp_buf[i], recvbuff + MESSAGE_HEADER_LEN, - priv->resp_len[i]); - kfree_skb(skb); - lbs_notify_command_response(priv, i); - - spin_unlock(&priv->driver_lock); - - lbs_deb_usbd(&cardp->udev->dev, - "Wake up main thread to handle cmd response\n"); -} - -/** - * if_usb_receive - read the packet into the upload buffer, - * wake up the main thread and initialise the Rx callack - * - * @urb: pointer to &struct urb - * returns: N/A - */ -static void if_usb_receive(struct urb *urb) -{ - struct if_usb_card *cardp = urb->context; - struct sk_buff *skb = cardp->rx_skb; - struct lbs_private *priv = cardp->priv; - int recvlength = urb->actual_length; - uint8_t *recvbuff = NULL; - uint32_t recvtype = 0; - __le32 *pkt = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); - uint32_t event; - - lbs_deb_enter(LBS_DEB_USB); - - if (recvlength) { - if (urb->status) { - lbs_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n", - urb->status); - kfree_skb(skb); - goto setup_for_next; - } - - recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; - recvtype = le32_to_cpu(pkt[0]); - lbs_deb_usbd(&cardp->udev->dev, - "Recv length = 0x%x, Recv type = 0x%X\n", - recvlength, recvtype); - } else if (urb->status) { - kfree_skb(skb); - goto rx_exit; - } - - switch (recvtype) { - case CMD_TYPE_DATA: - process_cmdtypedata(recvlength, skb, cardp, priv); - break; - - case CMD_TYPE_REQUEST: - process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); - break; - - case CMD_TYPE_INDICATION: - /* Event handling */ - event = le32_to_cpu(pkt[1]); - lbs_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event); - kfree_skb(skb); - - /* Icky undocumented magic special case */ - if (event & 0xffff0000) { - u32 trycount = (event & 0xffff0000) >> 16; - - lbs_send_tx_feedback(priv, trycount); - } else - lbs_queue_event(priv, event & 0xFF); - break; - - default: - lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n", - recvtype); - kfree_skb(skb); - break; - } - -setup_for_next: - if_usb_submit_rx_urb(cardp); -rx_exit: - lbs_deb_leave(LBS_DEB_USB); -} - -/** - * if_usb_host_to_card - downloads data to FW - * @priv: pointer to &struct lbs_private structure - * @type: type of data - * @payload: pointer to data buffer - * @nb: number of bytes - * returns: 0 for success or negative error code - */ -static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, - uint8_t *payload, uint16_t nb) -{ - struct if_usb_card *cardp = priv->card; - - lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type); - lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb); - - if (type == MVMS_CMD) { - *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); - priv->dnld_sent = DNLD_CMD_SENT; - } else { - *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); - priv->dnld_sent = DNLD_DATA_SENT; - } - - memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); - - return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN); -} - -/** - * if_usb_issue_boot_command - issues Boot command to the Boot2 code - * @cardp: pointer to &if_usb_card - * @ivalue: 1:Boot from FW by USB-Download - * 2:Boot from FW in EEPROM - * returns: 0 for success or negative error code - */ -static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) -{ - struct bootcmd *bootcmd = cardp->ep_out_buf; - - /* Prepare command */ - bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); - bootcmd->cmd = ivalue; - memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); - - /* Issue command */ - usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd)); - - return 0; -} - - -/** - * check_fwfile_format - check the validity of Boot2/FW image - * - * @data: pointer to image - * @totlen: image length - * returns: 0 (good) or 1 (failure) - */ -static int check_fwfile_format(const uint8_t *data, uint32_t totlen) -{ - uint32_t bincmd, exit; - uint32_t blksize, offset, len; - int ret; - - ret = 1; - exit = len = 0; - - do { - struct fwheader *fwh = (void *)data; - - bincmd = le32_to_cpu(fwh->dnldcmd); - blksize = le32_to_cpu(fwh->datalength); - switch (bincmd) { - case FW_HAS_DATA_TO_RECV: - offset = sizeof(struct fwheader) + blksize; - data += offset; - len += offset; - if (len >= totlen) - exit = 1; - break; - case FW_HAS_LAST_BLOCK: - exit = 1; - ret = 0; - break; - default: - exit = 1; - break; - } - } while (!exit); - - if (ret) - pr_err("firmware file format check FAIL\n"); - else - lbs_deb_fw("firmware file format check PASS\n"); - - return ret; -} - -static void if_usb_prog_firmware(struct lbs_private *priv, int ret, - const struct firmware *fw, - const struct firmware *unused) -{ - struct if_usb_card *cardp = priv->card; - int i = 0; - static int reset_count = 10; - - lbs_deb_enter(LBS_DEB_USB); - - if (ret) { - pr_err("failed to find firmware (%d)\n", ret); - goto done; - } - - cardp->fw = fw; - if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { - ret = -EINVAL; - goto done; - } - - /* Cancel any pending usb business */ - usb_kill_urb(cardp->rx_urb); - usb_kill_urb(cardp->tx_urb); - - cardp->fwlastblksent = 0; - cardp->fwdnldover = 0; - cardp->totalbytes = 0; - cardp->fwfinalblk = 0; - cardp->bootcmdresp = 0; - -restart: - if (if_usb_submit_rx_urb_fwload(cardp) < 0) { - lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); - ret = -EIO; - goto done; - } - - cardp->bootcmdresp = 0; - do { - int j = 0; - i++; - if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); - /* wait for command response */ - do { - j++; - msleep_interruptible(100); - } while (cardp->bootcmdresp == 0 && j < 10); - } while (cardp->bootcmdresp == 0 && i < 5); - - if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) { - /* Return to normal operation */ - ret = -EOPNOTSUPP; - usb_kill_urb(cardp->rx_urb); - usb_kill_urb(cardp->tx_urb); - if (if_usb_submit_rx_urb(cardp) < 0) - ret = -EIO; - goto done; - } else if (cardp->bootcmdresp <= 0) { - if (--reset_count >= 0) { - if_usb_reset_device(cardp); - goto restart; - } - ret = -EIO; - goto done; - } - - i = 0; - - cardp->totalbytes = 0; - cardp->fwlastblksent = 0; - cardp->CRC_OK = 1; - cardp->fwdnldover = 0; - cardp->fwseqnum = -1; - cardp->totalbytes = 0; - cardp->fwfinalblk = 0; - - /* Send the first firmware packet... */ - if_usb_send_fw_pkt(cardp); - - /* ... and wait for the process to complete */ - wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover); - - del_timer_sync(&cardp->fw_timeout); - usb_kill_urb(cardp->rx_urb); - - if (!cardp->fwdnldover) { - pr_info("failed to load fw, resetting device!\n"); - if (--reset_count >= 0) { - if_usb_reset_device(cardp); - goto restart; - } - - pr_info("FW download failure, time = %d ms\n", i * 100); - ret = -EIO; - goto done; - } - - cardp->priv->fw_ready = 1; - if_usb_submit_rx_urb(cardp); - - if (lbs_start_card(priv)) - goto done; - - if_usb_setup_firmware(priv); - - /* - * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware. - */ - priv->wol_criteria = EHS_REMOVE_WAKEUP; - if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) - priv->ehs_remove_supported = false; - - done: - cardp->fw = NULL; - lbs_deb_leave(LBS_DEB_USB); -} - - -#ifdef CONFIG_PM -static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct if_usb_card *cardp = usb_get_intfdata(intf); - struct lbs_private *priv = cardp->priv; - int ret; - - lbs_deb_enter(LBS_DEB_USB); - - if (priv->psstate != PS_STATE_FULL_POWER) { - ret = -1; - goto out; - } - -#ifdef CONFIG_OLPC - if (machine_is_olpc()) { - if (priv->wol_criteria == EHS_REMOVE_WAKEUP) - olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN); - else - olpc_ec_wakeup_set(EC_SCI_SRC_WLAN); - } -#endif - - ret = lbs_suspend(priv); - if (ret) - goto out; - - /* Unlink tx & rx urb */ - usb_kill_urb(cardp->tx_urb); - usb_kill_urb(cardp->rx_urb); - - out: - lbs_deb_leave(LBS_DEB_USB); - return ret; -} - -static int if_usb_resume(struct usb_interface *intf) -{ - struct if_usb_card *cardp = usb_get_intfdata(intf); - struct lbs_private *priv = cardp->priv; - - lbs_deb_enter(LBS_DEB_USB); - - if_usb_submit_rx_urb(cardp); - - lbs_resume(priv); - - lbs_deb_leave(LBS_DEB_USB); - return 0; -} -#else -#define if_usb_suspend NULL -#define if_usb_resume NULL -#endif - -static struct usb_driver if_usb_driver = { - .name = DRV_NAME, - .probe = if_usb_probe, - .disconnect = if_usb_disconnect, - .id_table = if_usb_table, - .suspend = if_usb_suspend, - .resume = if_usb_resume, - .reset_resume = if_usb_resume, - .disable_hub_initiated_lpm = 1, -}; - -module_usb_driver(if_usb_driver); - -MODULE_DESCRIPTION("8388 USB WLAN Driver"); -MODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc."); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h deleted file mode 100644 index 6e42eac331de..000000000000 --- a/drivers/net/wireless/libertas/if_usb.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef _LBS_IF_USB_H -#define _LBS_IF_USB_H - -#include -#include - -struct lbs_private; - -/* - * This file contains definition for USB interface. - */ -#define CMD_TYPE_REQUEST 0xF00DFACE -#define CMD_TYPE_DATA 0xBEADC0DE -#define CMD_TYPE_INDICATION 0xBEEFFACE - -#define IPFIELD_ALIGN_OFFSET 2 - -#define BOOT_CMD_FW_BY_USB 0x01 -#define BOOT_CMD_FW_IN_EEPROM 0x02 -#define BOOT_CMD_UPDATE_BOOT2 0x03 -#define BOOT_CMD_UPDATE_FW 0x04 -#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */ - -struct bootcmd -{ - __le32 magic; - uint8_t cmd; - uint8_t pad[11]; -}; - -#define BOOT_CMD_RESP_OK 0x0001 -#define BOOT_CMD_RESP_FAIL 0x0000 -#define BOOT_CMD_RESP_NOT_SUPPORTED 0x0002 - -struct bootcmdresp -{ - __le32 magic; - uint8_t cmd; - uint8_t result; - uint8_t pad[2]; -}; - -/* USB card description structure*/ -struct if_usb_card { - struct usb_device *udev; - uint32_t model; /* MODEL_* */ - struct urb *rx_urb, *tx_urb; - struct lbs_private *priv; - - struct sk_buff *rx_skb; - - uint8_t ep_in; - uint8_t ep_out; - - /* bootcmdresp == 0 means command is pending - * bootcmdresp < 0 means error - * bootcmdresp > 0 is a BOOT_CMD_RESP_* from firmware - */ - int8_t bootcmdresp; - - int ep_in_size; - - void *ep_out_buf; - int ep_out_size; - - const struct firmware *fw; - struct timer_list fw_timeout; - wait_queue_head_t fw_wq; - uint32_t fwseqnum; - uint32_t totalbytes; - uint32_t fwlastblksent; - uint8_t CRC_OK; - uint8_t fwdnldover; - uint8_t fwfinalblk; - uint8_t surprise_removed; - - __le16 boot2_version; -}; - -/* fwheader */ -struct fwheader { - __le32 dnldcmd; - __le32 baseaddr; - __le32 datalength; - __le32 CRC; -}; - -#define FW_MAX_DATA_BLK_SIZE 600 -/* FWData */ -struct fwdata { - struct fwheader hdr; - __le32 seqnum; - uint8_t data[0]; -}; - -/* fwsyncheader */ -struct fwsyncheader { - __le32 cmd; - __le32 seqnum; -}; - -#define FW_HAS_DATA_TO_RECV 0x00000001 -#define FW_HAS_LAST_BLOCK 0x00000004 - - -#endif diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c deleted file mode 100644 index 8079560f4965..000000000000 --- a/drivers/net/wireless/libertas/main.c +++ /dev/null @@ -1,1225 +0,0 @@ -/* - * This file contains the major functions in WLAN - * driver. It includes init, exit, open, close and main - * thread etc.. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "host.h" -#include "decl.h" -#include "dev.h" -#include "cfg.h" -#include "debugfs.h" -#include "cmd.h" -#include "mesh.h" - -#define DRIVER_RELEASE_VERSION "323.p0" -const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION -#ifdef DEBUG - "-dbg" -#endif - ""; - - -/* Module parameters */ -unsigned int lbs_debug; -EXPORT_SYMBOL_GPL(lbs_debug); -module_param_named(libertas_debug, lbs_debug, int, 0644); - -unsigned int lbs_disablemesh; -EXPORT_SYMBOL_GPL(lbs_disablemesh); -module_param_named(libertas_disablemesh, lbs_disablemesh, int, 0644); - - -/* - * This global structure is used to send the confirm_sleep command as - * fast as possible down to the firmware. - */ -struct cmd_confirm_sleep confirm_sleep; - - -/* - * the table to keep region code - */ -u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] = - { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; - -/* - * FW rate table. FW refers to rates by their index in this table, not by the - * rate value itself. Values of 0x00 are - * reserved positions. - */ -static u8 fw_data_rates[MAX_RATES] = - { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, - 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 -}; - -/** - * lbs_fw_index_to_data_rate - use index to get the data rate - * - * @idx: The index of data rate - * returns: data rate or 0 - */ -u32 lbs_fw_index_to_data_rate(u8 idx) -{ - if (idx >= sizeof(fw_data_rates)) - idx = 0; - return fw_data_rates[idx]; -} - -/** - * lbs_data_rate_to_fw_index - use rate to get the index - * - * @rate: data rate - * returns: index or 0 - */ -u8 lbs_data_rate_to_fw_index(u32 rate) -{ - u8 i; - - if (!rate) - return 0; - - for (i = 0; i < sizeof(fw_data_rates); i++) { - if (rate == fw_data_rates[i]) - return i; - } - return 0; -} - -int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type) -{ - int ret = 0; - - switch (type) { - case NL80211_IFTYPE_MONITOR: - ret = lbs_set_monitor_mode(priv, 1); - break; - case NL80211_IFTYPE_STATION: - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) - ret = lbs_set_monitor_mode(priv, 0); - if (!ret) - ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1); - break; - case NL80211_IFTYPE_ADHOC: - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) - ret = lbs_set_monitor_mode(priv, 0); - if (!ret) - ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2); - break; - default: - ret = -ENOTSUPP; - } - return ret; -} - -int lbs_start_iface(struct lbs_private *priv) -{ - struct cmd_ds_802_11_mac_address cmd; - int ret; - - if (priv->power_restore) { - ret = priv->power_restore(priv); - if (ret) - return ret; - } - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - memcpy(cmd.macadd, priv->current_addr, ETH_ALEN); - - ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); - if (ret) { - lbs_deb_net("set MAC address failed\n"); - goto err; - } - - ret = lbs_set_iface_type(priv, priv->wdev->iftype); - if (ret) { - lbs_deb_net("set iface type failed\n"); - goto err; - } - - ret = lbs_set_11d_domain_info(priv); - if (ret) { - lbs_deb_net("set 11d domain info failed\n"); - goto err; - } - - lbs_update_channel(priv); - - priv->iface_running = true; - return 0; - -err: - if (priv->power_save) - priv->power_save(priv); - return ret; -} - -/** - * lbs_dev_open - open the ethX interface - * - * @dev: A pointer to &net_device structure - * returns: 0 or -EBUSY if monitor mode active - */ -static int lbs_dev_open(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - int ret = 0; - - lbs_deb_enter(LBS_DEB_NET); - if (!priv->iface_running) { - ret = lbs_start_iface(priv); - if (ret) - goto out; - } - - spin_lock_irq(&priv->driver_lock); - - netif_carrier_off(dev); - - if (!priv->tx_pending_len) - netif_wake_queue(dev); - - spin_unlock_irq(&priv->driver_lock); - -out: - lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); - return ret; -} - -static bool lbs_command_queue_empty(struct lbs_private *priv) -{ - unsigned long flags; - bool ret; - spin_lock_irqsave(&priv->driver_lock, flags); - ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq); - spin_unlock_irqrestore(&priv->driver_lock, flags); - return ret; -} - -int lbs_stop_iface(struct lbs_private *priv) -{ - unsigned long flags; - int ret = 0; - - lbs_deb_enter(LBS_DEB_MAIN); - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->iface_running = false; - kfree_skb(priv->currenttxskb); - priv->currenttxskb = NULL; - priv->tx_pending_len = 0; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - cancel_work_sync(&priv->mcast_work); - del_timer_sync(&priv->tx_lockup_timer); - - /* Disable command processing, and wait for all commands to complete */ - lbs_deb_main("waiting for commands to complete\n"); - wait_event(priv->waitq, lbs_command_queue_empty(priv)); - lbs_deb_main("all commands completed\n"); - - if (priv->power_save) - ret = priv->power_save(priv); - - lbs_deb_leave(LBS_DEB_MAIN); - return ret; -} - -/** - * lbs_eth_stop - close the ethX interface - * - * @dev: A pointer to &net_device structure - * returns: 0 - */ -static int lbs_eth_stop(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - - lbs_deb_enter(LBS_DEB_NET); - - if (priv->connect_status == LBS_CONNECTED) - lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING); - - spin_lock_irq(&priv->driver_lock); - netif_stop_queue(dev); - spin_unlock_irq(&priv->driver_lock); - - lbs_update_mcast(priv); - cancel_delayed_work_sync(&priv->scan_work); - if (priv->scan_req) - lbs_scan_done(priv); - - netif_carrier_off(priv->dev); - - if (!lbs_iface_active(priv)) - lbs_stop_iface(priv); - - lbs_deb_leave(LBS_DEB_NET); - return 0; -} - -void lbs_host_to_card_done(struct lbs_private *priv) -{ - unsigned long flags; - - lbs_deb_enter(LBS_DEB_THREAD); - - spin_lock_irqsave(&priv->driver_lock, flags); - del_timer(&priv->tx_lockup_timer); - - priv->dnld_sent = DNLD_RES_RECEIVED; - - /* Wake main thread if commands are pending */ - if (!priv->cur_cmd || priv->tx_pending_len > 0) { - if (!priv->wakeup_dev_required) - wake_up(&priv->waitq); - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbs_deb_leave(LBS_DEB_THREAD); -} -EXPORT_SYMBOL_GPL(lbs_host_to_card_done); - -int lbs_set_mac_address(struct net_device *dev, void *addr) -{ - int ret = 0; - struct lbs_private *priv = dev->ml_priv; - struct sockaddr *phwaddr = addr; - - lbs_deb_enter(LBS_DEB_NET); - - /* - * Can only set MAC address when all interfaces are down, to be written - * to the hardware when one of them is brought up. - */ - if (lbs_iface_active(priv)) - return -EBUSY; - - /* In case it was called from the mesh device */ - dev = priv->dev; - - memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); - memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN); - if (priv->mesh_dev) - memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN); - - lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); - return ret; -} - - -static inline int mac_in_list(unsigned char *list, int list_len, - unsigned char *mac) -{ - while (list_len) { - if (!memcmp(list, mac, ETH_ALEN)) - return 1; - list += ETH_ALEN; - list_len--; - } - return 0; -} - - -static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, - struct net_device *dev, int nr_addrs) -{ - int i = nr_addrs; - struct netdev_hw_addr *ha; - int cnt; - - if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST)) - return nr_addrs; - - netif_addr_lock_bh(dev); - cnt = netdev_mc_count(dev); - netdev_for_each_mc_addr(ha, dev) { - if (mac_in_list(cmd->maclist, nr_addrs, ha->addr)) { - lbs_deb_net("mcast address %s:%pM skipped\n", dev->name, - ha->addr); - cnt--; - continue; - } - - if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE) - break; - memcpy(&cmd->maclist[6*i], ha->addr, ETH_ALEN); - lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name, - ha->addr); - i++; - cnt--; - } - netif_addr_unlock_bh(dev); - if (cnt) - return -EOVERFLOW; - - return i; -} - -void lbs_update_mcast(struct lbs_private *priv) -{ - struct cmd_ds_mac_multicast_adr mcast_cmd; - int dev_flags = 0; - int nr_addrs; - int old_mac_control = priv->mac_control; - - lbs_deb_enter(LBS_DEB_NET); - - if (netif_running(priv->dev)) - dev_flags |= priv->dev->flags; - if (priv->mesh_dev && netif_running(priv->mesh_dev)) - dev_flags |= priv->mesh_dev->flags; - - if (dev_flags & IFF_PROMISC) { - priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; - priv->mac_control &= ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE | - CMD_ACT_MAC_MULTICAST_ENABLE); - goto out_set_mac_control; - } else if (dev_flags & IFF_ALLMULTI) { - do_allmulti: - priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; - priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | - CMD_ACT_MAC_MULTICAST_ENABLE); - goto out_set_mac_control; - } - - /* Once for priv->dev, again for priv->mesh_dev if it exists */ - nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->dev, 0); - if (nr_addrs >= 0 && priv->mesh_dev) - nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->mesh_dev, nr_addrs); - if (nr_addrs < 0) - goto do_allmulti; - - if (nr_addrs) { - int size = offsetof(struct cmd_ds_mac_multicast_adr, - maclist[6*nr_addrs]); - - mcast_cmd.action = cpu_to_le16(CMD_ACT_SET); - mcast_cmd.hdr.size = cpu_to_le16(size); - mcast_cmd.nr_of_adrs = cpu_to_le16(nr_addrs); - - lbs_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &mcast_cmd.hdr, size); - - priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; - } else - priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; - - priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | - CMD_ACT_MAC_ALL_MULTICAST_ENABLE); - out_set_mac_control: - if (priv->mac_control != old_mac_control) - lbs_set_mac_control(priv); - - lbs_deb_leave(LBS_DEB_NET); -} - -static void lbs_set_mcast_worker(struct work_struct *work) -{ - struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); - lbs_update_mcast(priv); -} - -void lbs_set_multicast_list(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - - schedule_work(&priv->mcast_work); -} - -/** - * lbs_thread - handles the major jobs in the LBS driver. - * It handles all events generated by firmware, RX data received - * from firmware and TX data sent from kernel. - * - * @data: A pointer to &lbs_thread structure - * returns: 0 - */ -static int lbs_thread(void *data) -{ - struct net_device *dev = data; - struct lbs_private *priv = dev->ml_priv; - wait_queue_t wait; - - lbs_deb_enter(LBS_DEB_THREAD); - - init_waitqueue_entry(&wait, current); - - for (;;) { - int shouldsleep; - u8 resp_idx; - - lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", - priv->currenttxskb, priv->dnld_sent); - - add_wait_queue(&priv->waitq, &wait); - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irq(&priv->driver_lock); - - if (kthread_should_stop()) - shouldsleep = 0; /* Bye */ - else if (priv->surpriseremoved) - shouldsleep = 1; /* We need to wait until we're _told_ to die */ - else if (priv->psstate == PS_STATE_SLEEP) - shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ - else if (priv->cmd_timed_out) - shouldsleep = 0; /* Command timed out. Recover */ - else if (!priv->fw_ready) - shouldsleep = 1; /* Firmware not ready. We're waiting for it */ - else if (priv->dnld_sent) - shouldsleep = 1; /* Something is en route to the device already */ - else if (priv->tx_pending_len > 0) - shouldsleep = 0; /* We've a packet to send */ - else if (priv->resp_len[priv->resp_idx]) - shouldsleep = 0; /* We have a command response */ - else if (priv->cur_cmd) - shouldsleep = 1; /* Can't send a command; one already running */ - else if (!list_empty(&priv->cmdpendingq) && - !(priv->wakeup_dev_required)) - shouldsleep = 0; /* We have a command to send */ - else if (kfifo_len(&priv->event_fifo)) - shouldsleep = 0; /* We have an event to process */ - else - shouldsleep = 1; /* No command */ - - if (shouldsleep) { - lbs_deb_thread("sleeping, connect_status %d, " - "psmode %d, psstate %d\n", - priv->connect_status, - priv->psmode, priv->psstate); - spin_unlock_irq(&priv->driver_lock); - schedule(); - } else - spin_unlock_irq(&priv->driver_lock); - - lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", - priv->currenttxskb, priv->dnld_sent); - - set_current_state(TASK_RUNNING); - remove_wait_queue(&priv->waitq, &wait); - - lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", - priv->currenttxskb, priv->dnld_sent); - - if (kthread_should_stop()) { - lbs_deb_thread("break from main thread\n"); - break; - } - - if (priv->surpriseremoved) { - lbs_deb_thread("adapter removed; waiting to die...\n"); - continue; - } - - lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", - priv->currenttxskb, priv->dnld_sent); - - /* Process any pending command response */ - spin_lock_irq(&priv->driver_lock); - resp_idx = priv->resp_idx; - if (priv->resp_len[resp_idx]) { - spin_unlock_irq(&priv->driver_lock); - lbs_process_command_response(priv, - priv->resp_buf[resp_idx], - priv->resp_len[resp_idx]); - spin_lock_irq(&priv->driver_lock); - priv->resp_len[resp_idx] = 0; - } - spin_unlock_irq(&priv->driver_lock); - - /* Process hardware events, e.g. card removed, link lost */ - spin_lock_irq(&priv->driver_lock); - while (kfifo_len(&priv->event_fifo)) { - u32 event; - - if (kfifo_out(&priv->event_fifo, - (unsigned char *) &event, sizeof(event)) != - sizeof(event)) - break; - spin_unlock_irq(&priv->driver_lock); - lbs_process_event(priv, event); - spin_lock_irq(&priv->driver_lock); - } - spin_unlock_irq(&priv->driver_lock); - - if (priv->wakeup_dev_required) { - lbs_deb_thread("Waking up device...\n"); - /* Wake up device */ - if (priv->exit_deep_sleep(priv)) - lbs_deb_thread("Wakeup device failed\n"); - continue; - } - - /* command timeout stuff */ - if (priv->cmd_timed_out && priv->cur_cmd) { - struct cmd_ctrl_node *cmdnode = priv->cur_cmd; - - netdev_info(dev, "Timeout submitting command 0x%04x\n", - le16_to_cpu(cmdnode->cmdbuf->command)); - lbs_complete_command(priv, cmdnode, -ETIMEDOUT); - - /* Reset card, but only when it isn't in the process - * of being shutdown anyway. */ - if (!dev->dismantle && priv->reset_card) - priv->reset_card(priv); - } - priv->cmd_timed_out = 0; - - if (!priv->fw_ready) - continue; - - /* Check if we need to confirm Sleep Request received previously */ - if (priv->psstate == PS_STATE_PRE_SLEEP && - !priv->dnld_sent && !priv->cur_cmd) { - if (priv->connect_status == LBS_CONNECTED) { - lbs_deb_thread("pre-sleep, currenttxskb %p, " - "dnld_sent %d, cur_cmd %p\n", - priv->currenttxskb, priv->dnld_sent, - priv->cur_cmd); - - lbs_ps_confirm_sleep(priv); - } else { - /* workaround for firmware sending - * deauth/linkloss event immediately - * after sleep request; remove this - * after firmware fixes it - */ - priv->psstate = PS_STATE_AWAKE; - netdev_alert(dev, - "ignore PS_SleepConfirm in non-connected state\n"); - } - } - - /* The PS state is changed during processing of Sleep Request - * event above - */ - if ((priv->psstate == PS_STATE_SLEEP) || - (priv->psstate == PS_STATE_PRE_SLEEP)) - continue; - - if (priv->is_deep_sleep) - continue; - - /* Execute the next command */ - if (!priv->dnld_sent && !priv->cur_cmd) - lbs_execute_next_command(priv); - - spin_lock_irq(&priv->driver_lock); - if (!priv->dnld_sent && priv->tx_pending_len > 0) { - int ret = priv->hw_host_to_card(priv, MVMS_DAT, - priv->tx_pending_buf, - priv->tx_pending_len); - if (ret) { - lbs_deb_tx("host_to_card failed %d\n", ret); - priv->dnld_sent = DNLD_RES_RECEIVED; - } else { - mod_timer(&priv->tx_lockup_timer, - jiffies + (HZ * 5)); - } - priv->tx_pending_len = 0; - if (!priv->currenttxskb) { - /* We can wake the queues immediately if we aren't - waiting for TX feedback */ - if (priv->connect_status == LBS_CONNECTED) - netif_wake_queue(priv->dev); - if (priv->mesh_dev && - netif_running(priv->mesh_dev)) - netif_wake_queue(priv->mesh_dev); - } - } - spin_unlock_irq(&priv->driver_lock); - } - - del_timer(&priv->command_timer); - del_timer(&priv->tx_lockup_timer); - del_timer(&priv->auto_deepsleep_timer); - - lbs_deb_leave(LBS_DEB_THREAD); - return 0; -} - -/** - * lbs_setup_firmware - gets the HW spec from the firmware and sets - * some basic parameters - * - * @priv: A pointer to &struct lbs_private structure - * returns: 0 or -1 - */ -static int lbs_setup_firmware(struct lbs_private *priv) -{ - int ret = -1; - s16 curlevel = 0, minlevel = 0, maxlevel = 0; - - lbs_deb_enter(LBS_DEB_FW); - - /* Read MAC address from firmware */ - eth_broadcast_addr(priv->current_addr); - ret = lbs_update_hw_spec(priv); - if (ret) - goto done; - - /* Read power levels if available */ - ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel); - if (ret == 0) { - priv->txpower_cur = curlevel; - priv->txpower_min = minlevel; - priv->txpower_max = maxlevel; - } - - /* Send cmd to FW to enable 11D function */ - ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); - if (ret) - goto done; - - ret = lbs_set_mac_control_sync(priv); -done: - lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); - return ret; -} - -int lbs_suspend(struct lbs_private *priv) -{ - int ret; - - lbs_deb_enter(LBS_DEB_FW); - - if (priv->is_deep_sleep) { - ret = lbs_set_deep_sleep(priv, 0); - if (ret) { - netdev_err(priv->dev, - "deep sleep cancellation failed: %d\n", ret); - return ret; - } - priv->deep_sleep_required = 1; - } - - ret = lbs_set_host_sleep(priv, 1); - - netif_device_detach(priv->dev); - if (priv->mesh_dev) - netif_device_detach(priv->mesh_dev); - - lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(lbs_suspend); - -int lbs_resume(struct lbs_private *priv) -{ - int ret; - - lbs_deb_enter(LBS_DEB_FW); - - ret = lbs_set_host_sleep(priv, 0); - - netif_device_attach(priv->dev); - if (priv->mesh_dev) - netif_device_attach(priv->mesh_dev); - - if (priv->deep_sleep_required) { - priv->deep_sleep_required = 0; - ret = lbs_set_deep_sleep(priv, 1); - if (ret) - netdev_err(priv->dev, - "deep sleep activation failed: %d\n", ret); - } - - if (priv->setup_fw_on_resume) - ret = lbs_setup_firmware(priv); - - lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(lbs_resume); - -/** - * lbs_cmd_timeout_handler - handles the timeout of command sending. - * It will re-send the same command again. - * - * @data: &struct lbs_private pointer - */ -static void lbs_cmd_timeout_handler(unsigned long data) -{ - struct lbs_private *priv = (struct lbs_private *)data; - unsigned long flags; - - lbs_deb_enter(LBS_DEB_CMD); - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!priv->cur_cmd) - goto out; - - netdev_info(priv->dev, "command 0x%04x timed out\n", - le16_to_cpu(priv->cur_cmd->cmdbuf->command)); - - priv->cmd_timed_out = 1; - - /* - * If the device didn't even acknowledge the command, reset the state - * so that we don't block all future commands due to this one timeout. - */ - if (priv->dnld_sent == DNLD_CMD_SENT) - priv->dnld_sent = DNLD_RES_RECEIVED; - - wake_up(&priv->waitq); -out: - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbs_deb_leave(LBS_DEB_CMD); -} - -/** - * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames - * to the hardware. This is known to frequently happen with SD8686 when - * waking up after a Wake-on-WLAN-triggered resume. - * - * @data: &struct lbs_private pointer - */ -static void lbs_tx_lockup_handler(unsigned long data) -{ - struct lbs_private *priv = (struct lbs_private *)data; - unsigned long flags; - - lbs_deb_enter(LBS_DEB_TX); - spin_lock_irqsave(&priv->driver_lock, flags); - - netdev_info(priv->dev, "TX lockup detected\n"); - if (priv->reset_card) - priv->reset_card(priv); - - priv->dnld_sent = DNLD_RES_RECEIVED; - wake_up_interruptible(&priv->waitq); - - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbs_deb_leave(LBS_DEB_TX); -} - -/** - * auto_deepsleep_timer_fn - put the device back to deep sleep mode when - * timer expires and no activity (command, event, data etc.) is detected. - * @data: &struct lbs_private pointer - * returns: N/A - */ -static void auto_deepsleep_timer_fn(unsigned long data) -{ - struct lbs_private *priv = (struct lbs_private *)data; - - lbs_deb_enter(LBS_DEB_CMD); - - if (priv->is_activity_detected) { - priv->is_activity_detected = 0; - } else { - if (priv->is_auto_deep_sleep_enabled && - (!priv->wakeup_dev_required) && - (priv->connect_status != LBS_CONNECTED)) { - struct cmd_header cmd; - - lbs_deb_main("Entering auto deep sleep mode...\n"); - memset(&cmd, 0, sizeof(cmd)); - cmd.size = cpu_to_le16(sizeof(cmd)); - lbs_cmd_async(priv, CMD_802_11_DEEP_SLEEP, &cmd, - sizeof(cmd)); - } - } - mod_timer(&priv->auto_deepsleep_timer , jiffies + - (priv->auto_deep_sleep_timeout * HZ)/1000); - lbs_deb_leave(LBS_DEB_CMD); -} - -int lbs_enter_auto_deep_sleep(struct lbs_private *priv) -{ - lbs_deb_enter(LBS_DEB_SDIO); - - priv->is_auto_deep_sleep_enabled = 1; - if (priv->is_deep_sleep) - priv->wakeup_dev_required = 1; - mod_timer(&priv->auto_deepsleep_timer , - jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000); - - lbs_deb_leave(LBS_DEB_SDIO); - return 0; -} - -int lbs_exit_auto_deep_sleep(struct lbs_private *priv) -{ - lbs_deb_enter(LBS_DEB_SDIO); - - priv->is_auto_deep_sleep_enabled = 0; - priv->auto_deep_sleep_timeout = 0; - del_timer(&priv->auto_deepsleep_timer); - - lbs_deb_leave(LBS_DEB_SDIO); - return 0; -} - -static int lbs_init_adapter(struct lbs_private *priv) -{ - int ret; - - lbs_deb_enter(LBS_DEB_MAIN); - - eth_broadcast_addr(priv->current_addr); - - priv->connect_status = LBS_DISCONNECTED; - priv->channel = DEFAULT_AD_HOC_CHANNEL; - priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; - priv->radio_on = 1; - priv->psmode = LBS802_11POWERMODECAM; - priv->psstate = PS_STATE_FULL_POWER; - priv->is_deep_sleep = 0; - priv->is_auto_deep_sleep_enabled = 0; - priv->deep_sleep_required = 0; - priv->wakeup_dev_required = 0; - init_waitqueue_head(&priv->ds_awake_q); - init_waitqueue_head(&priv->scan_q); - priv->authtype_auto = 1; - priv->is_host_sleep_configured = 0; - priv->is_host_sleep_activated = 0; - init_waitqueue_head(&priv->host_sleep_q); - init_waitqueue_head(&priv->fw_waitq); - mutex_init(&priv->lock); - - setup_timer(&priv->command_timer, lbs_cmd_timeout_handler, - (unsigned long)priv); - setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler, - (unsigned long)priv); - setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, - (unsigned long)priv); - - INIT_LIST_HEAD(&priv->cmdfreeq); - INIT_LIST_HEAD(&priv->cmdpendingq); - - spin_lock_init(&priv->driver_lock); - - /* Allocate the command buffers */ - if (lbs_allocate_cmd_buffer(priv)) { - pr_err("Out of memory allocating command buffers\n"); - ret = -ENOMEM; - goto out; - } - priv->resp_idx = 0; - priv->resp_len[0] = priv->resp_len[1] = 0; - - /* Create the event FIFO */ - ret = kfifo_alloc(&priv->event_fifo, sizeof(u32) * 16, GFP_KERNEL); - if (ret) { - pr_err("Out of memory allocating event FIFO buffer\n"); - goto out; - } - -out: - lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); - - return ret; -} - -static void lbs_free_adapter(struct lbs_private *priv) -{ - lbs_deb_enter(LBS_DEB_MAIN); - - lbs_free_cmd_buffer(priv); - kfifo_free(&priv->event_fifo); - del_timer(&priv->command_timer); - del_timer(&priv->tx_lockup_timer); - del_timer(&priv->auto_deepsleep_timer); - - lbs_deb_leave(LBS_DEB_MAIN); -} - -static const struct net_device_ops lbs_netdev_ops = { - .ndo_open = lbs_dev_open, - .ndo_stop = lbs_eth_stop, - .ndo_start_xmit = lbs_hard_start_xmit, - .ndo_set_mac_address = lbs_set_mac_address, - .ndo_set_rx_mode = lbs_set_multicast_list, - .ndo_change_mtu = eth_change_mtu, - .ndo_validate_addr = eth_validate_addr, -}; - -/** - * lbs_add_card - adds the card. It will probe the - * card, allocate the lbs_priv and initialize the device. - * - * @card: A pointer to card - * @dmdev: A pointer to &struct device - * returns: A pointer to &struct lbs_private structure - */ -struct lbs_private *lbs_add_card(void *card, struct device *dmdev) -{ - struct net_device *dev; - struct wireless_dev *wdev; - struct lbs_private *priv = NULL; - - lbs_deb_enter(LBS_DEB_MAIN); - - /* Allocate an Ethernet device and register it */ - wdev = lbs_cfg_alloc(dmdev); - if (IS_ERR(wdev)) { - pr_err("cfg80211 init failed\n"); - goto done; - } - - wdev->iftype = NL80211_IFTYPE_STATION; - priv = wdev_priv(wdev); - priv->wdev = wdev; - - if (lbs_init_adapter(priv)) { - pr_err("failed to initialize adapter structure\n"); - goto err_wdev; - } - - dev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup); - if (!dev) { - dev_err(dmdev, "no memory for network device instance\n"); - goto err_adapter; - } - - dev->ieee80211_ptr = wdev; - dev->ml_priv = priv; - SET_NETDEV_DEV(dev, dmdev); - wdev->netdev = dev; - priv->dev = dev; - - dev->netdev_ops = &lbs_netdev_ops; - dev->watchdog_timeo = 5 * HZ; - dev->ethtool_ops = &lbs_ethtool_ops; - dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - - priv->card = card; - - strcpy(dev->name, "wlan%d"); - - lbs_deb_thread("Starting main thread...\n"); - init_waitqueue_head(&priv->waitq); - priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); - if (IS_ERR(priv->main_thread)) { - lbs_deb_thread("Error creating main thread.\n"); - goto err_ndev; - } - - priv->work_thread = create_singlethread_workqueue("lbs_worker"); - INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); - - priv->wol_criteria = EHS_REMOVE_WAKEUP; - priv->wol_gpio = 0xff; - priv->wol_gap = 20; - priv->ehs_remove_supported = true; - - goto done; - - err_ndev: - free_netdev(dev); - - err_adapter: - lbs_free_adapter(priv); - - err_wdev: - lbs_cfg_free(priv); - - priv = NULL; - -done: - lbs_deb_leave_args(LBS_DEB_MAIN, "priv %p", priv); - return priv; -} -EXPORT_SYMBOL_GPL(lbs_add_card); - - -void lbs_remove_card(struct lbs_private *priv) -{ - struct net_device *dev = priv->dev; - - lbs_deb_enter(LBS_DEB_MAIN); - - lbs_remove_mesh(priv); - - if (priv->wiphy_registered) - lbs_scan_deinit(priv); - - lbs_wait_for_firmware_load(priv); - - /* worker thread destruction blocks on the in-flight command which - * should have been cleared already in lbs_stop_card(). - */ - lbs_deb_main("destroying worker thread\n"); - destroy_workqueue(priv->work_thread); - lbs_deb_main("done destroying worker thread\n"); - - if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { - priv->psmode = LBS802_11POWERMODECAM; - lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, true); - } - - if (priv->is_deep_sleep) { - priv->is_deep_sleep = 0; - wake_up_interruptible(&priv->ds_awake_q); - } - - priv->is_host_sleep_configured = 0; - priv->is_host_sleep_activated = 0; - wake_up_interruptible(&priv->host_sleep_q); - - /* Stop the thread servicing the interrupts */ - priv->surpriseremoved = 1; - kthread_stop(priv->main_thread); - - lbs_free_adapter(priv); - lbs_cfg_free(priv); - free_netdev(dev); - - lbs_deb_leave(LBS_DEB_MAIN); -} -EXPORT_SYMBOL_GPL(lbs_remove_card); - - -int lbs_rtap_supported(struct lbs_private *priv) -{ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) - return 1; - - /* newer firmware use a capability mask */ - return ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && - (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)); -} - - -int lbs_start_card(struct lbs_private *priv) -{ - struct net_device *dev = priv->dev; - int ret = -1; - - lbs_deb_enter(LBS_DEB_MAIN); - - /* poke the firmware */ - ret = lbs_setup_firmware(priv); - if (ret) - goto done; - - if (!lbs_disablemesh) - lbs_init_mesh(priv); - else - pr_info("%s: mesh disabled\n", dev->name); - - if (lbs_cfg_register(priv)) { - pr_err("cannot register device\n"); - goto done; - } - - if (lbs_mesh_activated(priv)) - lbs_start_mesh(priv); - - lbs_debugfs_init_one(priv, dev); - - netdev_info(dev, "Marvell WLAN 802.11 adapter\n"); - - ret = 0; - -done: - lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(lbs_start_card); - - -void lbs_stop_card(struct lbs_private *priv) -{ - struct net_device *dev; - - lbs_deb_enter(LBS_DEB_MAIN); - - if (!priv) - goto out; - dev = priv->dev; - - /* If the netdev isn't registered, it means that lbs_start_card() was - * never called so we have nothing to do here. */ - if (dev->reg_state != NETREG_REGISTERED) - goto out; - - netif_stop_queue(dev); - netif_carrier_off(dev); - - lbs_debugfs_remove_one(priv); - lbs_deinit_mesh(priv); - unregister_netdev(dev); - -out: - lbs_deb_leave(LBS_DEB_MAIN); -} -EXPORT_SYMBOL_GPL(lbs_stop_card); - - -void lbs_queue_event(struct lbs_private *priv, u32 event) -{ - unsigned long flags; - - lbs_deb_enter(LBS_DEB_THREAD); - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->psstate == PS_STATE_SLEEP) - priv->psstate = PS_STATE_AWAKE; - - kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32)); - - wake_up(&priv->waitq); - - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbs_deb_leave(LBS_DEB_THREAD); -} -EXPORT_SYMBOL_GPL(lbs_queue_event); - -void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) -{ - lbs_deb_enter(LBS_DEB_THREAD); - - if (priv->psstate == PS_STATE_SLEEP) - priv->psstate = PS_STATE_AWAKE; - - /* Swap buffers by flipping the response index */ - BUG_ON(resp_idx > 1); - priv->resp_idx = resp_idx; - - wake_up(&priv->waitq); - - lbs_deb_leave(LBS_DEB_THREAD); -} -EXPORT_SYMBOL_GPL(lbs_notify_command_response); - -static int __init lbs_init_module(void) -{ - lbs_deb_enter(LBS_DEB_MAIN); - memset(&confirm_sleep, 0, sizeof(confirm_sleep)); - confirm_sleep.hdr.command = cpu_to_le16(CMD_802_11_PS_MODE); - confirm_sleep.hdr.size = cpu_to_le16(sizeof(confirm_sleep)); - confirm_sleep.action = cpu_to_le16(PS_MODE_ACTION_SLEEP_CONFIRMED); - lbs_debugfs_init(); - lbs_deb_leave(LBS_DEB_MAIN); - return 0; -} - -static void __exit lbs_exit_module(void) -{ - lbs_deb_enter(LBS_DEB_MAIN); - lbs_debugfs_remove(); - lbs_deb_leave(LBS_DEB_MAIN); -} - -module_init(lbs_init_module); -module_exit(lbs_exit_module); - -MODULE_DESCRIPTION("Libertas WLAN Driver Library"); -MODULE_AUTHOR("Marvell International Ltd."); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c deleted file mode 100644 index d0c881dd5846..000000000000 --- a/drivers/net/wireless/libertas/mesh.c +++ /dev/null @@ -1,1187 +0,0 @@ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mesh.h" -#include "decl.h" -#include "cmd.h" - - -static int lbs_add_mesh(struct lbs_private *priv); - -/*************************************************************************** - * Mesh command handling - */ - -static int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, - struct cmd_ds_mesh_access *cmd) -{ - int ret; - - lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); - - cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS); - cmd->hdr.size = cpu_to_le16(sizeof(*cmd)); - cmd->hdr.result = 0; - - cmd->action = cpu_to_le16(cmd_action); - - ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -static int __lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type) -{ - int ret; - u16 command = CMD_MESH_CONFIG_OLD; - - lbs_deb_enter(LBS_DEB_CMD); - - /* - * Command id is 0xac for v10 FW along with mesh interface - * id in bits 14-13-12. - */ - if (priv->mesh_tlv == TLV_TYPE_MESH_ID) - command = CMD_MESH_CONFIG | - (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET); - - cmd->hdr.command = cpu_to_le16(command); - cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config)); - cmd->hdr.result = 0; - - cmd->type = cpu_to_le16(type); - cmd->action = cpu_to_le16(action); - - ret = lbs_cmd_with_response(priv, command, cmd); - - lbs_deb_leave(LBS_DEB_CMD); - return ret; -} - -static int lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type) -{ - int ret; - - if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) - return -EOPNOTSUPP; - - ret = __lbs_mesh_config_send(priv, cmd, action, type); - return ret; -} - -/* This function is the CMD_MESH_CONFIG legacy function. It only handles the - * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG - * are all handled by preparing a struct cmd_ds_mesh_config and passing it to - * lbs_mesh_config_send. - */ -static int lbs_mesh_config(struct lbs_private *priv, uint16_t action, - uint16_t chan) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_meshie *ie; - - memset(&cmd, 0, sizeof(cmd)); - cmd.channel = cpu_to_le16(chan); - ie = (struct mrvl_meshie *)cmd.data; - - switch (action) { - case CMD_ACT_MESH_CONFIG_START: - ie->id = WLAN_EID_VENDOR_SPECIFIC; - ie->val.oui[0] = 0x00; - ie->val.oui[1] = 0x50; - ie->val.oui[2] = 0x43; - ie->val.type = MARVELL_MESH_IE_TYPE; - ie->val.subtype = MARVELL_MESH_IE_SUBTYPE; - ie->val.version = MARVELL_MESH_IE_VERSION; - ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP; - ie->val.active_metric_id = MARVELL_MESH_METRIC_ID; - ie->val.mesh_capability = MARVELL_MESH_CAPABILITY; - ie->val.mesh_id_len = priv->mesh_ssid_len; - memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len); - ie->len = sizeof(struct mrvl_meshie_val) - - IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len; - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val)); - break; - case CMD_ACT_MESH_CONFIG_STOP: - break; - default: - return -1; - } - lbs_deb_cmd("mesh config action %d type %x channel %d SSID %*pE\n", - action, priv->mesh_tlv, chan, priv->mesh_ssid_len, - priv->mesh_ssid); - - return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); -} - -int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel) -{ - priv->mesh_channel = channel; - return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel); -} - -static uint16_t lbs_mesh_get_channel(struct lbs_private *priv) -{ - return priv->mesh_channel ?: 1; -} - -/*************************************************************************** - * Mesh sysfs support - */ - -/* - * Attributes exported through sysfs - */ - -/** - * lbs_anycast_get - Get function for sysfs attribute anycast_mask - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t lbs_anycast_get(struct device *dev, - struct device_attribute *attr, char * buf) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - - memset(&mesh_access, 0, sizeof(mesh_access)); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access); - if (ret) - return ret; - - return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0])); -} - -/** - * lbs_anycast_set - Set function for sysfs attribute anycast_mask - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t lbs_anycast_set(struct device *dev, - struct device_attribute *attr, const char * buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - uint32_t datum; - int ret; - - memset(&mesh_access, 0, sizeof(mesh_access)); - sscanf(buf, "%x", &datum); - mesh_access.data[0] = cpu_to_le32(datum); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * lbs_prb_rsp_limit_get - Get function for sysfs attribute prb_rsp_limit - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t lbs_prb_rsp_limit_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - u32 retry_limit; - - memset(&mesh_access, 0, sizeof(mesh_access)); - mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, - &mesh_access); - if (ret) - return ret; - - retry_limit = le32_to_cpu(mesh_access.data[1]); - return snprintf(buf, 10, "%d\n", retry_limit); -} - -/** - * lbs_prb_rsp_limit_set - Set function for sysfs attribute prb_rsp_limit - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t lbs_prb_rsp_limit_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - unsigned long retry_limit; - - memset(&mesh_access, 0, sizeof(mesh_access)); - mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET); - - if (!kstrtoul(buf, 10, &retry_limit)) - return -ENOTSUPP; - if (retry_limit > 15) - return -ENOTSUPP; - - mesh_access.data[1] = cpu_to_le32(retry_limit); - - ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, - &mesh_access); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * lbs_mesh_get - Get function for sysfs attribute mesh - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t lbs_mesh_get(struct device *dev, - struct device_attribute *attr, char * buf) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev); -} - -/** - * lbs_mesh_set - Set function for sysfs attribute mesh - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t lbs_mesh_set(struct device *dev, - struct device_attribute *attr, const char * buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - int enable; - - sscanf(buf, "%x", &enable); - enable = !!enable; - if (enable == !!priv->mesh_dev) - return count; - - if (enable) - lbs_add_mesh(priv); - else - lbs_remove_mesh(priv); - - return count; -} - -/* - * lbs_mesh attribute to be exported per ethX interface - * through sysfs (/sys/class/net/ethX/lbs_mesh) - */ -static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set); - -/* - * anycast_mask attribute to be exported per mshX interface - * through sysfs (/sys/class/net/mshX/anycast_mask) - */ -static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set); - -/* - * prb_rsp_limit attribute to be exported per mshX interface - * through sysfs (/sys/class/net/mshX/prb_rsp_limit) - */ -static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get, - lbs_prb_rsp_limit_set); - -static struct attribute *lbs_mesh_sysfs_entries[] = { - &dev_attr_anycast_mask.attr, - &dev_attr_prb_rsp_limit.attr, - NULL, -}; - -static const struct attribute_group lbs_mesh_attr_group = { - .attrs = lbs_mesh_sysfs_entries, -}; - - -/*************************************************************************** - * Persistent configuration support - */ - -static int mesh_get_default_parameters(struct device *dev, - struct mrvl_mesh_defaults *defs) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - int ret; - - memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET, - CMD_TYPE_MESH_GET_DEFAULTS); - - if (ret) - return -EOPNOTSUPP; - - memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults)); - - return 0; -} - -/** - * bootflag_get - Get function for sysfs attribute bootflag - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t bootflag_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag)); -} - -/** - * bootflag_set - Set function for sysfs attribute bootflag - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 1)) - return -EINVAL; - - *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum); - cmd.length = cpu_to_le16(sizeof(uint32_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_BOOTFLAG); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * boottime_get - Get function for sysfs attribute boottime - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t boottime_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 12, "%d\n", defs.boottime); -} - -/** - * boottime_set - Set function for sysfs attribute boottime - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t boottime_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* A too small boot time will result in the device booting into - * standalone (no-host) mode before the host can take control of it, - * so the change will be hard to revert. This may be a desired - * feature (e.g to configure a very fast boot time for devices that - * will not be attached to a host), but dangerous. So I'm enforcing a - * lower limit of 20 seconds: remove and recompile the driver if this - * does not work for you. - */ - datum = (datum < 20) ? 20 : datum; - cmd.data[0] = datum; - cmd.length = cpu_to_le16(sizeof(uint8_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_BOOTTIME); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * channel_get - Get function for sysfs attribute channel - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t channel_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel)); -} - -/** - * channel_set - Set function for sysfs attribute channel - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t channel_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if (ret != 1 || datum < 1 || datum > 11) - return -EINVAL; - - *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum); - cmd.length = cpu_to_le16(sizeof(uint16_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_DEF_CHANNEL); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * mesh_id_get - Get function for sysfs attribute mesh_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) { - dev_err(dev, "inconsistent mesh ID length\n"); - defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN; - } - - memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len); - buf[defs.meshie.val.mesh_id_len] = '\n'; - buf[defs.meshie.val.mesh_id_len + 1] = '\0'; - - return defs.meshie.val.mesh_id_len + 1; -} - -/** - * mesh_id_set - Set function for sysfs attribute mesh_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - int len; - int ret; - - if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1) - return -EINVAL; - - memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); - ie = (struct mrvl_meshie *) &cmd.data[0]; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - - len = count - 1; - memcpy(ie->val.mesh_id, buf, len); - /* SSID len */ - ie->val.mesh_id_len = len; - /* IE len */ - ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * protocol_id_get - Get function for sysfs attribute protocol_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t protocol_id_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id); -} - -/** - * protocol_id_set - Set function for sysfs attribute protocol_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t protocol_id_set(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update protocol id */ - ie->val.active_protocol_id = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * metric_id_get - Get function for sysfs attribute metric_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t metric_id_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id); -} - -/** - * metric_id_set - Set function for sysfs attribute metric_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update metric id */ - ie->val.active_metric_id = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * capability_get - Get function for sysfs attribute capability - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t capability_get(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability); -} - -/** - * capability_set - Set function for sysfs attribute capability - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t capability_set(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update value */ - ie->val.mesh_capability = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - - -static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set); -static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set); -static DEVICE_ATTR(channel, 0644, channel_get, channel_set); -static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set); -static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set); -static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set); -static DEVICE_ATTR(capability, 0644, capability_get, capability_set); - -static struct attribute *boot_opts_attrs[] = { - &dev_attr_bootflag.attr, - &dev_attr_boottime.attr, - &dev_attr_channel.attr, - NULL -}; - -static const struct attribute_group boot_opts_group = { - .name = "boot_options", - .attrs = boot_opts_attrs, -}; - -static struct attribute *mesh_ie_attrs[] = { - &dev_attr_mesh_id.attr, - &dev_attr_protocol_id.attr, - &dev_attr_metric_id.attr, - &dev_attr_capability.attr, - NULL -}; - -static const struct attribute_group mesh_ie_group = { - .name = "mesh_ie", - .attrs = mesh_ie_attrs, -}; - -static void lbs_persist_config_init(struct net_device *dev) -{ - int ret; - ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group); - ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group); -} - -static void lbs_persist_config_remove(struct net_device *dev) -{ - sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group); - sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group); -} - - -/*************************************************************************** - * Initializing and starting, stopping mesh - */ - -/* - * Check mesh FW version and appropriately send the mesh start - * command - */ -int lbs_init_mesh(struct lbs_private *priv) -{ - int ret = 0; - - lbs_deb_enter(LBS_DEB_MESH); - - /* Determine mesh_fw_ver from fwrelease and fwcapinfo */ - /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */ - /* 5.110.22 have mesh command with 0xa3 command id */ - /* 10.0.0.p0 FW brings in mesh config command with different id */ - /* Check FW version MSB and initialize mesh_fw_ver */ - if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) { - /* Enable mesh, if supported, and work out which TLV it uses. - 0x100 + 291 is an unofficial value used in 5.110.20.pXX - 0x100 + 37 is the official value used in 5.110.21.pXX - but we check them in that order because 20.pXX doesn't - give an error -- it just silently fails. */ - - /* 5.110.20.pXX firmware will fail the command if the channel - doesn't match the existing channel. But only if the TLV - is correct. If the channel is wrong, _BOTH_ versions will - give an error to 0x100+291, and allow 0x100+37 to succeed. - It's just that 5.110.20.pXX will not have done anything - useful */ - - priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) { - priv->mesh_tlv = TLV_TYPE_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) - priv->mesh_tlv = 0; - } - } else - if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && - (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) { - /* 10.0.0.pXX new firmwares should succeed with TLV - * 0x100+37; Do not invoke command with old TLV. - */ - priv->mesh_tlv = TLV_TYPE_MESH_ID; - if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) - priv->mesh_tlv = 0; - } - - /* Stop meshing until interface is brought up */ - lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1); - - if (priv->mesh_tlv) { - sprintf(priv->mesh_ssid, "mesh"); - priv->mesh_ssid_len = 4; - ret = 1; - } - - lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); - return ret; -} - -void lbs_start_mesh(struct lbs_private *priv) -{ - lbs_add_mesh(priv); - - if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh)) - netdev_err(priv->dev, "cannot register lbs_mesh attribute\n"); -} - -int lbs_deinit_mesh(struct lbs_private *priv) -{ - struct net_device *dev = priv->dev; - int ret = 0; - - lbs_deb_enter(LBS_DEB_MESH); - - if (priv->mesh_tlv) { - device_remove_file(&dev->dev, &dev_attr_lbs_mesh); - ret = 1; - } - - lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); - return ret; -} - - -/** - * lbs_mesh_stop - close the mshX interface - * - * @dev: A pointer to &net_device structure - * returns: 0 - */ -static int lbs_mesh_stop(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - - lbs_deb_enter(LBS_DEB_MESH); - lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, - lbs_mesh_get_channel(priv)); - - spin_lock_irq(&priv->driver_lock); - - netif_stop_queue(dev); - netif_carrier_off(dev); - - spin_unlock_irq(&priv->driver_lock); - - lbs_update_mcast(priv); - if (!lbs_iface_active(priv)) - lbs_stop_iface(priv); - - lbs_deb_leave(LBS_DEB_MESH); - return 0; -} - -/** - * lbs_mesh_dev_open - open the mshX interface - * - * @dev: A pointer to &net_device structure - * returns: 0 or -EBUSY if monitor mode active - */ -static int lbs_mesh_dev_open(struct net_device *dev) -{ - struct lbs_private *priv = dev->ml_priv; - int ret = 0; - - lbs_deb_enter(LBS_DEB_NET); - if (!priv->iface_running) { - ret = lbs_start_iface(priv); - if (ret) - goto out; - } - - spin_lock_irq(&priv->driver_lock); - - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { - ret = -EBUSY; - spin_unlock_irq(&priv->driver_lock); - goto out; - } - - netif_carrier_on(dev); - - if (!priv->tx_pending_len) - netif_wake_queue(dev); - - spin_unlock_irq(&priv->driver_lock); - - ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, - lbs_mesh_get_channel(priv)); - -out: - lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); - return ret; -} - -static const struct net_device_ops mesh_netdev_ops = { - .ndo_open = lbs_mesh_dev_open, - .ndo_stop = lbs_mesh_stop, - .ndo_start_xmit = lbs_hard_start_xmit, - .ndo_set_mac_address = lbs_set_mac_address, - .ndo_set_rx_mode = lbs_set_multicast_list, -}; - -/** - * lbs_add_mesh - add mshX interface - * - * @priv: A pointer to the &struct lbs_private structure - * returns: 0 if successful, -X otherwise - */ -static int lbs_add_mesh(struct lbs_private *priv) -{ - struct net_device *mesh_dev = NULL; - struct wireless_dev *mesh_wdev; - int ret = 0; - - lbs_deb_enter(LBS_DEB_MESH); - - /* Allocate a virtual mesh device */ - mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); - if (!mesh_wdev) { - lbs_deb_mesh("init mshX wireless device failed\n"); - ret = -ENOMEM; - goto done; - } - - mesh_dev = alloc_netdev(0, "msh%d", NET_NAME_UNKNOWN, ether_setup); - if (!mesh_dev) { - lbs_deb_mesh("init mshX device failed\n"); - ret = -ENOMEM; - goto err_free_wdev; - } - - mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT; - mesh_wdev->wiphy = priv->wdev->wiphy; - mesh_wdev->netdev = mesh_dev; - - mesh_dev->ml_priv = priv; - mesh_dev->ieee80211_ptr = mesh_wdev; - priv->mesh_dev = mesh_dev; - - mesh_dev->netdev_ops = &mesh_netdev_ops; - mesh_dev->ethtool_ops = &lbs_ethtool_ops; - eth_hw_addr_inherit(mesh_dev, priv->dev); - - SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); - - mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - /* Register virtual mesh interface */ - ret = register_netdev(mesh_dev); - if (ret) { - pr_err("cannot register mshX virtual interface\n"); - goto err_free_netdev; - } - - ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); - if (ret) - goto err_unregister; - - lbs_persist_config_init(mesh_dev); - - /* Everything successful */ - ret = 0; - goto done; - -err_unregister: - unregister_netdev(mesh_dev); - -err_free_netdev: - free_netdev(mesh_dev); - -err_free_wdev: - kfree(mesh_wdev); - -done: - lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); - return ret; -} - -void lbs_remove_mesh(struct lbs_private *priv) -{ - struct net_device *mesh_dev; - - mesh_dev = priv->mesh_dev; - if (!mesh_dev) - return; - - lbs_deb_enter(LBS_DEB_MESH); - netif_stop_queue(mesh_dev); - netif_carrier_off(mesh_dev); - sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); - lbs_persist_config_remove(mesh_dev); - unregister_netdev(mesh_dev); - priv->mesh_dev = NULL; - kfree(mesh_dev->ieee80211_ptr); - free_netdev(mesh_dev); - lbs_deb_leave(LBS_DEB_MESH); -} - - -/*************************************************************************** - * Sending and receiving - */ -struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, - struct net_device *dev, struct rxpd *rxpd) -{ - if (priv->mesh_dev) { - if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) { - if (rxpd->rx_control & RxPD_MESH_FRAME) - dev = priv->mesh_dev; - } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) { - if (rxpd->u.bss.bss_num == MESH_IFACE_ID) - dev = priv->mesh_dev; - } - } - return dev; -} - - -void lbs_mesh_set_txpd(struct lbs_private *priv, - struct net_device *dev, struct txpd *txpd) -{ - if (dev == priv->mesh_dev) { - if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) - txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME); - else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) - txpd->u.bss.bss_num = MESH_IFACE_ID; - } -} - - -/*************************************************************************** - * Ethtool related - */ - -static const char * const mesh_stat_strings[] = { - "drop_duplicate_bcast", - "drop_ttl_zero", - "drop_no_fwd_route", - "drop_no_buffers", - "fwded_unicast_cnt", - "fwded_bcast_cnt", - "drop_blind_table", - "tx_failed_cnt" -}; - -void lbs_mesh_ethtool_get_stats(struct net_device *dev, - struct ethtool_stats *stats, uint64_t *data) -{ - struct lbs_private *priv = dev->ml_priv; - struct cmd_ds_mesh_access mesh_access; - int ret; - - lbs_deb_enter(LBS_DEB_ETHTOOL); - - /* Get Mesh Statistics */ - ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access); - - if (ret) { - memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t))); - return; - } - - priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]); - priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]); - priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]); - priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]); - priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]); - priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]); - priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]); - priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]); - - data[0] = priv->mstats.fwd_drop_rbt; - data[1] = priv->mstats.fwd_drop_ttl; - data[2] = priv->mstats.fwd_drop_noroute; - data[3] = priv->mstats.fwd_drop_nobuf; - data[4] = priv->mstats.fwd_unicast_cnt; - data[5] = priv->mstats.fwd_bcast_cnt; - data[6] = priv->mstats.drop_blind; - data[7] = priv->mstats.tx_failed_cnt; - - lbs_deb_enter(LBS_DEB_ETHTOOL); -} - -int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset) -{ - struct lbs_private *priv = dev->ml_priv; - - if (sset == ETH_SS_STATS && dev == priv->mesh_dev) - return MESH_STATS_NUM; - - return -EOPNOTSUPP; -} - -void lbs_mesh_ethtool_get_strings(struct net_device *dev, - uint32_t stringset, uint8_t *s) -{ - int i; - - lbs_deb_enter(LBS_DEB_ETHTOOL); - - switch (stringset) { - case ETH_SS_STATS: - for (i = 0; i < MESH_STATS_NUM; i++) { - memcpy(s + i * ETH_GSTRING_LEN, - mesh_stat_strings[i], - ETH_GSTRING_LEN); - } - break; - } - lbs_deb_enter(LBS_DEB_ETHTOOL); -} diff --git a/drivers/net/wireless/libertas/mesh.h b/drivers/net/wireless/libertas/mesh.h deleted file mode 100644 index 6603f341c874..000000000000 --- a/drivers/net/wireless/libertas/mesh.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Contains all definitions needed for the Libertas' MESH implementation. - */ -#ifndef _LBS_MESH_H_ -#define _LBS_MESH_H_ - - -#include -#include - -#include "host.h" -#include "dev.h" - -#ifdef CONFIG_LIBERTAS_MESH - -struct net_device; - -int lbs_init_mesh(struct lbs_private *priv); -void lbs_start_mesh(struct lbs_private *priv); -int lbs_deinit_mesh(struct lbs_private *priv); - -void lbs_remove_mesh(struct lbs_private *priv); - -static inline bool lbs_mesh_activated(struct lbs_private *priv) -{ - /* Mesh SSID is only programmed after successful init */ - return priv->mesh_ssid_len != 0; -} - -int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel); - -/* Sending / Receiving */ - -struct rxpd; -struct txpd; - -struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, - struct net_device *dev, struct rxpd *rxpd); -void lbs_mesh_set_txpd(struct lbs_private *priv, - struct net_device *dev, struct txpd *txpd); - - -/* Command handling */ - -struct cmd_ds_command; -struct cmd_ds_mesh_access; -struct cmd_ds_mesh_config; - - -/* Ethtool statistics */ - -struct ethtool_stats; - -void lbs_mesh_ethtool_get_stats(struct net_device *dev, - struct ethtool_stats *stats, uint64_t *data); -int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset); -void lbs_mesh_ethtool_get_strings(struct net_device *dev, - uint32_t stringset, uint8_t *s); - - -#else - -#define lbs_init_mesh(priv) -#define lbs_deinit_mesh(priv) -#define lbs_start_mesh(priv) -#define lbs_add_mesh(priv) -#define lbs_remove_mesh(priv) -#define lbs_mesh_set_dev(priv, dev, rxpd) (dev) -#define lbs_mesh_set_txpd(priv, dev, txpd) -#define lbs_mesh_set_channel(priv, channel) (0) -#define lbs_mesh_activated(priv) (false) - -#endif - - - -#endif diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h deleted file mode 100644 index b3c8ea6d610e..000000000000 --- a/drivers/net/wireless/libertas/radiotap.h +++ /dev/null @@ -1,44 +0,0 @@ -#include - -struct tx_radiotap_hdr { - struct ieee80211_radiotap_header hdr; - u8 rate; - u8 txpower; - u8 rts_retries; - u8 data_retries; -} __packed; - -#define TX_RADIOTAP_PRESENT ( \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ - (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \ - (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \ - 0) - -#define IEEE80211_FC_VERSION_MASK 0x0003 -#define IEEE80211_FC_TYPE_MASK 0x000c -#define IEEE80211_FC_TYPE_MGT 0x0000 -#define IEEE80211_FC_TYPE_CTL 0x0004 -#define IEEE80211_FC_TYPE_DATA 0x0008 -#define IEEE80211_FC_SUBTYPE_MASK 0x00f0 -#define IEEE80211_FC_TOFROMDS_MASK 0x0300 -#define IEEE80211_FC_TODS_MASK 0x0100 -#define IEEE80211_FC_FROMDS_MASK 0x0200 -#define IEEE80211_FC_NODS 0x0000 -#define IEEE80211_FC_TODS 0x0100 -#define IEEE80211_FC_FROMDS 0x0200 -#define IEEE80211_FC_DSTODS 0x0300 - -struct rx_radiotap_hdr { - struct ieee80211_radiotap_header hdr; - u8 flags; - u8 rate; - u8 antsignal; -} __packed; - -#define RX_RADIOTAP_PRESENT ( \ - (1 << IEEE80211_RADIOTAP_FLAGS) | \ - (1 << IEEE80211_RADIOTAP_RATE) | \ - (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ - 0) - diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c deleted file mode 100644 index e446fed7b345..000000000000 --- a/drivers/net/wireless/libertas/rx.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * This file contains the handling of RX in wlan driver. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include - -#include "defs.h" -#include "host.h" -#include "radiotap.h" -#include "decl.h" -#include "dev.h" -#include "mesh.h" - -struct eth803hdr { - u8 dest_addr[6]; - u8 src_addr[6]; - u16 h803_len; -} __packed; - -struct rfc1042hdr { - u8 llc_dsap; - u8 llc_ssap; - u8 llc_ctrl; - u8 snap_oui[3]; - u16 snap_type; -} __packed; - -struct rxpackethdr { - struct eth803hdr eth803_hdr; - struct rfc1042hdr rfc1042_hdr; -} __packed; - -struct rx80211packethdr { - struct rxpd rx_pd; - void *eth80211_hdr; -} __packed; - -static int process_rxed_802_11_packet(struct lbs_private *priv, - struct sk_buff *skb); - -/** - * lbs_process_rxed_packet - processes received packet and forwards it - * to kernel/upper layer - * - * @priv: A pointer to &struct lbs_private - * @skb: A pointer to skb which includes the received packet - * returns: 0 or -1 - */ -int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) -{ - int ret = 0; - struct net_device *dev = priv->dev; - struct rxpackethdr *p_rx_pkt; - struct rxpd *p_rx_pd; - int hdrchop; - struct ethhdr *p_ethhdr; - static const u8 rfc1042_eth_hdr[] = { - 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 - }; - - lbs_deb_enter(LBS_DEB_RX); - - BUG_ON(!skb); - - skb->ip_summed = CHECKSUM_NONE; - - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { - ret = process_rxed_802_11_packet(priv, skb); - goto done; - } - - p_rx_pd = (struct rxpd *) skb->data; - p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd + - le32_to_cpu(p_rx_pd->pkt_ptr)); - - dev = lbs_mesh_set_dev(priv, dev, p_rx_pd); - - lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, - min_t(unsigned int, skb->len, 100)); - - if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { - lbs_deb_rx("rx err: frame received with bad length\n"); - dev->stats.rx_length_errors++; - ret = -EINVAL; - dev_kfree_skb(skb); - goto done; - } - - lbs_deb_rx("rx data: skb->len - pkt_ptr = %d-%zd = %zd\n", - skb->len, (size_t)le32_to_cpu(p_rx_pd->pkt_ptr), - skb->len - (size_t)le32_to_cpu(p_rx_pd->pkt_ptr)); - - lbs_deb_hex(LBS_DEB_RX, "RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, - sizeof(p_rx_pkt->eth803_hdr.dest_addr)); - lbs_deb_hex(LBS_DEB_RX, "RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, - sizeof(p_rx_pkt->eth803_hdr.src_addr)); - - if (memcmp(&p_rx_pkt->rfc1042_hdr, - rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { - /* - * Replace the 803 header and rfc1042 header (llc/snap) with an - * EthernetII header, keep the src/dst and snap_type (ethertype) - * - * The firmware only passes up SNAP frames converting - * all RX Data from 802.11 to 802.2/LLC/SNAP frames. - * - * To create the Ethernet II, just move the src, dst address right - * before the snap_type. - */ - p_ethhdr = (struct ethhdr *) - ((u8 *) &p_rx_pkt->eth803_hdr - + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) - - sizeof(p_rx_pkt->eth803_hdr.dest_addr) - - sizeof(p_rx_pkt->eth803_hdr.src_addr) - - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); - - memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, - sizeof(p_ethhdr->h_source)); - memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, - sizeof(p_ethhdr->h_dest)); - - /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header - * that was removed - */ - hdrchop = (u8 *)p_ethhdr - (u8 *)p_rx_pd; - } else { - lbs_deb_hex(LBS_DEB_RX, "RX Data: LLC/SNAP", - (u8 *) &p_rx_pkt->rfc1042_hdr, - sizeof(p_rx_pkt->rfc1042_hdr)); - - /* Chop off the rxpd */ - hdrchop = (u8 *)&p_rx_pkt->eth803_hdr - (u8 *)p_rx_pd; - } - - /* Chop off the leading header bytes so the skb points to the start of - * either the reconstructed EthII frame or the 802.2/llc/snap frame - */ - skb_pull(skb, hdrchop); - - priv->cur_rate = lbs_fw_index_to_data_rate(p_rx_pd->rx_rate); - - lbs_deb_rx("rx data: size of actual packet %d\n", skb->len); - dev->stats.rx_bytes += skb->len; - dev->stats.rx_packets++; - - skb->protocol = eth_type_trans(skb, dev); - if (in_interrupt()) - netif_rx(skb); - else - netif_rx_ni(skb); - - ret = 0; -done: - lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(lbs_process_rxed_packet); - -/** - * convert_mv_rate_to_radiotap - converts Tx/Rx rates from Marvell WLAN format - * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) - * - * @rate: Input rate - * returns: Output Rate (0 if invalid) - */ -static u8 convert_mv_rate_to_radiotap(u8 rate) -{ - switch (rate) { - case 0: /* 1 Mbps */ - return 2; - case 1: /* 2 Mbps */ - return 4; - case 2: /* 5.5 Mbps */ - return 11; - case 3: /* 11 Mbps */ - return 22; - /* case 4: reserved */ - case 5: /* 6 Mbps */ - return 12; - case 6: /* 9 Mbps */ - return 18; - case 7: /* 12 Mbps */ - return 24; - case 8: /* 18 Mbps */ - return 36; - case 9: /* 24 Mbps */ - return 48; - case 10: /* 36 Mbps */ - return 72; - case 11: /* 48 Mbps */ - return 96; - case 12: /* 54 Mbps */ - return 108; - } - pr_alert("Invalid Marvell WLAN rate %i\n", rate); - return 0; -} - -/** - * process_rxed_802_11_packet - processes a received 802.11 packet and forwards - * it to kernel/upper layer - * - * @priv: A pointer to &struct lbs_private - * @skb: A pointer to skb which includes the received packet - * returns: 0 or -1 - */ -static int process_rxed_802_11_packet(struct lbs_private *priv, - struct sk_buff *skb) -{ - int ret = 0; - struct net_device *dev = priv->dev; - struct rx80211packethdr *p_rx_pkt; - struct rxpd *prxpd; - struct rx_radiotap_hdr radiotap_hdr; - struct rx_radiotap_hdr *pradiotap_hdr; - - lbs_deb_enter(LBS_DEB_RX); - - p_rx_pkt = (struct rx80211packethdr *) skb->data; - prxpd = &p_rx_pkt->rx_pd; - - /* lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); */ - - if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { - lbs_deb_rx("rx err: frame received with bad length\n"); - dev->stats.rx_length_errors++; - ret = -EINVAL; - kfree_skb(skb); - goto done; - } - - lbs_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n", - skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); - - /* create the exported radio header */ - - /* radiotap header */ - memset(&radiotap_hdr, 0, sizeof(radiotap_hdr)); - /* XXX must check radiotap_hdr.hdr.it_pad for pad */ - radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr)); - radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT); - radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); - /* XXX must check no carryout */ - radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; - - /* chop the rxpd */ - skb_pull(skb, sizeof(struct rxpd)); - - /* add space for the new radio header */ - if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && - pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, GFP_ATOMIC)) { - netdev_alert(dev, "%s: couldn't pskb_expand_head\n", __func__); - ret = -ENOMEM; - kfree_skb(skb); - goto done; - } - - pradiotap_hdr = (void *)skb_push(skb, sizeof(struct rx_radiotap_hdr)); - memcpy(pradiotap_hdr, &radiotap_hdr, sizeof(struct rx_radiotap_hdr)); - - priv->cur_rate = lbs_fw_index_to_data_rate(prxpd->rx_rate); - - lbs_deb_rx("rx data: size of actual packet %d\n", skb->len); - dev->stats.rx_bytes += skb->len; - dev->stats.rx_packets++; - - skb->protocol = eth_type_trans(skb, priv->dev); - - if (in_interrupt()) - netif_rx(skb); - else - netif_rx_ni(skb); - - ret = 0; - -done: - lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret); - return ret; -} diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c deleted file mode 100644 index c025f9c18282..000000000000 --- a/drivers/net/wireless/libertas/tx.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * This file contains the handling of TX in wlan driver. - */ -#include -#include -#include -#include -#include -#include - -#include "host.h" -#include "radiotap.h" -#include "decl.h" -#include "defs.h" -#include "dev.h" -#include "mesh.h" - -/** - * convert_radiotap_rate_to_mv - converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE - * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1) - * - * @rate: Input rate - * returns: Output Rate (0 if invalid) - */ -static u32 convert_radiotap_rate_to_mv(u8 rate) -{ - switch (rate) { - case 2: /* 1 Mbps */ - return 0 | (1 << 4); - case 4: /* 2 Mbps */ - return 1 | (1 << 4); - case 11: /* 5.5 Mbps */ - return 2 | (1 << 4); - case 22: /* 11 Mbps */ - return 3 | (1 << 4); - case 12: /* 6 Mbps */ - return 4 | (1 << 4); - case 18: /* 9 Mbps */ - return 5 | (1 << 4); - case 24: /* 12 Mbps */ - return 6 | (1 << 4); - case 36: /* 18 Mbps */ - return 7 | (1 << 4); - case 48: /* 24 Mbps */ - return 8 | (1 << 4); - case 72: /* 36 Mbps */ - return 9 | (1 << 4); - case 96: /* 48 Mbps */ - return 10 | (1 << 4); - case 108: /* 54 Mbps */ - return 11 | (1 << 4); - } - return 0; -} - -/** - * lbs_hard_start_xmit - checks the conditions and sends packet to IF - * layer if everything is ok - * - * @skb: A pointer to skb which includes TX packet - * @dev: A pointer to the &struct net_device - * returns: 0 or -1 - */ -netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned long flags; - struct lbs_private *priv = dev->ml_priv; - struct txpd *txpd; - char *p802x_hdr; - uint16_t pkt_len; - netdev_tx_t ret = NETDEV_TX_OK; - - lbs_deb_enter(LBS_DEB_TX); - - /* We need to protect against the queues being restarted before - we get round to stopping them */ - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->surpriseremoved) - goto free; - - if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { - lbs_deb_tx("tx err: skb length %d 0 or > %zd\n", - skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); - /* We'll never manage to send this one; drop it and return 'OK' */ - - dev->stats.tx_dropped++; - dev->stats.tx_errors++; - goto free; - } - - - netif_stop_queue(priv->dev); - if (priv->mesh_dev) - netif_stop_queue(priv->mesh_dev); - - if (priv->tx_pending_len) { - /* This can happen if packets come in on the mesh and eth - device simultaneously -- there's no mutual exclusion on - hard_start_xmit() calls between devices. */ - lbs_deb_tx("Packet on %s while busy\n", dev->name); - ret = NETDEV_TX_BUSY; - goto unlock; - } - - priv->tx_pending_len = -1; - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbs_deb_hex(LBS_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); - - txpd = (void *)priv->tx_pending_buf; - memset(txpd, 0, sizeof(struct txpd)); - - p802x_hdr = skb->data; - pkt_len = skb->len; - - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { - struct tx_radiotap_hdr *rtap_hdr = (void *)skb->data; - - /* set txpd fields from the radiotap header */ - txpd->tx_control = cpu_to_le32(convert_radiotap_rate_to_mv(rtap_hdr->rate)); - - /* skip the radiotap header */ - p802x_hdr += sizeof(*rtap_hdr); - pkt_len -= sizeof(*rtap_hdr); - - /* copy destination address from 802.11 header */ - memcpy(txpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN); - } else { - /* copy destination address from 802.3 header */ - memcpy(txpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN); - } - - txpd->tx_packet_length = cpu_to_le16(pkt_len); - txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); - - lbs_mesh_set_txpd(priv, dev, txpd); - - lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd)); - - lbs_deb_hex(LBS_DEB_TX, "Tx Data", (u8 *) p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); - - memcpy(&txpd[1], p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->tx_pending_len = pkt_len + sizeof(struct txpd); - - lbs_deb_tx("%s lined up packet\n", __func__); - - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { - /* Keep the skb to echo it back once Tx feedback is - received from FW */ - skb_orphan(skb); - - /* Keep the skb around for when we get feedback */ - priv->currenttxskb = skb; - } else { - free: - dev_kfree_skb_any(skb); - } - - unlock: - spin_unlock_irqrestore(&priv->driver_lock, flags); - wake_up(&priv->waitq); - - lbs_deb_leave_args(LBS_DEB_TX, "ret %d", ret); - return ret; -} - -/** - * lbs_send_tx_feedback - sends to the host the last transmitted packet, - * filling the radiotap headers with transmission information. - * - * @priv: A pointer to &struct lbs_private structure - * @try_count: A 32-bit value containing transmission retry status. - * - * returns: void - */ -void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count) -{ - struct tx_radiotap_hdr *radiotap_hdr; - - if (priv->wdev->iftype != NL80211_IFTYPE_MONITOR || - priv->currenttxskb == NULL) - return; - - radiotap_hdr = (struct tx_radiotap_hdr *)priv->currenttxskb->data; - - radiotap_hdr->data_retries = try_count ? - (1 + priv->txretrycount - try_count) : 0; - - priv->currenttxskb->protocol = eth_type_trans(priv->currenttxskb, - priv->dev); - netif_rx(priv->currenttxskb); - - priv->currenttxskb = NULL; - - if (priv->connect_status == LBS_CONNECTED) - netif_wake_queue(priv->dev); - - if (priv->mesh_dev && netif_running(priv->mesh_dev)) - netif_wake_queue(priv->mesh_dev); -} -EXPORT_SYMBOL_GPL(lbs_send_tx_feedback); diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h deleted file mode 100644 index cf1d9b047ee6..000000000000 --- a/drivers/net/wireless/libertas/types.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * This header file contains definition for global types - */ -#ifndef _LBS_TYPES_H_ -#define _LBS_TYPES_H_ - -#include -#include -#include - -struct ieee_ie_header { - u8 id; - u8 len; -} __packed; - -struct ieee_ie_cf_param_set { - struct ieee_ie_header header; - - u8 cfpcnt; - u8 cfpperiod; - __le16 cfpmaxduration; - __le16 cfpdurationremaining; -} __packed; - - -struct ieee_ie_ibss_param_set { - struct ieee_ie_header header; - - __le16 atimwindow; -} __packed; - -union ieee_ss_param_set { - struct ieee_ie_cf_param_set cf; - struct ieee_ie_ibss_param_set ibss; -} __packed; - -struct ieee_ie_fh_param_set { - struct ieee_ie_header header; - - __le16 dwelltime; - u8 hopset; - u8 hoppattern; - u8 hopindex; -} __packed; - -struct ieee_ie_ds_param_set { - struct ieee_ie_header header; - - u8 channel; -} __packed; - -union ieee_phy_param_set { - struct ieee_ie_fh_param_set fh; - struct ieee_ie_ds_param_set ds; -} __packed; - -/* TLV type ID definition */ -#define PROPRIETARY_TLV_BASE_ID 0x0100 - -/* Terminating TLV type */ -#define MRVL_TERMINATE_TLV_ID 0xffff - -#define TLV_TYPE_SSID 0x0000 -#define TLV_TYPE_RATES 0x0001 -#define TLV_TYPE_PHY_FH 0x0002 -#define TLV_TYPE_PHY_DS 0x0003 -#define TLV_TYPE_CF 0x0004 -#define TLV_TYPE_IBSS 0x0006 - -#define TLV_TYPE_DOMAIN 0x0007 - -#define TLV_TYPE_POWER_CAPABILITY 0x0021 - -#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) -#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) -#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) -#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) -#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) -#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) -#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) -#define TLV_TYPE_LED_GPIO (PROPRIETARY_TLV_BASE_ID + 8) -#define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) -#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) -#define TLV_TYPE_REASSOCAP (PROPRIETARY_TLV_BASE_ID + 11) -#define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) -#define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) -#define TLV_TYPE_BCASTPROBE (PROPRIETARY_TLV_BASE_ID + 14) -#define TLV_TYPE_NUMSSID_PROBE (PROPRIETARY_TLV_BASE_ID + 15) -#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) -#define TLV_TYPE_CRYPTO_DATA (PROPRIETARY_TLV_BASE_ID + 17) -#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) -#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) -#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) -#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) -#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) -#define TLV_TYPE_MESH_ID (PROPRIETARY_TLV_BASE_ID + 37) -#define TLV_TYPE_OLD_MESH_ID (PROPRIETARY_TLV_BASE_ID + 291) - -/* TLV related data structures */ -struct mrvl_ie_header { - __le16 type; - __le16 len; -} __packed; - -struct mrvl_ie_data { - struct mrvl_ie_header header; - u8 Data[1]; -} __packed; - -struct mrvl_ie_rates_param_set { - struct mrvl_ie_header header; - u8 rates[1]; -} __packed; - -struct mrvl_ie_ssid_param_set { - struct mrvl_ie_header header; - u8 ssid[1]; -} __packed; - -struct mrvl_ie_wildcard_ssid_param_set { - struct mrvl_ie_header header; - u8 MaxSsidlength; - u8 ssid[1]; -} __packed; - -struct chanscanmode { -#ifdef __BIG_ENDIAN_BITFIELD - u8 reserved_2_7:6; - u8 disablechanfilt:1; - u8 passivescan:1; -#else - u8 passivescan:1; - u8 disablechanfilt:1; - u8 reserved_2_7:6; -#endif -} __packed; - -struct chanscanparamset { - u8 radiotype; - u8 channumber; - struct chanscanmode chanscanmode; - __le16 minscantime; - __le16 maxscantime; -} __packed; - -struct mrvl_ie_chanlist_param_set { - struct mrvl_ie_header header; - struct chanscanparamset chanscanparam[1]; -} __packed; - -struct mrvl_ie_cf_param_set { - struct mrvl_ie_header header; - u8 cfpcnt; - u8 cfpperiod; - __le16 cfpmaxduration; - __le16 cfpdurationremaining; -} __packed; - -struct mrvl_ie_ds_param_set { - struct mrvl_ie_header header; - u8 channel; -} __packed; - -struct mrvl_ie_rsn_param_set { - struct mrvl_ie_header header; - u8 rsnie[1]; -} __packed; - -struct mrvl_ie_tsf_timestamp { - struct mrvl_ie_header header; - __le64 tsftable[1]; -} __packed; - -/* v9 and later firmware only */ -struct mrvl_ie_auth_type { - struct mrvl_ie_header header; - __le16 auth; -} __packed; - -/* Local Power capability */ -struct mrvl_ie_power_capability { - struct mrvl_ie_header header; - s8 minpower; - s8 maxpower; -} __packed; - -/* used in CMD_802_11_SUBSCRIBE_EVENT for SNR, RSSI and Failure */ -struct mrvl_ie_thresholds { - struct mrvl_ie_header header; - u8 value; - u8 freq; -} __packed; - -struct mrvl_ie_beacons_missed { - struct mrvl_ie_header header; - u8 beaconmissed; - u8 reserved; -} __packed; - -struct mrvl_ie_num_probes { - struct mrvl_ie_header header; - __le16 numprobes; -} __packed; - -struct mrvl_ie_bcast_probe { - struct mrvl_ie_header header; - __le16 bcastprobe; -} __packed; - -struct mrvl_ie_num_ssid_probe { - struct mrvl_ie_header header; - __le16 numssidprobe; -} __packed; - -struct led_pin { - u8 led; - u8 pin; -} __packed; - -struct mrvl_ie_ledgpio { - struct mrvl_ie_header header; - struct led_pin ledpin[1]; -} __packed; - -struct led_bhv { - uint8_t firmwarestate; - uint8_t led; - uint8_t ledstate; - uint8_t ledarg; -} __packed; - - -struct mrvl_ie_ledbhv { - struct mrvl_ie_header header; - struct led_bhv ledbhv[1]; -} __packed; - -/* - * Meant to be packed as the value member of a struct ieee80211_info_element. - * Note that the len member of the ieee80211_info_element varies depending on - * the mesh_id_len - */ -struct mrvl_meshie_val { - uint8_t oui[3]; - uint8_t type; - uint8_t subtype; - uint8_t version; - uint8_t active_protocol_id; - uint8_t active_metric_id; - uint8_t mesh_capability; - uint8_t mesh_id_len; - uint8_t mesh_id[IEEE80211_MAX_SSID_LEN]; -} __packed; - -struct mrvl_meshie { - u8 id, len; - struct mrvl_meshie_val val; -} __packed; - -struct mrvl_mesh_defaults { - __le32 bootflag; - uint8_t boottime; - uint8_t reserved; - __le16 channel; - struct mrvl_meshie meshie; -} __packed; - -#endif diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig new file mode 100644 index 000000000000..7842096bea53 --- /dev/null +++ b/drivers/net/wireless/marvell/Kconfig @@ -0,0 +1,16 @@ +config WLAN_VENDOR_MARVELL + bool "Marvell devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_MARVELL + +source "drivers/net/wireless/marvell/libertas/Kconfig" + +endif # WLAN_VENDOR_MARVELL diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile new file mode 100644 index 000000000000..6f7ac46ded33 --- /dev/null +++ b/drivers/net/wireless/marvell/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_LIBERTAS) += libertas/ diff --git a/drivers/net/wireless/marvell/libertas/Kconfig b/drivers/net/wireless/marvell/libertas/Kconfig new file mode 100644 index 000000000000..e6268ceacbf1 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/Kconfig @@ -0,0 +1,45 @@ +config LIBERTAS + tristate "Marvell 8xxx Libertas WLAN driver support" + depends on CFG80211 + select WIRELESS_EXT + select WEXT_SPY + select LIB80211 + select FW_LOADER + ---help--- + A library for Marvell Libertas 8xxx devices. + +config LIBERTAS_USB + tristate "Marvell Libertas 8388 USB 802.11b/g cards" + depends on LIBERTAS && USB + ---help--- + A driver for Marvell Libertas 8388 USB devices. + +config LIBERTAS_CS + tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards" + depends on LIBERTAS && PCMCIA && HAS_IOPORT_MAP + ---help--- + A driver for Marvell Libertas 8385 CompactFlash devices. + +config LIBERTAS_SDIO + tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards" + depends on LIBERTAS && MMC + ---help--- + A driver for Marvell Libertas 8385/8686/8688 SDIO devices. + +config LIBERTAS_SPI + tristate "Marvell Libertas 8686 SPI 802.11b/g cards" + depends on LIBERTAS && SPI + ---help--- + A driver for Marvell Libertas 8686 SPI devices. + +config LIBERTAS_DEBUG + bool "Enable full debugging output in the Libertas module." + depends on LIBERTAS + ---help--- + Debugging support. + +config LIBERTAS_MESH + bool "Enable mesh support" + depends on LIBERTAS + help + This enables Libertas' MESH support, used by e.g. the OLPC people. diff --git a/drivers/net/wireless/marvell/libertas/LICENSE b/drivers/net/wireless/marvell/libertas/LICENSE new file mode 100644 index 000000000000..8862742213b9 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/LICENSE @@ -0,0 +1,16 @@ + Copyright (c) 2003-2006, Marvell International Ltd. + All Rights Reserved + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + diff --git a/drivers/net/wireless/marvell/libertas/Makefile b/drivers/net/wireless/marvell/libertas/Makefile new file mode 100644 index 000000000000..eac72f7bd341 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/Makefile @@ -0,0 +1,21 @@ +libertas-y += cfg.o +libertas-y += cmd.o +libertas-y += cmdresp.o +libertas-y += debugfs.o +libertas-y += ethtool.o +libertas-y += main.o +libertas-y += rx.o +libertas-y += tx.o +libertas-y += firmware.o +libertas-$(CONFIG_LIBERTAS_MESH) += mesh.o + +usb8xxx-objs += if_usb.o +libertas_cs-objs += if_cs.o +libertas_sdio-objs += if_sdio.o +libertas_spi-objs += if_spi.o + +obj-$(CONFIG_LIBERTAS) += libertas.o +obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o +obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o +obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o +obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o diff --git a/drivers/net/wireless/marvell/libertas/README b/drivers/net/wireless/marvell/libertas/README new file mode 100644 index 000000000000..1a554a685e91 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/README @@ -0,0 +1,239 @@ +================================================================================ + README for Libertas + + (c) Copyright © 2003-2006, Marvell International Ltd. + All Rights Reserved + + This software file (the "File") is distributed by Marvell International + Ltd. under the terms of the GNU General Public License Version 2, June 1991 + (the "License"). You may use, redistribute and/or modify this File in + accordance with the terms and conditions of the License, a copy of which + is available along with the File in the license.txt file or on the worldwide + web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + ARE EXPRESSLY DISCLAIMED. The License provides additional details about + this warranty disclaimer. +================================================================================ + +===================== +DRIVER LOADING +===================== + + o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/ + + o. Load driver by using the following command: + + insmod usb8388.ko [fw_name=usb8388.bin] + +========================= +ETHTOOL +========================= + + +Use the -i option to retrieve version information from the driver. + +# ethtool -i eth0 +driver: libertas +version: COMM-USB8388-318.p4 +firmware-version: 5.110.7 +bus-info: + +Use the -e option to read the EEPROM contents of the card. + + Usage: + ethtool -e ethX [raw on|off] [offset N] [length N] + + -e retrieves and prints an EEPROM dump for the specified ethernet + device. When raw is enabled, then it dumps the raw EEPROM data + to stdout. The length and offset parameters allow dumping cer- + tain portions of the EEPROM. Default is to dump the entire EEP- + ROM. + +# ethtool -e eth0 offset 0 length 16 +Offset Values +------ ------ +0x0000 38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00 + +======================== +DEBUGFS COMMANDS +======================== + +those commands are used via debugfs interface + +=========== +rdmac +rdbbp +rdrf + These commands are used to read the MAC, BBP and RF registers from the + card. These commands take one parameter that specifies the offset + location that is to be read. This parameter must be specified in + hexadecimal (its possible to precede preceding the number with a "0x"). + + Path: /sys/kernel/debug/libertas_wireless/ethX/registers/ + + Usage: + echo "0xa123" > rdmac ; cat rdmac + echo "0xa123" > rdbbp ; cat rdbbp + echo "0xa123" > rdrf ; cat rdrf +wrmac +wrbbp +wrrf + These commands are used to write the MAC, BBP and RF registers in the + card. These commands take two parameters that specify the offset + location and the value that is to be written. This parameters must + be specified in hexadecimal (its possible to precede the number + with a "0x"). + + Usage: + echo "0xa123 0xaa" > wrmac + echo "0xa123 0xaa" > wrbbp + echo "0xa123 0xaa" > wrrf + +sleepparams + This command is used to set the sleepclock configurations + + Path: /sys/kernel/debug/libertas_wireless/ethX/ + + Usage: + cat sleepparams: reads the current sleepclock configuration + + echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration. + + where: + p1 is Sleep clock error in ppm (0-65535) + p2 is Wakeup offset in usec (0-65535) + p3 is Clock stabilization time in usec (0-65535) + p4 is Control periodic calibration (0-2) + p5 is Control the use of external sleep clock (0-2) + p6 is reserved for debug (0-65535) + +subscribed_events + + The subscribed_events directory contains the interface for the + subscribed events API. + + Path: /sys/kernel/debug/libertas_wireless/ethX/subscribed_events/ + + Each event is represented by a filename. Each filename consists of the + following three fields: + Value Frequency Subscribed + + To read the current values for a given event, do: + cat event + To set the current values, do: + echo "60 2 1" > event + + Frequency field specifies the reporting frequency for this event. + If it is set to 0, then the event is reported only once, and then + automatically unsubscribed. If it is set to 1, then the event is + reported every time it occurs. If it is set to N, then the event is + reported every Nth time it occurs. + + beacon_missed + Value field specifies the number of consecutive missing beacons which + triggers the LINK_LOSS event. This event is generated only once after + which the firmware resets its state. At initialization, the LINK_LOSS + event is subscribed by default. The default value of MissedBeacons is + 60. + + failure_count + Value field specifies the consecutive failure count threshold which + triggers the generation of the MAX_FAIL event. Once this event is + generated, the consecutive failure count is reset to 0. + At initialization, the MAX_FAIL event is NOT subscribed by + default. + + high_rssi + This event is generated when the average received RSSI in beacons goes + above a threshold, specified by Value. + + low_rssi + This event is generated when the average received RSSI in beacons goes + below a threshold, specified by Value. + + high_snr + This event is generated when the average received SNR in beacons goes + above a threshold, specified by Value. + + low_snr + This event is generated when the average received SNR in beacons goes + below a threshold, specified by Value. + +extscan + This command is used to do a specific scan. + + Path: /sys/kernel/debug/libertas_wireless/ethX/ + + Usage: echo "SSID" > extscan + + Example: + echo "LINKSYS-AP" > extscan + + To see the results of use getscantable command. + +getscantable + + Display the current contents of the driver scan table (ie. get the + scan results). + + Path: /sys/kernel/debug/libertas_wireless/ethX/ + + Usage: + cat getscantable + +setuserscan + Initiate a customized scan and retrieve the results + + + Path: /sys/kernel/debug/libertas_wireless/ethX/ + + Usage: + echo "[ARGS]" > setuserscan + + where [ARGS]: + + bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan + ssid="[SSID]" specify a SSID filter for the scan + keep=[0 or 1] keep the previous scan results (1), discard (0) + dur=[scan time] time to scan for each channel in milliseconds + type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) + + Any combination of the above arguments can be supplied on the command + line. If dur tokens are absent, the driver default setting will be used. + The bssid and ssid fields, if blank, will produce an unfiltered scan. + The type field will default to 3 (Any) and the keep field will default + to 0 (Discard). + + Examples: + 1) Perform a passive scan on all channels for 20 ms per channel: + echo "dur=20" > setuserscan + + 2) Perform an active scan for a specific SSID: + echo "ssid="TestAP"" > setuserscan + + 3) Scan all available channels (B/G, A bands) for a specific BSSID, keep + the current scan table intact, update existing or append new scan data: + echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan + + 4) Scan for all infrastructure networks. + Keep the previous scan table intact. Update any duplicate BSSID/SSID + matches with the new scan data: + echo "type=1 keep=1" > setuserscan + + All entries in the scan table (not just the new scan data when keep=1) + will be displayed upon completion by use of the getscantable ioctl. + +hostsleep + This command is used to enable/disable host sleep. + Note: Host sleep parameters should be configured using + "ethtool -s ethX wol X" command before enabling host sleep. + + Path: /sys/kernel/debug/libertas_wireless/ethX/ + + Usage: + cat hostsleep: reads the current hostsleep state + echo "1" > hostsleep : enable host sleep. + echo "0" > hostsleep : disable host sleep + diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c new file mode 100644 index 000000000000..8317afd065b4 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/cfg.c @@ -0,0 +1,2215 @@ +/* + * Implement cfg80211 ("iw") support. + * + * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany + * Holger Schurig + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "decl.h" +#include "cfg.h" +#include "cmd.h" +#include "mesh.h" + + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel lbs_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +#define RATETAB_ENT(_rate, _hw_value, _flags) { \ + .bitrate = (_rate), \ + .hw_value = (_hw_value), \ + .flags = (_flags), \ +} + + +/* Table 6 in section 3.2.1.1 */ +static struct ieee80211_rate lbs_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, 0), + RATETAB_ENT(55, 2, 0), + RATETAB_ENT(110, 3, 0), + RATETAB_ENT(60, 9, 0), + RATETAB_ENT(90, 6, 0), + RATETAB_ENT(120, 7, 0), + RATETAB_ENT(180, 8, 0), + RATETAB_ENT(240, 9, 0), + RATETAB_ENT(360, 10, 0), + RATETAB_ENT(480, 11, 0), + RATETAB_ENT(540, 12, 0), +}; + +static struct ieee80211_supported_band lbs_band_2ghz = { + .channels = lbs_2ghz_channels, + .n_channels = ARRAY_SIZE(lbs_2ghz_channels), + .bitrates = lbs_rates, + .n_bitrates = ARRAY_SIZE(lbs_rates), +}; + + +static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, +}; + +/* Time to stay on the channel */ +#define LBS_DWELL_PASSIVE 100 +#define LBS_DWELL_ACTIVE 40 + + +/*************************************************************************** + * Misc utility functions + * + * TLVs are Marvell specific. They are very similar to IEs, they have the + * same structure: type, length, data*. The only difference: for IEs, the + * type and length are u8, but for TLVs they're __le16. + */ + +/* + * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1 + * in the firmware spec + */ +static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type) +{ + int ret = -ENOTSUPP; + + switch (auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + case NL80211_AUTHTYPE_SHARED_KEY: + ret = auth_type; + break; + case NL80211_AUTHTYPE_AUTOMATIC: + ret = NL80211_AUTHTYPE_OPEN_SYSTEM; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + ret = 0x80; + break; + default: + /* silence compiler */ + break; + } + return ret; +} + + +/* + * Various firmware commands need the list of supported rates, but with + * the hight-bit set for basic rates + */ +static int lbs_add_rates(u8 *rates) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { + u8 rate = lbs_rates[i].bitrate / 5; + if (rate == 0x02 || rate == 0x04 || + rate == 0x0b || rate == 0x16) + rate |= 0x80; + rates[i] = rate; + } + return ARRAY_SIZE(lbs_rates); +} + + +/*************************************************************************** + * TLV utility functions + * + * TLVs are Marvell specific. They are very similar to IEs, they have the + * same structure: type, length, data*. The only difference: for IEs, the + * type and length are u8, but for TLVs they're __le16. + */ + + +/* + * Add ssid TLV + */ +#define LBS_MAX_SSID_TLV_SIZE \ + (sizeof(struct mrvl_ie_header) \ + + IEEE80211_MAX_SSID_LEN) + +static int lbs_add_ssid_tlv(u8 *tlv, const u8 *ssid, int ssid_len) +{ + struct mrvl_ie_ssid_param_set *ssid_tlv = (void *)tlv; + + /* + * TLV-ID SSID 00 00 + * length 06 00 + * ssid 4d 4e 54 45 53 54 + */ + ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID); + ssid_tlv->header.len = cpu_to_le16(ssid_len); + memcpy(ssid_tlv->ssid, ssid, ssid_len); + return sizeof(ssid_tlv->header) + ssid_len; +} + + +/* + * Add channel list TLV (section 8.4.2) + * + * Actual channel data comes from priv->wdev->wiphy->channels. + */ +#define LBS_MAX_CHANNEL_LIST_TLV_SIZE \ + (sizeof(struct mrvl_ie_header) \ + + (LBS_SCAN_BEFORE_NAP * sizeof(struct chanscanparamset))) + +static int lbs_add_channel_list_tlv(struct lbs_private *priv, u8 *tlv, + int last_channel, int active_scan) +{ + int chanscanparamsize = sizeof(struct chanscanparamset) * + (last_channel - priv->scan_channel); + + struct mrvl_ie_header *header = (void *) tlv; + + /* + * TLV-ID CHANLIST 01 01 + * length 0e 00 + * channel 00 01 00 00 00 64 00 + * radio type 00 + * channel 01 + * scan type 00 + * min scan time 00 00 + * max scan time 64 00 + * channel 2 00 02 00 00 00 64 00 + * + */ + + header->type = cpu_to_le16(TLV_TYPE_CHANLIST); + header->len = cpu_to_le16(chanscanparamsize); + tlv += sizeof(struct mrvl_ie_header); + + /* lbs_deb_scan("scan: channels %d to %d\n", priv->scan_channel, + last_channel); */ + memset(tlv, 0, chanscanparamsize); + + while (priv->scan_channel < last_channel) { + struct chanscanparamset *param = (void *) tlv; + + param->radiotype = CMD_SCAN_RADIO_TYPE_BG; + param->channumber = + priv->scan_req->channels[priv->scan_channel]->hw_value; + if (active_scan) { + param->maxscantime = cpu_to_le16(LBS_DWELL_ACTIVE); + } else { + param->chanscanmode.passivescan = 1; + param->maxscantime = cpu_to_le16(LBS_DWELL_PASSIVE); + } + tlv += sizeof(struct chanscanparamset); + priv->scan_channel++; + } + return sizeof(struct mrvl_ie_header) + chanscanparamsize; +} + + +/* + * Add rates TLV + * + * The rates are in lbs_bg_rates[], but for the 802.11b + * rates the high bit is set. We add this TLV only because + * there's a firmware which otherwise doesn't report all + * APs in range. + */ +#define LBS_MAX_RATES_TLV_SIZE \ + (sizeof(struct mrvl_ie_header) \ + + (ARRAY_SIZE(lbs_rates))) + +/* Adds a TLV with all rates the hardware supports */ +static int lbs_add_supported_rates_tlv(u8 *tlv) +{ + size_t i; + struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; + + /* + * TLV-ID RATES 01 00 + * length 0e 00 + * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c + */ + rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); + tlv += sizeof(rate_tlv->header); + i = lbs_add_rates(tlv); + tlv += i; + rate_tlv->header.len = cpu_to_le16(i); + return sizeof(rate_tlv->header) + i; +} + +/* Add common rates from a TLV and return the new end of the TLV */ +static u8 * +add_ie_rates(u8 *tlv, const u8 *ie, int *nrates) +{ + int hw, ap, ap_max = ie[1]; + u8 hw_rate; + + /* Advance past IE header */ + ie += 2; + + lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max); + + for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { + hw_rate = lbs_rates[hw].bitrate / 5; + for (ap = 0; ap < ap_max; ap++) { + if (hw_rate == (ie[ap] & 0x7f)) { + *tlv++ = ie[ap]; + *nrates = *nrates + 1; + } + } + } + return tlv; +} + +/* + * Adds a TLV with all rates the hardware *and* BSS supports. + */ +static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) +{ + struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv; + const u8 *rates_eid, *ext_rates_eid; + int n = 0; + + rcu_read_lock(); + rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); + ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); + + /* + * 01 00 TLV_TYPE_RATES + * 04 00 len + * 82 84 8b 96 rates + */ + rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES); + tlv += sizeof(rate_tlv->header); + + /* Add basic rates */ + if (rates_eid) { + tlv = add_ie_rates(tlv, rates_eid, &n); + + /* Add extended rates, if any */ + if (ext_rates_eid) + tlv = add_ie_rates(tlv, ext_rates_eid, &n); + } else { + lbs_deb_assoc("assoc: bss had no basic rate IE\n"); + /* Fallback: add basic 802.11b rates */ + *tlv++ = 0x82; + *tlv++ = 0x84; + *tlv++ = 0x8b; + *tlv++ = 0x96; + n = 4; + } + rcu_read_unlock(); + + rate_tlv->header.len = cpu_to_le16(n); + return sizeof(rate_tlv->header) + n; +} + + +/* + * Add auth type TLV. + * + * This is only needed for newer firmware (V9 and up). + */ +#define LBS_MAX_AUTH_TYPE_TLV_SIZE \ + sizeof(struct mrvl_ie_auth_type) + +static int lbs_add_auth_type_tlv(u8 *tlv, enum nl80211_auth_type auth_type) +{ + struct mrvl_ie_auth_type *auth = (void *) tlv; + + /* + * 1f 01 TLV_TYPE_AUTH_TYPE + * 01 00 len + * 01 auth type + */ + auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth->header.len = cpu_to_le16(sizeof(*auth)-sizeof(auth->header)); + auth->auth = cpu_to_le16(lbs_auth_to_authtype(auth_type)); + return sizeof(*auth); +} + + +/* + * Add channel (phy ds) TLV + */ +#define LBS_MAX_CHANNEL_TLV_SIZE \ + sizeof(struct mrvl_ie_header) + +static int lbs_add_channel_tlv(u8 *tlv, u8 channel) +{ + struct mrvl_ie_ds_param_set *ds = (void *) tlv; + + /* + * 03 00 TLV_TYPE_PHY_DS + * 01 00 len + * 06 channel + */ + ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); + ds->header.len = cpu_to_le16(sizeof(*ds)-sizeof(ds->header)); + ds->channel = channel; + return sizeof(*ds); +} + + +/* + * Add (empty) CF param TLV of the form: + */ +#define LBS_MAX_CF_PARAM_TLV_SIZE \ + sizeof(struct mrvl_ie_header) + +static int lbs_add_cf_param_tlv(u8 *tlv) +{ + struct mrvl_ie_cf_param_set *cf = (void *)tlv; + + /* + * 04 00 TLV_TYPE_CF + * 06 00 len + * 00 cfpcnt + * 00 cfpperiod + * 00 00 cfpmaxduration + * 00 00 cfpdurationremaining + */ + cf->header.type = cpu_to_le16(TLV_TYPE_CF); + cf->header.len = cpu_to_le16(sizeof(*cf)-sizeof(cf->header)); + return sizeof(*cf); +} + +/* + * Add WPA TLV + */ +#define LBS_MAX_WPA_TLV_SIZE \ + (sizeof(struct mrvl_ie_header) \ + + 128 /* TODO: I guessed the size */) + +static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) +{ + size_t tlv_len; + + /* + * We need just convert an IE to an TLV. IEs use u8 for the header, + * u8 type + * u8 len + * u8[] data + * but TLVs use __le16 instead: + * __le16 type + * __le16 len + * u8[] data + */ + *tlv++ = *ie++; + *tlv++ = 0; + tlv_len = *tlv++ = *ie++; + *tlv++ = 0; + while (tlv_len--) + *tlv++ = *ie++; + /* the TLV is two bytes larger than the IE */ + return ie_len + 2; +} + +/* + * Set Channel + */ + +static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = -ENOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", + chandef->chan->center_freq, + cfg80211_get_chandef_type(chandef)); + + if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) + goto out; + + ret = lbs_set_channel(priv, chandef->chan->hw_value); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy, + struct net_device *netdev, + struct ieee80211_channel *channel) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = -ENOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d", + netdev_name(netdev), channel->center_freq); + + if (netdev != priv->mesh_dev) + goto out; + + ret = lbs_mesh_set_channel(priv, channel->hw_value); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + +/* + * Scanning + */ + +/* + * When scanning, the firmware doesn't send a nul packet with the power-safe + * bit to the AP. So we cannot stay away from our current channel too long, + * otherwise we loose data. So take a "nap" while scanning every other + * while. + */ +#define LBS_SCAN_BEFORE_NAP 4 + + +/* + * When the firmware reports back a scan-result, it gives us an "u8 rssi", + * which isn't really an RSSI, as it becomes larger when moving away from + * the AP. Anyway, we need to convert that into mBm. + */ +#define LBS_SCAN_RSSI_TO_MBM(rssi) \ + ((-(int)rssi + 3)*100) + +static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, + struct cmd_header *resp) +{ + struct cfg80211_bss *bss; + struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp; + int bsssize; + const u8 *pos; + const u8 *tsfdesc; + int tsfsize; + int i; + int ret = -EILSEQ; + + lbs_deb_enter(LBS_DEB_CFG80211); + + bsssize = get_unaligned_le16(&scanresp->bssdescriptsize); + + lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n", + scanresp->nr_sets, bsssize, le16_to_cpu(resp->size)); + + if (scanresp->nr_sets == 0) { + ret = 0; + goto done; + } + + /* + * The general layout of the scan response is described in chapter + * 5.7.1. Basically we have a common part, then any number of BSS + * descriptor sections. Finally we have section with the same number + * of TSFs. + * + * cmd_ds_802_11_scan_rsp + * cmd_header + * pos_size + * nr_sets + * bssdesc 1 + * bssid + * rssi + * timestamp + * intvl + * capa + * IEs + * bssdesc 2 + * bssdesc n + * MrvlIEtypes_TsfFimestamp_t + * TSF for BSS 1 + * TSF for BSS 2 + * TSF for BSS n + */ + + pos = scanresp->bssdesc_and_tlvbuffer; + + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer, + scanresp->bssdescriptsize); + + tsfdesc = pos + bsssize; + tsfsize = 4 + 8 * scanresp->nr_sets; + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize); + + /* Validity check: we expect a Marvell-Local TLV */ + i = get_unaligned_le16(tsfdesc); + tsfdesc += 2; + if (i != TLV_TYPE_TSFTIMESTAMP) { + lbs_deb_scan("scan response: invalid TSF Timestamp %d\n", i); + goto done; + } + + /* + * Validity check: the TLV holds TSF values with 8 bytes each, so + * the size in the TLV must match the nr_sets value + */ + i = get_unaligned_le16(tsfdesc); + tsfdesc += 2; + if (i / 8 != scanresp->nr_sets) { + lbs_deb_scan("scan response: invalid number of TSF timestamp " + "sets (expected %d got %d)\n", scanresp->nr_sets, + i / 8); + goto done; + } + + for (i = 0; i < scanresp->nr_sets; i++) { + const u8 *bssid; + const u8 *ie; + int left; + int ielen; + int rssi; + u16 intvl; + u16 capa; + int chan_no = -1; + const u8 *ssid = NULL; + u8 ssid_len = 0; + + int len = get_unaligned_le16(pos); + pos += 2; + + /* BSSID */ + bssid = pos; + pos += ETH_ALEN; + /* RSSI */ + rssi = *pos++; + /* Packet time stamp */ + pos += 8; + /* Beacon interval */ + intvl = get_unaligned_le16(pos); + pos += 2; + /* Capabilities */ + capa = get_unaligned_le16(pos); + pos += 2; + + /* To find out the channel, we must parse the IEs */ + ie = pos; + /* + * 6+1+8+2+2: size of BSSID, RSSI, time stamp, beacon + * interval, capabilities + */ + ielen = left = len - (6 + 1 + 8 + 2 + 2); + while (left >= 2) { + u8 id, elen; + id = *pos++; + elen = *pos++; + left -= 2; + if (elen > left) { + lbs_deb_scan("scan response: invalid IE fmt\n"); + goto done; + } + + if (id == WLAN_EID_DS_PARAMS) + chan_no = *pos; + if (id == WLAN_EID_SSID) { + ssid = pos; + ssid_len = elen; + } + left -= elen; + pos += elen; + } + + /* No channel, no luck */ + if (chan_no != -1) { + struct wiphy *wiphy = priv->wdev->wiphy; + int freq = ieee80211_channel_to_frequency(chan_no, + IEEE80211_BAND_2GHZ); + struct ieee80211_channel *channel = + ieee80211_get_channel(wiphy, freq); + + lbs_deb_scan("scan: %pM, capa %04x, chan %2d, %*pE, %d dBm\n", + bssid, capa, chan_no, ssid_len, ssid, + LBS_SCAN_RSSI_TO_MBM(rssi)/100); + + if (channel && + !(channel->flags & IEEE80211_CHAN_DISABLED)) { + bss = cfg80211_inform_bss(wiphy, channel, + CFG80211_BSS_FTYPE_UNKNOWN, + bssid, get_unaligned_le64(tsfdesc), + capa, intvl, ie, ielen, + LBS_SCAN_RSSI_TO_MBM(rssi), + GFP_KERNEL); + cfg80211_put_bss(wiphy, bss); + } + } else + lbs_deb_scan("scan response: missing BSS channel IE\n"); + + tsfdesc += 8; + } + ret = 0; + + done: + lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret); + return ret; +} + + +/* + * Our scan command contains a TLV, consting of a SSID TLV, a channel list + * TLV and a rates TLV. Determine the maximum size of them: + */ +#define LBS_SCAN_MAX_CMD_SIZE \ + (sizeof(struct cmd_ds_802_11_scan) \ + + LBS_MAX_SSID_TLV_SIZE \ + + LBS_MAX_CHANNEL_LIST_TLV_SIZE \ + + LBS_MAX_RATES_TLV_SIZE) + +/* + * Assumes priv->scan_req is initialized and valid + * Assumes priv->scan_channel is initialized + */ +static void lbs_scan_worker(struct work_struct *work) +{ + struct lbs_private *priv = + container_of(work, struct lbs_private, scan_work.work); + struct cmd_ds_802_11_scan *scan_cmd; + u8 *tlv; /* pointer into our current, growing TLV storage area */ + int last_channel; + int running, carrier; + + lbs_deb_enter(LBS_DEB_SCAN); + + scan_cmd = kzalloc(LBS_SCAN_MAX_CMD_SIZE, GFP_KERNEL); + if (scan_cmd == NULL) + goto out_no_scan_cmd; + + /* prepare fixed part of scan command */ + scan_cmd->bsstype = CMD_BSS_TYPE_ANY; + + /* stop network while we're away from our main channel */ + running = !netif_queue_stopped(priv->dev); + carrier = netif_carrier_ok(priv->dev); + if (running) + netif_stop_queue(priv->dev); + if (carrier) + netif_carrier_off(priv->dev); + + /* prepare fixed part of scan command */ + tlv = scan_cmd->tlvbuffer; + + /* add SSID TLV */ + if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0) + tlv += lbs_add_ssid_tlv(tlv, + priv->scan_req->ssids[0].ssid, + priv->scan_req->ssids[0].ssid_len); + + /* add channel TLVs */ + last_channel = priv->scan_channel + LBS_SCAN_BEFORE_NAP; + if (last_channel > priv->scan_req->n_channels) + last_channel = priv->scan_req->n_channels; + tlv += lbs_add_channel_list_tlv(priv, tlv, last_channel, + priv->scan_req->n_ssids); + + /* add rates TLV */ + tlv += lbs_add_supported_rates_tlv(tlv); + + if (priv->scan_channel < priv->scan_req->n_channels) { + cancel_delayed_work(&priv->scan_work); + if (netif_running(priv->dev)) + queue_delayed_work(priv->work_thread, &priv->scan_work, + msecs_to_jiffies(300)); + } + + /* This is the final data we are about to send */ + scan_cmd->hdr.size = cpu_to_le16(tlv - (u8 *)scan_cmd); + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd, + sizeof(*scan_cmd)); + lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer, + tlv - scan_cmd->tlvbuffer); + + __lbs_cmd(priv, CMD_802_11_SCAN, &scan_cmd->hdr, + le16_to_cpu(scan_cmd->hdr.size), + lbs_ret_scan, 0); + + if (priv->scan_channel >= priv->scan_req->n_channels) { + /* Mark scan done */ + cancel_delayed_work(&priv->scan_work); + lbs_scan_done(priv); + } + + /* Restart network */ + if (carrier) + netif_carrier_on(priv->dev); + if (running && !priv->tx_pending_len) + netif_wake_queue(priv->dev); + + kfree(scan_cmd); + + /* Wake up anything waiting on scan completion */ + if (priv->scan_req == NULL) { + lbs_deb_scan("scan: waking up waiters\n"); + wake_up_all(&priv->scan_q); + } + + out_no_scan_cmd: + lbs_deb_leave(LBS_DEB_SCAN); +} + +static void _internal_start_scan(struct lbs_private *priv, bool internal, + struct cfg80211_scan_request *request) +{ + lbs_deb_enter(LBS_DEB_CFG80211); + + lbs_deb_scan("scan: ssids %d, channels %d, ie_len %zd\n", + request->n_ssids, request->n_channels, request->ie_len); + + priv->scan_channel = 0; + priv->scan_req = request; + priv->internal_scan = internal; + + queue_delayed_work(priv->work_thread, &priv->scan_work, + msecs_to_jiffies(50)); + + lbs_deb_leave(LBS_DEB_CFG80211); +} + +/* + * Clean up priv->scan_req. Should be used to handle the allocation details. + */ +void lbs_scan_done(struct lbs_private *priv) +{ + WARN_ON(!priv->scan_req); + + if (priv->internal_scan) + kfree(priv->scan_req); + else + cfg80211_scan_done(priv->scan_req, false); + + priv->scan_req = NULL; +} + +static int lbs_cfg_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = 0; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (priv->scan_req || delayed_work_pending(&priv->scan_work)) { + /* old scan request not yet processed */ + ret = -EAGAIN; + goto out; + } + + _internal_start_scan(priv, false, request); + + if (priv->surpriseremoved) + ret = -EIO; + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + + +/* + * Events + */ + +void lbs_send_disconnect_notification(struct lbs_private *priv, + bool locally_generated) +{ + lbs_deb_enter(LBS_DEB_CFG80211); + + cfg80211_disconnected(priv->dev, 0, NULL, 0, locally_generated, + GFP_KERNEL); + + lbs_deb_leave(LBS_DEB_CFG80211); +} + +void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event) +{ + lbs_deb_enter(LBS_DEB_CFG80211); + + cfg80211_michael_mic_failure(priv->dev, + priv->assoc_bss, + event == MACREG_INT_CODE_MIC_ERR_MULTICAST ? + NL80211_KEYTYPE_GROUP : + NL80211_KEYTYPE_PAIRWISE, + -1, + NULL, + GFP_KERNEL); + + lbs_deb_leave(LBS_DEB_CFG80211); +} + + + + +/* + * Connect/disconnect + */ + + +/* + * This removes all WEP keys + */ +static int lbs_remove_wep_keys(struct lbs_private *priv) +{ + struct cmd_ds_802_11_set_wep cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CFG80211); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.keyindex = cpu_to_le16(priv->wep_tx_key); + cmd.action = cpu_to_le16(CMD_ACT_REMOVE); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); + + lbs_deb_leave(LBS_DEB_CFG80211); + return ret; +} + +/* + * Set WEP keys + */ +static int lbs_set_wep_keys(struct lbs_private *priv) +{ + struct cmd_ds_802_11_set_wep cmd; + int i; + int ret; + + lbs_deb_enter(LBS_DEB_CFG80211); + + /* + * command 13 00 + * size 50 00 + * sequence xx xx + * result 00 00 + * action 02 00 ACT_ADD + * transmit key 00 00 + * type for key 1 01 WEP40 + * type for key 2 00 + * type for key 3 00 + * type for key 4 00 + * key 1 39 39 39 39 39 00 00 00 + * 00 00 00 00 00 00 00 00 + * key 2 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * key 3 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * key 4 00 00 00 00 00 00 00 00 + */ + if (priv->wep_key_len[0] || priv->wep_key_len[1] || + priv->wep_key_len[2] || priv->wep_key_len[3]) { + /* Only set wep keys if we have at least one of them */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.keyindex = cpu_to_le16(priv->wep_tx_key); + cmd.action = cpu_to_le16(CMD_ACT_ADD); + + for (i = 0; i < 4; i++) { + switch (priv->wep_key_len[i]) { + case WLAN_KEY_LEN_WEP40: + cmd.keytype[i] = CMD_TYPE_WEP_40_BIT; + break; + case WLAN_KEY_LEN_WEP104: + cmd.keytype[i] = CMD_TYPE_WEP_104_BIT; + break; + default: + cmd.keytype[i] = 0; + break; + } + memcpy(cmd.keymaterial[i], priv->wep_key[i], + priv->wep_key_len[i]); + } + + ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd); + } else { + /* Otherwise remove all wep keys */ + ret = lbs_remove_wep_keys(priv); + } + + lbs_deb_leave(LBS_DEB_CFG80211); + return ret; +} + + +/* + * Enable/Disable RSN status + */ +static int lbs_enable_rsn(struct lbs_private *priv, int enable) +{ + struct cmd_ds_802_11_enable_rsn cmd; + int ret; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", enable); + + /* + * cmd 2f 00 + * size 0c 00 + * sequence xx xx + * result 00 00 + * action 01 00 ACT_SET + * enable 01 00 + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.enable = cpu_to_le16(enable); + + ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd); + + lbs_deb_leave(LBS_DEB_CFG80211); + return ret; +} + + +/* + * Set WPA/WPA key material + */ + +/* + * like "struct cmd_ds_802_11_key_material", but with cmd_header. Once we + * get rid of WEXT, this should go into host.h + */ + +struct cmd_key_material { + struct cmd_header hdr; + + __le16 action; + struct MrvlIEtype_keyParamSet param; +} __packed; + +static int lbs_set_key_material(struct lbs_private *priv, + int key_type, int key_info, + const u8 *key, u16 key_len) +{ + struct cmd_key_material cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CFG80211); + + /* + * Example for WPA (TKIP): + * + * cmd 5e 00 + * size 34 00 + * sequence xx xx + * result 00 00 + * action 01 00 + * TLV type 00 01 key param + * length 00 26 + * key type 01 00 TKIP + * key info 06 00 UNICAST | ENABLED + * key len 20 00 + * key 32 bytes + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.param.type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + cmd.param.length = cpu_to_le16(sizeof(cmd.param) - 4); + cmd.param.keytypeid = cpu_to_le16(key_type); + cmd.param.keyinfo = cpu_to_le16(key_info); + cmd.param.keylen = cpu_to_le16(key_len); + if (key && key_len) + memcpy(cmd.param.key, key, key_len); + + ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd); + + lbs_deb_leave(LBS_DEB_CFG80211); + return ret; +} + + +/* + * Sets the auth type (open, shared, etc) in the firmware. That + * we use CMD_802_11_AUTHENTICATE is misleading, this firmware + * command doesn't send an authentication frame at all, it just + * stores the auth_type. + */ +static int lbs_set_authtype(struct lbs_private *priv, + struct cfg80211_connect_params *sme) +{ + struct cmd_ds_802_11_authenticate cmd; + int ret; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "%d", sme->auth_type); + + /* + * cmd 11 00 + * size 19 00 + * sequence xx xx + * result 00 00 + * BSS id 00 13 19 80 da 30 + * auth type 00 + * reserved 00 00 00 00 00 00 00 00 00 00 + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + if (sme->bssid) + memcpy(cmd.bssid, sme->bssid, ETH_ALEN); + /* convert auth_type */ + ret = lbs_auth_to_authtype(sme->auth_type); + if (ret < 0) + goto done; + + cmd.authtype = ret; + ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd); + + done: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + +/* + * Create association request + */ +#define LBS_ASSOC_MAX_CMD_SIZE \ + (sizeof(struct cmd_ds_802_11_associate) \ + - 512 /* cmd_ds_802_11_associate.iebuf */ \ + + LBS_MAX_SSID_TLV_SIZE \ + + LBS_MAX_CHANNEL_TLV_SIZE \ + + LBS_MAX_CF_PARAM_TLV_SIZE \ + + LBS_MAX_AUTH_TYPE_TLV_SIZE \ + + LBS_MAX_WPA_TLV_SIZE) + +static int lbs_associate(struct lbs_private *priv, + struct cfg80211_bss *bss, + struct cfg80211_connect_params *sme) +{ + struct cmd_ds_802_11_associate_response *resp; + struct cmd_ds_802_11_associate *cmd = kzalloc(LBS_ASSOC_MAX_CMD_SIZE, + GFP_KERNEL); + const u8 *ssid_eid; + size_t len, resp_ie_len; + int status; + int ret; + u8 *pos = &(cmd->iebuf[0]); + u8 *tmp; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (!cmd) { + ret = -ENOMEM; + goto done; + } + + /* + * cmd 50 00 + * length 34 00 + * sequence xx xx + * result 00 00 + * BSS id 00 13 19 80 da 30 + * capabilities 11 00 + * listen interval 0a 00 + * beacon interval 00 00 + * DTIM period 00 + * TLVs xx (up to 512 bytes) + */ + cmd->hdr.command = cpu_to_le16(CMD_802_11_ASSOCIATE); + + /* Fill in static fields */ + memcpy(cmd->bssid, bss->bssid, ETH_ALEN); + cmd->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL); + cmd->capability = cpu_to_le16(bss->capability); + + /* add SSID TLV */ + rcu_read_lock(); + ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (ssid_eid) + pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); + else + lbs_deb_assoc("no SSID\n"); + rcu_read_unlock(); + + /* add DS param TLV */ + if (bss->channel) + pos += lbs_add_channel_tlv(pos, bss->channel->hw_value); + else + lbs_deb_assoc("no channel\n"); + + /* add (empty) CF param TLV */ + pos += lbs_add_cf_param_tlv(pos); + + /* add rates TLV */ + tmp = pos + 4; /* skip Marvell IE header */ + pos += lbs_add_common_rates_tlv(pos, bss); + lbs_deb_hex(LBS_DEB_ASSOC, "Common Rates", tmp, pos - tmp); + + /* add auth type TLV */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) >= 9) + pos += lbs_add_auth_type_tlv(pos, sme->auth_type); + + /* add WPA/WPA2 TLV */ + if (sme->ie && sme->ie_len) + pos += lbs_add_wpa_tlv(pos, sme->ie, sme->ie_len); + + len = (sizeof(*cmd) - sizeof(cmd->iebuf)) + + (u16)(pos - (u8 *) &cmd->iebuf); + cmd->hdr.size = cpu_to_le16(len); + + lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_CMD", (u8 *) cmd, + le16_to_cpu(cmd->hdr.size)); + + /* store for later use */ + memcpy(priv->assoc_bss, bss->bssid, ETH_ALEN); + + ret = lbs_cmd_with_response(priv, CMD_802_11_ASSOCIATE, cmd); + if (ret) + goto done; + + /* generate connect message to cfg80211 */ + + resp = (void *) cmd; /* recast for easier field access */ + status = le16_to_cpu(resp->statuscode); + + /* Older FW versions map the IEEE 802.11 Status Code in the association + * response to the following values returned in resp->statuscode: + * + * IEEE Status Code Marvell Status Code + * 0 -> 0x0000 ASSOC_RESULT_SUCCESS + * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED + * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED + * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED + * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED + * others -> 0x0003 ASSOC_RESULT_REFUSED + * + * Other response codes: + * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused) + * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for + * association response from the AP) + */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { + switch (status) { + case 0: + break; + case 1: + lbs_deb_assoc("invalid association parameters\n"); + status = WLAN_STATUS_CAPS_UNSUPPORTED; + break; + case 2: + lbs_deb_assoc("timer expired while waiting for AP\n"); + status = WLAN_STATUS_AUTH_TIMEOUT; + break; + case 3: + lbs_deb_assoc("association refused by AP\n"); + status = WLAN_STATUS_ASSOC_DENIED_UNSPEC; + break; + case 4: + lbs_deb_assoc("authentication refused by AP\n"); + status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + break; + default: + lbs_deb_assoc("association failure %d\n", status); + /* v5 OLPC firmware does return the AP status code if + * it's not one of the values above. Let that through. + */ + break; + } + } + + lbs_deb_assoc("status %d, statuscode 0x%04x, capability 0x%04x, " + "aid 0x%04x\n", status, le16_to_cpu(resp->statuscode), + le16_to_cpu(resp->capability), le16_to_cpu(resp->aid)); + + resp_ie_len = le16_to_cpu(resp->hdr.size) + - sizeof(resp->hdr) + - 6; + cfg80211_connect_result(priv->dev, + priv->assoc_bss, + sme->ie, sme->ie_len, + resp->iebuf, resp_ie_len, + status, + GFP_KERNEL); + + if (status == 0) { + /* TODO: get rid of priv->connect_status */ + priv->connect_status = LBS_CONNECTED; + netif_carrier_on(priv->dev); + if (!priv->tx_pending_len) + netif_tx_wake_all_queues(priv->dev); + } + + kfree(cmd); +done: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +static struct cfg80211_scan_request * +_new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme) +{ + struct cfg80211_scan_request *creq = NULL; + int i, n_channels = ieee80211_get_num_supported_channels(wiphy); + enum ieee80211_band band; + + creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + + n_channels * sizeof(void *), + GFP_ATOMIC); + if (!creq) + return NULL; + + /* SSIDs come after channels */ + creq->ssids = (void *)&creq->channels[n_channels]; + creq->n_channels = n_channels; + creq->n_ssids = 1; + + /* Scan all available channels */ + i = 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + int j; + + if (!wiphy->bands[band]) + continue; + + for (j = 0; j < wiphy->bands[band]->n_channels; j++) { + /* ignore disabled channels */ + if (wiphy->bands[band]->channels[j].flags & + IEEE80211_CHAN_DISABLED) + continue; + + creq->channels[i] = &wiphy->bands[band]->channels[j]; + i++; + } + } + if (i) { + /* Set real number of channels specified in creq->channels[] */ + creq->n_channels = i; + + /* Scan for the SSID we're going to connect to */ + memcpy(creq->ssids[0].ssid, sme->ssid, sme->ssid_len); + creq->ssids[0].ssid_len = sme->ssid_len; + } else { + /* No channels found... */ + kfree(creq); + creq = NULL; + } + + return creq; +} + +static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + struct cfg80211_bss *bss = NULL; + int ret = 0; + u8 preamble = RADIO_PREAMBLE_SHORT; + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (!sme->bssid) { + struct cfg80211_scan_request *creq; + + /* + * Scan for the requested network after waiting for existing + * scans to finish. + */ + lbs_deb_assoc("assoc: waiting for existing scans\n"); + wait_event_interruptible_timeout(priv->scan_q, + (priv->scan_req == NULL), + (15 * HZ)); + + creq = _new_connect_scan_req(wiphy, sme); + if (!creq) { + ret = -EINVAL; + goto done; + } + + lbs_deb_assoc("assoc: scanning for compatible AP\n"); + _internal_start_scan(priv, true, creq); + + lbs_deb_assoc("assoc: waiting for scan to complete\n"); + wait_event_interruptible_timeout(priv->scan_q, + (priv->scan_req == NULL), + (15 * HZ)); + lbs_deb_assoc("assoc: scanning completed\n"); + } + + /* Find the BSS we want using available scan results */ + bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, + sme->ssid, sme->ssid_len, IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY_ANY); + if (!bss) { + wiphy_err(wiphy, "assoc: bss %pM not in scan results\n", + sme->bssid); + ret = -ENOENT; + goto done; + } + lbs_deb_assoc("trying %pM\n", bss->bssid); + lbs_deb_assoc("cipher 0x%x, key index %d, key len %d\n", + sme->crypto.cipher_group, + sme->key_idx, sme->key_len); + + /* As this is a new connection, clear locally stored WEP keys */ + priv->wep_tx_key = 0; + memset(priv->wep_key, 0, sizeof(priv->wep_key)); + memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); + + /* set/remove WEP keys */ + switch (sme->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* Store provided WEP keys in priv-> */ + priv->wep_tx_key = sme->key_idx; + priv->wep_key_len[sme->key_idx] = sme->key_len; + memcpy(priv->wep_key[sme->key_idx], sme->key, sme->key_len); + /* Set WEP keys and WEP mode */ + lbs_set_wep_keys(priv); + priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE; + lbs_set_mac_control(priv); + /* No RSN mode for WEP */ + lbs_enable_rsn(priv, 0); + break; + case 0: /* there's no WLAN_CIPHER_SUITE_NONE definition */ + /* + * If we don't have no WEP, no WPA and no WPA2, + * we remove all keys like in the WPA/WPA2 setup, + * we just don't set RSN. + * + * Therefore: fall-through + */ + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + /* Remove WEP keys and WEP mode */ + lbs_remove_wep_keys(priv); + priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE; + lbs_set_mac_control(priv); + + /* clear the WPA/WPA2 keys */ + lbs_set_key_material(priv, + KEY_TYPE_ID_WEP, /* doesn't matter */ + KEY_INFO_WPA_UNICAST, + NULL, 0); + lbs_set_key_material(priv, + KEY_TYPE_ID_WEP, /* doesn't matter */ + KEY_INFO_WPA_MCAST, + NULL, 0); + /* RSN mode for WPA/WPA2 */ + lbs_enable_rsn(priv, sme->crypto.cipher_group != 0); + break; + default: + wiphy_err(wiphy, "unsupported cipher group 0x%x\n", + sme->crypto.cipher_group); + ret = -ENOTSUPP; + goto done; + } + + ret = lbs_set_authtype(priv, sme); + if (ret == -ENOTSUPP) { + wiphy_err(wiphy, "unsupported authtype 0x%x\n", sme->auth_type); + goto done; + } + + lbs_set_radio(priv, preamble, 1); + + /* Do the actual association */ + ret = lbs_associate(priv, bss, sme); + + done: + if (bss) + cfg80211_put_bss(wiphy, bss); + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +int lbs_disconnect(struct lbs_private *priv, u16 reason) +{ + struct cmd_ds_802_11_deauthenticate cmd; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + /* Mildly ugly to use a locally store my own BSSID ... */ + memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN); + cmd.reasoncode = cpu_to_le16(reason); + + ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd); + if (ret) + return ret; + + cfg80211_disconnected(priv->dev, + reason, + NULL, 0, true, + GFP_KERNEL); + priv->connect_status = LBS_DISCONNECTED; + + return 0; +} + +static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code); + + /* store for lbs_cfg_ret_disconnect() */ + priv->disassoc_reason = reason_code; + + return lbs_disconnect(priv, reason_code); +} + +static int lbs_cfg_set_default_key(struct wiphy *wiphy, + struct net_device *netdev, + u8 key_index, bool unicast, + bool multicast) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + if (netdev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (key_index != priv->wep_tx_key) { + lbs_deb_assoc("set_default_key: to %d\n", key_index); + priv->wep_tx_key = key_index; + lbs_set_wep_keys(priv); + } + + return 0; +} + + +static int lbs_cfg_add_key(struct wiphy *wiphy, struct net_device *netdev, + u8 idx, bool pairwise, const u8 *mac_addr, + struct key_params *params) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + u16 key_info; + u16 key_type; + int ret = 0; + + if (netdev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter(LBS_DEB_CFG80211); + + lbs_deb_assoc("add_key: cipher 0x%x, mac_addr %pM\n", + params->cipher, mac_addr); + lbs_deb_assoc("add_key: key index %d, key len %d\n", + idx, params->key_len); + if (params->key_len) + lbs_deb_hex(LBS_DEB_CFG80211, "KEY", + params->key, params->key_len); + + lbs_deb_assoc("add_key: seq len %d\n", params->seq_len); + if (params->seq_len) + lbs_deb_hex(LBS_DEB_CFG80211, "SEQ", + params->seq, params->seq_len); + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* actually compare if something has changed ... */ + if ((priv->wep_key_len[idx] != params->key_len) || + memcmp(priv->wep_key[idx], + params->key, params->key_len) != 0) { + priv->wep_key_len[idx] = params->key_len; + memcpy(priv->wep_key[idx], + params->key, params->key_len); + lbs_set_wep_keys(priv); + } + break; + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + key_info = KEY_INFO_WPA_ENABLED | ((idx == 0) + ? KEY_INFO_WPA_UNICAST + : KEY_INFO_WPA_MCAST); + key_type = (params->cipher == WLAN_CIPHER_SUITE_TKIP) + ? KEY_TYPE_ID_TKIP + : KEY_TYPE_ID_AES; + lbs_set_key_material(priv, + key_type, + key_info, + params->key, params->key_len); + break; + default: + wiphy_err(wiphy, "unhandled cipher 0x%x\n", params->cipher); + ret = -ENOTSUPP; + break; + } + + return ret; +} + + +static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool pairwise, const u8 *mac_addr) +{ + + lbs_deb_enter(LBS_DEB_CFG80211); + + lbs_deb_assoc("del_key: key_idx %d, mac_addr %pM\n", + key_index, mac_addr); + +#ifdef TODO + struct lbs_private *priv = wiphy_priv(wiphy); + /* + * I think can keep this a NO-OP, because: + + * - we clear all keys whenever we do lbs_cfg_connect() anyway + * - neither "iw" nor "wpa_supplicant" won't call this during + * an ongoing connection + * - TODO: but I have to check if this is still true when + * I set the AP to periodic re-keying + * - we've not kzallec() something when we've added a key at + * lbs_cfg_connect() or lbs_cfg_add_key(). + * + * This causes lbs_cfg_del_key() only called at disconnect time, + * where we'd just waste time deleting a key that is not going + * to be used anyway. + */ + if (key_index < 3 && priv->wep_key_len[key_index]) { + priv->wep_key_len[key_index] = 0; + lbs_set_wep_keys(priv); + } +#endif + + return 0; +} + + +/* + * Get station + */ + +static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + s8 signal, noise; + int ret; + size_t i; + + lbs_deb_enter(LBS_DEB_CFG80211); + + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_RX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS); + sinfo->tx_bytes = priv->dev->stats.tx_bytes; + sinfo->tx_packets = priv->dev->stats.tx_packets; + sinfo->rx_bytes = priv->dev->stats.rx_bytes; + sinfo->rx_packets = priv->dev->stats.rx_packets; + + /* Get current RSSI */ + ret = lbs_get_rssi(priv, &signal, &noise); + if (ret == 0) { + sinfo->signal = signal; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); + } + + /* Convert priv->cur_rate from hw_value to NL80211 value */ + for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { + if (priv->cur_rate == lbs_rates[i].hw_value) { + sinfo->txrate.legacy = lbs_rates[i].bitrate; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); + break; + } + } + + return 0; +} + + + + +/* + * Change interface + */ + +static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = 0; + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + switch (type) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + break; + default: + return -EOPNOTSUPP; + } + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (priv->iface_running) + ret = lbs_set_iface_type(priv, type); + + if (!ret) + priv->wdev->iftype = type; + + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + +/* + * IBSS (Ad-Hoc) + */ + +/* + * The firmware needs the following bits masked out of the beacon-derived + * capability field when associating/joining to a BSS: + * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused) + */ +#define CAPINFO_MASK (~(0xda00)) + + +static void lbs_join_post(struct lbs_private *priv, + struct cfg80211_ibss_params *params, + u8 *bssid, u16 capability) +{ + u8 fake_ie[2 + IEEE80211_MAX_SSID_LEN + /* ssid */ + 2 + 4 + /* basic rates */ + 2 + 1 + /* DS parameter */ + 2 + 2 + /* atim */ + 2 + 8]; /* extended rates */ + u8 *fake = fake_ie; + struct cfg80211_bss *bss; + + lbs_deb_enter(LBS_DEB_CFG80211); + + /* + * For cfg80211_inform_bss, we'll need a fake IE, as we can't get + * the real IE from the firmware. So we fabricate a fake IE based on + * what the firmware actually sends (sniffed with wireshark). + */ + /* Fake SSID IE */ + *fake++ = WLAN_EID_SSID; + *fake++ = params->ssid_len; + memcpy(fake, params->ssid, params->ssid_len); + fake += params->ssid_len; + /* Fake supported basic rates IE */ + *fake++ = WLAN_EID_SUPP_RATES; + *fake++ = 4; + *fake++ = 0x82; + *fake++ = 0x84; + *fake++ = 0x8b; + *fake++ = 0x96; + /* Fake DS channel IE */ + *fake++ = WLAN_EID_DS_PARAMS; + *fake++ = 1; + *fake++ = params->chandef.chan->hw_value; + /* Fake IBSS params IE */ + *fake++ = WLAN_EID_IBSS_PARAMS; + *fake++ = 2; + *fake++ = 0; /* ATIM=0 */ + *fake++ = 0; + /* Fake extended rates IE, TODO: don't add this for 802.11b only, + * but I don't know how this could be checked */ + *fake++ = WLAN_EID_EXT_SUPP_RATES; + *fake++ = 8; + *fake++ = 0x0c; + *fake++ = 0x12; + *fake++ = 0x18; + *fake++ = 0x24; + *fake++ = 0x30; + *fake++ = 0x48; + *fake++ = 0x60; + *fake++ = 0x6c; + lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie); + + bss = cfg80211_inform_bss(priv->wdev->wiphy, + params->chandef.chan, + CFG80211_BSS_FTYPE_UNKNOWN, + bssid, + 0, + capability, + params->beacon_interval, + fake_ie, fake - fake_ie, + 0, GFP_KERNEL); + cfg80211_put_bss(priv->wdev->wiphy, bss); + + memcpy(priv->wdev->ssid, params->ssid, params->ssid_len); + priv->wdev->ssid_len = params->ssid_len; + + cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan, + GFP_KERNEL); + + /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */ + priv->connect_status = LBS_CONNECTED; + netif_carrier_on(priv->dev); + if (!priv->tx_pending_len) + netif_wake_queue(priv->dev); + + lbs_deb_leave(LBS_DEB_CFG80211); +} + +static int lbs_ibss_join_existing(struct lbs_private *priv, + struct cfg80211_ibss_params *params, + struct cfg80211_bss *bss) +{ + const u8 *rates_eid; + struct cmd_ds_802_11_ad_hoc_join cmd; + u8 preamble = RADIO_PREAMBLE_SHORT; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CFG80211); + + /* TODO: set preamble based on scan result */ + ret = lbs_set_radio(priv, preamble, 1); + if (ret) + goto out; + + /* + * Example CMD_802_11_AD_HOC_JOIN command: + * + * command 2c 00 CMD_802_11_AD_HOC_JOIN + * size 65 00 + * sequence xx xx + * result 00 00 + * bssid 02 27 27 97 2f 96 + * ssid 49 42 53 53 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * type 02 CMD_BSS_TYPE_IBSS + * beacon period 64 00 + * dtim period 00 + * timestamp 00 00 00 00 00 00 00 00 + * localtime 00 00 00 00 00 00 00 00 + * IE DS 03 + * IE DS len 01 + * IE DS channel 01 + * reserveed 00 00 00 00 + * IE IBSS 06 + * IE IBSS len 02 + * IE IBSS atim 00 00 + * reserved 00 00 00 00 + * capability 02 00 + * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c 00 + * fail timeout ff 00 + * probe delay 00 00 + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + + memcpy(cmd.bss.bssid, bss->bssid, ETH_ALEN); + memcpy(cmd.bss.ssid, params->ssid, params->ssid_len); + cmd.bss.type = CMD_BSS_TYPE_IBSS; + cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval); + cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS; + cmd.bss.ds.header.len = 1; + cmd.bss.ds.channel = params->chandef.chan->hw_value; + cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS; + cmd.bss.ibss.header.len = 2; + cmd.bss.ibss.atimwindow = 0; + cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK); + + /* set rates to the intersection of our rates and the rates in the + bss */ + rcu_read_lock(); + rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); + if (!rates_eid) { + lbs_add_rates(cmd.bss.rates); + } else { + int hw, i; + u8 rates_max = rates_eid[1]; + u8 *rates = cmd.bss.rates; + for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) { + u8 hw_rate = lbs_rates[hw].bitrate / 5; + for (i = 0; i < rates_max; i++) { + if (hw_rate == (rates_eid[i+2] & 0x7f)) { + u8 rate = rates_eid[i+2]; + if (rate == 0x02 || rate == 0x04 || + rate == 0x0b || rate == 0x16) + rate |= 0x80; + *rates++ = rate; + } + } + } + } + rcu_read_unlock(); + + /* Only v8 and below support setting this */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { + cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); + cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); + } + ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd); + if (ret) + goto out; + + /* + * This is a sample response to CMD_802_11_AD_HOC_JOIN: + * + * response 2c 80 + * size 09 00 + * sequence xx xx + * result 00 00 + * reserved 00 + */ + lbs_join_post(priv, params, bss->bssid, bss->capability); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + +static int lbs_ibss_start_new(struct lbs_private *priv, + struct cfg80211_ibss_params *params) +{ + struct cmd_ds_802_11_ad_hoc_start cmd; + struct cmd_ds_802_11_ad_hoc_result *resp = + (struct cmd_ds_802_11_ad_hoc_result *) &cmd; + u8 preamble = RADIO_PREAMBLE_SHORT; + int ret = 0; + u16 capability; + + lbs_deb_enter(LBS_DEB_CFG80211); + + ret = lbs_set_radio(priv, preamble, 1); + if (ret) + goto out; + + /* + * Example CMD_802_11_AD_HOC_START command: + * + * command 2b 00 CMD_802_11_AD_HOC_START + * size b1 00 + * sequence xx xx + * result 00 00 + * ssid 54 45 53 54 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 + * bss type 02 + * beacon period 64 00 + * dtim period 00 + * IE IBSS 06 + * IE IBSS len 02 + * IE IBSS atim 00 00 + * reserved 00 00 00 00 + * IE DS 03 + * IE DS len 01 + * IE DS channel 01 + * reserved 00 00 00 00 + * probe delay 00 00 + * capability 02 00 + * rates 82 84 8b 96 (basic rates with have bit 7 set) + * 0c 12 18 24 30 48 60 6c + * padding 100 bytes + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + memcpy(cmd.ssid, params->ssid, params->ssid_len); + cmd.bsstype = CMD_BSS_TYPE_IBSS; + cmd.beaconperiod = cpu_to_le16(params->beacon_interval); + cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS; + cmd.ibss.header.len = 2; + cmd.ibss.atimwindow = 0; + cmd.ds.header.id = WLAN_EID_DS_PARAMS; + cmd.ds.header.len = 1; + cmd.ds.channel = params->chandef.chan->hw_value; + /* Only v8 and below support setting probe delay */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) + cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); + /* TODO: mix in WLAN_CAPABILITY_PRIVACY */ + capability = WLAN_CAPABILITY_IBSS; + cmd.capability = cpu_to_le16(capability); + lbs_add_rates(cmd.rates); + + + ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd); + if (ret) + goto out; + + /* + * This is a sample response to CMD_802_11_AD_HOC_JOIN: + * + * response 2b 80 + * size 14 00 + * sequence xx xx + * result 00 00 + * reserved 00 + * bssid 02 2b 7b 0f 86 0e + */ + lbs_join_post(priv, params, resp->bssid, capability); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + +static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = 0; + struct cfg80211_bss *bss; + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (!params->chandef.chan) { + ret = -ENOTSUPP; + goto out; + } + + ret = lbs_set_channel(priv, params->chandef.chan->hw_value); + if (ret) + goto out; + + /* Search if someone is beaconing. This assumes that the + * bss list is populated already */ + bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid, + params->ssid, params->ssid_len, + IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY); + + if (bss) { + ret = lbs_ibss_join_existing(priv, params, bss); + cfg80211_put_bss(wiphy, bss); + } else + ret = lbs_ibss_start_new(priv, params); + + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + +static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + struct cmd_ds_802_11_ad_hoc_stop cmd; + int ret = 0; + + if (dev == priv->mesh_dev) + return -EOPNOTSUPP; + + lbs_deb_enter(LBS_DEB_CFG80211); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd); + + /* TODO: consider doing this at MACREG_INT_CODE_ADHOC_BCN_LOST time */ + lbs_mac_event_disconnected(priv, true); + + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + + + + +/* + * Initialization + */ + +static struct cfg80211_ops lbs_cfg80211_ops = { + .set_monitor_channel = lbs_cfg_set_monitor_channel, + .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel, + .scan = lbs_cfg_scan, + .connect = lbs_cfg_connect, + .disconnect = lbs_cfg_disconnect, + .add_key = lbs_cfg_add_key, + .del_key = lbs_cfg_del_key, + .set_default_key = lbs_cfg_set_default_key, + .get_station = lbs_cfg_get_station, + .change_virtual_intf = lbs_change_intf, + .join_ibss = lbs_join_ibss, + .leave_ibss = lbs_leave_ibss, +}; + + +/* + * At this time lbs_private *priv doesn't even exist, so we just allocate + * memory and don't initialize the wiphy further. This is postponed until we + * can talk to the firmware and happens at registration time in + * lbs_cfg_wiphy_register(). + */ +struct wireless_dev *lbs_cfg_alloc(struct device *dev) +{ + int ret = 0; + struct wireless_dev *wdev; + + lbs_deb_enter(LBS_DEB_CFG80211); + + wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!wdev) + return ERR_PTR(-ENOMEM); + + wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private)); + if (!wdev->wiphy) { + dev_err(dev, "cannot allocate wiphy\n"); + ret = -ENOMEM; + goto err_wiphy_new; + } + + lbs_deb_leave(LBS_DEB_CFG80211); + return wdev; + + err_wiphy_new: + kfree(wdev); + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ERR_PTR(ret); +} + + +static void lbs_cfg_set_regulatory_hint(struct lbs_private *priv) +{ + struct region_code_mapping { + const char *cn; + int code; + }; + + /* Section 5.17.2 */ + static const struct region_code_mapping regmap[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x20}, /* Canada */ + {"EU ", 0x30}, /* ETSI */ + {"ES ", 0x31}, /* Spain */ + {"FR ", 0x32}, /* France */ + {"JP ", 0x40}, /* Japan */ + }; + size_t i; + + lbs_deb_enter(LBS_DEB_CFG80211); + + for (i = 0; i < ARRAY_SIZE(regmap); i++) + if (regmap[i].code == priv->regioncode) { + regulatory_hint(priv->wdev->wiphy, regmap[i].cn); + break; + } + + lbs_deb_leave(LBS_DEB_CFG80211); +} + +static void lbs_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + + lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain " + "callback for domain %c%c\n", request->alpha2[0], + request->alpha2[1]); + + memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2)); + if (lbs_iface_active(priv)) + lbs_set_11d_domain_info(priv); + + lbs_deb_leave(LBS_DEB_CFG80211); +} + +/* + * This function get's called after lbs_setup_firmware() determined the + * firmware capabities. So we can setup the wiphy according to our + * hardware/firmware. + */ +int lbs_cfg_register(struct lbs_private *priv) +{ + struct wireless_dev *wdev = priv->wdev; + int ret; + + lbs_deb_enter(LBS_DEB_CFG80211); + + wdev->wiphy->max_scan_ssids = 1; + wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wdev->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + if (lbs_rtap_supported(priv)) + wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); + if (lbs_mesh_activated(priv)) + wdev->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT); + + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz; + + /* + * We could check priv->fwcapinfo && FW_CAPINFO_WPA, but I have + * never seen a firmware without WPA + */ + wdev->wiphy->cipher_suites = cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + wdev->wiphy->reg_notifier = lbs_reg_notifier; + + ret = wiphy_register(wdev->wiphy); + if (ret < 0) + pr_err("cannot register wiphy device\n"); + + priv->wiphy_registered = true; + + ret = register_netdev(priv->dev); + if (ret) + pr_err("cannot register network device\n"); + + INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker); + + lbs_cfg_set_regulatory_hint(priv); + + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +void lbs_scan_deinit(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_CFG80211); + cancel_delayed_work_sync(&priv->scan_work); +} + + +void lbs_cfg_free(struct lbs_private *priv) +{ + struct wireless_dev *wdev = priv->wdev; + + lbs_deb_enter(LBS_DEB_CFG80211); + + if (!wdev) + return; + + if (priv->wiphy_registered) + wiphy_unregister(wdev->wiphy); + + if (wdev->wiphy) + wiphy_free(wdev->wiphy); + + kfree(wdev); +} diff --git a/drivers/net/wireless/marvell/libertas/cfg.h b/drivers/net/wireless/marvell/libertas/cfg.h new file mode 100644 index 000000000000..acccc2922401 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/cfg.h @@ -0,0 +1,21 @@ +#ifndef __LBS_CFG80211_H__ +#define __LBS_CFG80211_H__ + +struct device; +struct lbs_private; +struct regulatory_request; +struct wiphy; + +struct wireless_dev *lbs_cfg_alloc(struct device *dev); +int lbs_cfg_register(struct lbs_private *priv); +void lbs_cfg_free(struct lbs_private *priv); + +void lbs_send_disconnect_notification(struct lbs_private *priv, + bool locally_generated); +void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event); + +void lbs_scan_done(struct lbs_private *priv); +void lbs_scan_deinit(struct lbs_private *priv); +int lbs_disconnect(struct lbs_private *priv, u16 reason); + +#endif diff --git a/drivers/net/wireless/marvell/libertas/cmd.c b/drivers/net/wireless/marvell/libertas/cmd.c new file mode 100644 index 000000000000..0387a5b380c8 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/cmd.c @@ -0,0 +1,1725 @@ +/* + * This file contains the handling of command. + * It prepares command and sends it to firmware when it is ready. + */ + +#include +#include +#include +#include +#include +#include + +#include "decl.h" +#include "cfg.h" +#include "cmd.h" + +#define CAL_NF(nf) ((s32)(-(s32)(nf))) +#define CAL_RSSI(snr, nf) ((s32)((s32)(snr) + CAL_NF(nf))) + +/** + * lbs_cmd_copyback - Simple callback that copies response back into command + * + * @priv: A pointer to &struct lbs_private structure + * @extra: A pointer to the original command structure for which + * 'resp' is a response + * @resp: A pointer to the command response + * + * returns: 0 on success, error on failure + */ +int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, + struct cmd_header *resp) +{ + struct cmd_header *buf = (void *)extra; + uint16_t copy_len; + + copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); + memcpy(buf, resp, copy_len); + return 0; +} +EXPORT_SYMBOL_GPL(lbs_cmd_copyback); + +/** + * lbs_cmd_async_callback - Simple callback that ignores the result. + * Use this if you just want to send a command to the hardware, but don't + * care for the result. + * + * @priv: ignored + * @extra: ignored + * @resp: ignored + * + * returns: 0 for success + */ +static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra, + struct cmd_header *resp) +{ + return 0; +} + + +/** + * is_command_allowed_in_ps - tests if a command is allowed in Power Save mode + * + * @cmd: the command ID + * + * returns: 1 if allowed, 0 if not allowed + */ +static u8 is_command_allowed_in_ps(u16 cmd) +{ + switch (cmd) { + case CMD_802_11_RSSI: + return 1; + case CMD_802_11_HOST_SLEEP_CFG: + return 1; + default: + break; + } + return 0; +} + +/** + * lbs_update_hw_spec - Updates the hardware details like MAC address + * and regulatory region + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: 0 on success, error on failure + */ +int lbs_update_hw_spec(struct lbs_private *priv) +{ + struct cmd_ds_get_hw_spec cmd; + int ret = -1; + u32 i; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); + ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); + if (ret) + goto out; + + priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); + + /* The firmware release is in an interesting format: the patch + * level is in the most significant nibble ... so fix that: */ + priv->fwrelease = le32_to_cpu(cmd.fwrelease); + priv->fwrelease = (priv->fwrelease << 8) | + (priv->fwrelease >> 24 & 0xff); + + /* Some firmware capabilities: + * CF card firmware 5.0.16p0: cap 0x00000303 + * USB dongle firmware 5.110.17p2: cap 0x00000303 + */ + netdev_info(priv->dev, "%pM, fw %u.%u.%up%u, cap 0x%08x\n", + cmd.permanentaddr, + priv->fwrelease >> 24 & 0xff, + priv->fwrelease >> 16 & 0xff, + priv->fwrelease >> 8 & 0xff, + priv->fwrelease & 0xff, + priv->fwcapinfo); + lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", + cmd.hwifversion, cmd.version); + + /* Clamp region code to 8-bit since FW spec indicates that it should + * only ever be 8-bit, even though the field size is 16-bit. Some firmware + * returns non-zero high 8 bits here. + * + * Firmware version 4.0.102 used in CF8381 has region code shifted. We + * need to check for this problem and handle it properly. + */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4) + priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF; + else + priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* use the region code to search for the index */ + if (priv->regioncode == lbs_region_code_to_index[i]) + break; + } + + /* if it's unidentified region code, use the default (USA) */ + if (i >= MRVDRV_MAX_REGION_CODE) { + priv->regioncode = 0x10; + netdev_info(priv->dev, + "unidentified region code; using the default (USA)\n"); + } + + if (priv->current_addr[0] == 0xff) + memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); + + if (!priv->copied_hwaddr) { + memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN); + if (priv->mesh_dev) + memcpy(priv->mesh_dev->dev_addr, + priv->current_addr, ETH_ALEN); + priv->copied_hwaddr = 1; + } + +out: + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy, + struct cmd_header *resp) +{ + lbs_deb_enter(LBS_DEB_CMD); + if (priv->is_host_sleep_activated) { + priv->is_host_sleep_configured = 0; + if (priv->psstate == PS_STATE_FULL_POWER) { + priv->is_host_sleep_activated = 0; + wake_up_interruptible(&priv->host_sleep_q); + } + } else { + priv->is_host_sleep_configured = 1; + } + lbs_deb_leave(LBS_DEB_CMD); + return 0; +} + +int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, + struct wol_config *p_wol_config) +{ + struct cmd_ds_host_sleep cmd_config; + int ret; + + /* + * Certain firmware versions do not support EHS_REMOVE_WAKEUP command + * and the card will return a failure. Since we need to be + * able to reset the mask, in those cases we set a 0 mask instead. + */ + if (criteria == EHS_REMOVE_WAKEUP && !priv->ehs_remove_supported) + criteria = 0; + + cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config)); + cmd_config.criteria = cpu_to_le32(criteria); + cmd_config.gpio = priv->wol_gpio; + cmd_config.gap = priv->wol_gap; + + if (p_wol_config != NULL) + memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config, + sizeof(struct wol_config)); + else + cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE; + + ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr, + le16_to_cpu(cmd_config.hdr.size), + lbs_ret_host_sleep_cfg, 0); + if (!ret) { + if (p_wol_config) + memcpy((uint8_t *) p_wol_config, + (uint8_t *)&cmd_config.wol_conf, + sizeof(struct wol_config)); + } else { + netdev_info(priv->dev, "HOST_SLEEP_CFG failed %d\n", ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg); + +/** + * lbs_set_ps_mode - Sets the Power Save mode + * + * @priv: A pointer to &struct lbs_private structure + * @cmd_action: The Power Save operation (PS_MODE_ACTION_ENTER_PS or + * PS_MODE_ACTION_EXIT_PS) + * @block: Whether to block on a response or not + * + * returns: 0 on success, error on failure + */ +int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block) +{ + struct cmd_ds_802_11_ps_mode cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(cmd_action); + + if (cmd_action == PS_MODE_ACTION_ENTER_PS) { + lbs_deb_cmd("PS_MODE: action ENTER_PS\n"); + cmd.multipledtim = cpu_to_le16(1); /* Default DTIM multiple */ + } else if (cmd_action == PS_MODE_ACTION_EXIT_PS) { + lbs_deb_cmd("PS_MODE: action EXIT_PS\n"); + } else { + /* We don't handle CONFIRM_SLEEP here because it needs to + * be fastpathed to the firmware. + */ + lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action); + ret = -EOPNOTSUPP; + goto out; + } + + if (block) + ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd); + else + lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd)); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, + struct sleep_params *sp) +{ + struct cmd_ds_802_11_sleep_params cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + if (cmd_action == CMD_ACT_GET) { + memset(&cmd, 0, sizeof(cmd)); + } else { + cmd.error = cpu_to_le16(sp->sp_error); + cmd.offset = cpu_to_le16(sp->sp_offset); + cmd.stabletime = cpu_to_le16(sp->sp_stabletime); + cmd.calcontrol = sp->sp_calcontrol; + cmd.externalsleepclk = sp->sp_extsleepclk; + cmd.reserved = cpu_to_le16(sp->sp_reserved); + } + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(cmd_action); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SLEEP_PARAMS, &cmd); + + if (!ret) { + lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, " + "calcontrol 0x%x extsleepclk 0x%x\n", + le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset), + le16_to_cpu(cmd.stabletime), cmd.calcontrol, + cmd.externalsleepclk); + + sp->sp_error = le16_to_cpu(cmd.error); + sp->sp_offset = le16_to_cpu(cmd.offset); + sp->sp_stabletime = le16_to_cpu(cmd.stabletime); + sp->sp_calcontrol = cmd.calcontrol; + sp->sp_extsleepclk = cmd.externalsleepclk; + sp->sp_reserved = le16_to_cpu(cmd.reserved); + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return 0; +} + +static int lbs_wait_for_ds_awake(struct lbs_private *priv) +{ + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + if (priv->is_deep_sleep) { + if (!wait_event_interruptible_timeout(priv->ds_awake_q, + !priv->is_deep_sleep, (10 * HZ))) { + netdev_err(priv->dev, "ds_awake_q: timer expired\n"); + ret = -1; + } + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep) +{ + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + if (deep_sleep) { + if (priv->is_deep_sleep != 1) { + lbs_deb_cmd("deep sleep: sleep\n"); + BUG_ON(!priv->enter_deep_sleep); + ret = priv->enter_deep_sleep(priv); + if (!ret) { + netif_stop_queue(priv->dev); + netif_carrier_off(priv->dev); + } + } else { + netdev_err(priv->dev, "deep sleep: already enabled\n"); + } + } else { + if (priv->is_deep_sleep) { + lbs_deb_cmd("deep sleep: wakeup\n"); + BUG_ON(!priv->exit_deep_sleep); + ret = priv->exit_deep_sleep(priv); + if (!ret) { + ret = lbs_wait_for_ds_awake(priv); + if (ret) + netdev_err(priv->dev, + "deep sleep: wakeup failed\n"); + } + } + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +static int lbs_ret_host_sleep_activate(struct lbs_private *priv, + unsigned long dummy, + struct cmd_header *cmd) +{ + lbs_deb_enter(LBS_DEB_FW); + priv->is_host_sleep_activated = 1; + wake_up_interruptible(&priv->host_sleep_q); + lbs_deb_leave(LBS_DEB_FW); + return 0; +} + +int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep) +{ + struct cmd_header cmd; + int ret = 0; + uint32_t criteria = EHS_REMOVE_WAKEUP; + + lbs_deb_enter(LBS_DEB_CMD); + + if (host_sleep) { + if (priv->is_host_sleep_activated != 1) { + memset(&cmd, 0, sizeof(cmd)); + ret = lbs_host_sleep_cfg(priv, priv->wol_criteria, + (struct wol_config *)NULL); + if (ret) { + netdev_info(priv->dev, + "Host sleep configuration failed: %d\n", + ret); + return ret; + } + if (priv->psstate == PS_STATE_FULL_POWER) { + ret = __lbs_cmd(priv, + CMD_802_11_HOST_SLEEP_ACTIVATE, + &cmd, + sizeof(cmd), + lbs_ret_host_sleep_activate, 0); + if (ret) + netdev_info(priv->dev, + "HOST_SLEEP_ACTIVATE failed: %d\n", + ret); + } + + if (!wait_event_interruptible_timeout( + priv->host_sleep_q, + priv->is_host_sleep_activated, + (10 * HZ))) { + netdev_err(priv->dev, + "host_sleep_q: timer expired\n"); + ret = -1; + } + } else { + netdev_err(priv->dev, "host sleep: already enabled\n"); + } + } else { + if (priv->is_host_sleep_activated) + ret = lbs_host_sleep_cfg(priv, criteria, + (struct wol_config *)NULL); + } + + return ret; +} + +/** + * lbs_set_snmp_mib - Set an SNMP MIB value + * + * @priv: A pointer to &struct lbs_private structure + * @oid: The OID to set in the firmware + * @val: Value to set the OID to + * + * returns: 0 on success, error on failure + */ +int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val) +{ + struct cmd_ds_802_11_snmp_mib cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof (cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.oid = cpu_to_le16((u16) oid); + + switch (oid) { + case SNMP_MIB_OID_BSS_TYPE: + cmd.bufsize = cpu_to_le16(sizeof(u8)); + cmd.value[0] = val; + break; + case SNMP_MIB_OID_11D_ENABLE: + case SNMP_MIB_OID_FRAG_THRESHOLD: + case SNMP_MIB_OID_RTS_THRESHOLD: + case SNMP_MIB_OID_SHORT_RETRY_LIMIT: + case SNMP_MIB_OID_LONG_RETRY_LIMIT: + cmd.bufsize = cpu_to_le16(sizeof(u16)); + *((__le16 *)(&cmd.value)) = cpu_to_le16(val); + break; + default: + lbs_deb_cmd("SNMP_CMD: (set) unhandled OID 0x%x\n", oid); + ret = -EINVAL; + goto out; + } + + lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n", + le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * lbs_get_snmp_mib - Get an SNMP MIB value + * + * @priv: A pointer to &struct lbs_private structure + * @oid: The OID to retrieve from the firmware + * @out_val: Location for the returned value + * + * returns: 0 on success, error on failure + */ +int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val) +{ + struct cmd_ds_802_11_snmp_mib cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof (cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_GET); + cmd.oid = cpu_to_le16(oid); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); + if (ret) + goto out; + + switch (le16_to_cpu(cmd.bufsize)) { + case sizeof(u8): + *out_val = cmd.value[0]; + break; + case sizeof(u16): + *out_val = le16_to_cpu(*((__le16 *)(&cmd.value))); + break; + default: + lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n", + oid, le16_to_cpu(cmd.bufsize)); + break; + } + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * lbs_get_tx_power - Get the min, max, and current TX power + * + * @priv: A pointer to &struct lbs_private structure + * @curlevel: Current power level in dBm + * @minlevel: Minimum supported power level in dBm (optional) + * @maxlevel: Maximum supported power level in dBm (optional) + * + * returns: 0 on success, error on failure + */ +int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, + s16 *maxlevel) +{ + struct cmd_ds_802_11_rf_tx_power cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_GET); + + ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); + if (ret == 0) { + *curlevel = le16_to_cpu(cmd.curlevel); + if (minlevel) + *minlevel = cmd.minlevel; + if (maxlevel) + *maxlevel = cmd.maxlevel; + } + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +/** + * lbs_set_tx_power - Set the TX power + * + * @priv: A pointer to &struct lbs_private structure + * @dbm: The desired power level in dBm + * + * returns: 0 on success, error on failure + */ +int lbs_set_tx_power(struct lbs_private *priv, s16 dbm) +{ + struct cmd_ds_802_11_rf_tx_power cmd; + int ret; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.curlevel = cpu_to_le16(dbm); + + lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm); + + ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +/** + * lbs_set_monitor_mode - Enable or disable monitor mode + * (only implemented on OLPC usb8388 FW) + * + * @priv: A pointer to &struct lbs_private structure + * @enable: 1 to enable monitor mode, 0 to disable + * + * returns: 0 on success, error on failure + */ +int lbs_set_monitor_mode(struct lbs_private *priv, int enable) +{ + struct cmd_ds_802_11_monitor_mode cmd; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + if (enable) + cmd.mode = cpu_to_le16(0x1); + + lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable); + + ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd); + if (ret == 0) { + priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP : + ARPHRD_ETHER; + } + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +/** + * lbs_get_channel - Get the radio channel + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: The channel on success, error on failure + */ +static int lbs_get_channel(struct lbs_private *priv) +{ + struct cmd_ds_802_11_rf_channel cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET); + + ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); + if (ret) + goto out; + + ret = le16_to_cpu(cmd.channel); + lbs_deb_cmd("current radio channel is %d\n", ret); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +int lbs_update_channel(struct lbs_private *priv) +{ + int ret; + + /* the channel in f/w could be out of sync; get the current channel */ + lbs_deb_enter(LBS_DEB_ASSOC); + + ret = lbs_get_channel(priv); + if (ret > 0) { + priv->channel = ret; + ret = 0; + } + lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); + return ret; +} + +/** + * lbs_set_channel - Set the radio channel + * + * @priv: A pointer to &struct lbs_private structure + * @channel: The desired channel, or 0 to clear a locked channel + * + * returns: 0 on success, error on failure + */ +int lbs_set_channel(struct lbs_private *priv, u8 channel) +{ + struct cmd_ds_802_11_rf_channel cmd; +#ifdef DEBUG + u8 old_channel = priv->channel; +#endif + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); + cmd.channel = cpu_to_le16(channel); + + ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); + if (ret) + goto out; + + priv->channel = (uint8_t) le16_to_cpu(cmd.channel); + lbs_deb_cmd("channel switch from %d to %d\n", old_channel, + priv->channel); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * lbs_get_rssi - Get current RSSI and noise floor + * + * @priv: A pointer to &struct lbs_private structure + * @rssi: On successful return, signal level in mBm + * @nf: On successful return, Noise floor + * + * returns: The channel on success, error on failure + */ +int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf) +{ + struct cmd_ds_802_11_rssi cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + BUG_ON(rssi == NULL); + BUG_ON(nf == NULL); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + /* Average SNR over last 8 beacons */ + cmd.n_or_snr = cpu_to_le16(8); + + ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd); + if (ret == 0) { + *nf = CAL_NF(le16_to_cpu(cmd.nf)); + *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf)); + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * lbs_set_11d_domain_info - Send regulatory and 802.11d domain information + * to the firmware + * + * @priv: pointer to &struct lbs_private + * + * returns: 0 on success, error code on failure +*/ +int lbs_set_11d_domain_info(struct lbs_private *priv) +{ + struct wiphy *wiphy = priv->wdev->wiphy; + struct ieee80211_supported_band **bands = wiphy->bands; + struct cmd_ds_802_11d_domain_info cmd; + struct mrvl_ie_domain_param_set *domain = &cmd.domain; + struct ieee80211_country_ie_triplet *t; + enum ieee80211_band band; + struct ieee80211_channel *ch; + u8 num_triplet = 0; + u8 num_parsed_chan = 0; + u8 first_channel = 0, next_chan = 0, max_pwr = 0; + u8 i, flag = 0; + size_t triplet_size; + int ret = 0; + + lbs_deb_enter(LBS_DEB_11D); + if (!priv->country_code[0]) + goto out; + + memset(&cmd, 0, sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + + lbs_deb_11d("Setting country code '%c%c'\n", + priv->country_code[0], priv->country_code[1]); + + domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); + + /* Set country code */ + domain->country_code[0] = priv->country_code[0]; + domain->country_code[1] = priv->country_code[1]; + domain->country_code[2] = ' '; + + /* Now set up the channel triplets; firmware is somewhat picky here + * and doesn't validate channel numbers and spans; hence it would + * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since + * the last 3 aren't valid channels, the driver is responsible for + * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20) + * etc. + */ + for (band = 0; + (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS); + band++) { + + if (!bands[band]) + continue; + + for (i = 0; + (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS); + i++) { + ch = &bands[band]->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (!flag) { + flag = 1; + next_chan = first_channel = (u32) ch->hw_value; + max_pwr = ch->max_power; + num_parsed_chan = 1; + continue; + } + + if ((ch->hw_value == next_chan + 1) && + (ch->max_power == max_pwr)) { + /* Consolidate adjacent channels */ + next_chan++; + num_parsed_chan++; + } else { + /* Add this triplet */ + lbs_deb_11d("11D triplet (%d, %d, %d)\n", + first_channel, num_parsed_chan, + max_pwr); + t = &domain->triplet[num_triplet]; + t->chans.first_channel = first_channel; + t->chans.num_channels = num_parsed_chan; + t->chans.max_power = max_pwr; + num_triplet++; + flag = 0; + } + } + + if (flag) { + /* Add last triplet */ + lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel, + num_parsed_chan, max_pwr); + t = &domain->triplet[num_triplet]; + t->chans.first_channel = first_channel; + t->chans.num_channels = num_parsed_chan; + t->chans.max_power = max_pwr; + num_triplet++; + } + } + + lbs_deb_11d("# triplets %d\n", num_triplet); + + /* Set command header sizes */ + triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet); + domain->header.len = cpu_to_le16(sizeof(domain->country_code) + + triplet_size); + + lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set", + (u8 *) &cmd.domain.country_code, + le16_to_cpu(domain->header.len)); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) + + sizeof(cmd.action) + + sizeof(cmd.domain.header) + + sizeof(cmd.domain.country_code) + + triplet_size); + + ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd); + +out: + lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); + return ret; +} + +/** + * lbs_get_reg - Read a MAC, Baseband, or RF register + * + * @priv: pointer to &struct lbs_private + * @reg: register command, one of CMD_MAC_REG_ACCESS, + * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS + * @offset: byte offset of the register to get + * @value: on success, the value of the register at 'offset' + * + * returns: 0 on success, error code on failure +*/ +int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) +{ + struct cmd_ds_reg_access cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + BUG_ON(value == NULL); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_GET); + cmd.offset = cpu_to_le16(offset); + + if (reg != CMD_MAC_REG_ACCESS && + reg != CMD_BBP_REG_ACCESS && + reg != CMD_RF_REG_ACCESS) { + ret = -EINVAL; + goto out; + } + + ret = lbs_cmd_with_response(priv, reg, &cmd); + if (!ret) { + if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) + *value = cmd.value.bbp_rf; + else if (reg == CMD_MAC_REG_ACCESS) + *value = le32_to_cpu(cmd.value.mac); + } + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +/** + * lbs_set_reg - Write a MAC, Baseband, or RF register + * + * @priv: pointer to &struct lbs_private + * @reg: register command, one of CMD_MAC_REG_ACCESS, + * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS + * @offset: byte offset of the register to set + * @value: the value to write to the register at 'offset' + * + * returns: 0 on success, error code on failure +*/ +int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value) +{ + struct cmd_ds_reg_access cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.offset = cpu_to_le16(offset); + + if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) + cmd.value.bbp_rf = (u8) (value & 0xFF); + else if (reg == CMD_MAC_REG_ACCESS) + cmd.value.mac = cpu_to_le32(value); + else { + ret = -EINVAL; + goto out; + } + + ret = lbs_cmd_with_response(priv, reg, &cmd); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +static void lbs_queue_cmd(struct lbs_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + int addtail = 1; + + lbs_deb_enter(LBS_DEB_HOST); + + if (!cmdnode) { + lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n"); + goto done; + } + if (!cmdnode->cmdbuf->size) { + lbs_deb_host("DNLD_CMD: cmd size is zero\n"); + goto done; + } + cmdnode->result = 0; + + /* Exit_PS command needs to be queued in the header always. */ + if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) { + struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf; + + if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { + if (priv->psstate != PS_STATE_FULL_POWER) + addtail = 0; + } + } + + if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM) + addtail = 0; + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (addtail) + list_add_tail(&cmdnode->list, &priv->cmdpendingq); + else + list_add(&cmdnode->list, &priv->cmdpendingq); + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", + le16_to_cpu(cmdnode->cmdbuf->command)); + +done: + lbs_deb_leave(LBS_DEB_HOST); +} + +static void lbs_submit_command(struct lbs_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + struct cmd_header *cmd; + uint16_t cmdsize; + uint16_t command; + int timeo = 3 * HZ; + int ret; + + lbs_deb_enter(LBS_DEB_HOST); + + cmd = cmdnode->cmdbuf; + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->seqnum++; + cmd->seqnum = cpu_to_le16(priv->seqnum); + priv->cur_cmd = cmdnode; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + cmdsize = le16_to_cpu(cmd->size); + command = le16_to_cpu(cmd->command); + + /* These commands take longer */ + if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE) + timeo = 5 * HZ; + + lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", + command, le16_to_cpu(cmd->seqnum), cmdsize); + lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); + + ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); + + if (ret) { + netdev_info(priv->dev, "DNLD_CMD: hw_host_to_card failed: %d\n", + ret); + /* Reset dnld state machine, report failure */ + priv->dnld_sent = DNLD_RES_RECEIVED; + lbs_complete_command(priv, cmdnode, ret); + } + + if (command == CMD_802_11_DEEP_SLEEP) { + if (priv->is_auto_deep_sleep_enabled) { + priv->wakeup_dev_required = 1; + priv->dnld_sent = 0; + } + priv->is_deep_sleep = 1; + lbs_complete_command(priv, cmdnode, 0); + } else { + /* Setup the timer after transmit command */ + mod_timer(&priv->command_timer, jiffies + timeo); + } + + lbs_deb_leave(LBS_DEB_HOST); +} + +/* + * This function inserts command node to cmdfreeq + * after cleans it. Requires priv->driver_lock held. + */ +static void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv, + struct cmd_ctrl_node *cmdnode) +{ + lbs_deb_enter(LBS_DEB_HOST); + + if (!cmdnode) + goto out; + + cmdnode->callback = NULL; + cmdnode->callback_arg = 0; + + memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); + + list_add_tail(&cmdnode->list, &priv->cmdfreeq); + out: + lbs_deb_leave(LBS_DEB_HOST); +} + +static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv, + struct cmd_ctrl_node *ptempcmd) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->driver_lock, flags); + __lbs_cleanup_and_insert_cmd(priv, ptempcmd); + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result) +{ + /* + * Normally, commands are removed from cmdpendingq before being + * submitted. However, we can arrive here on alternative codepaths + * where the command is still pending. Make sure the command really + * isn't part of a list at this point. + */ + list_del_init(&cmd->list); + + cmd->result = result; + cmd->cmdwaitqwoken = 1; + wake_up(&cmd->cmdwait_q); + + if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) + __lbs_cleanup_and_insert_cmd(priv, cmd); + priv->cur_cmd = NULL; + wake_up(&priv->waitq); +} + +void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result) +{ + unsigned long flags; + spin_lock_irqsave(&priv->driver_lock, flags); + __lbs_complete_command(priv, cmd, result); + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) +{ + struct cmd_ds_802_11_radio_control cmd; + int ret = -EINVAL; + + lbs_deb_enter(LBS_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.control = 0; + + /* Only v8 and below support setting the preamble */ + if (priv->fwrelease < 0x09000000) { + switch (preamble) { + case RADIO_PREAMBLE_SHORT: + case RADIO_PREAMBLE_AUTO: + case RADIO_PREAMBLE_LONG: + cmd.control = cpu_to_le16(preamble); + break; + default: + goto out; + } + } + + if (radio_on) + cmd.control |= cpu_to_le16(0x1); + else { + cmd.control &= cpu_to_le16(~0x1); + priv->txpower_cur = 0; + } + + lbs_deb_cmd("RADIO_CONTROL: radio %s, preamble %d\n", + radio_on ? "ON" : "OFF", preamble); + + priv->radio_on = radio_on; + + ret = lbs_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); + +out: + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} + +void lbs_set_mac_control(struct lbs_private *priv) +{ + struct cmd_ds_mac_control cmd; + + lbs_deb_enter(LBS_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(priv->mac_control); + cmd.reserved = 0; + + lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd)); + + lbs_deb_leave(LBS_DEB_CMD); +} + +int lbs_set_mac_control_sync(struct lbs_private *priv) +{ + struct cmd_ds_mac_control cmd; + int ret = 0; + + lbs_deb_enter(LBS_DEB_CMD); + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(priv->mac_control); + cmd.reserved = 0; + ret = lbs_cmd_with_response(priv, CMD_MAC_CONTROL, &cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +/** + * lbs_allocate_cmd_buffer - allocates the command buffer and links + * it to command free queue + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: 0 for success or -1 on error + */ +int lbs_allocate_cmd_buffer(struct lbs_private *priv) +{ + int ret = 0; + u32 bufsize; + u32 i; + struct cmd_ctrl_node *cmdarray; + + lbs_deb_enter(LBS_DEB_HOST); + + /* Allocate and initialize the command array */ + bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; + if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) { + lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); + ret = -1; + goto done; + } + priv->cmd_array = cmdarray; + + /* Allocate and initialize each command buffer in the command array */ + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); + if (!cmdarray[i].cmdbuf) { + lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); + ret = -1; + goto done; + } + } + + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + init_waitqueue_head(&cmdarray[i].cmdwait_q); + lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]); + } + ret = 0; + +done: + lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); + return ret; +} + +/** + * lbs_free_cmd_buffer - free the command buffer + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: 0 for success + */ +int lbs_free_cmd_buffer(struct lbs_private *priv) +{ + struct cmd_ctrl_node *cmdarray; + unsigned int i; + + lbs_deb_enter(LBS_DEB_HOST); + + /* need to check if cmd array is allocated or not */ + if (priv->cmd_array == NULL) { + lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); + goto done; + } + + cmdarray = priv->cmd_array; + + /* Release shared memory buffers */ + for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { + if (cmdarray[i].cmdbuf) { + kfree(cmdarray[i].cmdbuf); + cmdarray[i].cmdbuf = NULL; + } + } + + /* Release cmd_ctrl_node */ + if (priv->cmd_array) { + kfree(priv->cmd_array); + priv->cmd_array = NULL; + } + +done: + lbs_deb_leave(LBS_DEB_HOST); + return 0; +} + +/** + * lbs_get_free_cmd_node - gets a free command node if available in + * command free queue + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: A pointer to &cmd_ctrl_node structure on success + * or %NULL on error + */ +static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv) +{ + struct cmd_ctrl_node *tempnode; + unsigned long flags; + + lbs_deb_enter(LBS_DEB_HOST); + + if (!priv) + return NULL; + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!list_empty(&priv->cmdfreeq)) { + tempnode = list_first_entry(&priv->cmdfreeq, + struct cmd_ctrl_node, list); + list_del_init(&tempnode->list); + } else { + lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); + tempnode = NULL; + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbs_deb_leave(LBS_DEB_HOST); + return tempnode; +} + +/** + * lbs_execute_next_command - execute next command in command + * pending queue. Will put firmware back to PS mode if applicable. + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: 0 on success or -1 on error + */ +int lbs_execute_next_command(struct lbs_private *priv) +{ + struct cmd_ctrl_node *cmdnode = NULL; + struct cmd_header *cmd; + unsigned long flags; + int ret = 0; + + /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the + * only caller to us is lbs_thread() and we get even when a + * data packet is received */ + lbs_deb_enter(LBS_DEB_THREAD); + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->cur_cmd) { + netdev_alert(priv->dev, + "EXEC_NEXT_CMD: already processing command!\n"); + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + if (!list_empty(&priv->cmdpendingq)) { + cmdnode = list_first_entry(&priv->cmdpendingq, + struct cmd_ctrl_node, list); + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + if (cmdnode) { + cmd = cmdnode->cmdbuf; + + if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) { + if ((priv->psstate == PS_STATE_SLEEP) || + (priv->psstate == PS_STATE_PRE_SLEEP)) { + lbs_deb_host( + "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n", + le16_to_cpu(cmd->command), + priv->psstate); + ret = -1; + goto done; + } + lbs_deb_host("EXEC_NEXT_CMD: OK to send command " + "0x%04x in psstate %d\n", + le16_to_cpu(cmd->command), priv->psstate); + } else if (priv->psstate != PS_STATE_FULL_POWER) { + /* + * 1. Non-PS command: + * Queue it. set needtowakeup to TRUE if current state + * is SLEEP, otherwise call send EXIT_PS. + * 2. PS command but not EXIT_PS: + * Ignore it. + * 3. PS command EXIT_PS: + * Set needtowakeup to TRUE if current state is SLEEP, + * otherwise send this command down to firmware + * immediately. + */ + if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) { + /* Prepare to send Exit PS, + * this non PS command will be sent later */ + if ((priv->psstate == PS_STATE_SLEEP) + || (priv->psstate == PS_STATE_PRE_SLEEP) + ) { + /* w/ new scheme, it will not reach here. + since it is blocked in main_thread. */ + priv->needtowakeup = 1; + } else { + lbs_set_ps_mode(priv, + PS_MODE_ACTION_EXIT_PS, + false); + } + + ret = 0; + goto done; + } else { + /* + * PS command. Ignore it if it is not Exit_PS. + * otherwise send it down immediately. + */ + struct cmd_ds_802_11_ps_mode *psm = (void *)&cmd[1]; + + lbs_deb_host( + "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n", + psm->action); + if (psm->action != + cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { + lbs_deb_host( + "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); + lbs_complete_command(priv, cmdnode, 0); + + ret = 0; + goto done; + } + + if ((priv->psstate == PS_STATE_SLEEP) || + (priv->psstate == PS_STATE_PRE_SLEEP)) { + lbs_deb_host( + "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); + lbs_complete_command(priv, cmdnode, 0); + priv->needtowakeup = 1; + + ret = 0; + goto done; + } + + lbs_deb_host( + "EXEC_NEXT_CMD: sending EXIT_PS\n"); + } + } + spin_lock_irqsave(&priv->driver_lock, flags); + list_del_init(&cmdnode->list); + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", + le16_to_cpu(cmd->command)); + lbs_submit_command(priv, cmdnode); + } else { + /* + * check if in power save mode, if yes, put the device back + * to PS mode + */ +#ifdef TODO + /* + * This was the old code for libertas+wext. Someone that + * understands this beast should re-code it in a sane way. + * + * I actually don't understand why this is related to WPA + * and to connection status, shouldn't powering should be + * independ of such things? + */ + if ((priv->psmode != LBS802_11POWERMODECAM) && + (priv->psstate == PS_STATE_FULL_POWER) && + ((priv->connect_status == LBS_CONNECTED) || + lbs_mesh_connected(priv))) { + if (priv->secinfo.WPAenabled || + priv->secinfo.WPA2enabled) { + /* check for valid WPA group keys */ + if (priv->wpa_mcast_key.len || + priv->wpa_unicast_key.len) { + lbs_deb_host( + "EXEC_NEXT_CMD: WPA enabled and GTK_SET" + " go back to PS_SLEEP"); + lbs_set_ps_mode(priv, + PS_MODE_ACTION_ENTER_PS, + false); + } + } else { + lbs_deb_host( + "EXEC_NEXT_CMD: cmdpendingq empty, " + "go back to PS_SLEEP"); + lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS, + false); + } + } +#endif + } + + ret = 0; +done: + lbs_deb_leave(LBS_DEB_THREAD); + return ret; +} + +static void lbs_send_confirmsleep(struct lbs_private *priv) +{ + unsigned long flags; + int ret; + + lbs_deb_enter(LBS_DEB_HOST); + lbs_deb_hex(LBS_DEB_HOST, "sleep confirm", (u8 *) &confirm_sleep, + sizeof(confirm_sleep)); + + ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep, + sizeof(confirm_sleep)); + if (ret) { + netdev_alert(priv->dev, "confirm_sleep failed\n"); + goto out; + } + + spin_lock_irqsave(&priv->driver_lock, flags); + + /* We don't get a response on the sleep-confirmation */ + priv->dnld_sent = DNLD_RES_RECEIVED; + + if (priv->is_host_sleep_configured) { + priv->is_host_sleep_activated = 1; + wake_up_interruptible(&priv->host_sleep_q); + } + + /* If nothing to do, go back to sleep (?) */ + if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx]) + priv->psstate = PS_STATE_SLEEP; + + spin_unlock_irqrestore(&priv->driver_lock, flags); + +out: + lbs_deb_leave(LBS_DEB_HOST); +} + +/** + * lbs_ps_confirm_sleep - checks condition and prepares to + * send sleep confirm command to firmware if ok + * + * @priv: A pointer to &struct lbs_private structure + * + * returns: n/a + */ +void lbs_ps_confirm_sleep(struct lbs_private *priv) +{ + unsigned long flags =0; + int allowed = 1; + + lbs_deb_enter(LBS_DEB_HOST); + + spin_lock_irqsave(&priv->driver_lock, flags); + if (priv->dnld_sent) { + allowed = 0; + lbs_deb_host("dnld_sent was set\n"); + } + + /* In-progress command? */ + if (priv->cur_cmd) { + allowed = 0; + lbs_deb_host("cur_cmd was set\n"); + } + + /* Pending events or command responses? */ + if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) { + allowed = 0; + lbs_deb_host("pending events or command responses\n"); + } + spin_unlock_irqrestore(&priv->driver_lock, flags); + + if (allowed) { + lbs_deb_host("sending lbs_ps_confirm_sleep\n"); + lbs_send_confirmsleep(priv); + } else { + lbs_deb_host("sleep confirm has been delayed\n"); + } + + lbs_deb_leave(LBS_DEB_HOST); +} + + +/** + * lbs_set_tpc_cfg - Configures the transmission power control functionality + * + * @priv: A pointer to &struct lbs_private structure + * @enable: Transmission power control enable + * @p0: Power level when link quality is good (dBm). + * @p1: Power level when link quality is fair (dBm). + * @p2: Power level when link quality is poor (dBm). + * @usesnr: Use Signal to Noise Ratio in TPC + * + * returns: 0 on success + */ +int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, + int8_t p2, int usesnr) +{ + struct cmd_ds_802_11_tpc_cfg cmd; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.enable = !!enable; + cmd.usesnr = !!usesnr; + cmd.P0 = p0; + cmd.P1 = p1; + cmd.P2 = p2; + + ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd); + + return ret; +} + +/** + * lbs_set_power_adapt_cfg - Configures the power adaptation settings + * + * @priv: A pointer to &struct lbs_private structure + * @enable: Power adaptation enable + * @p0: Power level for 1, 2, 5.5 and 11 Mbps (dBm). + * @p1: Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm). + * @p2: Power level for 48 and 54 Mbps (dBm). + * + * returns: 0 on Success + */ + +int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, + int8_t p1, int8_t p2) +{ + struct cmd_ds_802_11_pa_cfg cmd; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.enable = !!enable; + cmd.P0 = p0; + cmd.P1 = p1; + cmd.P2 = p2; + + ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd); + + return ret; +} + + +struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, + uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), + unsigned long callback_arg) +{ + struct cmd_ctrl_node *cmdnode; + + lbs_deb_enter(LBS_DEB_HOST); + + if (priv->surpriseremoved) { + lbs_deb_host("PREP_CMD: card removed\n"); + cmdnode = ERR_PTR(-ENOENT); + goto done; + } + + /* No commands are allowed in Deep Sleep until we toggle the GPIO + * to wake up the card and it has signaled that it's ready. + */ + if (!priv->is_auto_deep_sleep_enabled) { + if (priv->is_deep_sleep) { + lbs_deb_cmd("command not allowed in deep sleep\n"); + cmdnode = ERR_PTR(-EBUSY); + goto done; + } + } + + cmdnode = lbs_get_free_cmd_node(priv); + if (cmdnode == NULL) { + lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); + + /* Wake up main thread to execute next command */ + wake_up(&priv->waitq); + cmdnode = ERR_PTR(-ENOBUFS); + goto done; + } + + cmdnode->callback = callback; + cmdnode->callback_arg = callback_arg; + + /* Copy the incoming command to the buffer */ + memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); + + /* Set command, clean result, move to buffer */ + cmdnode->cmdbuf->command = cpu_to_le16(command); + cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); + cmdnode->cmdbuf->result = 0; + + lbs_deb_host("PREP_CMD: command 0x%04x\n", command); + + cmdnode->cmdwaitqwoken = 0; + lbs_queue_cmd(priv, cmdnode); + wake_up(&priv->waitq); + + done: + lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode); + return cmdnode; +} + +void lbs_cmd_async(struct lbs_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size) +{ + lbs_deb_enter(LBS_DEB_CMD); + __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, + lbs_cmd_async_callback, 0); + lbs_deb_leave(LBS_DEB_CMD); +} + +int __lbs_cmd(struct lbs_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), + unsigned long callback_arg) +{ + struct cmd_ctrl_node *cmdnode; + unsigned long flags; + int ret = 0; + + lbs_deb_enter(LBS_DEB_HOST); + + cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size, + callback, callback_arg); + if (IS_ERR(cmdnode)) { + ret = PTR_ERR(cmdnode); + goto done; + } + + might_sleep(); + + /* + * Be careful with signals here. A signal may be received as the system + * goes into suspend or resume. We do not want this to interrupt the + * command, so we perform an uninterruptible sleep. + */ + wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); + + spin_lock_irqsave(&priv->driver_lock, flags); + ret = cmdnode->result; + if (ret) + netdev_info(priv->dev, "PREP_CMD: command 0x%04x failed: %d\n", + command, ret); + + __lbs_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&priv->driver_lock, flags); + +done: + lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(__lbs_cmd); diff --git a/drivers/net/wireless/marvell/libertas/cmd.h b/drivers/net/wireless/marvell/libertas/cmd.h new file mode 100644 index 000000000000..0c5444b02c64 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/cmd.h @@ -0,0 +1,141 @@ +/* Copyright (C) 2007, Red Hat, Inc. */ + +#ifndef _LBS_CMD_H_ +#define _LBS_CMD_H_ + +#include + +#include "host.h" +#include "dev.h" + + +/* Command & response transfer between host and card */ + +struct cmd_ctrl_node { + struct list_head list; + int result; + /* command response */ + int (*callback)(struct lbs_private *, + unsigned long, + struct cmd_header *); + unsigned long callback_arg; + /* command data */ + struct cmd_header *cmdbuf; + /* wait queue */ + u16 cmdwaitqwoken; + wait_queue_head_t cmdwait_q; +}; + + +/* lbs_cmd() infers the size of the buffer to copy data back into, from + the size of the target of the pointer. Since the command to be sent + may often be smaller, that size is set in cmd->size by the caller.*/ +#define lbs_cmd(priv, cmdnr, cmd, cb, cb_arg) ({ \ + uint16_t __sz = le16_to_cpu((cmd)->hdr.size); \ + (cmd)->hdr.size = cpu_to_le16(sizeof(*(cmd))); \ + __lbs_cmd(priv, cmdnr, &(cmd)->hdr, __sz, cb, cb_arg); \ +}) + +#define lbs_cmd_with_response(priv, cmdnr, cmd) \ + lbs_cmd(priv, cmdnr, cmd, lbs_cmd_copyback, (unsigned long) (cmd)) + +void lbs_cmd_async(struct lbs_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size); + +int __lbs_cmd(struct lbs_private *priv, uint16_t command, + struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), + unsigned long callback_arg); + +struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, + uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, + int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), + unsigned long callback_arg); + +int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra, + struct cmd_header *resp); + +int lbs_allocate_cmd_buffer(struct lbs_private *priv); +int lbs_free_cmd_buffer(struct lbs_private *priv); + +int lbs_execute_next_command(struct lbs_private *priv); +void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result); +void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result); +int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); + + +/* From cmdresp.c */ + +void lbs_mac_event_disconnected(struct lbs_private *priv, + bool locally_generated); + + + +/* Events */ + +int lbs_process_event(struct lbs_private *priv, u32 event); + + +/* Actual commands */ + +int lbs_update_hw_spec(struct lbs_private *priv); + +int lbs_set_channel(struct lbs_private *priv, u8 channel); + +int lbs_update_channel(struct lbs_private *priv); + +int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria, + struct wol_config *p_wol_config); + +int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action, + struct sleep_params *sp); + +void lbs_ps_confirm_sleep(struct lbs_private *priv); + +int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on); + +void lbs_set_mac_control(struct lbs_private *priv); +int lbs_set_mac_control_sync(struct lbs_private *priv); + +int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, + s16 *maxlevel); + +int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val); + +int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val); + + +/* Commands only used in wext.c, assoc. and scan.c */ + +int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, + int8_t p1, int8_t p2); + +int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, + int8_t p2, int usesnr); + +int lbs_set_data_rate(struct lbs_private *priv, u8 rate); + +int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv, + uint16_t cmd_action); + +int lbs_set_tx_power(struct lbs_private *priv, s16 dbm); + +int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep); + +int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep); + +int lbs_set_monitor_mode(struct lbs_private *priv, int enable); + +int lbs_get_rssi(struct lbs_private *priv, s8 *snr, s8 *nf); + +int lbs_set_11d_domain_info(struct lbs_private *priv); + +int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value); + +int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value); + +int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block); + +#endif /* _LBS_CMD_H */ diff --git a/drivers/net/wireless/marvell/libertas/cmdresp.c b/drivers/net/wireless/marvell/libertas/cmdresp.c new file mode 100644 index 000000000000..e5442e8956f7 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/cmdresp.c @@ -0,0 +1,353 @@ +/* + * This file contains the handling of command + * responses as well as events generated by firmware. + */ + +#include +#include +#include +#include +#include +#include + +#include "cfg.h" +#include "cmd.h" + +/** + * lbs_mac_event_disconnected - handles disconnect event. It + * reports disconnect to upper layer, clean tx/rx packets, + * reset link state etc. + * + * @priv: A pointer to struct lbs_private structure + * @locally_generated: indicates disconnect was requested locally + * (usually by userspace) + * + * returns: n/a + */ +void lbs_mac_event_disconnected(struct lbs_private *priv, + bool locally_generated) +{ + if (priv->connect_status != LBS_CONNECTED) + return; + + lbs_deb_enter(LBS_DEB_ASSOC); + + /* + * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. + * It causes problem in the Supplicant + */ + msleep_interruptible(1000); + + if (priv->wdev->iftype == NL80211_IFTYPE_STATION) + lbs_send_disconnect_notification(priv, locally_generated); + + /* report disconnect to upper layer */ + netif_stop_queue(priv->dev); + netif_carrier_off(priv->dev); + + /* Free Tx and Rx packets */ + kfree_skb(priv->currenttxskb); + priv->currenttxskb = NULL; + priv->tx_pending_len = 0; + + priv->connect_status = LBS_DISCONNECTED; + + if (priv->psstate != PS_STATE_FULL_POWER) { + /* make firmware to exit PS mode */ + lbs_deb_cmd("disconnected, so exit PS mode\n"); + lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); + } + lbs_deb_leave(LBS_DEB_ASSOC); +} + +int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) +{ + uint16_t respcmd, curcmd; + struct cmd_header *resp; + int ret = 0; + unsigned long flags; + uint16_t result; + + lbs_deb_enter(LBS_DEB_HOST); + + mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!priv->cur_cmd) { + lbs_deb_host("CMD_RESP: cur_cmd is NULL\n"); + ret = -1; + spin_unlock_irqrestore(&priv->driver_lock, flags); + goto done; + } + + resp = (void *)data; + curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); + respcmd = le16_to_cpu(resp->command); + result = le16_to_cpu(resp->result); + + lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n", + respcmd, le16_to_cpu(resp->seqnum), len); + lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len); + + if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { + netdev_info(priv->dev, + "Received CMD_RESP with invalid sequence %d (expected %d)\n", + le16_to_cpu(resp->seqnum), + le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum)); + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + if (respcmd != CMD_RET(curcmd) && + respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) { + netdev_info(priv->dev, "Invalid CMD_RESP %x to command %x!\n", + respcmd, curcmd); + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + if (resp->result == cpu_to_le16(0x0004)) { + /* 0x0004 means -EAGAIN. Drop the response, let it time out + and be resubmitted */ + netdev_info(priv->dev, + "Firmware returns DEFER to command %x. Will let it time out...\n", + le16_to_cpu(resp->command)); + spin_unlock_irqrestore(&priv->driver_lock, flags); + ret = -1; + goto done; + } + + /* Now we got response from FW, cancel the command timer */ + del_timer(&priv->command_timer); + priv->cmd_timed_out = 0; + + if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) { + struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1]; + u16 action = le16_to_cpu(psmode->action); + + lbs_deb_host( + "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n", + result, action); + + if (result) { + lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n", + result); + /* + * We should not re-try enter-ps command in + * ad-hoc mode. It takes place in + * lbs_execute_next_command(). + */ + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR && + action == PS_MODE_ACTION_ENTER_PS) + priv->psmode = LBS802_11POWERMODECAM; + } else if (action == PS_MODE_ACTION_ENTER_PS) { + priv->needtowakeup = 0; + priv->psstate = PS_STATE_AWAKE; + + lbs_deb_host("CMD_RESP: ENTER_PS command response\n"); + if (priv->connect_status != LBS_CONNECTED) { + /* + * When Deauth Event received before Enter_PS command + * response, We need to wake up the firmware. + */ + lbs_deb_host( + "disconnected, invoking lbs_ps_wakeup\n"); + + spin_unlock_irqrestore(&priv->driver_lock, flags); + mutex_unlock(&priv->lock); + lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, + false); + mutex_lock(&priv->lock); + spin_lock_irqsave(&priv->driver_lock, flags); + } + } else if (action == PS_MODE_ACTION_EXIT_PS) { + priv->needtowakeup = 0; + priv->psstate = PS_STATE_FULL_POWER; + lbs_deb_host("CMD_RESP: EXIT_PS command response\n"); + } else { + lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); + } + + __lbs_complete_command(priv, priv->cur_cmd, result); + spin_unlock_irqrestore(&priv->driver_lock, flags); + + ret = 0; + goto done; + } + + /* If the command is not successful, cleanup and return failure */ + if ((result != 0 || !(respcmd & 0x8000))) { + lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n", + result, respcmd); + /* + * Handling errors here + */ + switch (respcmd) { + case CMD_RET(CMD_GET_HW_SPEC): + case CMD_RET(CMD_802_11_RESET): + lbs_deb_host("CMD_RESP: reset failed\n"); + break; + + } + __lbs_complete_command(priv, priv->cur_cmd, result); + spin_unlock_irqrestore(&priv->driver_lock, flags); + + ret = -1; + goto done; + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + + if (priv->cur_cmd && priv->cur_cmd->callback) { + ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, + resp); + } + + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->cur_cmd) { + /* Clean up and Put current command back to cmdfreeq */ + __lbs_complete_command(priv, priv->cur_cmd, result); + } + spin_unlock_irqrestore(&priv->driver_lock, flags); + +done: + mutex_unlock(&priv->lock); + lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); + return ret; +} + +int lbs_process_event(struct lbs_private *priv, u32 event) +{ + int ret = 0; + struct cmd_header cmd; + + lbs_deb_enter(LBS_DEB_CMD); + + switch (event) { + case MACREG_INT_CODE_LINK_SENSED: + lbs_deb_cmd("EVENT: link sensed\n"); + break; + + case MACREG_INT_CODE_DEAUTHENTICATED: + lbs_deb_cmd("EVENT: deauthenticated\n"); + lbs_mac_event_disconnected(priv, false); + break; + + case MACREG_INT_CODE_DISASSOCIATED: + lbs_deb_cmd("EVENT: disassociated\n"); + lbs_mac_event_disconnected(priv, false); + break; + + case MACREG_INT_CODE_LINK_LOST_NO_SCAN: + lbs_deb_cmd("EVENT: link lost\n"); + lbs_mac_event_disconnected(priv, true); + break; + + case MACREG_INT_CODE_PS_SLEEP: + lbs_deb_cmd("EVENT: ps sleep\n"); + + /* handle unexpected PS SLEEP event */ + if (priv->psstate == PS_STATE_FULL_POWER) { + lbs_deb_cmd( + "EVENT: in FULL POWER mode, ignoring PS_SLEEP\n"); + break; + } + priv->psstate = PS_STATE_PRE_SLEEP; + + lbs_ps_confirm_sleep(priv); + + break; + + case MACREG_INT_CODE_HOST_AWAKE: + lbs_deb_cmd("EVENT: host awake\n"); + if (priv->reset_deep_sleep_wakeup) + priv->reset_deep_sleep_wakeup(priv); + priv->is_deep_sleep = 0; + lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd, + sizeof(cmd)); + priv->is_host_sleep_activated = 0; + wake_up_interruptible(&priv->host_sleep_q); + break; + + case MACREG_INT_CODE_DEEP_SLEEP_AWAKE: + if (priv->reset_deep_sleep_wakeup) + priv->reset_deep_sleep_wakeup(priv); + lbs_deb_cmd("EVENT: ds awake\n"); + priv->is_deep_sleep = 0; + priv->wakeup_dev_required = 0; + wake_up_interruptible(&priv->ds_awake_q); + break; + + case MACREG_INT_CODE_PS_AWAKE: + lbs_deb_cmd("EVENT: ps awake\n"); + /* handle unexpected PS AWAKE event */ + if (priv->psstate == PS_STATE_FULL_POWER) { + lbs_deb_cmd( + "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); + break; + } + + priv->psstate = PS_STATE_AWAKE; + + if (priv->needtowakeup) { + /* + * wait for the command processing to finish + * before resuming sending + * priv->needtowakeup will be set to FALSE + * in lbs_ps_wakeup() + */ + lbs_deb_cmd("waking up ...\n"); + lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); + } + break; + + case MACREG_INT_CODE_MIC_ERR_UNICAST: + lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n"); + lbs_send_mic_failureevent(priv, event); + break; + + case MACREG_INT_CODE_MIC_ERR_MULTICAST: + lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n"); + lbs_send_mic_failureevent(priv, event); + break; + + case MACREG_INT_CODE_MIB_CHANGED: + lbs_deb_cmd("EVENT: MIB CHANGED\n"); + break; + case MACREG_INT_CODE_INIT_DONE: + lbs_deb_cmd("EVENT: INIT DONE\n"); + break; + case MACREG_INT_CODE_ADHOC_BCN_LOST: + lbs_deb_cmd("EVENT: ADHOC beacon lost\n"); + break; + case MACREG_INT_CODE_RSSI_LOW: + netdev_alert(priv->dev, "EVENT: rssi low\n"); + break; + case MACREG_INT_CODE_SNR_LOW: + netdev_alert(priv->dev, "EVENT: snr low\n"); + break; + case MACREG_INT_CODE_MAX_FAIL: + netdev_alert(priv->dev, "EVENT: max fail\n"); + break; + case MACREG_INT_CODE_RSSI_HIGH: + netdev_alert(priv->dev, "EVENT: rssi high\n"); + break; + case MACREG_INT_CODE_SNR_HIGH: + netdev_alert(priv->dev, "EVENT: snr high\n"); + break; + + case MACREG_INT_CODE_MESH_AUTO_STARTED: + /* Ignore spurious autostart events */ + netdev_info(priv->dev, "EVENT: MESH_AUTO_STARTED (ignoring)\n"); + break; + + default: + netdev_alert(priv->dev, "EVENT: unknown event id %d\n", event); + break; + } + + lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); + return ret; +} diff --git a/drivers/net/wireless/marvell/libertas/debugfs.c b/drivers/net/wireless/marvell/libertas/debugfs.c new file mode 100644 index 000000000000..26cbf1dcc662 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/debugfs.c @@ -0,0 +1,989 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "decl.h" +#include "cmd.h" +#include "debugfs.h" + +static struct dentry *lbs_dir; +static char *szStates[] = { + "Connected", + "Disconnected" +}; + +#ifdef PROC_DEBUG +static void lbs_debug_init(struct lbs_private *priv); +#endif + +static ssize_t write_file_dummy(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static const size_t len = PAGE_SIZE; + +static ssize_t lbs_dev_info(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + ssize_t res; + if (!buf) + return -ENOMEM; + + pos += snprintf(buf+pos, len-pos, "state = %s\n", + szStates[priv->connect_status]); + pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", + (u32) priv->regioncode); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return res; +} + +static ssize_t lbs_sleepparams_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t buf_size, ret; + struct sleep_params sp; + int p1, p2, p3, p4, p5, p6; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, user_buf, buf_size)) { + ret = -EFAULT; + goto out_unlock; + } + ret = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); + if (ret != 6) { + ret = -EINVAL; + goto out_unlock; + } + sp.sp_error = p1; + sp.sp_offset = p2; + sp.sp_stabletime = p3; + sp.sp_calcontrol = p4; + sp.sp_extsleepclk = p5; + sp.sp_reserved = p6; + + ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_SET, &sp); + if (!ret) + ret = count; + else if (ret > 0) + ret = -EINVAL; + +out_unlock: + free_page(addr); + return ret; +} + +static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t ret; + size_t pos = 0; + struct sleep_params sp; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp); + if (ret) + goto out_unlock; + + pos += snprintf(buf, len, "%d %d %d %d %d %d\n", sp.sp_error, + sp.sp_offset, sp.sp_stabletime, + sp.sp_calcontrol, sp.sp_extsleepclk, + sp.sp_reserved); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + +out_unlock: + free_page(addr); + return ret; +} + +static ssize_t lbs_host_sleep_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t buf_size, ret; + int host_sleep; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, user_buf, buf_size)) { + ret = -EFAULT; + goto out_unlock; + } + ret = sscanf(buf, "%d", &host_sleep); + if (ret != 1) { + ret = -EINVAL; + goto out_unlock; + } + + if (host_sleep == 0) + ret = lbs_set_host_sleep(priv, 0); + else if (host_sleep == 1) { + if (priv->wol_criteria == EHS_REMOVE_WAKEUP) { + netdev_info(priv->dev, + "wake parameters not configured\n"); + ret = -EINVAL; + goto out_unlock; + } + ret = lbs_set_host_sleep(priv, 1); + } else { + netdev_err(priv->dev, "invalid option\n"); + ret = -EINVAL; + } + + if (!ret) + ret = count; + +out_unlock: + free_page(addr); + return ret; +} + +static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t ret; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return ret; +} + +/* + * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might + * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the + * firmware. Here's an example: + * 04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00 + * 00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * + * The 04 01 is the TLV type (here TLV_TYPE_RSSI_LOW), 02 00 is the length, + * 00 00 are the data bytes of this TLV. For this TLV, their meaning is + * defined in mrvlietypes_thresholds + * + * This function searches in this TLV data chunk for a given TLV type + * and returns a pointer to the first data byte of the TLV, or to NULL + * if the TLV hasn't been found. + */ +static void *lbs_tlv_find(uint16_t tlv_type, const uint8_t *tlv, uint16_t size) +{ + struct mrvl_ie_header *tlv_h; + uint16_t length; + ssize_t pos = 0; + + while (pos < size) { + tlv_h = (struct mrvl_ie_header *) tlv; + if (!tlv_h->len) + return NULL; + if (tlv_h->type == cpu_to_le16(tlv_type)) + return tlv_h; + length = le16_to_cpu(tlv_h->len) + sizeof(*tlv_h); + pos += length; + tlv += length; + } + return NULL; +} + + +static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask, + struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct cmd_ds_802_11_subscribe_event *subscribed; + struct mrvl_ie_thresholds *got; + struct lbs_private *priv = file->private_data; + ssize_t ret = 0; + size_t pos = 0; + char *buf; + u8 value; + u8 freq; + int events = 0; + + buf = (char *)get_zeroed_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + subscribed = kzalloc(sizeof(*subscribed), GFP_KERNEL); + if (!subscribed) { + ret = -ENOMEM; + goto out_page; + } + + subscribed->hdr.size = cpu_to_le16(sizeof(*subscribed)); + subscribed->action = cpu_to_le16(CMD_ACT_GET); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, subscribed); + if (ret) + goto out_cmd; + + got = lbs_tlv_find(tlv_type, subscribed->tlv, sizeof(subscribed->tlv)); + if (got) { + value = got->value; + freq = got->freq; + events = le16_to_cpu(subscribed->events); + + pos += snprintf(buf, len, "%d %d %d\n", value, freq, + !!(events & event_mask)); + } + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + out_cmd: + kfree(subscribed); + + out_page: + free_page((unsigned long)buf); + return ret; +} + + +static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask, + struct file *file, + const char __user *userbuf, size_t count, + loff_t *ppos) +{ + struct cmd_ds_802_11_subscribe_event *events; + struct mrvl_ie_thresholds *tlv; + struct lbs_private *priv = file->private_data; + ssize_t buf_size; + int value, freq, new_mask; + uint16_t curr_mask; + char *buf; + int ret; + + buf = (char *)get_zeroed_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + ret = -EFAULT; + goto out_page; + } + ret = sscanf(buf, "%d %d %d", &value, &freq, &new_mask); + if (ret != 3) { + ret = -EINVAL; + goto out_page; + } + events = kzalloc(sizeof(*events), GFP_KERNEL); + if (!events) { + ret = -ENOMEM; + goto out_page; + } + + events->hdr.size = cpu_to_le16(sizeof(*events)); + events->action = cpu_to_le16(CMD_ACT_GET); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events); + if (ret) + goto out_events; + + curr_mask = le16_to_cpu(events->events); + + if (new_mask) + new_mask = curr_mask | event_mask; + else + new_mask = curr_mask & ~event_mask; + + /* Now everything is set and we can send stuff down to the firmware */ + + tlv = (void *)events->tlv; + + events->action = cpu_to_le16(CMD_ACT_SET); + events->events = cpu_to_le16(new_mask); + tlv->header.type = cpu_to_le16(tlv_type); + tlv->header.len = cpu_to_le16(sizeof(*tlv) - sizeof(tlv->header)); + tlv->value = value; + if (tlv_type != TLV_TYPE_BCNMISS) + tlv->freq = freq; + + /* The command header, the action, the event mask, and one TLV */ + events->hdr.size = cpu_to_le16(sizeof(events->hdr) + 4 + sizeof(*tlv)); + + ret = lbs_cmd_with_response(priv, CMD_802_11_SUBSCRIBE_EVENT, events); + + if (!ret) + ret = count; + out_events: + kfree(events); + out_page: + free_page((unsigned long)buf); + return ret; +} + + +static ssize_t lbs_lowrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_lowrssi_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_lowsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_lowsnr_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_failcount_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_failcount_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_highrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_highrssi_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_highsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_highsnr_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH, + file, userbuf, count, ppos); +} + +static ssize_t lbs_bcnmiss_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_read(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_bcnmiss_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + return lbs_threshold_write(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS, + file, userbuf, count, ppos); +} + + +static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + u32 val = 0; + + if (!buf) + return -ENOMEM; + + ret = lbs_get_reg(priv, CMD_MAC_REG_ACCESS, priv->mac_offset, &val); + mdelay(10); + if (!ret) { + pos = snprintf(buf, len, "MAC[0x%x] = 0x%08x\n", + priv->mac_offset, val); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + } + free_page(addr); + return ret; +} + +static ssize_t lbs_rdmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->mac_offset = simple_strtoul(buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t lbs_wrmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + res = lbs_set_reg(priv, CMD_MAC_REG_ACCESS, offset, value); + mdelay(10); + + if (!res) + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + u32 val; + + if (!buf) + return -ENOMEM; + + ret = lbs_get_reg(priv, CMD_BBP_REG_ACCESS, priv->bbp_offset, &val); + mdelay(10); + if (!ret) { + pos = snprintf(buf, len, "BBP[0x%x] = 0x%08x\n", + priv->bbp_offset, val); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + } + free_page(addr); + + return ret; +} + +static ssize_t lbs_rdbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->bbp_offset = simple_strtoul(buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t lbs_wrbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + res = lbs_set_reg(priv, CMD_BBP_REG_ACCESS, offset, value); + mdelay(10); + + if (!res) + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + u32 val; + + if (!buf) + return -ENOMEM; + + ret = lbs_get_reg(priv, CMD_RF_REG_ACCESS, priv->rf_offset, &val); + mdelay(10); + if (!ret) { + pos = snprintf(buf, len, "RF[0x%x] = 0x%08x\n", + priv->rf_offset, val); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + } + free_page(addr); + + return ret; +} + +static ssize_t lbs_rdrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->rf_offset = simple_strtoul(buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t lbs_wrrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + struct lbs_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + res = lbs_set_reg(priv, CMD_RF_REG_ACCESS, offset, value); + mdelay(10); + + if (!res) + res = count; +out_unlock: + free_page(addr); + return res; +} + +#define FOPS(fread, fwrite) { \ + .owner = THIS_MODULE, \ + .open = simple_open, \ + .read = (fread), \ + .write = (fwrite), \ + .llseek = generic_file_llseek, \ +} + +struct lbs_debugfs_files { + const char *name; + umode_t perm; + struct file_operations fops; +}; + +static const struct lbs_debugfs_files debugfs_files[] = { + { "info", 0444, FOPS(lbs_dev_info, write_file_dummy), }, + { "sleepparams", 0644, FOPS(lbs_sleepparams_read, + lbs_sleepparams_write), }, + { "hostsleep", 0644, FOPS(lbs_host_sleep_read, + lbs_host_sleep_write), }, +}; + +static const struct lbs_debugfs_files debugfs_events_files[] = { + {"low_rssi", 0644, FOPS(lbs_lowrssi_read, + lbs_lowrssi_write), }, + {"low_snr", 0644, FOPS(lbs_lowsnr_read, + lbs_lowsnr_write), }, + {"failure_count", 0644, FOPS(lbs_failcount_read, + lbs_failcount_write), }, + {"beacon_missed", 0644, FOPS(lbs_bcnmiss_read, + lbs_bcnmiss_write), }, + {"high_rssi", 0644, FOPS(lbs_highrssi_read, + lbs_highrssi_write), }, + {"high_snr", 0644, FOPS(lbs_highsnr_read, + lbs_highsnr_write), }, +}; + +static const struct lbs_debugfs_files debugfs_regs_files[] = { + {"rdmac", 0644, FOPS(lbs_rdmac_read, lbs_rdmac_write), }, + {"wrmac", 0600, FOPS(NULL, lbs_wrmac_write), }, + {"rdbbp", 0644, FOPS(lbs_rdbbp_read, lbs_rdbbp_write), }, + {"wrbbp", 0600, FOPS(NULL, lbs_wrbbp_write), }, + {"rdrf", 0644, FOPS(lbs_rdrf_read, lbs_rdrf_write), }, + {"wrrf", 0600, FOPS(NULL, lbs_wrrf_write), }, +}; + +void lbs_debugfs_init(void) +{ + if (!lbs_dir) + lbs_dir = debugfs_create_dir("lbs_wireless", NULL); +} + +void lbs_debugfs_remove(void) +{ + debugfs_remove(lbs_dir); +} + +void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev) +{ + int i; + const struct lbs_debugfs_files *files; + if (!lbs_dir) + goto exit; + + priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir); + if (!priv->debugfs_dir) + goto exit; + + for (i=0; idebugfs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->debugfs_dir, + priv, + &files->fops); + } + + priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); + if (!priv->events_dir) + goto exit; + + for (i=0; idebugfs_events_files[i] = debugfs_create_file(files->name, + files->perm, + priv->events_dir, + priv, + &files->fops); + } + + priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); + if (!priv->regs_dir) + goto exit; + + for (i=0; idebugfs_regs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->regs_dir, + priv, + &files->fops); + } + +#ifdef PROC_DEBUG + lbs_debug_init(priv); +#endif +exit: + return; +} + +void lbs_debugfs_remove_one(struct lbs_private *priv) +{ + int i; + + for(i=0; idebugfs_regs_files[i]); + + debugfs_remove(priv->regs_dir); + + for(i=0; idebugfs_events_files[i]); + + debugfs_remove(priv->events_dir); +#ifdef PROC_DEBUG + debugfs_remove(priv->debugfs_debug); +#endif + for(i=0; idebugfs_files[i]); + debugfs_remove(priv->debugfs_dir); +} + + + +/* debug entry */ + +#ifdef PROC_DEBUG + +#define item_size(n) (FIELD_SIZEOF(struct lbs_private, n)) +#define item_addr(n) (offsetof(struct lbs_private, n)) + + +struct debug_data { + char name[32]; + u32 size; + size_t addr; +}; + +/* To debug any member of struct lbs_private, simply add one line here. + */ +static struct debug_data items[] = { + {"psmode", item_size(psmode), item_addr(psmode)}, + {"psstate", item_size(psstate), item_addr(psstate)}, +}; + +static int num_of_items = ARRAY_SIZE(items); + +/** + * lbs_debugfs_read - proc read function + * + * @file: file to read + * @userbuf: pointer to buffer + * @count: number of bytes to read + * @ppos: read data starting position + * + * returns: amount of data read or negative error code + */ +static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + int val = 0; + size_t pos = 0; + ssize_t res; + char *p; + int i; + struct debug_data *d; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + if (!buf) + return -ENOMEM; + + p = buf; + + d = file->private_data; + + for (i = 0; i < num_of_items; i++) { + if (d[i].size == 1) + val = *((u8 *) d[i].addr); + else if (d[i].size == 2) + val = *((u16 *) d[i].addr); + else if (d[i].size == 4) + val = *((u32 *) d[i].addr); + else if (d[i].size == 8) + val = *((u64 *) d[i].addr); + + pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); + } + + res = simple_read_from_buffer(userbuf, count, ppos, p, pos); + + free_page(addr); + return res; +} + +/** + * lbs_debugfs_write - proc write function + * + * @f: file pointer + * @buf: pointer to data buffer + * @cnt: data number to write + * @ppos: file position + * + * returns: amount of data written + */ +static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, + size_t cnt, loff_t *ppos) +{ + int r, i; + char *pdata; + char *p; + char *p0; + char *p1; + char *p2; + struct debug_data *d = f->private_data; + + if (cnt == 0) + return 0; + + pdata = kmalloc(cnt + 1, GFP_KERNEL); + if (pdata == NULL) + return 0; + + if (copy_from_user(pdata, buf, cnt)) { + lbs_deb_debugfs("Copy from user failed\n"); + kfree(pdata); + return 0; + } + pdata[cnt] = '\0'; + + p0 = pdata; + for (i = 0; i < num_of_items; i++) { + do { + p = strstr(p0, d[i].name); + if (p == NULL) + break; + p1 = strchr(p, '\n'); + if (p1 == NULL) + break; + p0 = p1++; + p2 = strchr(p, '='); + if (!p2) + break; + p2++; + r = simple_strtoul(p2, NULL, 0); + if (d[i].size == 1) + *((u8 *) d[i].addr) = (u8) r; + else if (d[i].size == 2) + *((u16 *) d[i].addr) = (u16) r; + else if (d[i].size == 4) + *((u32 *) d[i].addr) = (u32) r; + else if (d[i].size == 8) + *((u64 *) d[i].addr) = (u64) r; + break; + } while (1); + } + kfree(pdata); + + return (ssize_t)cnt; +} + +static const struct file_operations lbs_debug_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = lbs_debugfs_write, + .read = lbs_debugfs_read, + .llseek = default_llseek, +}; + +/** + * lbs_debug_init - create debug proc file + * + * @priv: pointer to &struct lbs_private + * + * returns: N/A + */ +static void lbs_debug_init(struct lbs_private *priv) +{ + int i; + + if (!priv->debugfs_dir) + return; + + for (i = 0; i < num_of_items; i++) + items[i].addr += (size_t) priv; + + priv->debugfs_debug = debugfs_create_file("debug", 0644, + priv->debugfs_dir, &items[0], + &lbs_debug_fops); +} +#endif diff --git a/drivers/net/wireless/marvell/libertas/debugfs.h b/drivers/net/wireless/marvell/libertas/debugfs.h new file mode 100644 index 000000000000..f2b9c7ffe0fd --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/debugfs.h @@ -0,0 +1,10 @@ +#ifndef _LBS_DEBUGFS_H_ +#define _LBS_DEBUGFS_H_ + +void lbs_debugfs_init(void); +void lbs_debugfs_remove(void); + +void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev); +void lbs_debugfs_remove_one(struct lbs_private *priv); + +#endif diff --git a/drivers/net/wireless/marvell/libertas/decl.h b/drivers/net/wireless/marvell/libertas/decl.h new file mode 100644 index 000000000000..84a3aa7ac570 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/decl.h @@ -0,0 +1,82 @@ + +/* + * This file contains declaration referring to + * functions defined in other source files + */ + +#ifndef _LBS_DECL_H_ +#define _LBS_DECL_H_ + +#include +#include +#include + +/* Should be terminated by a NULL entry */ +struct lbs_fw_table { + int model; + const char *helper; + const char *fwname; +}; + +struct lbs_private; +typedef void (*lbs_fw_cb)(struct lbs_private *priv, int ret, + const struct firmware *helper, const struct firmware *mainfw); + +struct lbs_private; +struct sk_buff; +struct net_device; +struct cmd_ds_command; + + +/* ethtool.c */ +extern const struct ethtool_ops lbs_ethtool_ops; + + +/* tx.c */ +void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count); +netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev); + +/* rx.c */ +int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *); + + +/* main.c */ +struct lbs_private *lbs_add_card(void *card, struct device *dmdev); +void lbs_remove_card(struct lbs_private *priv); +int lbs_start_card(struct lbs_private *priv); +void lbs_stop_card(struct lbs_private *priv); +void lbs_host_to_card_done(struct lbs_private *priv); + +int lbs_start_iface(struct lbs_private *priv); +int lbs_stop_iface(struct lbs_private *priv); +int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type); + +int lbs_rtap_supported(struct lbs_private *priv); + +int lbs_set_mac_address(struct net_device *dev, void *addr); +void lbs_set_multicast_list(struct net_device *dev); +void lbs_update_mcast(struct lbs_private *priv); + +int lbs_suspend(struct lbs_private *priv); +int lbs_resume(struct lbs_private *priv); + +void lbs_queue_event(struct lbs_private *priv, u32 event); +void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx); + +int lbs_enter_auto_deep_sleep(struct lbs_private *priv); +int lbs_exit_auto_deep_sleep(struct lbs_private *priv); + +u32 lbs_fw_index_to_data_rate(u8 index); +u8 lbs_data_rate_to_fw_index(u32 rate); + +int lbs_get_firmware(struct device *dev, u32 card_model, + const struct lbs_fw_table *fw_table, + const struct firmware **helper, + const struct firmware **mainfw); +int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, + u32 card_model, const struct lbs_fw_table *fw_table, + lbs_fw_cb callback); +void lbs_wait_for_firmware_load(struct lbs_private *priv); + +#endif diff --git a/drivers/net/wireless/marvell/libertas/defs.h b/drivers/net/wireless/marvell/libertas/defs.h new file mode 100644 index 000000000000..407784aca627 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/defs.h @@ -0,0 +1,394 @@ +/* + * This header file contains global constant/enum definitions, + * global variable declaration. + */ +#ifndef _LBS_DEFS_H_ +#define _LBS_DEFS_H_ + +#include + +#ifdef CONFIG_LIBERTAS_DEBUG +#define DEBUG +#define PROC_DEBUG +#endif + +#ifndef DRV_NAME +#define DRV_NAME "libertas" +#endif + + +#define LBS_DEB_ENTER 0x00000001 +#define LBS_DEB_LEAVE 0x00000002 +#define LBS_DEB_MAIN 0x00000004 +#define LBS_DEB_NET 0x00000008 +#define LBS_DEB_MESH 0x00000010 +#define LBS_DEB_WEXT 0x00000020 +#define LBS_DEB_IOCTL 0x00000040 +#define LBS_DEB_SCAN 0x00000080 +#define LBS_DEB_ASSOC 0x00000100 +#define LBS_DEB_JOIN 0x00000200 +#define LBS_DEB_11D 0x00000400 +#define LBS_DEB_DEBUGFS 0x00000800 +#define LBS_DEB_ETHTOOL 0x00001000 +#define LBS_DEB_HOST 0x00002000 +#define LBS_DEB_CMD 0x00004000 +#define LBS_DEB_RX 0x00008000 +#define LBS_DEB_TX 0x00010000 +#define LBS_DEB_USB 0x00020000 +#define LBS_DEB_CS 0x00040000 +#define LBS_DEB_FW 0x00080000 +#define LBS_DEB_THREAD 0x00100000 +#define LBS_DEB_HEX 0x00200000 +#define LBS_DEB_SDIO 0x00400000 +#define LBS_DEB_SYSFS 0x00800000 +#define LBS_DEB_SPI 0x01000000 +#define LBS_DEB_CFG80211 0x02000000 + +extern unsigned int lbs_debug; + +#ifdef DEBUG +#define LBS_DEB_LL(grp, grpnam, fmt, args...) \ +do { if ((lbs_debug & (grp)) == (grp)) \ + printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \ + in_interrupt() ? " (INT)" : "", ## args); } while (0) +#else +#define LBS_DEB_LL(grp, grpnam, fmt, args...) do {} while (0) +#endif + +#define lbs_deb_enter(grp) \ + LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s()\n", __func__); +#define lbs_deb_enter_args(grp, fmt, args...) \ + LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s(" fmt ")\n", __func__, ## args); +#define lbs_deb_leave(grp) \ + LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s()\n", __func__); +#define lbs_deb_leave_args(grp, fmt, args...) \ + LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s(), " fmt "\n", \ + __func__, ##args); +#define lbs_deb_main(fmt, args...) LBS_DEB_LL(LBS_DEB_MAIN, " main", fmt, ##args) +#define lbs_deb_net(fmt, args...) LBS_DEB_LL(LBS_DEB_NET, " net", fmt, ##args) +#define lbs_deb_mesh(fmt, args...) LBS_DEB_LL(LBS_DEB_MESH, " mesh", fmt, ##args) +#define lbs_deb_wext(fmt, args...) LBS_DEB_LL(LBS_DEB_WEXT, " wext", fmt, ##args) +#define lbs_deb_ioctl(fmt, args...) LBS_DEB_LL(LBS_DEB_IOCTL, " ioctl", fmt, ##args) +#define lbs_deb_scan(fmt, args...) LBS_DEB_LL(LBS_DEB_SCAN, " scan", fmt, ##args) +#define lbs_deb_assoc(fmt, args...) LBS_DEB_LL(LBS_DEB_ASSOC, " assoc", fmt, ##args) +#define lbs_deb_join(fmt, args...) LBS_DEB_LL(LBS_DEB_JOIN, " join", fmt, ##args) +#define lbs_deb_11d(fmt, args...) LBS_DEB_LL(LBS_DEB_11D, " 11d", fmt, ##args) +#define lbs_deb_debugfs(fmt, args...) LBS_DEB_LL(LBS_DEB_DEBUGFS, " debugfs", fmt, ##args) +#define lbs_deb_ethtool(fmt, args...) LBS_DEB_LL(LBS_DEB_ETHTOOL, " ethtool", fmt, ##args) +#define lbs_deb_host(fmt, args...) LBS_DEB_LL(LBS_DEB_HOST, " host", fmt, ##args) +#define lbs_deb_cmd(fmt, args...) LBS_DEB_LL(LBS_DEB_CMD, " cmd", fmt, ##args) +#define lbs_deb_rx(fmt, args...) LBS_DEB_LL(LBS_DEB_RX, " rx", fmt, ##args) +#define lbs_deb_tx(fmt, args...) LBS_DEB_LL(LBS_DEB_TX, " tx", fmt, ##args) +#define lbs_deb_fw(fmt, args...) LBS_DEB_LL(LBS_DEB_FW, " fw", fmt, ##args) +#define lbs_deb_usb(fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usb", fmt, ##args) +#define lbs_deb_usbd(dev, fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usbd", "%s:" fmt, dev_name(dev), ##args) +#define lbs_deb_cs(fmt, args...) LBS_DEB_LL(LBS_DEB_CS, " cs", fmt, ##args) +#define lbs_deb_thread(fmt, args...) LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args) +#define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " sdio", fmt, ##args) +#define lbs_deb_sysfs(fmt, args...) LBS_DEB_LL(LBS_DEB_SYSFS, " sysfs", fmt, ##args) +#define lbs_deb_spi(fmt, args...) LBS_DEB_LL(LBS_DEB_SPI, " spi", fmt, ##args) +#define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args) + +#ifdef DEBUG +static inline void lbs_deb_hex(unsigned int grp, const char *prompt, + const u8 *buf, int len) +{ + int i = 0; + + if (len && + (lbs_debug & LBS_DEB_HEX) && + (lbs_debug & grp)) + { + for (i = 1; i <= len; i++) { + if ((i & 0xf) == 1) { + if (i != 1) + printk("\n"); + printk(DRV_NAME " %s: ", prompt); + } + printk("%02x ", (u8) * buf); + buf++; + } + printk("\n"); + } +} +#else +#define lbs_deb_hex(grp,prompt,buf,len) do {} while (0) +#endif + + + +/* Buffer Constants */ + +/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical + * addresses of TxPD buffers. Station has only 8 TxPD available, Whereas + * driver has more local TxPDs. Each TxPD on the host memory is associated + * with a Tx control node. The driver maintains 8 RxPD descriptors for + * station firmware to store Rx packet information. + * + * Current version of MAC has a 32x6 multicast address buffer. + * + * 802.11b can have up to 14 channels, the driver keeps the + * BSSID(MAC address) of each APs or Ad hoc stations it has sensed. + */ + +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 +#define LBS_NUM_CMD_BUFFERS 10 +#define LBS_CMD_BUFFER_SIZE (2 * 1024) +#define MRVDRV_MAX_CHANNEL_SIZE 14 +#define MRVDRV_ASSOCIATION_TIME_OUT 255 +#define MRVDRV_SNAP_HEADER_LEN 8 + +#define LBS_UPLD_SIZE 2312 +#define DEV_NAME_LEN 32 + +/* Wake criteria for HOST_SLEEP_CFG command */ +#define EHS_WAKE_ON_BROADCAST_DATA 0x0001 +#define EHS_WAKE_ON_UNICAST_DATA 0x0002 +#define EHS_WAKE_ON_MAC_EVENT 0x0004 +#define EHS_WAKE_ON_MULTICAST_DATA 0x0008 +#define EHS_REMOVE_WAKEUP 0xFFFFFFFF +/* Wake rules for Host_Sleep_CFG command */ +#define WOL_RULE_NET_TYPE_INFRA_OR_IBSS 0x00 +#define WOL_RULE_NET_TYPE_MESH 0x10 +#define WOL_RULE_ADDR_TYPE_BCAST 0x01 +#define WOL_RULE_ADDR_TYPE_MCAST 0x08 +#define WOL_RULE_ADDR_TYPE_UCAST 0x02 +#define WOL_RULE_OP_AND 0x01 +#define WOL_RULE_OP_OR 0x02 +#define WOL_RULE_OP_INVALID 0xFF +#define WOL_RESULT_VALID_CMD 0 +#define WOL_RESULT_NOSPC_ERR 1 +#define WOL_RESULT_EEXIST_ERR 2 + +/* Misc constants */ +/* This section defines 802.11 specific contants */ + +#define MRVDRV_MAX_BSS_DESCRIPTS 16 +#define MRVDRV_MAX_REGION_CODE 6 + +#define MRVDRV_DEFAULT_LISTEN_INTERVAL 10 + +#define MRVDRV_CHANNELS_PER_SCAN 4 +#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 + +#define MRVDRV_MIN_BEACON_INTERVAL 20 +#define MRVDRV_MAX_BEACON_INTERVAL 1000 +#define MRVDRV_BEACON_INTERVAL 100 + +#define MARVELL_MESH_IE_LENGTH 9 + +/* + * Values used to populate the struct mrvl_mesh_ie. The only time you need this + * is when enabling the mesh using CMD_MESH_CONFIG. + */ +#define MARVELL_MESH_IE_TYPE 4 +#define MARVELL_MESH_IE_SUBTYPE 0 +#define MARVELL_MESH_IE_VERSION 0 +#define MARVELL_MESH_PROTO_ID_HWMP 0 +#define MARVELL_MESH_METRIC_ID 0 +#define MARVELL_MESH_CAPABILITY 0 + +/* INT status Bit Definition */ +#define MRVDRV_TX_DNLD_RDY 0x0001 +#define MRVDRV_RX_UPLD_RDY 0x0002 +#define MRVDRV_CMD_DNLD_RDY 0x0004 +#define MRVDRV_CMD_UPLD_RDY 0x0008 +#define MRVDRV_CARDEVENT 0x0010 + +/* Automatic TX control default levels */ +#define POW_ADAPT_DEFAULT_P0 13 +#define POW_ADAPT_DEFAULT_P1 15 +#define POW_ADAPT_DEFAULT_P2 18 +#define TPC_DEFAULT_P0 5 +#define TPC_DEFAULT_P1 10 +#define TPC_DEFAULT_P2 13 + +/* TxPD status */ + +/* + * Station firmware use TxPD status field to report final Tx transmit + * result, Bit masks are used to present combined situations. + */ + +#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 +#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 + +/* Tx mesh flag */ +/* + * Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define TxPD_CONTROL_WDS_FRAME (1<<17) +#define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME + +/* Mesh interface ID */ +#define MESH_IFACE_ID 0x0001 +/* Mesh id should be in bits 14-13-12 */ +#define MESH_IFACE_BIT_OFFSET 0x000c +/* Mesh enable bit in FW capability */ +#define MESH_CAPINFO_ENABLE_MASK (1<<16) + +/* FW definition from Marvell v4 */ +#define MRVL_FW_V4 (0x04) +/* FW definition from Marvell v5 */ +#define MRVL_FW_V5 (0x05) +/* FW definition from Marvell v10 */ +#define MRVL_FW_V10 (0x0a) +/* FW major revision definition */ +#define MRVL_FW_MAJOR_REV(x) ((x)>>24) + +/* RxPD status */ + +#define MRVDRV_RXPD_STATUS_OK 0x0001 + +/* RxPD status - Received packet types */ +/* Rx mesh flag */ +/* + * Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define RxPD_CONTROL_WDS_FRAME (0x40) +#define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME + +/* RSSI-related defines */ +/* + * RSSI constants are used to implement 802.11 RSSI threshold + * indication. if the Rx packet signal got too weak for 5 consecutive + * times, miniport driver (driver) will report this event to wrapper + */ + +#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) + +/* RTS/FRAG related defines */ +#define MRVDRV_RTS_MIN_VALUE 0 +#define MRVDRV_RTS_MAX_VALUE 2347 +#define MRVDRV_FRAG_MIN_VALUE 256 +#define MRVDRV_FRAG_MAX_VALUE 2346 + +/* This is for firmware specific length */ +#define EXTRA_LEN 36 + +#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) + +#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct rxpd) \ + + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) + +#define CMD_F_HOSTCMD (1 << 0) +#define FW_CAPINFO_WPA (1 << 0) +#define FW_CAPINFO_PS (1 << 1) +#define FW_CAPINFO_FIRMWARE_UPGRADE (1 << 13) +#define FW_CAPINFO_BOOT2_UPGRADE (1<<14) +#define FW_CAPINFO_PERSISTENT_CONFIG (1<<15) + +#define KEY_LEN_WPA_AES 16 +#define KEY_LEN_WPA_TKIP 32 +#define KEY_LEN_WEP_104 13 +#define KEY_LEN_WEP_40 5 + +#define RF_ANTENNA_1 0x1 +#define RF_ANTENNA_2 0x2 +#define RF_ANTENNA_AUTO 0xFFFF + +#define BAND_B (0x01) +#define BAND_G (0x02) +#define ALL_802_11_BANDS (BAND_B | BAND_G) + +#define MAX_RATES 14 + +#define MAX_LEDS 8 + +/* Global Variable Declaration */ +extern const char lbs_driver_version[]; +extern u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE]; + + +/* ENUM definition */ +/* SNRNF_TYPE */ +enum SNRNF_TYPE { + TYPE_BEACON = 0, + TYPE_RXPD, + MAX_TYPE_B +}; + +/* SNRNF_DATA */ +enum SNRNF_DATA { + TYPE_NOAVG = 0, + TYPE_AVG, + MAX_TYPE_AVG +}; + +/* LBS_802_11_POWER_MODE */ +enum LBS_802_11_POWER_MODE { + LBS802_11POWERMODECAM, + LBS802_11POWERMODEMAX_PSP, + LBS802_11POWERMODEFAST_PSP, + /* not a real mode, defined as an upper bound */ + LBS802_11POWEMODEMAX +}; + +/* PS_STATE */ +enum PS_STATE { + PS_STATE_FULL_POWER, + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP +}; + +/* DNLD_STATE */ +enum DNLD_STATE { + DNLD_RES_RECEIVED, + DNLD_DATA_SENT, + DNLD_CMD_SENT, + DNLD_BOOTCMD_SENT, +}; + +/* LBS_MEDIA_STATE */ +enum LBS_MEDIA_STATE { + LBS_CONNECTED, + LBS_DISCONNECTED +}; + +/* LBS_802_11_PRIVACY_FILTER */ +enum LBS_802_11_PRIVACY_FILTER { + LBS802_11PRIVFILTERACCEPTALL, + LBS802_11PRIVFILTER8021XWEP +}; + +/* mv_ms_type */ +enum mv_ms_type { + MVMS_DAT = 0, + MVMS_CMD = 1, + MVMS_TXDONE = 2, + MVMS_EVENT +}; + +/* KEY_TYPE_ID */ +enum KEY_TYPE_ID { + KEY_TYPE_ID_WEP = 0, + KEY_TYPE_ID_TKIP, + KEY_TYPE_ID_AES +}; + +/* KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */ +enum KEY_INFO_WPA { + KEY_INFO_WPA_MCAST = 0x01, + KEY_INFO_WPA_UNICAST = 0x02, + KEY_INFO_WPA_ENABLED = 0x04 +}; + +/* Default values for fwt commands. */ +#define FWT_DEFAULT_METRIC 0 +#define FWT_DEFAULT_DIR 1 +/* Default Rate, 11Mbps */ +#define FWT_DEFAULT_RATE 3 +#define FWT_DEFAULT_SSN 0xffffffff +#define FWT_DEFAULT_DSN 0 +#define FWT_DEFAULT_HOPCOUNT 0 +#define FWT_DEFAULT_TTL 0 +#define FWT_DEFAULT_EXPIRATION 0 +#define FWT_DEFAULT_SLEEPMODE 0 +#define FWT_DEFAULT_SNR 0 + +#endif diff --git a/drivers/net/wireless/marvell/libertas/dev.h b/drivers/net/wireless/marvell/libertas/dev.h new file mode 100644 index 000000000000..6bd1608992b0 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/dev.h @@ -0,0 +1,211 @@ +/* + * This file contains definitions and data structures specific + * to Marvell 802.11 NIC. It contains the Device Information + * structure struct lbs_private.. + */ +#ifndef _LBS_DEV_H_ +#define _LBS_DEV_H_ + +#include "defs.h" +#include "decl.h" +#include "host.h" + +#include + +/* sleep_params */ +struct sleep_params { + uint16_t sp_error; + uint16_t sp_offset; + uint16_t sp_stabletime; + uint8_t sp_calcontrol; + uint8_t sp_extsleepclk; + uint16_t sp_reserved; +}; + +/* Mesh statistics */ +struct lbs_mesh_stats { + u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ + u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ + u32 fwd_drop_ttl; /* Fwd: TTL zero */ + u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ + u32 fwd_drop_noroute; /* Fwd: No route to Destination */ + u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ + u32 drop_blind; /* Rx: Dropped by blinding table */ + u32 tx_failed_cnt; /* Tx: Failed transmissions */ +}; + +/* Private structure for the MV device */ +struct lbs_private { + + /* Basic networking */ + struct net_device *dev; + u32 connect_status; + struct work_struct mcast_work; + u32 nr_of_multicastmacaddr; + u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; + + /* CFG80211 */ + struct wireless_dev *wdev; + bool wiphy_registered; + struct cfg80211_scan_request *scan_req; + u8 assoc_bss[ETH_ALEN]; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + u8 disassoc_reason; + + /* Mesh */ + struct net_device *mesh_dev; /* Virtual device */ +#ifdef CONFIG_LIBERTAS_MESH + struct lbs_mesh_stats mstats; + uint16_t mesh_tlv; + u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1]; + u8 mesh_ssid_len; + u8 mesh_channel; +#endif + + /* Debugfs */ + struct dentry *debugfs_dir; + struct dentry *debugfs_debug; + struct dentry *debugfs_files[6]; + struct dentry *events_dir; + struct dentry *debugfs_events_files[6]; + struct dentry *regs_dir; + struct dentry *debugfs_regs_files[6]; + + /* Hardware debugging */ + u32 mac_offset; + u32 bbp_offset; + u32 rf_offset; + + /* Power management */ + u16 psmode; + u32 psstate; + u8 needtowakeup; + + /* Deep sleep */ + int is_deep_sleep; + int deep_sleep_required; + int is_auto_deep_sleep_enabled; + int wakeup_dev_required; + int is_activity_detected; + int auto_deep_sleep_timeout; /* in ms */ + wait_queue_head_t ds_awake_q; + struct timer_list auto_deepsleep_timer; + + /* Host sleep*/ + int is_host_sleep_configured; + int is_host_sleep_activated; + wait_queue_head_t host_sleep_q; + + /* Hardware access */ + void *card; + bool iface_running; + u8 fw_ready; + u8 surpriseremoved; + u8 setup_fw_on_resume; + int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb); + void (*reset_card) (struct lbs_private *priv); + int (*power_save) (struct lbs_private *priv); + int (*power_restore) (struct lbs_private *priv); + int (*enter_deep_sleep) (struct lbs_private *priv); + int (*exit_deep_sleep) (struct lbs_private *priv); + int (*reset_deep_sleep_wakeup) (struct lbs_private *priv); + + /* Adapter info (from EEPROM) */ + u32 fwrelease; + u32 fwcapinfo; + u16 regioncode; + u8 current_addr[ETH_ALEN]; + u8 copied_hwaddr; + + /* Command download */ + u8 dnld_sent; + /* bit0 1/0=data_sent/data_tx_done, + bit1 1/0=cmd_sent/cmd_tx_done, + all other bits reserved 0 */ + u16 seqnum; + struct cmd_ctrl_node *cmd_array; + struct cmd_ctrl_node *cur_cmd; + struct list_head cmdfreeq; /* free command buffers */ + struct list_head cmdpendingq; /* pending command buffers */ + struct timer_list command_timer; + int cmd_timed_out; + + /* Command responses sent from the hardware to the driver */ + u8 resp_idx; + u8 resp_buf[2][LBS_UPLD_SIZE]; + u32 resp_len[2]; + + /* Events sent from hardware to driver */ + struct kfifo event_fifo; + + /* thread to service interrupts */ + struct task_struct *main_thread; + wait_queue_head_t waitq; + struct workqueue_struct *work_thread; + + /* Encryption stuff */ + u8 authtype_auto; + u8 wep_tx_key; + u8 wep_key[4][WLAN_KEY_LEN_WEP104]; + u8 wep_key_len[4]; + + /* Wake On LAN */ + uint32_t wol_criteria; + uint8_t wol_gpio; + uint8_t wol_gap; + bool ehs_remove_supported; + + /* Transmitting */ + int tx_pending_len; /* -1 while building packet */ + u8 tx_pending_buf[LBS_UPLD_SIZE]; + /* protected by hard_start_xmit serialization */ + u8 txretrycount; + struct sk_buff *currenttxskb; + struct timer_list tx_lockup_timer; + + /* Locks */ + struct mutex lock; + spinlock_t driver_lock; + + /* NIC/link operation characteristics */ + u16 mac_control; + u8 radio_on; + u8 cur_rate; + u8 channel; + s16 txpower_cur; + s16 txpower_min; + s16 txpower_max; + + /* Scanning */ + struct delayed_work scan_work; + int scan_channel; + /* Queue of things waiting for scan completion */ + wait_queue_head_t scan_q; + /* Whether the scan was initiated internally and not by cfg80211 */ + bool internal_scan; + + /* Firmware load */ + u32 fw_model; + wait_queue_head_t fw_waitq; + struct device *fw_device; + const struct firmware *helper_fw; + const struct lbs_fw_table *fw_table; + const struct lbs_fw_table *fw_iter; + lbs_fw_cb fw_callback; +}; + +extern struct cmd_confirm_sleep confirm_sleep; + +/* Check if there is an interface active. */ +static inline int lbs_iface_active(struct lbs_private *priv) +{ + int r; + + r = netif_running(priv->dev); + if (priv->mesh_dev) + r |= netif_running(priv->mesh_dev); + + return r; +} + +#endif diff --git a/drivers/net/wireless/marvell/libertas/ethtool.c b/drivers/net/wireless/marvell/libertas/ethtool.c new file mode 100644 index 000000000000..f955b2d66ed6 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/ethtool.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include + +#include "decl.h" +#include "cmd.h" +#include "mesh.h" + + +static void lbs_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct lbs_private *priv = dev->ml_priv; + + snprintf(info->fw_version, sizeof(info->fw_version), + "%u.%u.%u.p%u", + priv->fwrelease >> 24 & 0xff, + priv->fwrelease >> 16 & 0xff, + priv->fwrelease >> 8 & 0xff, + priv->fwrelease & 0xff); + strlcpy(info->driver, "libertas", sizeof(info->driver)); + strlcpy(info->version, lbs_driver_version, sizeof(info->version)); +} + +/* + * All 8388 parts have 16KiB EEPROM size at the time of writing. + * In case that changes this needs fixing. + */ +#define LBS_EEPROM_LEN 16384 + +static int lbs_ethtool_get_eeprom_len(struct net_device *dev) +{ + return LBS_EEPROM_LEN; +} + +static int lbs_ethtool_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 * bytes) +{ + struct lbs_private *priv = dev->ml_priv; + struct cmd_ds_802_11_eeprom_access cmd; + int ret; + + lbs_deb_enter(LBS_DEB_ETHTOOL); + + if (eeprom->offset + eeprom->len > LBS_EEPROM_LEN || + eeprom->len > LBS_EEPROM_READ_LEN) { + ret = -EINVAL; + goto out; + } + + cmd.hdr.size = cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) - + LBS_EEPROM_READ_LEN + eeprom->len); + cmd.action = cpu_to_le16(CMD_ACT_GET); + cmd.offset = cpu_to_le16(eeprom->offset); + cmd.len = cpu_to_le16(eeprom->len); + ret = lbs_cmd_with_response(priv, CMD_802_11_EEPROM_ACCESS, &cmd); + if (!ret) + memcpy(bytes, cmd.value, eeprom->len); + +out: + lbs_deb_leave_args(LBS_DEB_ETHTOOL, "ret %d", ret); + return ret; +} + +static void lbs_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct lbs_private *priv = dev->ml_priv; + + wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY; + + if (priv->wol_criteria == EHS_REMOVE_WAKEUP) + return; + + if (priv->wol_criteria & EHS_WAKE_ON_UNICAST_DATA) + wol->wolopts |= WAKE_UCAST; + if (priv->wol_criteria & EHS_WAKE_ON_MULTICAST_DATA) + wol->wolopts |= WAKE_MCAST; + if (priv->wol_criteria & EHS_WAKE_ON_BROADCAST_DATA) + wol->wolopts |= WAKE_BCAST; + if (priv->wol_criteria & EHS_WAKE_ON_MAC_EVENT) + wol->wolopts |= WAKE_PHY; +} + +static int lbs_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct lbs_private *priv = dev->ml_priv; + + if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY)) + return -EOPNOTSUPP; + + priv->wol_criteria = 0; + if (wol->wolopts & WAKE_UCAST) + priv->wol_criteria |= EHS_WAKE_ON_UNICAST_DATA; + if (wol->wolopts & WAKE_MCAST) + priv->wol_criteria |= EHS_WAKE_ON_MULTICAST_DATA; + if (wol->wolopts & WAKE_BCAST) + priv->wol_criteria |= EHS_WAKE_ON_BROADCAST_DATA; + if (wol->wolopts & WAKE_PHY) + priv->wol_criteria |= EHS_WAKE_ON_MAC_EVENT; + if (wol->wolopts == 0) + priv->wol_criteria |= EHS_REMOVE_WAKEUP; + return 0; +} + +const struct ethtool_ops lbs_ethtool_ops = { + .get_drvinfo = lbs_ethtool_get_drvinfo, + .get_eeprom = lbs_ethtool_get_eeprom, + .get_eeprom_len = lbs_ethtool_get_eeprom_len, +#ifdef CONFIG_LIBERTAS_MESH + .get_sset_count = lbs_mesh_ethtool_get_sset_count, + .get_ethtool_stats = lbs_mesh_ethtool_get_stats, + .get_strings = lbs_mesh_ethtool_get_strings, +#endif + .get_wol = lbs_ethtool_get_wol, + .set_wol = lbs_ethtool_set_wol, +}; + diff --git a/drivers/net/wireless/marvell/libertas/firmware.c b/drivers/net/wireless/marvell/libertas/firmware.c new file mode 100644 index 000000000000..51b92b5df119 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/firmware.c @@ -0,0 +1,227 @@ +/* + * Firmware loading and handling functions. + */ + +#include +#include +#include + +#include "dev.h" +#include "decl.h" + +static void load_next_firmware_from_table(struct lbs_private *private); + +static void lbs_fw_loaded(struct lbs_private *priv, int ret, + const struct firmware *helper, const struct firmware *mainfw) +{ + unsigned long flags; + + lbs_deb_fw("firmware load complete, code %d\n", ret); + + /* User must free helper/mainfw */ + priv->fw_callback(priv, ret, helper, mainfw); + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->fw_callback = NULL; + wake_up(&priv->fw_waitq); + spin_unlock_irqrestore(&priv->driver_lock, flags); +} + +static void do_load_firmware(struct lbs_private *priv, const char *name, + void (*cb)(const struct firmware *fw, void *context)) +{ + int ret; + + lbs_deb_fw("Requesting %s\n", name); + ret = request_firmware_nowait(THIS_MODULE, true, name, + priv->fw_device, GFP_KERNEL, priv, cb); + if (ret) { + lbs_deb_fw("request_firmware_nowait error %d\n", ret); + lbs_fw_loaded(priv, ret, NULL, NULL); + } +} + +static void main_firmware_cb(const struct firmware *firmware, void *context) +{ + struct lbs_private *priv = context; + + if (!firmware) { + /* Failed to find firmware: try next table entry */ + load_next_firmware_from_table(priv); + return; + } + + /* Firmware found! */ + lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); + if (priv->helper_fw) { + release_firmware (priv->helper_fw); + priv->helper_fw = NULL; + } + release_firmware (firmware); +} + +static void helper_firmware_cb(const struct firmware *firmware, void *context) +{ + struct lbs_private *priv = context; + + if (!firmware) { + /* Failed to find firmware: try next table entry */ + load_next_firmware_from_table(priv); + return; + } + + /* Firmware found! */ + if (priv->fw_iter->fwname) { + priv->helper_fw = firmware; + do_load_firmware(priv, priv->fw_iter->fwname, main_firmware_cb); + } else { + /* No main firmware needed for this helper --> success! */ + lbs_fw_loaded(priv, 0, firmware, NULL); + } +} + +static void load_next_firmware_from_table(struct lbs_private *priv) +{ + const struct lbs_fw_table *iter; + + if (!priv->fw_iter) + iter = priv->fw_table; + else + iter = ++priv->fw_iter; + + if (priv->helper_fw) { + release_firmware(priv->helper_fw); + priv->helper_fw = NULL; + } + +next: + if (!iter->helper) { + /* End of table hit. */ + lbs_fw_loaded(priv, -ENOENT, NULL, NULL); + return; + } + + if (iter->model != priv->fw_model) { + iter++; + goto next; + } + + priv->fw_iter = iter; + do_load_firmware(priv, iter->helper, helper_firmware_cb); +} + +void lbs_wait_for_firmware_load(struct lbs_private *priv) +{ + wait_event(priv->fw_waitq, priv->fw_callback == NULL); +} + +/** + * lbs_get_firmware_async - Retrieves firmware asynchronously. Can load + * either a helper firmware and a main firmware (2-stage), or just the helper. + * + * @priv: Pointer to lbs_private instance + * @dev: A pointer to &device structure + * @card_model: Bus-specific card model ID used to filter firmware table + * elements + * @fw_table: Table of firmware file names and device model numbers + * terminated by an entry with a NULL helper name + * @callback: User callback to invoke when firmware load succeeds or fails. + */ +int lbs_get_firmware_async(struct lbs_private *priv, struct device *device, + u32 card_model, const struct lbs_fw_table *fw_table, + lbs_fw_cb callback) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->driver_lock, flags); + if (priv->fw_callback) { + lbs_deb_fw("firmware load already in progress\n"); + spin_unlock_irqrestore(&priv->driver_lock, flags); + return -EBUSY; + } + + priv->fw_device = device; + priv->fw_callback = callback; + priv->fw_table = fw_table; + priv->fw_iter = NULL; + priv->fw_model = card_model; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbs_deb_fw("Starting async firmware load\n"); + load_next_firmware_from_table(priv); + return 0; +} +EXPORT_SYMBOL_GPL(lbs_get_firmware_async); + +/** + * lbs_get_firmware - Retrieves two-stage firmware + * + * @dev: A pointer to &device structure + * @card_model: Bus-specific card model ID used to filter firmware table + * elements + * @fw_table: Table of firmware file names and device model numbers + * terminated by an entry with a NULL helper name + * @helper: On success, the helper firmware; caller must free + * @mainfw: On success, the main firmware; caller must free + * + * Deprecated: use lbs_get_firmware_async() instead. + * + * returns: 0 on success, non-zero on failure + */ +int lbs_get_firmware(struct device *dev, u32 card_model, + const struct lbs_fw_table *fw_table, + const struct firmware **helper, + const struct firmware **mainfw) +{ + const struct lbs_fw_table *iter; + int ret; + + BUG_ON(helper == NULL); + BUG_ON(mainfw == NULL); + + /* Search for firmware to use from the table. */ + iter = fw_table; + while (iter && iter->helper) { + if (iter->model != card_model) + goto next; + + if (*helper == NULL) { + ret = request_firmware(helper, iter->helper, dev); + if (ret) + goto next; + + /* If the device has one-stage firmware (ie cf8305) and + * we've got it then we don't need to bother with the + * main firmware. + */ + if (iter->fwname == NULL) + return 0; + } + + if (*mainfw == NULL) { + ret = request_firmware(mainfw, iter->fwname, dev); + if (ret) { + /* Clear the helper to ensure we don't have + * mismatched firmware pairs. + */ + release_firmware(*helper); + *helper = NULL; + } + } + + if (*helper && *mainfw) + return 0; + + next: + iter++; + } + + /* Failed */ + release_firmware(*helper); + *helper = NULL; + release_firmware(*mainfw); + *mainfw = NULL; + + return -ENOENT; +} +EXPORT_SYMBOL_GPL(lbs_get_firmware); diff --git a/drivers/net/wireless/marvell/libertas/host.h b/drivers/net/wireless/marvell/libertas/host.h new file mode 100644 index 000000000000..96726f79a1dd --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/host.h @@ -0,0 +1,978 @@ +/* + * This file function prototypes, data structure + * and definitions for all the host/station commands + */ + +#ifndef _LBS_HOST_H_ +#define _LBS_HOST_H_ + +#include "types.h" +#include "defs.h" + +#define DEFAULT_AD_HOC_CHANNEL 6 + +#define CMD_OPTION_WAITFORRSP 0x0002 + +/* Host command IDs */ + +/* + * Return command are almost always the same as the host command, but with + * bit 15 set high. There are a few exceptions, though... + */ +#define CMD_RET(cmd) (0x8000 | cmd) + +/* Return command convention exceptions: */ +#define CMD_RET_802_11_ASSOCIATE 0x8012 + +/* Command codes */ +#define CMD_GET_HW_SPEC 0x0003 +#define CMD_EEPROM_UPDATE 0x0004 +#define CMD_802_11_RESET 0x0005 +#define CMD_802_11_SCAN 0x0006 +#define CMD_802_11_GET_LOG 0x000b +#define CMD_MAC_MULTICAST_ADR 0x0010 +#define CMD_802_11_AUTHENTICATE 0x0011 +#define CMD_802_11_EEPROM_ACCESS 0x0059 +#define CMD_802_11_ASSOCIATE 0x0050 +#define CMD_802_11_SET_WEP 0x0013 +#define CMD_802_11_GET_STAT 0x0014 +#define CMD_802_3_GET_STAT 0x0015 +#define CMD_802_11_SNMP_MIB 0x0016 +#define CMD_MAC_REG_MAP 0x0017 +#define CMD_BBP_REG_MAP 0x0018 +#define CMD_MAC_REG_ACCESS 0x0019 +#define CMD_BBP_REG_ACCESS 0x001a +#define CMD_RF_REG_ACCESS 0x001b +#define CMD_802_11_RADIO_CONTROL 0x001c +#define CMD_802_11_RF_CHANNEL 0x001d +#define CMD_802_11_RF_TX_POWER 0x001e +#define CMD_802_11_RSSI 0x001f +#define CMD_802_11_RF_ANTENNA 0x0020 +#define CMD_802_11_PS_MODE 0x0021 +#define CMD_802_11_DATA_RATE 0x0022 +#define CMD_RF_REG_MAP 0x0023 +#define CMD_802_11_DEAUTHENTICATE 0x0024 +#define CMD_802_11_REASSOCIATE 0x0025 +#define CMD_MAC_CONTROL 0x0028 +#define CMD_802_11_AD_HOC_START 0x002b +#define CMD_802_11_AD_HOC_JOIN 0x002c +#define CMD_802_11_QUERY_TKIP_REPLY_CNTRS 0x002e +#define CMD_802_11_ENABLE_RSN 0x002f +#define CMD_802_11_SET_AFC 0x003c +#define CMD_802_11_GET_AFC 0x003d +#define CMD_802_11_DEEP_SLEEP 0x003e +#define CMD_802_11_AD_HOC_STOP 0x0040 +#define CMD_802_11_HOST_SLEEP_CFG 0x0043 +#define CMD_802_11_WAKEUP_CONFIRM 0x0044 +#define CMD_802_11_HOST_SLEEP_ACTIVATE 0x0045 +#define CMD_802_11_BEACON_STOP 0x0049 +#define CMD_802_11_MAC_ADDRESS 0x004d +#define CMD_802_11_LED_GPIO_CTRL 0x004e +#define CMD_802_11_BAND_CONFIG 0x0058 +#define CMD_GSPI_BUS_CONFIG 0x005a +#define CMD_802_11D_DOMAIN_INFO 0x005b +#define CMD_802_11_KEY_MATERIAL 0x005e +#define CMD_802_11_SLEEP_PARAMS 0x0066 +#define CMD_802_11_INACTIVITY_TIMEOUT 0x0067 +#define CMD_802_11_SLEEP_PERIOD 0x0068 +#define CMD_802_11_TPC_CFG 0x0072 +#define CMD_802_11_PA_CFG 0x0073 +#define CMD_802_11_FW_WAKE_METHOD 0x0074 +#define CMD_802_11_SUBSCRIBE_EVENT 0x0075 +#define CMD_802_11_RATE_ADAPT_RATESET 0x0076 +#define CMD_802_11_TX_RATE_QUERY 0x007f +#define CMD_GET_TSF 0x0080 +#define CMD_BT_ACCESS 0x0087 +#define CMD_FWT_ACCESS 0x0095 +#define CMD_802_11_MONITOR_MODE 0x0098 +#define CMD_MESH_ACCESS 0x009b +#define CMD_MESH_CONFIG_OLD 0x00a3 +#define CMD_MESH_CONFIG 0x00ac +#define CMD_SET_BOOT2_VER 0x00a5 +#define CMD_FUNC_INIT 0x00a9 +#define CMD_FUNC_SHUTDOWN 0x00aa +#define CMD_802_11_BEACON_CTRL 0x00b0 + +/* For the IEEE Power Save */ +#define PS_MODE_ACTION_ENTER_PS 0x0030 +#define PS_MODE_ACTION_EXIT_PS 0x0031 +#define PS_MODE_ACTION_SLEEP_CONFIRMED 0x0034 + +#define CMD_ENABLE_RSN 0x0001 +#define CMD_DISABLE_RSN 0x0000 + +#define CMD_ACT_GET 0x0000 +#define CMD_ACT_SET 0x0001 + +/* Define action or option for CMD_802_11_SET_WEP */ +#define CMD_ACT_ADD 0x0002 +#define CMD_ACT_REMOVE 0x0004 + +#define CMD_TYPE_WEP_40_BIT 0x01 +#define CMD_TYPE_WEP_104_BIT 0x02 + +#define CMD_NUM_OF_WEP_KEYS 4 + +#define CMD_WEP_KEY_INDEX_MASK 0x3fff + +/* Define action or option for CMD_802_11_SCAN */ +#define CMD_BSS_TYPE_BSS 0x0001 +#define CMD_BSS_TYPE_IBSS 0x0002 +#define CMD_BSS_TYPE_ANY 0x0003 + +/* Define action or option for CMD_802_11_SCAN */ +#define CMD_SCAN_TYPE_ACTIVE 0x0000 +#define CMD_SCAN_TYPE_PASSIVE 0x0001 + +#define CMD_SCAN_RADIO_TYPE_BG 0 + +#define CMD_SCAN_PROBE_DELAY_TIME 0 + +/* Define action or option for CMD_MAC_CONTROL */ +#define CMD_ACT_MAC_RX_ON 0x0001 +#define CMD_ACT_MAC_TX_ON 0x0002 +#define CMD_ACT_MAC_LOOPBACK_ON 0x0004 +#define CMD_ACT_MAC_WEP_ENABLE 0x0008 +#define CMD_ACT_MAC_INT_ENABLE 0x0010 +#define CMD_ACT_MAC_MULTICAST_ENABLE 0x0020 +#define CMD_ACT_MAC_BROADCAST_ENABLE 0x0040 +#define CMD_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 +#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 +#define CMD_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400 + +/* Event flags for CMD_802_11_SUBSCRIBE_EVENT */ +#define CMD_SUBSCRIBE_RSSI_LOW 0x0001 +#define CMD_SUBSCRIBE_SNR_LOW 0x0002 +#define CMD_SUBSCRIBE_FAILCOUNT 0x0004 +#define CMD_SUBSCRIBE_BCNMISS 0x0008 +#define CMD_SUBSCRIBE_RSSI_HIGH 0x0010 +#define CMD_SUBSCRIBE_SNR_HIGH 0x0020 + +#define RADIO_PREAMBLE_LONG 0x00 +#define RADIO_PREAMBLE_SHORT 0x02 +#define RADIO_PREAMBLE_AUTO 0x04 + +/* Define action or option for CMD_802_11_RF_CHANNEL */ +#define CMD_OPT_802_11_RF_CHANNEL_GET 0x00 +#define CMD_OPT_802_11_RF_CHANNEL_SET 0x01 + +/* Define action or option for CMD_802_11_DATA_RATE */ +#define CMD_ACT_SET_TX_AUTO 0x0000 +#define CMD_ACT_SET_TX_FIX_RATE 0x0001 +#define CMD_ACT_GET_TX_RATE 0x0002 + +/* Options for CMD_802_11_FW_WAKE_METHOD */ +#define CMD_WAKE_METHOD_UNCHANGED 0x0000 +#define CMD_WAKE_METHOD_COMMAND_INT 0x0001 +#define CMD_WAKE_METHOD_GPIO 0x0002 + +/* Object IDs for CMD_802_11_SNMP_MIB */ +#define SNMP_MIB_OID_BSS_TYPE 0x0000 +#define SNMP_MIB_OID_OP_RATE_SET 0x0001 +#define SNMP_MIB_OID_BEACON_PERIOD 0x0002 /* Reserved on v9+ */ +#define SNMP_MIB_OID_DTIM_PERIOD 0x0003 /* Reserved on v9+ */ +#define SNMP_MIB_OID_ASSOC_TIMEOUT 0x0004 /* Reserved on v9+ */ +#define SNMP_MIB_OID_RTS_THRESHOLD 0x0005 +#define SNMP_MIB_OID_SHORT_RETRY_LIMIT 0x0006 +#define SNMP_MIB_OID_LONG_RETRY_LIMIT 0x0007 +#define SNMP_MIB_OID_FRAG_THRESHOLD 0x0008 +#define SNMP_MIB_OID_11D_ENABLE 0x0009 +#define SNMP_MIB_OID_11H_ENABLE 0x000A + +/* Define action or option for CMD_BT_ACCESS */ +enum cmd_bt_access_opts { + /* The bt commands start at 5 instead of 1 because the old dft commands + * are mapped to 1-4. These old commands are no longer maintained and + * should not be called. + */ + CMD_ACT_BT_ACCESS_ADD = 5, + CMD_ACT_BT_ACCESS_DEL, + CMD_ACT_BT_ACCESS_LIST, + CMD_ACT_BT_ACCESS_RESET, + CMD_ACT_BT_ACCESS_SET_INVERT, + CMD_ACT_BT_ACCESS_GET_INVERT +}; + +/* Define action or option for CMD_FWT_ACCESS */ +enum cmd_fwt_access_opts { + CMD_ACT_FWT_ACCESS_ADD = 1, + CMD_ACT_FWT_ACCESS_DEL, + CMD_ACT_FWT_ACCESS_LOOKUP, + CMD_ACT_FWT_ACCESS_LIST, + CMD_ACT_FWT_ACCESS_LIST_ROUTE, + CMD_ACT_FWT_ACCESS_LIST_NEIGHBOR, + CMD_ACT_FWT_ACCESS_RESET, + CMD_ACT_FWT_ACCESS_CLEANUP, + CMD_ACT_FWT_ACCESS_TIME, +}; + +/* Define action or option for CMD_802_11_HOST_SLEEP_CFG */ +enum cmd_wol_cfg_opts { + CMD_ACT_ACTION_NONE = 0, + CMD_ACT_SET_WOL_RULE, + CMD_ACT_GET_WOL_RULE, + CMD_ACT_RESET_WOL_RULE, +}; + +/* Define action or option for CMD_MESH_ACCESS */ +enum cmd_mesh_access_opts { + CMD_ACT_MESH_GET_TTL = 1, + CMD_ACT_MESH_SET_TTL, + CMD_ACT_MESH_GET_STATS, + CMD_ACT_MESH_GET_ANYCAST, + CMD_ACT_MESH_SET_ANYCAST, + CMD_ACT_MESH_SET_LINK_COSTS, + CMD_ACT_MESH_GET_LINK_COSTS, + CMD_ACT_MESH_SET_BCAST_RATE, + CMD_ACT_MESH_GET_BCAST_RATE, + CMD_ACT_MESH_SET_RREQ_DELAY, + CMD_ACT_MESH_GET_RREQ_DELAY, + CMD_ACT_MESH_SET_ROUTE_EXP, + CMD_ACT_MESH_GET_ROUTE_EXP, + CMD_ACT_MESH_SET_AUTOSTART_ENABLED, + CMD_ACT_MESH_GET_AUTOSTART_ENABLED, + CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT = 17, +}; + +/* Define actions and types for CMD_MESH_CONFIG */ +enum cmd_mesh_config_actions { + CMD_ACT_MESH_CONFIG_STOP = 0, + CMD_ACT_MESH_CONFIG_START, + CMD_ACT_MESH_CONFIG_SET, + CMD_ACT_MESH_CONFIG_GET, +}; + +enum cmd_mesh_config_types { + CMD_TYPE_MESH_SET_BOOTFLAG = 1, + CMD_TYPE_MESH_SET_BOOTTIME, + CMD_TYPE_MESH_SET_DEF_CHANNEL, + CMD_TYPE_MESH_SET_MESH_IE, + CMD_TYPE_MESH_GET_DEFAULTS, + CMD_TYPE_MESH_GET_MESH_IE, /* GET_DEFAULTS is superset of GET_MESHIE */ +}; + +/* Card Event definition */ +#define MACREG_INT_CODE_TX_PPA_FREE 0 +#define MACREG_INT_CODE_TX_DMA_DONE 1 +#define MACREG_INT_CODE_LINK_LOST_W_SCAN 2 +#define MACREG_INT_CODE_LINK_LOST_NO_SCAN 3 +#define MACREG_INT_CODE_LINK_SENSED 4 +#define MACREG_INT_CODE_CMD_FINISHED 5 +#define MACREG_INT_CODE_MIB_CHANGED 6 +#define MACREG_INT_CODE_INIT_DONE 7 +#define MACREG_INT_CODE_DEAUTHENTICATED 8 +#define MACREG_INT_CODE_DISASSOCIATED 9 +#define MACREG_INT_CODE_PS_AWAKE 10 +#define MACREG_INT_CODE_PS_SLEEP 11 +#define MACREG_INT_CODE_MIC_ERR_MULTICAST 13 +#define MACREG_INT_CODE_MIC_ERR_UNICAST 14 +#define MACREG_INT_CODE_WM_AWAKE 15 +#define MACREG_INT_CODE_DEEP_SLEEP_AWAKE 16 +#define MACREG_INT_CODE_ADHOC_BCN_LOST 17 +#define MACREG_INT_CODE_HOST_AWAKE 18 +#define MACREG_INT_CODE_STOP_TX 19 +#define MACREG_INT_CODE_START_TX 20 +#define MACREG_INT_CODE_CHANNEL_SWITCH 21 +#define MACREG_INT_CODE_MEASUREMENT_RDY 22 +#define MACREG_INT_CODE_WMM_CHANGE 23 +#define MACREG_INT_CODE_BG_SCAN_REPORT 24 +#define MACREG_INT_CODE_RSSI_LOW 25 +#define MACREG_INT_CODE_SNR_LOW 26 +#define MACREG_INT_CODE_MAX_FAIL 27 +#define MACREG_INT_CODE_RSSI_HIGH 28 +#define MACREG_INT_CODE_SNR_HIGH 29 +#define MACREG_INT_CODE_MESH_AUTO_STARTED 35 +#define MACREG_INT_CODE_FIRMWARE_READY 48 + + +/* 802.11-related definitions */ + +/* TxPD descriptor */ +struct txpd { + /* union to cope up with later FW revisions */ + union { + /* Current Tx packet status */ + __le32 tx_status; + struct { + /* BSS type: client, AP, etc. */ + u8 bss_type; + /* BSS number */ + u8 bss_num; + /* Reserved */ + __le16 reserved; + } bss; + } u; + /* Tx control */ + __le32 tx_control; + __le32 tx_packet_location; + /* Tx packet length */ + __le16 tx_packet_length; + /* First 2 byte of destination MAC address */ + u8 tx_dest_addr_high[2]; + /* Last 4 byte of destination MAC address */ + u8 tx_dest_addr_low[4]; + /* Pkt Priority */ + u8 priority; + /* Pkt Trasnit Power control */ + u8 powermgmt; + /* Amount of time the packet has been queued (units = 2ms) */ + u8 pktdelay_2ms; + /* reserved */ + u8 reserved1; +} __packed; + +/* RxPD Descriptor */ +struct rxpd { + /* union to cope up with later FW revisions */ + union { + /* Current Rx packet status */ + __le16 status; + struct { + /* BSS type: client, AP, etc. */ + u8 bss_type; + /* BSS number */ + u8 bss_num; + } __packed bss; + } __packed u; + + /* SNR */ + u8 snr; + + /* Tx control */ + u8 rx_control; + + /* Pkt length */ + __le16 pkt_len; + + /* Noise Floor */ + u8 nf; + + /* Rx Packet Rate */ + u8 rx_rate; + + /* Pkt addr */ + __le32 pkt_ptr; + + /* Next Rx RxPD addr */ + __le32 next_rxpd_ptr; + + /* Pkt Priority */ + u8 priority; + u8 reserved[3]; +} __packed; + +struct cmd_header { + __le16 command; + __le16 size; + __le16 seqnum; + __le16 result; +} __packed; + +/* Generic structure to hold all key types. */ +struct enc_key { + u16 len; + u16 flags; /* KEY_INFO_* from defs.h */ + u16 type; /* KEY_TYPE_* from defs.h */ + u8 key[32]; +}; + +/* lbs_offset_value */ +struct lbs_offset_value { + u32 offset; + u32 value; +} __packed; + +#define MAX_11D_TRIPLETS 83 + +struct mrvl_ie_domain_param_set { + struct mrvl_ie_header header; + + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + struct ieee80211_country_ie_triplet triplet[MAX_11D_TRIPLETS]; +} __packed; + +struct cmd_ds_802_11d_domain_info { + struct cmd_header hdr; + + __le16 action; + struct mrvl_ie_domain_param_set domain; +} __packed; + +/* + * Define data structure for CMD_GET_HW_SPEC + * This structure defines the response for the GET_HW_SPEC command + */ +struct cmd_ds_get_hw_spec { + struct cmd_header hdr; + + /* HW Interface version number */ + __le16 hwifversion; + /* HW version number */ + __le16 version; + /* Max number of TxPD FW can handle */ + __le16 nr_txpd; + /* Max no of Multicast address */ + __le16 nr_mcast_adr; + /* MAC address */ + u8 permanentaddr[6]; + + /* region Code */ + __le16 regioncode; + + /* Number of antenna used */ + __le16 nr_antenna; + + /* FW release number, example 0x01030304 = 2.3.4p1 */ + __le32 fwrelease; + + /* Base Address of TxPD queue */ + __le32 wcb_base; + /* Read Pointer of RxPd queue */ + __le32 rxpd_rdptr; + + /* Write Pointer of RxPd queue */ + __le32 rxpd_wrptr; + + /*FW/HW capability */ + __le32 fwcapinfo; +} __packed; + +struct cmd_ds_802_11_subscribe_event { + struct cmd_header hdr; + + __le16 action; + __le16 events; + + /* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a + * number of TLVs. From the v5.1 manual, those TLVs would add up to + * 40 bytes. However, future firmware might add additional TLVs, so I + * bump this up a bit. + */ + uint8_t tlv[128]; +} __packed; + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for CMD_802_11_SCAN + */ +struct cmd_ds_802_11_scan { + struct cmd_header hdr; + + uint8_t bsstype; + uint8_t bssid[ETH_ALEN]; + uint8_t tlvbuffer[0]; +} __packed; + +struct cmd_ds_802_11_scan_rsp { + struct cmd_header hdr; + + __le16 bssdescriptsize; + uint8_t nr_sets; + uint8_t bssdesc_and_tlvbuffer[0]; +} __packed; + +struct cmd_ds_802_11_get_log { + struct cmd_header hdr; + + __le32 mcasttxframe; + __le32 failed; + __le32 retry; + __le32 multiretry; + __le32 framedup; + __le32 rtssuccess; + __le32 rtsfailure; + __le32 ackfailure; + __le32 rxfrag; + __le32 mcastrxframe; + __le32 fcserror; + __le32 txframe; + __le32 wepundecryptable; +} __packed; + +struct cmd_ds_mac_control { + struct cmd_header hdr; + __le16 action; + u16 reserved; +} __packed; + +struct cmd_ds_mac_multicast_adr { + struct cmd_header hdr; + __le16 action; + __le16 nr_of_adrs; + u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; +} __packed; + +struct cmd_ds_802_11_authenticate { + struct cmd_header hdr; + + u8 bssid[ETH_ALEN]; + u8 authtype; + u8 reserved[10]; +} __packed; + +struct cmd_ds_802_11_deauthenticate { + struct cmd_header hdr; + + u8 macaddr[ETH_ALEN]; + __le16 reasoncode; +} __packed; + +struct cmd_ds_802_11_associate { + struct cmd_header hdr; + + u8 bssid[6]; + __le16 capability; + __le16 listeninterval; + __le16 bcnperiod; + u8 dtimperiod; + u8 iebuf[512]; /* Enough for required and most optional IEs */ +} __packed; + +struct cmd_ds_802_11_associate_response { + struct cmd_header hdr; + + __le16 capability; + __le16 statuscode; + __le16 aid; + u8 iebuf[512]; +} __packed; + +struct cmd_ds_802_11_set_wep { + struct cmd_header hdr; + + /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ + __le16 action; + + /* key Index selected for Tx */ + __le16 keyindex; + + /* 40, 128bit or TXWEP */ + uint8_t keytype[4]; + uint8_t keymaterial[4][16]; +} __packed; + +struct cmd_ds_802_11_snmp_mib { + struct cmd_header hdr; + + __le16 action; + __le16 oid; + __le16 bufsize; + u8 value[128]; +} __packed; + +struct cmd_ds_reg_access { + struct cmd_header hdr; + + __le16 action; + __le16 offset; + union { + u8 bbp_rf; /* for BBP and RF registers */ + __le32 mac; /* for MAC registers */ + } value; +} __packed; + +struct cmd_ds_802_11_radio_control { + struct cmd_header hdr; + + __le16 action; + __le16 control; +} __packed; + +struct cmd_ds_802_11_beacon_control { + struct cmd_header hdr; + + __le16 action; + __le16 beacon_enable; + __le16 beacon_period; +} __packed; + +struct cmd_ds_802_11_sleep_params { + struct cmd_header hdr; + + /* ACT_GET/ACT_SET */ + __le16 action; + + /* Sleep clock error in ppm */ + __le16 error; + + /* Wakeup offset in usec */ + __le16 offset; + + /* Clock stabilization time in usec */ + __le16 stabletime; + + /* control periodic calibration */ + uint8_t calcontrol; + + /* control the use of external sleep clock */ + uint8_t externalsleepclk; + + /* reserved field, should be set to zero */ + __le16 reserved; +} __packed; + +struct cmd_ds_802_11_rf_channel { + struct cmd_header hdr; + + __le16 action; + __le16 channel; + __le16 rftype; /* unused */ + __le16 reserved; /* unused */ + u8 channellist[32]; /* unused */ +} __packed; + +struct cmd_ds_802_11_rssi { + struct cmd_header hdr; + + /* + * request: number of beacons (N) to average the SNR and NF over + * response: SNR of most recent beacon + */ + __le16 n_or_snr; + + /* + * The following fields are only set in the response. + * In the request these are reserved and should be set to 0. + */ + __le16 nf; /* most recent beacon noise floor */ + __le16 avg_snr; /* average SNR weighted by N from request */ + __le16 avg_nf; /* average noise floor weighted by N from request */ +} __packed; + +struct cmd_ds_802_11_mac_address { + struct cmd_header hdr; + + __le16 action; + u8 macadd[ETH_ALEN]; +} __packed; + +struct cmd_ds_802_11_rf_tx_power { + struct cmd_header hdr; + + __le16 action; + __le16 curlevel; + s8 maxlevel; + s8 minlevel; +} __packed; + +/* MONITOR_MODE only exists in OLPC v5 firmware */ +struct cmd_ds_802_11_monitor_mode { + struct cmd_header hdr; + + __le16 action; + __le16 mode; +} __packed; + +struct cmd_ds_set_boot2_ver { + struct cmd_header hdr; + + __le16 action; + __le16 version; +} __packed; + +struct cmd_ds_802_11_fw_wake_method { + struct cmd_header hdr; + + __le16 action; + __le16 method; +} __packed; + +struct cmd_ds_802_11_ps_mode { + struct cmd_header hdr; + + __le16 action; + + /* + * Interval for keepalive in PS mode: + * 0x0000 = don't change + * 0x001E = firmware default + * 0xFFFF = disable + */ + __le16 nullpktinterval; + + /* + * Number of DTIM intervals to wake up for: + * 0 = don't change + * 1 = firmware default + * 5 = max + */ + __le16 multipledtim; + + __le16 reserved; + __le16 locallisteninterval; + + /* + * AdHoc awake period (FW v9+ only): + * 0 = don't change + * 1 = always awake (IEEE standard behavior) + * 2 - 31 = sleep for (n - 1) periods and awake for 1 period + * 32 - 254 = invalid + * 255 = sleep at each ATIM + */ + __le16 adhoc_awake_period; +} __packed; + +struct cmd_confirm_sleep { + struct cmd_header hdr; + + __le16 action; + __le16 nullpktinterval; + __le16 multipledtim; + __le16 reserved; + __le16 locallisteninterval; +} __packed; + +struct cmd_ds_802_11_data_rate { + struct cmd_header hdr; + + __le16 action; + __le16 reserved; + u8 rates[MAX_RATES]; +} __packed; + +struct cmd_ds_802_11_rate_adapt_rateset { + struct cmd_header hdr; + __le16 action; + __le16 enablehwauto; + __le16 bitmap; +} __packed; + +struct cmd_ds_802_11_ad_hoc_start { + struct cmd_header hdr; + + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 bsstype; + __le16 beaconperiod; + u8 dtimperiod; /* Reserved on v9 and later */ + struct ieee_ie_ibss_param_set ibss; + u8 reserved1[4]; + struct ieee_ie_ds_param_set ds; + u8 reserved2[4]; + __le16 probedelay; /* Reserved on v9 and later */ + __le16 capability; + u8 rates[MAX_RATES]; + u8 tlv_memory_size_pad[100]; +} __packed; + +struct cmd_ds_802_11_ad_hoc_result { + struct cmd_header hdr; + + u8 pad[3]; + u8 bssid[ETH_ALEN]; +} __packed; + +struct adhoc_bssdesc { + u8 bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 type; + __le16 beaconperiod; + u8 dtimperiod; + __le64 timestamp; + __le64 localtime; + struct ieee_ie_ds_param_set ds; + u8 reserved1[4]; + struct ieee_ie_ibss_param_set ibss; + u8 reserved2[4]; + __le16 capability; + u8 rates[MAX_RATES]; + + /* + * DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the + * Adhoc join command and will cause a binary layout mismatch with + * the firmware + */ +} __packed; + +struct cmd_ds_802_11_ad_hoc_join { + struct cmd_header hdr; + + struct adhoc_bssdesc bss; + __le16 failtimeout; /* Reserved on v9 and later */ + __le16 probedelay; /* Reserved on v9 and later */ +} __packed; + +struct cmd_ds_802_11_ad_hoc_stop { + struct cmd_header hdr; +} __packed; + +struct cmd_ds_802_11_enable_rsn { + struct cmd_header hdr; + + __le16 action; + __le16 enable; +} __packed; + +struct MrvlIEtype_keyParamSet { + /* type ID */ + __le16 type; + + /* length of Payload */ + __le16 length; + + /* type of key: WEP=0, TKIP=1, AES=2 */ + __le16 keytypeid; + + /* key control Info specific to a keytypeid */ + __le16 keyinfo; + + /* length of key */ + __le16 keylen; + + /* key material of size keylen */ + u8 key[32]; +} __packed; + +#define MAX_WOL_RULES 16 + +struct host_wol_rule { + uint8_t rule_no; + uint8_t rule_ops; + __le16 sig_offset; + __le16 sig_length; + __le16 reserve; + __be32 sig_mask; + __be32 signature; +} __packed; + +struct wol_config { + uint8_t action; + uint8_t pattern; + uint8_t no_rules_in_cmd; + uint8_t result; + struct host_wol_rule rule[MAX_WOL_RULES]; +} __packed; + +struct cmd_ds_host_sleep { + struct cmd_header hdr; + __le32 criteria; + uint8_t gpio; + uint16_t gap; + struct wol_config wol_conf; +} __packed; + + + +struct cmd_ds_802_11_key_material { + struct cmd_header hdr; + + __le16 action; + struct MrvlIEtype_keyParamSet keyParamSet[2]; +} __packed; + +struct cmd_ds_802_11_eeprom_access { + struct cmd_header hdr; + __le16 action; + __le16 offset; + __le16 len; + /* firmware says it returns a maximum of 20 bytes */ +#define LBS_EEPROM_READ_LEN 20 + u8 value[LBS_EEPROM_READ_LEN]; +} __packed; + +struct cmd_ds_802_11_tpc_cfg { + struct cmd_header hdr; + + __le16 action; + uint8_t enable; + int8_t P0; + int8_t P1; + int8_t P2; + uint8_t usesnr; +} __packed; + + +struct cmd_ds_802_11_pa_cfg { + struct cmd_header hdr; + + __le16 action; + uint8_t enable; + int8_t P0; + int8_t P1; + int8_t P2; +} __packed; + + +struct cmd_ds_802_11_led_ctrl { + struct cmd_header hdr; + + __le16 action; + __le16 numled; + u8 data[256]; +} __packed; + +/* Automatic Frequency Control */ +struct cmd_ds_802_11_afc { + struct cmd_header hdr; + + __le16 afc_auto; + union { + struct { + __le16 threshold; + __le16 period; + }; + struct { + __le16 timing_offset; /* signed */ + __le16 carrier_offset; /* signed */ + }; + }; +} __packed; + +struct cmd_tx_rate_query { + __le16 txrate; +} __packed; + +struct cmd_ds_get_tsf { + __le64 tsfvalue; +} __packed; + +struct cmd_ds_bt_access { + struct cmd_header hdr; + + __le16 action; + __le32 id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; +} __packed; + +struct cmd_ds_fwt_access { + struct cmd_header hdr; + + __le16 action; + __le32 id; + u8 valid; + u8 da[ETH_ALEN]; + u8 dir; + u8 ra[ETH_ALEN]; + __le32 ssn; + __le32 dsn; + __le32 metric; + u8 rate; + u8 hopcount; + u8 ttl; + __le32 expiration; + u8 sleepmode; + __le32 snr; + __le32 references; + u8 prec[ETH_ALEN]; +} __packed; + +struct cmd_ds_mesh_config { + struct cmd_header hdr; + + __le16 action; + __le16 channel; + __le16 type; + __le16 length; + u8 data[128]; /* last position reserved */ +} __packed; + +struct cmd_ds_mesh_access { + struct cmd_header hdr; + + __le16 action; + __le32 data[32]; /* last position reserved */ +} __packed; + +/* Number of stats counters returned by the firmware */ +#define MESH_STATS_NUM 8 +#endif diff --git a/drivers/net/wireless/marvell/libertas/if_cs.c b/drivers/net/wireless/marvell/libertas/if_cs.c new file mode 100644 index 000000000000..f499efc6abcf --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_cs.c @@ -0,0 +1,1006 @@ +/* + + Driver for the Marvell 8385 based compact flash WLAN cards. + + (C) 2007 by Holger Schurig + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define DRV_NAME "libertas_cs" + +#include "decl.h" +#include "defs.h" +#include "dev.h" + + +/********************************************************************/ +/* Module stuff */ +/********************************************************************/ + +MODULE_AUTHOR("Holger Schurig "); +MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards"); +MODULE_LICENSE("GPL"); + + + +/********************************************************************/ +/* Data structures */ +/********************************************************************/ + +struct if_cs_card { + struct pcmcia_device *p_dev; + struct lbs_private *priv; + void __iomem *iobase; + bool align_regs; + u32 model; +}; + + +enum { + MODEL_UNKNOWN = 0x00, + MODEL_8305 = 0x01, + MODEL_8381 = 0x02, + MODEL_8385 = 0x03 +}; + +static const struct lbs_fw_table fw_table[] = { + { MODEL_8305, "libertas/cf8305.bin", NULL }, + { MODEL_8305, "libertas_cs_helper.fw", NULL }, + { MODEL_8381, "libertas/cf8381_helper.bin", "libertas/cf8381.bin" }, + { MODEL_8381, "libertas_cs_helper.fw", "libertas_cs.fw" }, + { MODEL_8385, "libertas/cf8385_helper.bin", "libertas/cf8385.bin" }, + { MODEL_8385, "libertas_cs_helper.fw", "libertas_cs.fw" }, + { 0, NULL, NULL } +}; +MODULE_FIRMWARE("libertas/cf8305.bin"); +MODULE_FIRMWARE("libertas/cf8381_helper.bin"); +MODULE_FIRMWARE("libertas/cf8381.bin"); +MODULE_FIRMWARE("libertas/cf8385_helper.bin"); +MODULE_FIRMWARE("libertas/cf8385.bin"); +MODULE_FIRMWARE("libertas_cs_helper.fw"); +MODULE_FIRMWARE("libertas_cs.fw"); + + +/********************************************************************/ +/* Hardware access */ +/********************************************************************/ + +/* This define enables wrapper functions which allow you + to dump all register accesses. You normally won't this, + except for development */ +/* #define DEBUG_IO */ + +#ifdef DEBUG_IO +static int debug_output = 0; +#else +/* This way the compiler optimizes the printk's away */ +#define debug_output 0 +#endif + +static inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg) +{ + unsigned int val = ioread8(card->iobase + reg); + if (debug_output) + printk(KERN_INFO "inb %08x<%02x\n", reg, val); + return val; +} +static inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg) +{ + unsigned int val = ioread16(card->iobase + reg); + if (debug_output) + printk(KERN_INFO "inw %08x<%04x\n", reg, val); + return val; +} +static inline void if_cs_read16_rep( + struct if_cs_card *card, + uint reg, + void *buf, + unsigned long count) +{ + if (debug_output) + printk(KERN_INFO "insw %08x<(0x%lx words)\n", + reg, count); + ioread16_rep(card->iobase + reg, buf, count); +} + +static inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val) +{ + if (debug_output) + printk(KERN_INFO "outb %08x>%02x\n", reg, val); + iowrite8(val, card->iobase + reg); +} + +static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val) +{ + if (debug_output) + printk(KERN_INFO "outw %08x>%04x\n", reg, val); + iowrite16(val, card->iobase + reg); +} + +static inline void if_cs_write16_rep( + struct if_cs_card *card, + uint reg, + const void *buf, + unsigned long count) +{ + if (debug_output) + printk(KERN_INFO "outsw %08x>(0x%lx words)\n", + reg, count); + iowrite16_rep(card->iobase + reg, buf, count); +} + + +/* + * I know that polling/delaying is frowned upon. However, this procedure + * with polling is needed while downloading the firmware. At this stage, + * the hardware does unfortunately not create any interrupts. + * + * Fortunately, this function is never used once the firmware is in + * the card. :-) + * + * As a reference, see the "Firmware Specification v5.1", page 18 + * and 19. I did not follow their suggested timing to the word, + * but this works nice & fast anyway. + */ +static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 reg) +{ + int i; + + for (i = 0; i < 100000; i++) { + u8 val = if_cs_read8(card, addr); + if (val == reg) + return 0; + udelay(5); + } + return -ETIME; +} + + + +/* + * First the bitmasks for the host/card interrupt/status registers: + */ +#define IF_CS_BIT_TX 0x0001 +#define IF_CS_BIT_RX 0x0002 +#define IF_CS_BIT_COMMAND 0x0004 +#define IF_CS_BIT_RESP 0x0008 +#define IF_CS_BIT_EVENT 0x0010 +#define IF_CS_BIT_MASK 0x001f + + + +/* + * It's not really clear to me what the host status register is for. It + * needs to be set almost in union with "host int cause". The following + * bits from above are used: + * + * IF_CS_BIT_TX driver downloaded a data packet + * IF_CS_BIT_RX driver got a data packet + * IF_CS_BIT_COMMAND driver downloaded a command + * IF_CS_BIT_RESP not used (has some meaning with powerdown) + * IF_CS_BIT_EVENT driver read a host event + */ +#define IF_CS_HOST_STATUS 0x00000000 + +/* + * With the host int cause register can the host (that is, Linux) cause + * an interrupt in the firmware, to tell the firmware about those events: + * + * IF_CS_BIT_TX a data packet has been downloaded + * IF_CS_BIT_RX a received data packet has retrieved + * IF_CS_BIT_COMMAND a firmware block or a command has been downloaded + * IF_CS_BIT_RESP not used (has some meaning with powerdown) + * IF_CS_BIT_EVENT a host event (link lost etc) has been retrieved + */ +#define IF_CS_HOST_INT_CAUSE 0x00000002 + +/* + * The host int mask register is used to enable/disable interrupt. However, + * I have the suspicion that disabled interrupts are lost. + */ +#define IF_CS_HOST_INT_MASK 0x00000004 + +/* + * Used to send or receive data packets: + */ +#define IF_CS_WRITE 0x00000016 +#define IF_CS_WRITE_LEN 0x00000014 +#define IF_CS_READ 0x00000010 +#define IF_CS_READ_LEN 0x00000024 + +/* + * Used to send commands (and to send firmware block) and to + * receive command responses: + */ +#define IF_CS_CMD 0x0000001A +#define IF_CS_CMD_LEN 0x00000018 +#define IF_CS_RESP 0x00000012 +#define IF_CS_RESP_LEN 0x00000030 + +/* + * The card status registers shows what the card/firmware actually + * accepts: + * + * IF_CS_BIT_TX you may send a data packet + * IF_CS_BIT_RX you may retrieve a data packet + * IF_CS_BIT_COMMAND you may send a command + * IF_CS_BIT_RESP you may retrieve a command response + * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) + * + * When reading this register several times, you will get back the same + * results --- with one exception: the IF_CS_BIT_EVENT clear itself + * automatically. + * + * Not that we don't rely on BIT_RX,_BIT_RESP or BIT_EVENT because + * we handle this via the card int cause register. + */ +#define IF_CS_CARD_STATUS 0x00000020 +#define IF_CS_CARD_STATUS_MASK 0x7f00 + +/* + * The card int cause register is used by the card/firmware to notify us + * about the following events: + * + * IF_CS_BIT_TX a data packet has successfully been sentx + * IF_CS_BIT_RX a data packet has been received and can be retrieved + * IF_CS_BIT_COMMAND not used + * IF_CS_BIT_RESP the firmware has a command response for us + * IF_CS_BIT_EVENT the card has a event for use (link lost, snr low etc) + */ +#define IF_CS_CARD_INT_CAUSE 0x00000022 + +/* + * This is used to for handshaking with the card's bootloader/helper image + * to synchronize downloading of firmware blocks. + */ +#define IF_CS_SQ_READ_LOW 0x00000028 +#define IF_CS_SQ_HELPER_OK 0x10 + +/* + * The scratch register tells us ... + * + * IF_CS_SCRATCH_BOOT_OK the bootloader runs + * IF_CS_SCRATCH_HELPER_OK the helper firmware already runs + */ +#define IF_CS_SCRATCH 0x0000003F +#define IF_CS_SCRATCH_BOOT_OK 0x00 +#define IF_CS_SCRATCH_HELPER_OK 0x5a + +/* + * Used to detect ancient chips: + */ +#define IF_CS_PRODUCT_ID 0x0000001C +#define IF_CS_CF8385_B1_REV 0x12 +#define IF_CS_CF8381_B3_REV 0x04 +#define IF_CS_CF8305_B1_REV 0x03 + +/* + * Used to detect other cards than CF8385 since their revisions of silicon + * doesn't match those from CF8385, eg. CF8381 B3 works with this driver. + */ +#define CF8305_MANFID 0x02db +#define CF8305_CARDID 0x8103 +#define CF8381_MANFID 0x02db +#define CF8381_CARDID 0x6064 +#define CF8385_MANFID 0x02df +#define CF8385_CARDID 0x8103 + +/* + * FIXME: just use the 'driver_info' field of 'struct pcmcia_device_id' when + * that gets fixed. Currently there's no way to access it from the probe hook. + */ +static inline u32 get_model(u16 manf_id, u16 card_id) +{ + /* NOTE: keep in sync with if_cs_ids */ + if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID) + return MODEL_8305; + else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID) + return MODEL_8381; + else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID) + return MODEL_8385; + return MODEL_UNKNOWN; +} + +/********************************************************************/ +/* I/O and interrupt handling */ +/********************************************************************/ + +static inline void if_cs_enable_ints(struct if_cs_card *card) +{ + lbs_deb_enter(LBS_DEB_CS); + if_cs_write16(card, IF_CS_HOST_INT_MASK, 0); +} + +static inline void if_cs_disable_ints(struct if_cs_card *card) +{ + lbs_deb_enter(LBS_DEB_CS); + if_cs_write16(card, IF_CS_HOST_INT_MASK, IF_CS_BIT_MASK); +} + +/* + * Called from if_cs_host_to_card to send a command to the hardware + */ +static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb) +{ + struct if_cs_card *card = (struct if_cs_card *)priv->card; + int ret = -1; + int loops = 0; + + lbs_deb_enter(LBS_DEB_CS); + if_cs_disable_ints(card); + + /* Is hardware ready? */ + while (1) { + u16 status = if_cs_read16(card, IF_CS_CARD_STATUS); + if (status & IF_CS_BIT_COMMAND) + break; + if (++loops > 100) { + netdev_err(priv->dev, "card not ready for commands\n"); + goto done; + } + mdelay(1); + } + + if_cs_write16(card, IF_CS_CMD_LEN, nb); + + if_cs_write16_rep(card, IF_CS_CMD, buf, nb / 2); + /* Are we supposed to transfer an odd amount of bytes? */ + if (nb & 1) + if_cs_write8(card, IF_CS_CMD, buf[nb-1]); + + /* "Assert the download over interrupt command in the Host + * status register" */ + if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); + + /* "Assert the download over interrupt command in the Card + * interrupt case register" */ + if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); + ret = 0; + +done: + if_cs_enable_ints(card); + lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); + return ret; +} + +/* + * Called from if_cs_host_to_card to send a data to the hardware + */ +static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) +{ + struct if_cs_card *card = (struct if_cs_card *)priv->card; + u16 status; + + lbs_deb_enter(LBS_DEB_CS); + if_cs_disable_ints(card); + + status = if_cs_read16(card, IF_CS_CARD_STATUS); + BUG_ON((status & IF_CS_BIT_TX) == 0); + + if_cs_write16(card, IF_CS_WRITE_LEN, nb); + + /* write even number of bytes, then odd byte if necessary */ + if_cs_write16_rep(card, IF_CS_WRITE, buf, nb / 2); + if (nb & 1) + if_cs_write8(card, IF_CS_WRITE, buf[nb-1]); + + if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_TX); + if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_TX); + if_cs_enable_ints(card); + + lbs_deb_leave(LBS_DEB_CS); +} + +/* + * Get the command result out of the card. + */ +static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len) +{ + unsigned long flags; + int ret = -1; + u16 status; + + lbs_deb_enter(LBS_DEB_CS); + + /* is hardware ready? */ + status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); + if ((status & IF_CS_BIT_RESP) == 0) { + netdev_err(priv->dev, "no cmd response in card\n"); + *len = 0; + goto out; + } + + *len = if_cs_read16(priv->card, IF_CS_RESP_LEN); + if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) { + netdev_err(priv->dev, + "card cmd buffer has invalid # of bytes (%d)\n", + *len); + goto out; + } + + /* read even number of bytes, then odd byte if necessary */ + if_cs_read16_rep(priv->card, IF_CS_RESP, data, *len/sizeof(u16)); + if (*len & 1) + data[*len-1] = if_cs_read8(priv->card, IF_CS_RESP); + + /* This is a workaround for a firmware that reports too much + * bytes */ + *len -= 8; + ret = 0; + + /* Clear this flag again */ + spin_lock_irqsave(&priv->driver_lock, flags); + priv->dnld_sent = DNLD_RES_RECEIVED; + spin_unlock_irqrestore(&priv->driver_lock, flags); + +out: + lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len); + return ret; +} + +static struct sk_buff *if_cs_receive_data(struct lbs_private *priv) +{ + struct sk_buff *skb = NULL; + u16 len; + u8 *data; + + lbs_deb_enter(LBS_DEB_CS); + + len = if_cs_read16(priv->card, IF_CS_READ_LEN); + if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { + netdev_err(priv->dev, + "card data buffer has invalid # of bytes (%d)\n", + len); + priv->dev->stats.rx_dropped++; + goto dat_err; + } + + skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2); + if (!skb) + goto out; + skb_put(skb, len); + skb_reserve(skb, 2);/* 16 byte align */ + data = skb->data; + + /* read even number of bytes, then odd byte if necessary */ + if_cs_read16_rep(priv->card, IF_CS_READ, data, len/sizeof(u16)); + if (len & 1) + data[len-1] = if_cs_read8(priv->card, IF_CS_READ); + +dat_err: + if_cs_write16(priv->card, IF_CS_HOST_STATUS, IF_CS_BIT_RX); + if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_RX); + +out: + lbs_deb_leave_args(LBS_DEB_CS, "ret %p", skb); + return skb; +} + +static irqreturn_t if_cs_interrupt(int irq, void *data) +{ + struct if_cs_card *card = data; + struct lbs_private *priv = card->priv; + u16 cause; + + lbs_deb_enter(LBS_DEB_CS); + + /* Ask card interrupt cause register if there is something for us */ + cause = if_cs_read16(card, IF_CS_CARD_INT_CAUSE); + lbs_deb_cs("cause 0x%04x\n", cause); + + if (cause == 0) { + /* Not for us */ + return IRQ_NONE; + } + + if (cause == 0xffff) { + /* Read in junk, the card has probably been removed */ + card->priv->surpriseremoved = 1; + return IRQ_HANDLED; + } + + if (cause & IF_CS_BIT_RX) { + struct sk_buff *skb; + lbs_deb_cs("rx packet\n"); + skb = if_cs_receive_data(priv); + if (skb) + lbs_process_rxed_packet(priv, skb); + } + + if (cause & IF_CS_BIT_TX) { + lbs_deb_cs("tx done\n"); + lbs_host_to_card_done(priv); + } + + if (cause & IF_CS_BIT_RESP) { + unsigned long flags; + u8 i; + + lbs_deb_cs("cmd resp\n"); + spin_lock_irqsave(&priv->driver_lock, flags); + i = (priv->resp_idx == 0) ? 1 : 0; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + BUG_ON(priv->resp_len[i]); + if_cs_receive_cmdres(priv, priv->resp_buf[i], + &priv->resp_len[i]); + + spin_lock_irqsave(&priv->driver_lock, flags); + lbs_notify_command_response(priv, i); + spin_unlock_irqrestore(&priv->driver_lock, flags); + } + + if (cause & IF_CS_BIT_EVENT) { + u16 status = if_cs_read16(priv->card, IF_CS_CARD_STATUS); + if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, + IF_CS_BIT_EVENT); + lbs_queue_event(priv, (status & IF_CS_CARD_STATUS_MASK) >> 8); + } + + /* Clear interrupt cause */ + if_cs_write16(card, IF_CS_CARD_INT_CAUSE, cause & IF_CS_BIT_MASK); + + lbs_deb_leave(LBS_DEB_CS); + return IRQ_HANDLED; +} + + + + +/********************************************************************/ +/* Firmware */ +/********************************************************************/ + +/* + * Tries to program the helper firmware. + * + * Return 0 on success + */ +static int if_cs_prog_helper(struct if_cs_card *card, const struct firmware *fw) +{ + int ret = 0; + int sent = 0; + u8 scratch; + + lbs_deb_enter(LBS_DEB_CS); + + /* + * This is the only place where an unaligned register access happens on + * the CF8305 card, therefore for the sake of speed of the driver, we do + * the alignment correction here. + */ + if (card->align_regs) + scratch = if_cs_read16(card, IF_CS_SCRATCH) >> 8; + else + scratch = if_cs_read8(card, IF_CS_SCRATCH); + + /* "If the value is 0x5a, the firmware is already + * downloaded successfully" + */ + if (scratch == IF_CS_SCRATCH_HELPER_OK) + goto done; + + /* "If the value is != 00, it is invalid value of register */ + if (scratch != IF_CS_SCRATCH_BOOT_OK) { + ret = -ENODEV; + goto done; + } + + lbs_deb_cs("helper size %td\n", fw->size); + + /* "Set the 5 bytes of the helper image to 0" */ + /* Not needed, this contains an ARM branch instruction */ + + for (;;) { + /* "the number of bytes to send is 256" */ + int count = 256; + int remain = fw->size - sent; + + if (remain < count) + count = remain; + + /* + * "write the number of bytes to be sent to the I/O Command + * write length register" + */ + if_cs_write16(card, IF_CS_CMD_LEN, count); + + /* "write this to I/O Command port register as 16 bit writes */ + if (count) + if_cs_write16_rep(card, IF_CS_CMD, + &fw->data[sent], + count >> 1); + + /* + * "Assert the download over interrupt command in the Host + * status register" + */ + if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); + + /* + * "Assert the download over interrupt command in the Card + * interrupt case register" + */ + if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); + + /* + * "The host polls the Card Status register ... for 50 ms before + * declaring a failure" + */ + ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, + IF_CS_BIT_COMMAND); + if (ret < 0) { + pr_err("can't download helper at 0x%x, ret %d\n", + sent, ret); + goto done; + } + + if (count == 0) + break; + + sent += count; + } + +done: + lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); + return ret; +} + + +static int if_cs_prog_real(struct if_cs_card *card, const struct firmware *fw) +{ + int ret = 0; + int retry = 0; + int len = 0; + int sent; + + lbs_deb_enter(LBS_DEB_CS); + + lbs_deb_cs("fw size %td\n", fw->size); + + ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW, + IF_CS_SQ_HELPER_OK); + if (ret < 0) { + pr_err("helper firmware doesn't answer\n"); + goto done; + } + + for (sent = 0; sent < fw->size; sent += len) { + len = if_cs_read16(card, IF_CS_SQ_READ_LOW); + if (len & 1) { + retry++; + pr_info("odd, need to retry this firmware block\n"); + } else { + retry = 0; + } + + if (retry > 20) { + pr_err("could not download firmware\n"); + ret = -ENODEV; + goto done; + } + if (retry) { + sent -= len; + } + + + if_cs_write16(card, IF_CS_CMD_LEN, len); + + if_cs_write16_rep(card, IF_CS_CMD, + &fw->data[sent], + (len+1) >> 1); + if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND); + if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND); + + ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS, + IF_CS_BIT_COMMAND); + if (ret < 0) { + pr_err("can't download firmware at 0x%x\n", sent); + goto done; + } + } + + ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a); + if (ret < 0) + pr_err("firmware download failed\n"); + +done: + lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); + return ret; +} + +static void if_cs_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *helper, + const struct firmware *mainfw) +{ + struct if_cs_card *card = priv->card; + + if (ret) { + pr_err("failed to find firmware (%d)\n", ret); + return; + } + + /* Load the firmware */ + ret = if_cs_prog_helper(card, helper); + if (ret == 0 && (card->model != MODEL_8305)) + ret = if_cs_prog_real(card, mainfw); + if (ret) + return; + + /* Now actually get the IRQ */ + ret = request_irq(card->p_dev->irq, if_cs_interrupt, + IRQF_SHARED, DRV_NAME, card); + if (ret) { + pr_err("error in request_irq\n"); + return; + } + + /* + * Clear any interrupt cause that happened while sending + * firmware/initializing card + */ + if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK); + if_cs_enable_ints(card); + + /* And finally bring the card up */ + priv->fw_ready = 1; + if (lbs_start_card(priv) != 0) { + pr_err("could not activate card\n"); + free_irq(card->p_dev->irq, card); + } +} + + +/********************************************************************/ +/* Callback functions for libertas.ko */ +/********************************************************************/ + +/* Send commands or data packets to the card */ +static int if_cs_host_to_card(struct lbs_private *priv, + u8 type, + u8 *buf, + u16 nb) +{ + int ret = -1; + + lbs_deb_enter_args(LBS_DEB_CS, "type %d, bytes %d", type, nb); + + switch (type) { + case MVMS_DAT: + priv->dnld_sent = DNLD_DATA_SENT; + if_cs_send_data(priv, buf, nb); + ret = 0; + break; + case MVMS_CMD: + priv->dnld_sent = DNLD_CMD_SENT; + ret = if_cs_send_cmd(priv, buf, nb); + break; + default: + netdev_err(priv->dev, "%s: unsupported type %d\n", + __func__, type); + } + + lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); + return ret; +} + + +static void if_cs_release(struct pcmcia_device *p_dev) +{ + struct if_cs_card *card = p_dev->priv; + + lbs_deb_enter(LBS_DEB_CS); + + free_irq(p_dev->irq, card); + pcmcia_disable_device(p_dev); + if (card->iobase) + ioport_unmap(card->iobase); + + lbs_deb_leave(LBS_DEB_CS); +} + + +static int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data) +{ + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + + if (p_dev->resource[1]->end) { + pr_err("wrong CIS (check number of IO windows)\n"); + return -ENODEV; + } + + /* This reserves IO space but doesn't actually enable it */ + return pcmcia_request_io(p_dev); +} + +static int if_cs_probe(struct pcmcia_device *p_dev) +{ + int ret = -ENOMEM; + unsigned int prod_id; + struct lbs_private *priv; + struct if_cs_card *card; + + lbs_deb_enter(LBS_DEB_CS); + + card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL); + if (!card) + goto out; + + card->p_dev = p_dev; + p_dev->priv = card; + + p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; + + if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) { + pr_err("error in pcmcia_loop_config\n"); + goto out1; + } + + /* + * Allocate an interrupt line. Note that this does not assign + * a handler to the interrupt, unless the 'Handler' member of + * the irq structure is initialized. + */ + if (!p_dev->irq) + goto out1; + + /* Initialize io access */ + card->iobase = ioport_map(p_dev->resource[0]->start, + resource_size(p_dev->resource[0])); + if (!card->iobase) { + pr_err("error in ioport_map\n"); + ret = -EIO; + goto out1; + } + + ret = pcmcia_enable_device(p_dev); + if (ret) { + pr_err("error in pcmcia_enable_device\n"); + goto out2; + } + + /* Finally, report what we've done */ + lbs_deb_cs("irq %d, io %pR", p_dev->irq, p_dev->resource[0]); + + /* + * Most of the libertas cards can do unaligned register access, but some + * weird ones cannot. That's especially true for the CF8305 card. + */ + card->align_regs = false; + + card->model = get_model(p_dev->manf_id, p_dev->card_id); + if (card->model == MODEL_UNKNOWN) { + pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n", + p_dev->manf_id, p_dev->card_id); + ret = -ENODEV; + goto out2; + } + + /* Check if we have a current silicon */ + prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID); + if (card->model == MODEL_8305) { + card->align_regs = true; + if (prod_id < IF_CS_CF8305_B1_REV) { + pr_err("8305 rev B0 and older are not supported\n"); + ret = -ENODEV; + goto out2; + } + } + + if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) { + pr_err("8381 rev B2 and older are not supported\n"); + ret = -ENODEV; + goto out2; + } + + if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) { + pr_err("8385 rev B0 and older are not supported\n"); + ret = -ENODEV; + goto out2; + } + + /* Make this card known to the libertas driver */ + priv = lbs_add_card(card, &p_dev->dev); + if (!priv) { + ret = -ENOMEM; + goto out2; + } + + /* Set up fields in lbs_private */ + card->priv = priv; + priv->card = card; + priv->hw_host_to_card = if_cs_host_to_card; + priv->enter_deep_sleep = NULL; + priv->exit_deep_sleep = NULL; + priv->reset_deep_sleep_wakeup = NULL; + + /* Get firmware */ + ret = lbs_get_firmware_async(priv, &p_dev->dev, card->model, fw_table, + if_cs_prog_firmware); + if (ret) { + pr_err("failed to find firmware (%d)\n", ret); + goto out3; + } + + goto out; + +out3: + lbs_remove_card(priv); +out2: + ioport_unmap(card->iobase); +out1: + pcmcia_disable_device(p_dev); +out: + lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); + return ret; +} + + +static void if_cs_detach(struct pcmcia_device *p_dev) +{ + struct if_cs_card *card = p_dev->priv; + + lbs_deb_enter(LBS_DEB_CS); + + lbs_stop_card(card->priv); + lbs_remove_card(card->priv); + if_cs_disable_ints(card); + if_cs_release(p_dev); + kfree(card); + + lbs_deb_leave(LBS_DEB_CS); +} + + + +/********************************************************************/ +/* Module initialization */ +/********************************************************************/ + +static const struct pcmcia_device_id if_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID), + PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID), + PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID), + /* NOTE: keep in sync with get_model() */ + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); + +static struct pcmcia_driver lbs_driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .probe = if_cs_probe, + .remove = if_cs_detach, + .id_table = if_cs_ids, +}; +module_pcmcia_driver(lbs_driver); diff --git a/drivers/net/wireless/marvell/libertas/if_sdio.c b/drivers/net/wireless/marvell/libertas/if_sdio.c new file mode 100644 index 000000000000..33ceda296c9c --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_sdio.c @@ -0,0 +1,1453 @@ +/* + * linux/drivers/net/wireless/libertas/if_sdio.c + * + * Copyright 2007-2008 Pierre Ossman + * + * Inspired by if_cs.c, Copyright 2007 Holger Schurig + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This hardware has more or less no CMD53 support, so all registers + * must be accessed using sdio_readb()/sdio_writeb(). + * + * Transfers must be in one transaction or the firmware goes bonkers. + * This means that the transfer must either be small enough to do a + * byte based transfer or it must be padded to a multiple of the + * current block size. + * + * As SDIO is still new to the kernel, it is unfortunately common with + * bugs in the host controllers related to that. One such bug is that + * controllers cannot do transfers that aren't a multiple of 4 bytes. + * If you don't have time to fix the host controller driver, you can + * work around the problem by modifying if_sdio_host_to_card() and + * if_sdio_card_to_host() to pad the data. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "cmd.h" +#include "if_sdio.h" + +static void if_sdio_interrupt(struct sdio_func *func); + +/* The if_sdio_remove() callback function is called when + * user removes this module from kernel space or ejects + * the card from the slot. The driver handles these 2 cases + * differently for SD8688 combo chip. + * If the user is removing the module, the FUNC_SHUTDOWN + * command for SD8688 is sent to the firmware. + * If the card is removed, there is no need to send this command. + * + * The variable 'user_rmmod' is used to distinguish these two + * scenarios. This flag is initialized as FALSE in case the card + * is removed, and will be set to TRUE for module removal when + * module_exit function is called. + */ +static u8 user_rmmod; + +static const struct sdio_device_id if_sdio_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, + SDIO_DEVICE_ID_MARVELL_LIBERTAS) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, + SDIO_DEVICE_ID_MARVELL_8688WLAN) }, + { /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(sdio, if_sdio_ids); + +#define MODEL_8385 0x04 +#define MODEL_8686 0x0b +#define MODEL_8688 0x10 + +static const struct lbs_fw_table fw_table[] = { + { MODEL_8385, "libertas/sd8385_helper.bin", "libertas/sd8385.bin" }, + { MODEL_8385, "sd8385_helper.bin", "sd8385.bin" }, + { MODEL_8686, "libertas/sd8686_v9_helper.bin", "libertas/sd8686_v9.bin" }, + { MODEL_8686, "libertas/sd8686_v8_helper.bin", "libertas/sd8686_v8.bin" }, + { MODEL_8686, "sd8686_helper.bin", "sd8686.bin" }, + { MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" }, + { MODEL_8688, "sd8688_helper.bin", "sd8688.bin" }, + { 0, NULL, NULL } +}; +MODULE_FIRMWARE("libertas/sd8385_helper.bin"); +MODULE_FIRMWARE("libertas/sd8385.bin"); +MODULE_FIRMWARE("sd8385_helper.bin"); +MODULE_FIRMWARE("sd8385.bin"); +MODULE_FIRMWARE("libertas/sd8686_v9_helper.bin"); +MODULE_FIRMWARE("libertas/sd8686_v9.bin"); +MODULE_FIRMWARE("libertas/sd8686_v8_helper.bin"); +MODULE_FIRMWARE("libertas/sd8686_v8.bin"); +MODULE_FIRMWARE("sd8686_helper.bin"); +MODULE_FIRMWARE("sd8686.bin"); +MODULE_FIRMWARE("libertas/sd8688_helper.bin"); +MODULE_FIRMWARE("libertas/sd8688.bin"); +MODULE_FIRMWARE("sd8688_helper.bin"); +MODULE_FIRMWARE("sd8688.bin"); + +struct if_sdio_packet { + struct if_sdio_packet *next; + u16 nb; + u8 buffer[0] __attribute__((aligned(4))); +}; + +struct if_sdio_card { + struct sdio_func *func; + struct lbs_private *priv; + + int model; + unsigned long ioport; + unsigned int scratch_reg; + bool started; + wait_queue_head_t pwron_waitq; + + u8 buffer[65536] __attribute__((aligned(4))); + + spinlock_t lock; + struct if_sdio_packet *packets; + + struct workqueue_struct *workqueue; + struct work_struct packet_worker; + + u8 rx_unit; +}; + +static void if_sdio_finish_power_on(struct if_sdio_card *card); +static int if_sdio_power_off(struct if_sdio_card *card); + +/********************************************************************/ +/* I/O */ +/********************************************************************/ + +/* + * For SD8385/SD8686, this function reads firmware status after + * the image is downloaded, or reads RX packet length when + * interrupt (with IF_SDIO_H_INT_UPLD bit set) is received. + * For SD8688, this function reads firmware status only. + */ +static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err) +{ + int ret; + u16 scratch; + + scratch = sdio_readb(card->func, card->scratch_reg, &ret); + if (!ret) + scratch |= sdio_readb(card->func, card->scratch_reg + 1, + &ret) << 8; + + if (err) + *err = ret; + + if (ret) + return 0xffff; + + return scratch; +} + +static u8 if_sdio_read_rx_unit(struct if_sdio_card *card) +{ + int ret; + u8 rx_unit; + + rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret); + + if (ret) + rx_unit = 0; + + return rx_unit; +} + +static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err) +{ + int ret; + u16 rx_len; + + switch (card->model) { + case MODEL_8385: + case MODEL_8686: + rx_len = if_sdio_read_scratch(card, &ret); + break; + case MODEL_8688: + default: /* for newer chipsets */ + rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret); + if (!ret) + rx_len <<= card->rx_unit; + else + rx_len = 0xffff; /* invalid length */ + + break; + } + + if (err) + *err = ret; + + return rx_len; +} + +static int if_sdio_handle_cmd(struct if_sdio_card *card, + u8 *buffer, unsigned size) +{ + struct lbs_private *priv = card->priv; + int ret; + unsigned long flags; + u8 i; + + lbs_deb_enter(LBS_DEB_SDIO); + + if (size > LBS_CMD_BUFFER_SIZE) { + lbs_deb_sdio("response packet too large (%d bytes)\n", + (int)size); + ret = -E2BIG; + goto out; + } + + spin_lock_irqsave(&priv->driver_lock, flags); + + i = (priv->resp_idx == 0) ? 1 : 0; + BUG_ON(priv->resp_len[i]); + priv->resp_len[i] = size; + memcpy(priv->resp_buf[i], buffer, size); + lbs_notify_command_response(priv, i); + + spin_unlock_irqrestore(&card->priv->driver_lock, flags); + + ret = 0; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +static int if_sdio_handle_data(struct if_sdio_card *card, + u8 *buffer, unsigned size) +{ + int ret; + struct sk_buff *skb; + char *data; + + lbs_deb_enter(LBS_DEB_SDIO); + + if (size > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { + lbs_deb_sdio("response packet too large (%d bytes)\n", + (int)size); + ret = -E2BIG; + goto out; + } + + skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN); + if (!skb) { + ret = -ENOMEM; + goto out; + } + + skb_reserve(skb, NET_IP_ALIGN); + + data = skb_put(skb, size); + + memcpy(data, buffer, size); + + lbs_process_rxed_packet(card->priv, skb); + + ret = 0; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; +} + +static int if_sdio_handle_event(struct if_sdio_card *card, + u8 *buffer, unsigned size) +{ + int ret; + u32 event; + + lbs_deb_enter(LBS_DEB_SDIO); + + if (card->model == MODEL_8385) { + event = sdio_readb(card->func, IF_SDIO_EVENT, &ret); + if (ret) + goto out; + + /* right shift 3 bits to get the event id */ + event >>= 3; + } else { + if (size < 4) { + lbs_deb_sdio("event packet too small (%d bytes)\n", + (int)size); + ret = -EINVAL; + goto out; + } + event = buffer[3] << 24; + event |= buffer[2] << 16; + event |= buffer[1] << 8; + event |= buffer[0] << 0; + } + + lbs_queue_event(card->priv, event & 0xFF); + ret = 0; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; +} + +static int if_sdio_wait_status(struct if_sdio_card *card, const u8 condition) +{ + u8 status; + unsigned long timeout; + int ret = 0; + + timeout = jiffies + HZ; + while (1) { + status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); + if (ret) + return ret; + if ((status & condition) == condition) + break; + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + mdelay(1); + } + return ret; +} + +static int if_sdio_card_to_host(struct if_sdio_card *card) +{ + int ret; + u16 size, type, chunk; + + lbs_deb_enter(LBS_DEB_SDIO); + + size = if_sdio_read_rx_len(card, &ret); + if (ret) + goto out; + + if (size < 4) { + lbs_deb_sdio("invalid packet size (%d bytes) from firmware\n", + (int)size); + ret = -EINVAL; + goto out; + } + + ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); + if (ret) + goto out; + + /* + * The transfer must be in one transaction or the firmware + * goes suicidal. There's no way to guarantee that for all + * controllers, but we can at least try. + */ + chunk = sdio_align_size(card->func, size); + + ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk); + if (ret) + goto out; + + chunk = card->buffer[0] | (card->buffer[1] << 8); + type = card->buffer[2] | (card->buffer[3] << 8); + + lbs_deb_sdio("packet of type %d and size %d bytes\n", + (int)type, (int)chunk); + + if (chunk > size) { + lbs_deb_sdio("packet fragment (%d > %d)\n", + (int)chunk, (int)size); + ret = -EINVAL; + goto out; + } + + if (chunk < size) { + lbs_deb_sdio("packet fragment (%d < %d)\n", + (int)chunk, (int)size); + } + + switch (type) { + case MVMS_CMD: + ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4); + if (ret) + goto out; + break; + case MVMS_DAT: + ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4); + if (ret) + goto out; + break; + case MVMS_EVENT: + ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4); + if (ret) + goto out; + break; + default: + lbs_deb_sdio("invalid type (%d) from firmware\n", + (int)type); + ret = -EINVAL; + goto out; + } + +out: + if (ret) + pr_err("problem fetching packet from firmware\n"); + + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; +} + +static void if_sdio_host_to_card_worker(struct work_struct *work) +{ + struct if_sdio_card *card; + struct if_sdio_packet *packet; + int ret; + unsigned long flags; + + lbs_deb_enter(LBS_DEB_SDIO); + + card = container_of(work, struct if_sdio_card, packet_worker); + + while (1) { + spin_lock_irqsave(&card->lock, flags); + packet = card->packets; + if (packet) + card->packets = packet->next; + spin_unlock_irqrestore(&card->lock, flags); + + if (!packet) + break; + + sdio_claim_host(card->func); + + ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY); + if (ret == 0) { + ret = sdio_writesb(card->func, card->ioport, + packet->buffer, packet->nb); + } + + if (ret) + pr_err("error %d sending packet to firmware\n", ret); + + sdio_release_host(card->func); + + kfree(packet); + } + + lbs_deb_leave(LBS_DEB_SDIO); +} + +/********************************************************************/ +/* Firmware */ +/********************************************************************/ + +#define FW_DL_READY_STATUS (IF_SDIO_IO_RDY | IF_SDIO_DL_RDY) + +static int if_sdio_prog_helper(struct if_sdio_card *card, + const struct firmware *fw) +{ + int ret; + unsigned long timeout; + u8 *chunk_buffer; + u32 chunk_size; + const u8 *firmware; + size_t size; + + lbs_deb_enter(LBS_DEB_SDIO); + + chunk_buffer = kzalloc(64, GFP_KERNEL); + if (!chunk_buffer) { + ret = -ENOMEM; + goto out; + } + + sdio_claim_host(card->func); + + ret = sdio_set_block_size(card->func, 32); + if (ret) + goto release; + + firmware = fw->data; + size = fw->size; + + while (size) { + ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); + if (ret) + goto release; + + /* On some platforms (like Davinci) the chip needs more time + * between helper blocks. + */ + mdelay(2); + + chunk_size = min_t(size_t, size, 60); + + *((__le32*)chunk_buffer) = cpu_to_le32(chunk_size); + memcpy(chunk_buffer + 4, firmware, chunk_size); +/* + lbs_deb_sdio("sending %d bytes chunk\n", chunk_size); +*/ + ret = sdio_writesb(card->func, card->ioport, + chunk_buffer, 64); + if (ret) + goto release; + + firmware += chunk_size; + size -= chunk_size; + } + + /* an empty block marks the end of the transfer */ + memset(chunk_buffer, 0, 4); + ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64); + if (ret) + goto release; + + lbs_deb_sdio("waiting for helper to boot...\n"); + + /* wait for the helper to boot by looking at the size register */ + timeout = jiffies + HZ; + while (1) { + u16 req_size; + + req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); + if (ret) + goto release; + + req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; + if (ret) + goto release; + + if (req_size != 0) + break; + + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + goto release; + } + + msleep(10); + } + + ret = 0; + +release: + sdio_release_host(card->func); + kfree(chunk_buffer); + +out: + if (ret) + pr_err("failed to load helper firmware\n"); + + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +static int if_sdio_prog_real(struct if_sdio_card *card, + const struct firmware *fw) +{ + int ret; + unsigned long timeout; + u8 *chunk_buffer; + u32 chunk_size; + const u8 *firmware; + size_t size, req_size; + + lbs_deb_enter(LBS_DEB_SDIO); + + chunk_buffer = kzalloc(512, GFP_KERNEL); + if (!chunk_buffer) { + ret = -ENOMEM; + goto out; + } + + sdio_claim_host(card->func); + + ret = sdio_set_block_size(card->func, 32); + if (ret) + goto release; + + firmware = fw->data; + size = fw->size; + + while (size) { + timeout = jiffies + HZ; + while (1) { + ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); + if (ret) + goto release; + + req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, + &ret); + if (ret) + goto release; + + req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, + &ret) << 8; + if (ret) + goto release; + + /* + * For SD8688 wait until the length is not 0, 1 or 2 + * before downloading the first FW block, + * since BOOT code writes the register to indicate the + * helper/FW download winner, + * the value could be 1 or 2 (Func1 or Func2). + */ + if ((size != fw->size) || (req_size > 2)) + break; + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + goto release; + } + mdelay(1); + } + +/* + lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size); +*/ + if (req_size == 0) { + lbs_deb_sdio("firmware helper gave up early\n"); + ret = -EIO; + goto release; + } + + if (req_size & 0x01) { + lbs_deb_sdio("firmware helper signalled error\n"); + ret = -EIO; + goto release; + } + + if (req_size > size) + req_size = size; + + while (req_size) { + chunk_size = min_t(size_t, req_size, 512); + + memcpy(chunk_buffer, firmware, chunk_size); +/* + lbs_deb_sdio("sending %d bytes (%d bytes) chunk\n", + chunk_size, (chunk_size + 31) / 32 * 32); +*/ + ret = sdio_writesb(card->func, card->ioport, + chunk_buffer, roundup(chunk_size, 32)); + if (ret) + goto release; + + firmware += chunk_size; + size -= chunk_size; + req_size -= chunk_size; + } + } + + ret = 0; + + lbs_deb_sdio("waiting for firmware to boot...\n"); + + /* wait for the firmware to boot */ + timeout = jiffies + HZ; + while (1) { + u16 scratch; + + scratch = if_sdio_read_scratch(card, &ret); + if (ret) + goto release; + + if (scratch == IF_SDIO_FIRMWARE_OK) + break; + + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + goto release; + } + + msleep(10); + } + + ret = 0; + +release: + sdio_release_host(card->func); + kfree(chunk_buffer); + +out: + if (ret) + pr_err("failed to load firmware\n"); + + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *helper, + const struct firmware *mainfw) +{ + struct if_sdio_card *card = priv->card; + + if (ret) { + pr_err("failed to find firmware (%d)\n", ret); + return; + } + + ret = if_sdio_prog_helper(card, helper); + if (ret) + return; + + lbs_deb_sdio("Helper firmware loaded\n"); + + ret = if_sdio_prog_real(card, mainfw); + if (ret) + return; + + lbs_deb_sdio("Firmware loaded\n"); + if_sdio_finish_power_on(card); +} + +static int if_sdio_prog_firmware(struct if_sdio_card *card) +{ + int ret; + u16 scratch; + + lbs_deb_enter(LBS_DEB_SDIO); + + /* + * Disable interrupts + */ + sdio_claim_host(card->func); + sdio_writeb(card->func, 0x00, IF_SDIO_H_INT_MASK, &ret); + sdio_release_host(card->func); + + sdio_claim_host(card->func); + scratch = if_sdio_read_scratch(card, &ret); + sdio_release_host(card->func); + + lbs_deb_sdio("firmware status = %#x\n", scratch); + lbs_deb_sdio("scratch ret = %d\n", ret); + + if (ret) + goto out; + + + /* + * The manual clearly describes that FEDC is the right code to use + * to detect firmware presence, but for SD8686 it is not that simple. + * Scratch is also used to store the RX packet length, so we lose + * the FEDC value early on. So we use a non-zero check in order + * to validate firmware presence. + * Additionally, the SD8686 in the Gumstix always has the high scratch + * bit set, even when the firmware is not loaded. So we have to + * exclude that from the test. + */ + if (scratch == IF_SDIO_FIRMWARE_OK) { + lbs_deb_sdio("firmware already loaded\n"); + if_sdio_finish_power_on(card); + return 0; + } else if ((card->model == MODEL_8686) && (scratch & 0x7fff)) { + lbs_deb_sdio("firmware may be running\n"); + if_sdio_finish_power_on(card); + return 0; + } + + ret = lbs_get_firmware_async(card->priv, &card->func->dev, card->model, + fw_table, if_sdio_do_prog_firmware); + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +/********************************************************************/ +/* Power management */ +/********************************************************************/ + +/* Finish power on sequence (after firmware is loaded) */ +static void if_sdio_finish_power_on(struct if_sdio_card *card) +{ + struct sdio_func *func = card->func; + struct lbs_private *priv = card->priv; + int ret; + + sdio_claim_host(func); + sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE); + + /* + * Get rx_unit if the chip is SD8688 or newer. + * SD8385 & SD8686 do not have rx_unit. + */ + if ((card->model != MODEL_8385) + && (card->model != MODEL_8686)) + card->rx_unit = if_sdio_read_rx_unit(card); + else + card->rx_unit = 0; + + /* + * Set up the interrupt handler late. + * + * If we set it up earlier, the (buggy) hardware generates a spurious + * interrupt, even before the interrupt has been enabled, with + * CCCR_INTx = 0. + * + * We register the interrupt handler late so that we can handle any + * spurious interrupts, and also to avoid generation of that known + * spurious interrupt in the first place. + */ + ret = sdio_claim_irq(func, if_sdio_interrupt); + if (ret) + goto release; + + /* + * Enable interrupts now that everything is set up + */ + sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); + if (ret) + goto release_irq; + + sdio_release_host(func); + + /* Set fw_ready before queuing any commands so that + * lbs_thread won't block from sending them to firmware. + */ + priv->fw_ready = 1; + + /* + * FUNC_INIT is required for SD8688 WLAN/BT multiple functions + */ + if (card->model == MODEL_8688) { + struct cmd_header cmd; + + memset(&cmd, 0, sizeof(cmd)); + + lbs_deb_sdio("send function INIT command\n"); + if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd), + lbs_cmd_copyback, (unsigned long) &cmd)) + netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n"); + } + + wake_up(&card->pwron_waitq); + + if (!card->started) { + ret = lbs_start_card(priv); + if_sdio_power_off(card); + if (ret == 0) { + card->started = true; + /* Tell PM core that we don't need the card to be + * powered now */ + pm_runtime_put(&func->dev); + } + } + + return; + +release_irq: + sdio_release_irq(func); +release: + sdio_release_host(func); +} + +static int if_sdio_power_on(struct if_sdio_card *card) +{ + struct sdio_func *func = card->func; + struct mmc_host *host = func->card->host; + int ret; + + sdio_claim_host(func); + + ret = sdio_enable_func(func); + if (ret) + goto release; + + /* For 1-bit transfers to the 8686 model, we need to enable the + * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0 + * bit to allow access to non-vendor registers. */ + if ((card->model == MODEL_8686) && + (host->caps & MMC_CAP_SDIO_IRQ) && + (host->ios.bus_width == MMC_BUS_WIDTH_1)) { + u8 reg; + + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret); + if (ret) + goto disable; + + reg |= SDIO_BUS_ECSI; + sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret); + if (ret) + goto disable; + } + + card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); + if (ret) + goto disable; + + card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; + if (ret) + goto disable; + + card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; + if (ret) + goto disable; + + sdio_release_host(func); + ret = if_sdio_prog_firmware(card); + if (ret) { + sdio_claim_host(func); + goto disable; + } + + return 0; + +disable: + sdio_disable_func(func); +release: + sdio_release_host(func); + return ret; +} + +static int if_sdio_power_off(struct if_sdio_card *card) +{ + struct sdio_func *func = card->func; + struct lbs_private *priv = card->priv; + + priv->fw_ready = 0; + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_disable_func(func); + sdio_release_host(func); + return 0; +} + + +/*******************************************************************/ +/* Libertas callbacks */ +/*******************************************************************/ + +static int if_sdio_host_to_card(struct lbs_private *priv, + u8 type, u8 *buf, u16 nb) +{ + int ret; + struct if_sdio_card *card; + struct if_sdio_packet *packet, *cur; + u16 size; + unsigned long flags; + + lbs_deb_enter_args(LBS_DEB_SDIO, "type %d, bytes %d", type, nb); + + card = priv->card; + + if (nb > (65536 - sizeof(struct if_sdio_packet) - 4)) { + ret = -EINVAL; + goto out; + } + + /* + * The transfer must be in one transaction or the firmware + * goes suicidal. There's no way to guarantee that for all + * controllers, but we can at least try. + */ + size = sdio_align_size(card->func, nb + 4); + + packet = kzalloc(sizeof(struct if_sdio_packet) + size, + GFP_ATOMIC); + if (!packet) { + ret = -ENOMEM; + goto out; + } + + packet->next = NULL; + packet->nb = size; + + /* + * SDIO specific header. + */ + packet->buffer[0] = (nb + 4) & 0xff; + packet->buffer[1] = ((nb + 4) >> 8) & 0xff; + packet->buffer[2] = type; + packet->buffer[3] = 0; + + memcpy(packet->buffer + 4, buf, nb); + + spin_lock_irqsave(&card->lock, flags); + + if (!card->packets) + card->packets = packet; + else { + cur = card->packets; + while (cur->next) + cur = cur->next; + cur->next = packet; + } + + switch (type) { + case MVMS_CMD: + priv->dnld_sent = DNLD_CMD_SENT; + break; + case MVMS_DAT: + priv->dnld_sent = DNLD_DATA_SENT; + break; + default: + lbs_deb_sdio("unknown packet type %d\n", (int)type); + } + + spin_unlock_irqrestore(&card->lock, flags); + + queue_work(card->workqueue, &card->packet_worker); + + ret = 0; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; +} + +static int if_sdio_enter_deep_sleep(struct lbs_private *priv) +{ + int ret = -1; + struct cmd_header cmd; + + memset(&cmd, 0, sizeof(cmd)); + + lbs_deb_sdio("send DEEP_SLEEP command\n"); + ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd), + lbs_cmd_copyback, (unsigned long) &cmd); + if (ret) + netdev_err(priv->dev, "DEEP_SLEEP cmd failed\n"); + + mdelay(200); + return ret; +} + +static int if_sdio_exit_deep_sleep(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int ret = -1; + + lbs_deb_enter(LBS_DEB_SDIO); + sdio_claim_host(card->func); + + sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); + if (ret) + netdev_err(priv->dev, "sdio_writeb failed!\n"); + + sdio_release_host(card->func); + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; +} + +static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int ret = -1; + + lbs_deb_enter(LBS_DEB_SDIO); + sdio_claim_host(card->func); + + sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret); + if (ret) + netdev_err(priv->dev, "sdio_writeb failed!\n"); + + sdio_release_host(card->func); + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + return ret; + +} + +static struct mmc_host *reset_host; + +static void if_sdio_reset_card_worker(struct work_struct *work) +{ + /* + * The actual reset operation must be run outside of lbs_thread. This + * is because mmc_remove_host() will cause the device to be instantly + * destroyed, and the libertas driver then needs to end lbs_thread, + * leading to a deadlock. + * + * We run it in a workqueue totally independent from the if_sdio_card + * instance for that reason. + */ + + pr_info("Resetting card..."); + mmc_remove_host(reset_host); + mmc_add_host(reset_host); +} +static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker); + +static void if_sdio_reset_card(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + + if (work_pending(&card_reset_work)) + return; + + reset_host = card->func->card->host; + schedule_work(&card_reset_work); +} + +static int if_sdio_power_save(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int ret; + + flush_workqueue(card->workqueue); + + ret = if_sdio_power_off(card); + + /* Let runtime PM know the card is powered off */ + pm_runtime_put_sync(&card->func->dev); + + return ret; +} + +static int if_sdio_power_restore(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + int r; + + /* Make sure the card will not be powered off by runtime PM */ + pm_runtime_get_sync(&card->func->dev); + + r = if_sdio_power_on(card); + if (r) + return r; + + wait_event(card->pwron_waitq, priv->fw_ready); + return 0; +} + + +/*******************************************************************/ +/* SDIO callbacks */ +/*******************************************************************/ + +static void if_sdio_interrupt(struct sdio_func *func) +{ + int ret; + struct if_sdio_card *card; + u8 cause; + + lbs_deb_enter(LBS_DEB_SDIO); + + card = sdio_get_drvdata(func); + + cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret); + if (ret || !cause) + goto out; + + lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause); + + sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret); + if (ret) + goto out; + + /* + * Ignore the define name, this really means the card has + * successfully received the command. + */ + card->priv->is_activity_detected = 1; + if (cause & IF_SDIO_H_INT_DNLD) + lbs_host_to_card_done(card->priv); + + + if (cause & IF_SDIO_H_INT_UPLD) { + ret = if_sdio_card_to_host(card); + if (ret) + goto out; + } + + ret = 0; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); +} + +static int if_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct if_sdio_card *card; + struct lbs_private *priv; + int ret, i; + unsigned int model; + struct if_sdio_packet *packet; + + lbs_deb_enter(LBS_DEB_SDIO); + + for (i = 0;i < func->card->num_info;i++) { + if (sscanf(func->card->info[i], + "802.11 SDIO ID: %x", &model) == 1) + break; + if (sscanf(func->card->info[i], + "ID: %x", &model) == 1) + break; + if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) { + model = MODEL_8385; + break; + } + } + + if (i == func->card->num_info) { + pr_err("unable to identify card model\n"); + return -ENODEV; + } + + card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->func = func; + card->model = model; + + switch (card->model) { + case MODEL_8385: + card->scratch_reg = IF_SDIO_SCRATCH_OLD; + break; + case MODEL_8686: + card->scratch_reg = IF_SDIO_SCRATCH; + break; + case MODEL_8688: + default: /* for newer chipsets */ + card->scratch_reg = IF_SDIO_FW_STATUS; + break; + } + + spin_lock_init(&card->lock); + card->workqueue = create_workqueue("libertas_sdio"); + INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); + init_waitqueue_head(&card->pwron_waitq); + + /* Check if we support this card */ + for (i = 0; i < ARRAY_SIZE(fw_table); i++) { + if (card->model == fw_table[i].model) + break; + } + if (i == ARRAY_SIZE(fw_table)) { + pr_err("unknown card model 0x%x\n", card->model); + ret = -ENODEV; + goto free; + } + + sdio_set_drvdata(func, card); + + lbs_deb_sdio("class = 0x%X, vendor = 0x%X, " + "device = 0x%X, model = 0x%X, ioport = 0x%X\n", + func->class, func->vendor, func->device, + model, (unsigned)card->ioport); + + + priv = lbs_add_card(card, &func->dev); + if (!priv) { + ret = -ENOMEM; + goto free; + } + + card->priv = priv; + + priv->card = card; + priv->hw_host_to_card = if_sdio_host_to_card; + priv->enter_deep_sleep = if_sdio_enter_deep_sleep; + priv->exit_deep_sleep = if_sdio_exit_deep_sleep; + priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; + priv->reset_card = if_sdio_reset_card; + priv->power_save = if_sdio_power_save; + priv->power_restore = if_sdio_power_restore; + + ret = if_sdio_power_on(card); + if (ret) + goto err_activate_card; + +out: + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; + +err_activate_card: + flush_workqueue(card->workqueue); + lbs_remove_card(priv); +free: + destroy_workqueue(card->workqueue); + while (card->packets) { + packet = card->packets; + card->packets = card->packets->next; + kfree(packet); + } + + kfree(card); + + goto out; +} + +static void if_sdio_remove(struct sdio_func *func) +{ + struct if_sdio_card *card; + struct if_sdio_packet *packet; + + lbs_deb_enter(LBS_DEB_SDIO); + + card = sdio_get_drvdata(func); + + /* Undo decrement done above in if_sdio_probe */ + pm_runtime_get_noresume(&func->dev); + + if (user_rmmod && (card->model == MODEL_8688)) { + /* + * FUNC_SHUTDOWN is required for SD8688 WLAN/BT + * multiple functions + */ + struct cmd_header cmd; + + memset(&cmd, 0, sizeof(cmd)); + + lbs_deb_sdio("send function SHUTDOWN command\n"); + if (__lbs_cmd(card->priv, CMD_FUNC_SHUTDOWN, + &cmd, sizeof(cmd), lbs_cmd_copyback, + (unsigned long) &cmd)) + pr_alert("CMD_FUNC_SHUTDOWN cmd failed\n"); + } + + + lbs_deb_sdio("call remove card\n"); + lbs_stop_card(card->priv); + lbs_remove_card(card->priv); + + flush_workqueue(card->workqueue); + destroy_workqueue(card->workqueue); + + while (card->packets) { + packet = card->packets; + card->packets = card->packets->next; + kfree(packet); + } + + kfree(card); + lbs_deb_leave(LBS_DEB_SDIO); +} + +static int if_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + int ret; + struct if_sdio_card *card = sdio_get_drvdata(func); + + mmc_pm_flag_t flags = sdio_get_host_pm_caps(func); + + /* If we're powered off anyway, just let the mmc layer remove the + * card. */ + if (!lbs_iface_active(card->priv)) + return -ENOSYS; + + dev_info(dev, "%s: suspend: PM flags = 0x%x\n", + sdio_func_id(func), flags); + + /* If we aren't being asked to wake on anything, we should bail out + * and let the SD stack power down the card. + */ + if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) { + dev_info(dev, "Suspend without wake params -- powering down card\n"); + return -ENOSYS; + } + + if (!(flags & MMC_PM_KEEP_POWER)) { + dev_err(dev, "%s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + return -ENOSYS; + } + + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) + return ret; + + ret = lbs_suspend(card->priv); + if (ret) + return ret; + + return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); +} + +static int if_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct if_sdio_card *card = sdio_get_drvdata(func); + int ret; + + dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func)); + + ret = lbs_resume(card->priv); + + return ret; +} + +static const struct dev_pm_ops if_sdio_pm_ops = { + .suspend = if_sdio_suspend, + .resume = if_sdio_resume, +}; + +static struct sdio_driver if_sdio_driver = { + .name = "libertas_sdio", + .id_table = if_sdio_ids, + .probe = if_sdio_probe, + .remove = if_sdio_remove, + .drv = { + .pm = &if_sdio_pm_ops, + }, +}; + +/*******************************************************************/ +/* Module functions */ +/*******************************************************************/ + +static int __init if_sdio_init_module(void) +{ + int ret = 0; + + lbs_deb_enter(LBS_DEB_SDIO); + + printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n"); + printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n"); + + ret = sdio_register_driver(&if_sdio_driver); + + /* Clear the flag in case user removes the card. */ + user_rmmod = 0; + + lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); + + return ret; +} + +static void __exit if_sdio_exit_module(void) +{ + lbs_deb_enter(LBS_DEB_SDIO); + + /* Set the flag as user is removing this module. */ + user_rmmod = 1; + + cancel_work_sync(&card_reset_work); + + sdio_unregister_driver(&if_sdio_driver); + + lbs_deb_leave(LBS_DEB_SDIO); +} + +module_init(if_sdio_init_module); +module_exit(if_sdio_exit_module); + +MODULE_DESCRIPTION("Libertas SDIO WLAN Driver"); +MODULE_AUTHOR("Pierre Ossman"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/marvell/libertas/if_sdio.h b/drivers/net/wireless/marvell/libertas/if_sdio.h new file mode 100644 index 000000000000..62fda3592f67 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_sdio.h @@ -0,0 +1,52 @@ +/* + * linux/drivers/net/wireless/libertas/if_sdio.h + * + * Copyright 2007 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef _LBS_IF_SDIO_H +#define _LBS_IF_SDIO_H + +#define IF_SDIO_IOPORT 0x00 + +#define IF_SDIO_H_INT_MASK 0x04 +#define IF_SDIO_H_INT_OFLOW 0x08 +#define IF_SDIO_H_INT_UFLOW 0x04 +#define IF_SDIO_H_INT_DNLD 0x02 +#define IF_SDIO_H_INT_UPLD 0x01 + +#define IF_SDIO_H_INT_STATUS 0x05 +#define IF_SDIO_H_INT_RSR 0x06 +#define IF_SDIO_H_INT_STATUS2 0x07 + +#define IF_SDIO_RD_BASE 0x10 + +#define IF_SDIO_STATUS 0x20 +#define IF_SDIO_IO_RDY 0x08 +#define IF_SDIO_CIS_RDY 0x04 +#define IF_SDIO_UL_RDY 0x02 +#define IF_SDIO_DL_RDY 0x01 + +#define IF_SDIO_C_INT_MASK 0x24 +#define IF_SDIO_C_INT_STATUS 0x28 +#define IF_SDIO_C_INT_RSR 0x2C + +#define IF_SDIO_SCRATCH 0x34 +#define IF_SDIO_SCRATCH_OLD 0x80fe +#define IF_SDIO_FW_STATUS 0x40 +#define IF_SDIO_FIRMWARE_OK 0xfedc + +#define IF_SDIO_RX_LEN 0x42 +#define IF_SDIO_RX_UNIT 0x43 + +#define IF_SDIO_EVENT 0x80fc + +#define IF_SDIO_BLOCK_SIZE 256 +#define CONFIGURATION_REG 0x03 +#define HOST_POWER_UP (0x1U << 1) +#endif diff --git a/drivers/net/wireless/marvell/libertas/if_spi.c b/drivers/net/wireless/marvell/libertas/if_spi.c new file mode 100644 index 000000000000..82c0796377aa --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_spi.c @@ -0,0 +1,1318 @@ +/* + * linux/drivers/net/wireless/libertas/if_spi.c + * + * Driver for Marvell SPI WLAN cards. + * + * Copyright 2008 Analog Devices Inc. + * + * Authors: + * Andrey Yurovsky + * Colin McCabe + * + * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "if_spi.h" + +struct if_spi_packet { + struct list_head list; + u16 blen; + u8 buffer[0] __attribute__((aligned(4))); +}; + +struct if_spi_card { + struct spi_device *spi; + struct lbs_private *priv; + struct libertas_spi_platform_data *pdata; + + /* The card ID and card revision, as reported by the hardware. */ + u16 card_id; + u8 card_rev; + + /* The last time that we initiated an SPU operation */ + unsigned long prev_xfer_time; + + int use_dummy_writes; + unsigned long spu_port_delay; + unsigned long spu_reg_delay; + + /* Handles all SPI communication (except for FW load) */ + struct workqueue_struct *workqueue; + struct work_struct packet_work; + struct work_struct resume_work; + + u8 cmd_buffer[IF_SPI_CMD_BUF_SIZE]; + + /* A buffer of incoming packets from libertas core. + * Since we can't sleep in hw_host_to_card, we have to buffer + * them. */ + struct list_head cmd_packet_list; + struct list_head data_packet_list; + + /* Protects cmd_packet_list and data_packet_list */ + spinlock_t buffer_lock; + + /* True is card suspended */ + u8 suspended; +}; + +static void free_if_spi_card(struct if_spi_card *card) +{ + struct list_head *cursor, *next; + struct if_spi_packet *packet; + + list_for_each_safe(cursor, next, &card->cmd_packet_list) { + packet = container_of(cursor, struct if_spi_packet, list); + list_del(&packet->list); + kfree(packet); + } + list_for_each_safe(cursor, next, &card->data_packet_list) { + packet = container_of(cursor, struct if_spi_packet, list); + list_del(&packet->list); + kfree(packet); + } + kfree(card); +} + +#define MODEL_8385 0x04 +#define MODEL_8686 0x0b +#define MODEL_8688 0x10 + +static const struct lbs_fw_table fw_table[] = { + { MODEL_8385, "libertas/gspi8385_helper.bin", "libertas/gspi8385.bin" }, + { MODEL_8385, "libertas/gspi8385_hlp.bin", "libertas/gspi8385.bin" }, + { MODEL_8686, "libertas/gspi8686_v9_helper.bin", "libertas/gspi8686_v9.bin" }, + { MODEL_8686, "libertas/gspi8686_hlp.bin", "libertas/gspi8686.bin" }, + { MODEL_8688, "libertas/gspi8688_helper.bin", "libertas/gspi8688.bin" }, + { 0, NULL, NULL } +}; +MODULE_FIRMWARE("libertas/gspi8385_helper.bin"); +MODULE_FIRMWARE("libertas/gspi8385_hlp.bin"); +MODULE_FIRMWARE("libertas/gspi8385.bin"); +MODULE_FIRMWARE("libertas/gspi8686_v9_helper.bin"); +MODULE_FIRMWARE("libertas/gspi8686_v9.bin"); +MODULE_FIRMWARE("libertas/gspi8686_hlp.bin"); +MODULE_FIRMWARE("libertas/gspi8686.bin"); +MODULE_FIRMWARE("libertas/gspi8688_helper.bin"); +MODULE_FIRMWARE("libertas/gspi8688.bin"); + + +/* + * SPI Interface Unit Routines + * + * The SPU sits between the host and the WLAN module. + * All communication with the firmware is through SPU transactions. + * + * First we have to put a SPU register name on the bus. Then we can + * either read from or write to that register. + * + */ + +static void spu_transaction_init(struct if_spi_card *card) +{ + if (!time_after(jiffies, card->prev_xfer_time + 1)) { + /* Unfortunately, the SPU requires a delay between successive + * transactions. If our last transaction was more than a jiffy + * ago, we have obviously already delayed enough. + * If not, we have to busy-wait to be on the safe side. */ + ndelay(400); + } +} + +static void spu_transaction_finish(struct if_spi_card *card) +{ + card->prev_xfer_time = jiffies; +} + +/* + * Write out a byte buffer to an SPI register, + * using a series of 16-bit transfers. + */ +static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len) +{ + int err = 0; + __le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK); + struct spi_message m; + struct spi_transfer reg_trans; + struct spi_transfer data_trans; + + spi_message_init(&m); + memset(®_trans, 0, sizeof(reg_trans)); + memset(&data_trans, 0, sizeof(data_trans)); + + /* You must give an even number of bytes to the SPU, even if it + * doesn't care about the last one. */ + BUG_ON(len & 0x1); + + spu_transaction_init(card); + + /* write SPU register index */ + reg_trans.tx_buf = ®_out; + reg_trans.len = sizeof(reg_out); + + data_trans.tx_buf = buf; + data_trans.len = len; + + spi_message_add_tail(®_trans, &m); + spi_message_add_tail(&data_trans, &m); + + err = spi_sync(card->spi, &m); + spu_transaction_finish(card); + return err; +} + +static inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val) +{ + __le16 buff; + + buff = cpu_to_le16(val); + return spu_write(card, reg, (u8 *)&buff, sizeof(u16)); +} + +static inline int spu_reg_is_port_reg(u16 reg) +{ + switch (reg) { + case IF_SPI_IO_RDWRPORT_REG: + case IF_SPI_CMD_RDWRPORT_REG: + case IF_SPI_DATA_RDWRPORT_REG: + return 1; + default: + return 0; + } +} + +static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len) +{ + unsigned int delay; + int err = 0; + __le16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK); + struct spi_message m; + struct spi_transfer reg_trans; + struct spi_transfer dummy_trans; + struct spi_transfer data_trans; + + /* + * You must take an even number of bytes from the SPU, even if you + * don't care about the last one. + */ + BUG_ON(len & 0x1); + + spu_transaction_init(card); + + spi_message_init(&m); + memset(®_trans, 0, sizeof(reg_trans)); + memset(&dummy_trans, 0, sizeof(dummy_trans)); + memset(&data_trans, 0, sizeof(data_trans)); + + /* write SPU register index */ + reg_trans.tx_buf = ®_out; + reg_trans.len = sizeof(reg_out); + spi_message_add_tail(®_trans, &m); + + delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay : + card->spu_reg_delay; + if (card->use_dummy_writes) { + /* Clock in dummy cycles while the SPU fills the FIFO */ + dummy_trans.len = delay / 8; + spi_message_add_tail(&dummy_trans, &m); + } else { + /* Busy-wait while the SPU fills the FIFO */ + reg_trans.delay_usecs = + DIV_ROUND_UP((100 + (delay * 10)), 1000); + } + + /* read in data */ + data_trans.rx_buf = buf; + data_trans.len = len; + spi_message_add_tail(&data_trans, &m); + + err = spi_sync(card->spi, &m); + spu_transaction_finish(card); + return err; +} + +/* Read 16 bits from an SPI register */ +static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val) +{ + __le16 buf; + int ret; + + ret = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); + if (ret == 0) + *val = le16_to_cpup(&buf); + return ret; +} + +/* + * Read 32 bits from an SPI register. + * The low 16 bits are read first. + */ +static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val) +{ + __le32 buf; + int err; + + err = spu_read(card, reg, (u8 *)&buf, sizeof(buf)); + if (!err) + *val = le32_to_cpup(&buf); + return err; +} + +/* + * Keep reading 16 bits from an SPI register until you get the correct result. + * + * If mask = 0, the correct result is any non-zero number. + * If mask != 0, the correct result is any number where + * number & target_mask == target + * + * Returns -ETIMEDOUT if a second passes without the correct result. + */ +static int spu_wait_for_u16(struct if_spi_card *card, u16 reg, + u16 target_mask, u16 target) +{ + int err; + unsigned long timeout = jiffies + 5*HZ; + while (1) { + u16 val; + err = spu_read_u16(card, reg, &val); + if (err) + return err; + if (target_mask) { + if ((val & target_mask) == target) + return 0; + } else { + if (val) + return 0; + } + udelay(100); + if (time_after(jiffies, timeout)) { + pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n", + __func__, val, target_mask, target); + return -ETIMEDOUT; + } + } +} + +/* + * Read 16 bits from an SPI register until you receive a specific value. + * Returns -ETIMEDOUT if a 4 tries pass without success. + */ +static int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target) +{ + int err, try; + for (try = 0; try < 4; ++try) { + u32 val = 0; + err = spu_read_u32(card, reg, &val); + if (err) + return err; + if (val == target) + return 0; + mdelay(100); + } + return -ETIMEDOUT; +} + +static int spu_set_interrupt_mode(struct if_spi_card *card, + int suppress_host_int, + int auto_int) +{ + int err = 0; + + /* + * We can suppress a host interrupt by clearing the appropriate + * bit in the "host interrupt status mask" register + */ + if (suppress_host_int) { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); + if (err) + return err; + } else { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, + IF_SPI_HISM_TX_DOWNLOAD_RDY | + IF_SPI_HISM_RX_UPLOAD_RDY | + IF_SPI_HISM_CMD_DOWNLOAD_RDY | + IF_SPI_HISM_CARDEVENT | + IF_SPI_HISM_CMD_UPLOAD_RDY); + if (err) + return err; + } + + /* + * If auto-interrupts are on, the completion of certain transactions + * will trigger an interrupt automatically. If auto-interrupts + * are off, we need to set the "Card Interrupt Cause" register to + * trigger a card interrupt. + */ + if (auto_int) { + err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG, + IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO | + IF_SPI_HICT_RX_UPLOAD_OVER_AUTO | + IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO | + IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO); + if (err) + return err; + } else { + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0); + if (err) + return err; + } + return err; +} + +static int spu_get_chip_revision(struct if_spi_card *card, + u16 *card_id, u8 *card_rev) +{ + int err = 0; + u32 dev_ctrl; + err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl); + if (err) + return err; + *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl); + *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl); + return err; +} + +static int spu_set_bus_mode(struct if_spi_card *card, u16 mode) +{ + int err = 0; + u16 rval; + /* set bus mode */ + err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode); + if (err) + return err; + /* Check that we were able to read back what we just wrote. */ + err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval); + if (err) + return err; + if ((rval & 0xF) != mode) { + pr_err("Can't read bus mode register\n"); + return -EIO; + } + return 0; +} + +static int spu_init(struct if_spi_card *card, int use_dummy_writes) +{ + int err = 0; + u32 delay; + + /* + * We have to start up in timed delay mode so that we can safely + * read the Delay Read Register. + */ + card->use_dummy_writes = 0; + err = spu_set_bus_mode(card, + IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | + IF_SPI_BUS_MODE_DELAY_METHOD_TIMED | + IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); + if (err) + return err; + card->spu_port_delay = 1000; + card->spu_reg_delay = 1000; + err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay); + if (err) + return err; + card->spu_port_delay = delay & 0x0000ffff; + card->spu_reg_delay = (delay & 0xffff0000) >> 16; + + /* If dummy clock delay mode has been requested, switch to it now */ + if (use_dummy_writes) { + card->use_dummy_writes = 1; + err = spu_set_bus_mode(card, + IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | + IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK | + IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA); + if (err) + return err; + } + + lbs_deb_spi("Initialized SPU unit. " + "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n", + card->spu_port_delay, card->spu_reg_delay); + return err; +} + +/* + * Firmware Loading + */ + +static int if_spi_prog_helper_firmware(struct if_spi_card *card, + const struct firmware *firmware) +{ + int err = 0; + int bytes_remaining; + const u8 *fw; + u8 temp[HELPER_FW_LOAD_CHUNK_SZ]; + + lbs_deb_enter(LBS_DEB_SPI); + + err = spu_set_interrupt_mode(card, 1, 0); + if (err) + goto out; + + bytes_remaining = firmware->size; + fw = firmware->data; + + /* Load helper firmware image */ + while (bytes_remaining > 0) { + /* + * Scratch pad 1 should contain the number of bytes we + * want to download to the firmware + */ + err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, + HELPER_FW_LOAD_CHUNK_SZ); + if (err) + goto out; + + err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, + IF_SPI_HIST_CMD_DOWNLOAD_RDY, + IF_SPI_HIST_CMD_DOWNLOAD_RDY); + if (err) + goto out; + + /* + * Feed the data into the command read/write port reg + * in chunks of 64 bytes + */ + memset(temp, 0, sizeof(temp)); + memcpy(temp, fw, + min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ)); + mdelay(10); + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, + temp, HELPER_FW_LOAD_CHUNK_SZ); + if (err) + goto out; + + /* Interrupt the boot code */ + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, + IF_SPI_CIC_CMD_DOWNLOAD_OVER); + if (err) + goto out; + bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ; + fw += HELPER_FW_LOAD_CHUNK_SZ; + } + + /* + * Once the helper / single stage firmware download is complete, + * write 0 to scratch pad 1 and interrupt the + * bootloader. This completes the helper download. + */ + err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, + IF_SPI_CIC_CMD_DOWNLOAD_OVER); +out: + if (err) + pr_err("failed to load helper firmware (err=%d)\n", err); + lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); + return err; +} + +/* + * Returns the length of the next packet the firmware expects us to send. + * Sets crc_err if the previous transfer had a CRC error. + */ +static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card, + int *crc_err) +{ + u16 len; + int err = 0; + + /* + * wait until the host interrupt status register indicates + * that we are ready to download + */ + err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG, + IF_SPI_HIST_CMD_DOWNLOAD_RDY, + IF_SPI_HIST_CMD_DOWNLOAD_RDY); + if (err) { + pr_err("timed out waiting for host_int_status\n"); + return err; + } + + /* Ask the device how many bytes of firmware it wants. */ + err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); + if (err) + return err; + + if (len > IF_SPI_CMD_BUF_SIZE) { + pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n", + len); + return -EIO; + } + if (len & 0x1) { + lbs_deb_spi("%s: crc error\n", __func__); + len &= ~0x1; + *crc_err = 1; + } else + *crc_err = 0; + + return len; +} + +static int if_spi_prog_main_firmware(struct if_spi_card *card, + const struct firmware *firmware) +{ + struct lbs_private *priv = card->priv; + int len, prev_len; + int bytes, crc_err = 0, err = 0; + const u8 *fw; + u16 num_crc_errs; + + lbs_deb_enter(LBS_DEB_SPI); + + err = spu_set_interrupt_mode(card, 1, 0); + if (err) + goto out; + + err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0); + if (err) { + netdev_err(priv->dev, + "%s: timed out waiting for initial scratch reg = 0\n", + __func__); + goto out; + } + + num_crc_errs = 0; + prev_len = 0; + bytes = firmware->size; + fw = firmware->data; + while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) { + if (len < 0) { + err = len; + goto out; + } + if (bytes < 0) { + /* + * If there are no more bytes left, we would normally + * expect to have terminated with len = 0 + */ + netdev_err(priv->dev, + "Firmware load wants more bytes than we have to offer.\n"); + break; + } + if (crc_err) { + /* Previous transfer failed. */ + if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) { + pr_err("Too many CRC errors encountered in firmware load.\n"); + err = -EIO; + goto out; + } + } else { + /* Previous transfer succeeded. Advance counters. */ + bytes -= prev_len; + fw += prev_len; + } + if (bytes < len) { + memset(card->cmd_buffer, 0, len); + memcpy(card->cmd_buffer, fw, bytes); + } else + memcpy(card->cmd_buffer, fw, len); + + err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0); + if (err) + goto out; + err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, + card->cmd_buffer, len); + if (err) + goto out; + err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG , + IF_SPI_CIC_CMD_DOWNLOAD_OVER); + if (err) + goto out; + prev_len = len; + } + if (bytes > prev_len) { + pr_err("firmware load wants fewer bytes than we have to offer\n"); + } + + /* Confirm firmware download */ + err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG, + SUCCESSFUL_FW_DOWNLOAD_MAGIC); + if (err) { + pr_err("failed to confirm the firmware download\n"); + goto out; + } + +out: + if (err) + pr_err("failed to load firmware (err=%d)\n", err); + lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err); + return err; +} + +/* + * SPI Transfer Thread + * + * The SPI worker handles all SPI transfers, so there is no need for a lock. + */ + +/* Move a command from the card to the host */ +static int if_spi_c2h_cmd(struct if_spi_card *card) +{ + struct lbs_private *priv = card->priv; + unsigned long flags; + int err = 0; + u16 len; + u8 i; + + /* + * We need a buffer big enough to handle whatever people send to + * hw_host_to_card + */ + BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE); + BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE); + + /* + * It's just annoying if the buffer size isn't a multiple of 4, because + * then we might have len < IF_SPI_CMD_BUF_SIZE but + * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE + */ + BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0); + + lbs_deb_enter(LBS_DEB_SPI); + + /* How many bytes are there to read? */ + err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len); + if (err) + goto out; + if (!len) { + netdev_err(priv->dev, "%s: error: card has no data for host\n", + __func__); + err = -EINVAL; + goto out; + } else if (len > IF_SPI_CMD_BUF_SIZE) { + netdev_err(priv->dev, + "%s: error: response packet too large: %d bytes, but maximum is %d\n", + __func__, len, IF_SPI_CMD_BUF_SIZE); + err = -EINVAL; + goto out; + } + + /* Read the data from the WLAN module into our command buffer */ + err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG, + card->cmd_buffer, ALIGN(len, 4)); + if (err) + goto out; + + spin_lock_irqsave(&priv->driver_lock, flags); + i = (priv->resp_idx == 0) ? 1 : 0; + BUG_ON(priv->resp_len[i]); + priv->resp_len[i] = len; + memcpy(priv->resp_buf[i], card->cmd_buffer, len); + lbs_notify_command_response(priv, i); + spin_unlock_irqrestore(&priv->driver_lock, flags); + +out: + if (err) + netdev_err(priv->dev, "%s: err=%d\n", __func__, err); + lbs_deb_leave(LBS_DEB_SPI); + return err; +} + +/* Move data from the card to the host */ +static int if_spi_c2h_data(struct if_spi_card *card) +{ + struct lbs_private *priv = card->priv; + struct sk_buff *skb; + char *data; + u16 len; + int err = 0; + + lbs_deb_enter(LBS_DEB_SPI); + + /* How many bytes are there to read? */ + err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len); + if (err) + goto out; + if (!len) { + netdev_err(priv->dev, "%s: error: card has no data for host\n", + __func__); + err = -EINVAL; + goto out; + } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { + netdev_err(priv->dev, + "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n", + __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); + err = -EINVAL; + goto out; + } + + /* TODO: should we allocate a smaller skb if we have less data? */ + skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); + if (!skb) { + err = -ENOBUFS; + goto out; + } + skb_reserve(skb, IPFIELD_ALIGN_OFFSET); + data = skb_put(skb, len); + + /* Read the data from the WLAN module into our skb... */ + err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 4)); + if (err) + goto free_skb; + + /* pass the SKB to libertas */ + err = lbs_process_rxed_packet(card->priv, skb); + if (err) + goto free_skb; + + /* success */ + goto out; + +free_skb: + dev_kfree_skb(skb); +out: + if (err) + netdev_err(priv->dev, "%s: err=%d\n", __func__, err); + lbs_deb_leave(LBS_DEB_SPI); + return err; +} + +/* Move data or a command from the host to the card. */ +static void if_spi_h2c(struct if_spi_card *card, + struct if_spi_packet *packet, int type) +{ + struct lbs_private *priv = card->priv; + int err = 0; + u16 int_type, port_reg; + + switch (type) { + case MVMS_DAT: + int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER; + port_reg = IF_SPI_DATA_RDWRPORT_REG; + break; + case MVMS_CMD: + int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER; + port_reg = IF_SPI_CMD_RDWRPORT_REG; + break; + default: + netdev_err(priv->dev, "can't transfer buffer of type %d\n", + type); + err = -EINVAL; + goto out; + } + + /* Write the data to the card */ + err = spu_write(card, port_reg, packet->buffer, packet->blen); + if (err) + goto out; + +out: + kfree(packet); + + if (err) + netdev_err(priv->dev, "%s: error %d\n", __func__, err); +} + +/* Inform the host about a card event */ +static void if_spi_e2h(struct if_spi_card *card) +{ + int err = 0; + u32 cause; + struct lbs_private *priv = card->priv; + + err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause); + if (err) + goto out; + + /* re-enable the card event interrupt */ + spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, + ~IF_SPI_HICU_CARD_EVENT); + + /* generate a card interrupt */ + spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT); + + lbs_queue_event(priv, cause & 0xff); +out: + if (err) + netdev_err(priv->dev, "%s: error %d\n", __func__, err); +} + +static void if_spi_host_to_card_worker(struct work_struct *work) +{ + int err; + struct if_spi_card *card; + u16 hiStatus; + unsigned long flags; + struct if_spi_packet *packet; + struct lbs_private *priv; + + card = container_of(work, struct if_spi_card, packet_work); + priv = card->priv; + + lbs_deb_enter(LBS_DEB_SPI); + + /* + * Read the host interrupt status register to see what we + * can do. + */ + err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, + &hiStatus); + if (err) { + netdev_err(priv->dev, "I/O error\n"); + goto err; + } + + if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) { + err = if_spi_c2h_cmd(card); + if (err) + goto err; + } + if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) { + err = if_spi_c2h_data(card); + if (err) + goto err; + } + + /* + * workaround: in PS mode, the card does not set the Command + * Download Ready bit, but it sets TX Download Ready. + */ + if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY || + (card->priv->psstate != PS_STATE_FULL_POWER && + (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { + /* + * This means two things. First of all, + * if there was a previous command sent, the card has + * successfully received it. + * Secondly, it is now ready to download another + * command. + */ + lbs_host_to_card_done(card->priv); + + /* Do we have any command packets from the host to send? */ + packet = NULL; + spin_lock_irqsave(&card->buffer_lock, flags); + if (!list_empty(&card->cmd_packet_list)) { + packet = (struct if_spi_packet *)(card-> + cmd_packet_list.next); + list_del(&packet->list); + } + spin_unlock_irqrestore(&card->buffer_lock, flags); + + if (packet) + if_spi_h2c(card, packet, MVMS_CMD); + } + if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) { + /* Do we have any data packets from the host to send? */ + packet = NULL; + spin_lock_irqsave(&card->buffer_lock, flags); + if (!list_empty(&card->data_packet_list)) { + packet = (struct if_spi_packet *)(card-> + data_packet_list.next); + list_del(&packet->list); + } + spin_unlock_irqrestore(&card->buffer_lock, flags); + + if (packet) + if_spi_h2c(card, packet, MVMS_DAT); + } + if (hiStatus & IF_SPI_HIST_CARD_EVENT) + if_spi_e2h(card); + +err: + if (err) + netdev_err(priv->dev, "%s: got error %d\n", __func__, err); + + lbs_deb_leave(LBS_DEB_SPI); +} + +/* + * Host to Card + * + * Called from Libertas to transfer some data to the WLAN device + * We can't sleep here. + */ +static int if_spi_host_to_card(struct lbs_private *priv, + u8 type, u8 *buf, u16 nb) +{ + int err = 0; + unsigned long flags; + struct if_spi_card *card = priv->card; + struct if_spi_packet *packet; + u16 blen; + + lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb); + + if (nb == 0) { + netdev_err(priv->dev, "%s: invalid size requested: %d\n", + __func__, nb); + err = -EINVAL; + goto out; + } + blen = ALIGN(nb, 4); + packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC); + if (!packet) { + err = -ENOMEM; + goto out; + } + packet->blen = blen; + memcpy(packet->buffer, buf, nb); + memset(packet->buffer + nb, 0, blen - nb); + + switch (type) { + case MVMS_CMD: + priv->dnld_sent = DNLD_CMD_SENT; + spin_lock_irqsave(&card->buffer_lock, flags); + list_add_tail(&packet->list, &card->cmd_packet_list); + spin_unlock_irqrestore(&card->buffer_lock, flags); + break; + case MVMS_DAT: + priv->dnld_sent = DNLD_DATA_SENT; + spin_lock_irqsave(&card->buffer_lock, flags); + list_add_tail(&packet->list, &card->data_packet_list); + spin_unlock_irqrestore(&card->buffer_lock, flags); + break; + default: + kfree(packet); + netdev_err(priv->dev, "can't transfer buffer of type %d\n", + type); + err = -EINVAL; + break; + } + + /* Queue spi xfer work */ + queue_work(card->workqueue, &card->packet_work); +out: + lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err); + return err; +} + +/* + * Host Interrupts + * + * Service incoming interrupts from the WLAN device. We can't sleep here, so + * don't try to talk on the SPI bus, just queue the SPI xfer work. + */ +static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id) +{ + struct if_spi_card *card = dev_id; + + queue_work(card->workqueue, &card->packet_work); + + return IRQ_HANDLED; +} + +/* + * SPI callbacks + */ + +static int if_spi_init_card(struct if_spi_card *card) +{ + struct lbs_private *priv = card->priv; + int err, i; + u32 scratch; + const struct firmware *helper = NULL; + const struct firmware *mainfw = NULL; + + lbs_deb_enter(LBS_DEB_SPI); + + err = spu_init(card, card->pdata->use_dummy_writes); + if (err) + goto out; + err = spu_get_chip_revision(card, &card->card_id, &card->card_rev); + if (err) + goto out; + + err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch); + if (err) + goto out; + if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC) + lbs_deb_spi("Firmware is already loaded for " + "Marvell WLAN 802.11 adapter\n"); + else { + /* Check if we support this card */ + for (i = 0; i < ARRAY_SIZE(fw_table); i++) { + if (card->card_id == fw_table[i].model) + break; + } + if (i == ARRAY_SIZE(fw_table)) { + netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n", + card->card_id); + err = -ENODEV; + goto out; + } + + err = lbs_get_firmware(&card->spi->dev, card->card_id, + &fw_table[0], &helper, &mainfw); + if (err) { + netdev_err(priv->dev, "failed to find firmware (%d)\n", + err); + goto out; + } + + lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter " + "(chip_id = 0x%04x, chip_rev = 0x%02x) " + "attached to SPI bus_num %d, chip_select %d. " + "spi->max_speed_hz=%d\n", + card->card_id, card->card_rev, + card->spi->master->bus_num, + card->spi->chip_select, + card->spi->max_speed_hz); + err = if_spi_prog_helper_firmware(card, helper); + if (err) + goto out; + err = if_spi_prog_main_firmware(card, mainfw); + if (err) + goto out; + lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n"); + } + + err = spu_set_interrupt_mode(card, 0, 1); + if (err) + goto out; + +out: + lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); + return err; +} + +static void if_spi_resume_worker(struct work_struct *work) +{ + struct if_spi_card *card; + + card = container_of(work, struct if_spi_card, resume_work); + + if (card->suspended) { + if (card->pdata->setup) + card->pdata->setup(card->spi); + + /* Init card ... */ + if_spi_init_card(card); + + enable_irq(card->spi->irq); + + /* And resume it ... */ + lbs_resume(card->priv); + + card->suspended = 0; + } +} + +static int if_spi_probe(struct spi_device *spi) +{ + struct if_spi_card *card; + struct lbs_private *priv = NULL; + struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev); + int err = 0; + + lbs_deb_enter(LBS_DEB_SPI); + + if (!pdata) { + err = -EINVAL; + goto out; + } + + if (pdata->setup) { + err = pdata->setup(spi); + if (err) + goto out; + } + + /* Allocate card structure to represent this specific device */ + card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL); + if (!card) { + err = -ENOMEM; + goto teardown; + } + spi_set_drvdata(spi, card); + card->pdata = pdata; + card->spi = spi; + card->prev_xfer_time = jiffies; + + INIT_LIST_HEAD(&card->cmd_packet_list); + INIT_LIST_HEAD(&card->data_packet_list); + spin_lock_init(&card->buffer_lock); + + /* Initialize the SPI Interface Unit */ + + /* Firmware load */ + err = if_spi_init_card(card); + if (err) + goto free_card; + + /* + * Register our card with libertas. + * This will call alloc_etherdev. + */ + priv = lbs_add_card(card, &spi->dev); + if (!priv) { + err = -ENOMEM; + goto free_card; + } + card->priv = priv; + priv->setup_fw_on_resume = 1; + priv->card = card; + priv->hw_host_to_card = if_spi_host_to_card; + priv->enter_deep_sleep = NULL; + priv->exit_deep_sleep = NULL; + priv->reset_deep_sleep_wakeup = NULL; + priv->fw_ready = 1; + + /* Initialize interrupt handling stuff. */ + card->workqueue = create_workqueue("libertas_spi"); + INIT_WORK(&card->packet_work, if_spi_host_to_card_worker); + INIT_WORK(&card->resume_work, if_spi_resume_worker); + + err = request_irq(spi->irq, if_spi_host_interrupt, + IRQF_TRIGGER_FALLING, "libertas_spi", card); + if (err) { + pr_err("can't get host irq line-- request_irq failed\n"); + goto terminate_workqueue; + } + + /* + * Start the card. + * This will call register_netdev, and we'll start + * getting interrupts... + */ + err = lbs_start_card(priv); + if (err) + goto release_irq; + + lbs_deb_spi("Finished initializing WLAN module.\n"); + + /* successful exit */ + goto out; + +release_irq: + free_irq(spi->irq, card); +terminate_workqueue: + flush_workqueue(card->workqueue); + destroy_workqueue(card->workqueue); + lbs_remove_card(priv); /* will call free_netdev */ +free_card: + free_if_spi_card(card); +teardown: + if (pdata->teardown) + pdata->teardown(spi); +out: + lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); + return err; +} + +static int libertas_spi_remove(struct spi_device *spi) +{ + struct if_spi_card *card = spi_get_drvdata(spi); + struct lbs_private *priv = card->priv; + + lbs_deb_spi("libertas_spi_remove\n"); + lbs_deb_enter(LBS_DEB_SPI); + + cancel_work_sync(&card->resume_work); + + lbs_stop_card(priv); + lbs_remove_card(priv); /* will call free_netdev */ + + free_irq(spi->irq, card); + flush_workqueue(card->workqueue); + destroy_workqueue(card->workqueue); + if (card->pdata->teardown) + card->pdata->teardown(spi); + free_if_spi_card(card); + lbs_deb_leave(LBS_DEB_SPI); + return 0; +} + +static int if_spi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct if_spi_card *card = spi_get_drvdata(spi); + + if (!card->suspended) { + lbs_suspend(card->priv); + flush_workqueue(card->workqueue); + disable_irq(spi->irq); + + if (card->pdata->teardown) + card->pdata->teardown(spi); + card->suspended = 1; + } + + return 0; +} + +static int if_spi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct if_spi_card *card = spi_get_drvdata(spi); + + /* Schedule delayed work */ + schedule_work(&card->resume_work); + + return 0; +} + +static const struct dev_pm_ops if_spi_pm_ops = { + .suspend = if_spi_suspend, + .resume = if_spi_resume, +}; + +static struct spi_driver libertas_spi_driver = { + .probe = if_spi_probe, + .remove = libertas_spi_remove, + .driver = { + .name = "libertas_spi", + .pm = &if_spi_pm_ops, + }, +}; + +/* + * Module functions + */ + +static int __init if_spi_init_module(void) +{ + int ret = 0; + lbs_deb_enter(LBS_DEB_SPI); + printk(KERN_INFO "libertas_spi: Libertas SPI driver\n"); + ret = spi_register_driver(&libertas_spi_driver); + lbs_deb_leave(LBS_DEB_SPI); + return ret; +} + +static void __exit if_spi_exit_module(void) +{ + lbs_deb_enter(LBS_DEB_SPI); + spi_unregister_driver(&libertas_spi_driver); + lbs_deb_leave(LBS_DEB_SPI); +} + +module_init(if_spi_init_module); +module_exit(if_spi_exit_module); + +MODULE_DESCRIPTION("Libertas SPI WLAN Driver"); +MODULE_AUTHOR("Andrey Yurovsky , " + "Colin McCabe "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:libertas_spi"); diff --git a/drivers/net/wireless/marvell/libertas/if_spi.h b/drivers/net/wireless/marvell/libertas/if_spi.h new file mode 100644 index 000000000000..e450e31fd11d --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_spi.h @@ -0,0 +1,206 @@ +/* + * linux/drivers/net/wireless/libertas/if_spi.c + * + * Driver for Marvell SPI WLAN cards. + * + * Copyright 2008 Analog Devices Inc. + * + * Authors: + * Andrey Yurovsky + * Colin McCabe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef _LBS_IF_SPI_H_ +#define _LBS_IF_SPI_H_ + +#define IPFIELD_ALIGN_OFFSET 2 +#define IF_SPI_CMD_BUF_SIZE 2400 + +/***************** Firmware *****************/ + +#define IF_SPI_FW_NAME_MAX 30 + +#define MAX_MAIN_FW_LOAD_CRC_ERR 10 + +/* Chunk size when loading the helper firmware */ +#define HELPER_FW_LOAD_CHUNK_SZ 64 + +/* Value to write to indicate end of helper firmware dnld */ +#define FIRMWARE_DNLD_OK 0x0000 + +/* Value to check once the main firmware is downloaded */ +#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888 + +/***************** SPI Interface Unit *****************/ +/* Masks used in SPI register read/write operations */ +#define IF_SPI_READ_OPERATION_MASK 0x0 +#define IF_SPI_WRITE_OPERATION_MASK 0x8000 + +/* SPI register offsets. 4-byte aligned. */ +#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */ +#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */ +#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */ +#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */ + +#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */ +#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */ +#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */ + +#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */ +#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */ +#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */ + +#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */ +#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */ +#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */ +#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */ + +#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */ +#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */ + +#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */ + +#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */ +#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interrupt status reg */ +#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */ +#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */ + +#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */ + +#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */ +#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */ +#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */ +#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */ +#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */ + +#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */ +#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */ + +/***************** IF_SPI_DEVICEID_CTRL_REG *****************/ +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16) +#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff) + +/***************** IF_SPI_HOST_INT_CTRL_REG *****************/ +/* Host Interrupt Control bit : Wake up */ +#define IF_SPI_HICT_WAKE_UP (1<<0) +/* Host Interrupt Control bit : WLAN ready */ +#define IF_SPI_HICT_WLAN_READY (1<<1) +/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */ +/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */ +/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */ +/* Host Interrupt Control bit : Tx auto download */ +#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5) +/* Host Interrupt Control bit : Rx auto upload */ +#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6) +/* Host Interrupt Control bit : Command auto download */ +#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7) +/* Host Interrupt Control bit : Command auto upload */ +#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8) + +/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/ +/* Card Interrupt Case bit : Tx download over */ +#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0) +/* Card Interrupt Case bit : Rx upload over */ +#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1) +/* Card Interrupt Case bit : Command download over */ +#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2) +/* Card Interrupt Case bit : Host event */ +#define IF_SPI_CIC_HOST_EVENT (1<<3) +/* Card Interrupt Case bit : Command upload over */ +#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4) +/* Card Interrupt Case bit : Power down */ +#define IF_SPI_CIC_POWER_DOWN (1<<5) + +/***************** IF_SPI_CARD_INT_STATUS_REG *****************/ +#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0) +#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1) +#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2) +#define IF_SPI_CIS_HOST_EVENT (1<<3) +#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4) +#define IF_SPI_CIS_POWER_DOWN (1<<5) + +/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/ +#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0) +#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1) +#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2) +#define IF_SPI_HICU_CARD_EVENT (1<<3) +#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4) +#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5) +#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6) +#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7) +#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8) +#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9) +#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_HOST_INT_STATUS_REG *****************/ +/* Host Interrupt Status bit : Tx download ready */ +#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0) +/* Host Interrupt Status bit : Rx upload ready */ +#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1) +/* Host Interrupt Status bit : Command download ready */ +#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2) +/* Host Interrupt Status bit : Card event */ +#define IF_SPI_HIST_CARD_EVENT (1<<3) +/* Host Interrupt Status bit : Command upload ready */ +#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4) +/* Host Interrupt Status bit : I/O write FIFO overflow */ +#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5) +/* Host Interrupt Status bit : I/O read FIFO underflow */ +#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6) +/* Host Interrupt Status bit : Data write FIFO overflow */ +#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7) +/* Host Interrupt Status bit : Data read FIFO underflow */ +#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8) +/* Host Interrupt Status bit : Command write FIFO overflow */ +#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9) +/* Host Interrupt Status bit : Command read FIFO underflow */ +#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/ +/* Host Interrupt Status Mask bit : Tx download ready */ +#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0) +/* Host Interrupt Status Mask bit : Rx upload ready */ +#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1) +/* Host Interrupt Status Mask bit : Command download ready */ +#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2) +/* Host Interrupt Status Mask bit : Card event */ +#define IF_SPI_HISM_CARDEVENT (1<<3) +/* Host Interrupt Status Mask bit : Command upload ready */ +#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4) +/* Host Interrupt Status Mask bit : I/O write FIFO overflow */ +#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5) +/* Host Interrupt Status Mask bit : I/O read FIFO underflow */ +#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6) +/* Host Interrupt Status Mask bit : Data write FIFO overflow */ +#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7) +/* Host Interrupt Status Mask bit : Data write FIFO underflow */ +#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8) +/* Host Interrupt Status Mask bit : Command write FIFO overflow */ +#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9) +/* Host Interrupt Status Mask bit : Command write FIFO underflow */ +#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10) + +/***************** IF_SPI_SPU_BUS_MODE_REG *****************/ +/* SCK edge on which the WLAN module outputs data on MISO */ +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8 +#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0 + +/* In a SPU read operation, there is a delay between writing the SPU + * register name and getting back data from the WLAN module. + * This can be specified in terms of nanoseconds or in terms of dummy + * clock cycles which the master must output before receiving a response. */ +#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4 +#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0 + +/* Some different modes of SPI operation */ +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00 +#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01 +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02 +#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03 + +#endif diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c new file mode 100644 index 000000000000..dff08a2896a3 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_usb.c @@ -0,0 +1,1018 @@ +/* + * This file contains functions used in USB interface module. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OLPC +#include +#endif + +#define DRV_NAME "usb8xxx" + +#include "host.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "cmd.h" +#include "if_usb.h" + +#define INSANEDEBUG 0 +#define lbs_deb_usb2(...) do { if (INSANEDEBUG) lbs_deb_usbd(__VA_ARGS__); } while (0) + +#define MESSAGE_HEADER_LEN 4 + +MODULE_FIRMWARE("libertas/usb8388_v9.bin"); +MODULE_FIRMWARE("libertas/usb8388_v5.bin"); +MODULE_FIRMWARE("libertas/usb8388.bin"); +MODULE_FIRMWARE("libertas/usb8682.bin"); +MODULE_FIRMWARE("usb8388.bin"); + +enum { + MODEL_UNKNOWN = 0x0, + MODEL_8388 = 0x1, + MODEL_8682 = 0x2 +}; + +/* table of firmware file names */ +static const struct lbs_fw_table fw_table[] = { + { MODEL_8388, "libertas/usb8388_olpc.bin", NULL }, + { MODEL_8388, "libertas/usb8388_v9.bin", NULL }, + { MODEL_8388, "libertas/usb8388_v5.bin", NULL }, + { MODEL_8388, "libertas/usb8388.bin", NULL }, + { MODEL_8388, "usb8388.bin", NULL }, + { MODEL_8682, "libertas/usb8682.bin", NULL } +}; + +static struct usb_device_id if_usb_table[] = { + /* Enter the device signature inside */ + { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 }, + { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, if_usb_table); + +static void if_usb_receive(struct urb *urb); +static void if_usb_receive_fwload(struct urb *urb); +static void if_usb_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *fw, + const struct firmware *unused); +static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, + uint8_t *payload, uint16_t nb); +static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, + uint16_t nb); +static void if_usb_free(struct if_usb_card *cardp); +static int if_usb_submit_rx_urb(struct if_usb_card *cardp); +static int if_usb_reset_device(struct if_usb_card *cardp); + +/** + * if_usb_write_bulk_callback - callback function to handle the status + * of the URB + * @urb: pointer to &urb structure + * returns: N/A + */ +static void if_usb_write_bulk_callback(struct urb *urb) +{ + struct if_usb_card *cardp = (struct if_usb_card *) urb->context; + + /* handle the transmission complete validations */ + + if (urb->status == 0) { + struct lbs_private *priv = cardp->priv; + + lbs_deb_usb2(&urb->dev->dev, "URB status is successful\n"); + lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", + urb->actual_length); + + /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not + * passed up to the lbs level. + */ + if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT) + lbs_host_to_card_done(priv); + } else { + /* print the failure status number for debug */ + pr_info("URB in failure status: %d\n", urb->status); + } +} + +/** + * if_usb_free - free tx/rx urb, skb and rx buffer + * @cardp: pointer to &if_usb_card + * returns: N/A + */ +static void if_usb_free(struct if_usb_card *cardp) +{ + lbs_deb_enter(LBS_DEB_USB); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + usb_free_urb(cardp->tx_urb); + cardp->tx_urb = NULL; + + usb_free_urb(cardp->rx_urb); + cardp->rx_urb = NULL; + + kfree(cardp->ep_out_buf); + cardp->ep_out_buf = NULL; + + lbs_deb_leave(LBS_DEB_USB); +} + +static void if_usb_setup_firmware(struct lbs_private *priv) +{ + struct if_usb_card *cardp = priv->card; + struct cmd_ds_set_boot2_ver b2_cmd; + struct cmd_ds_802_11_fw_wake_method wake_method; + + b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); + b2_cmd.action = 0; + b2_cmd.version = cardp->boot2_version; + + if (lbs_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) + lbs_deb_usb("Setting boot2 version failed\n"); + + priv->wol_gpio = 2; /* Wake via GPIO2... */ + priv->wol_gap = 20; /* ... after 20ms */ + lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA, + (struct wol_config *) NULL); + + wake_method.hdr.size = cpu_to_le16(sizeof(wake_method)); + wake_method.action = cpu_to_le16(CMD_ACT_GET); + if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) { + netdev_info(priv->dev, "Firmware does not seem to support PS mode\n"); + priv->fwcapinfo &= ~FW_CAPINFO_PS; + } else { + if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) { + lbs_deb_usb("Firmware seems to support PS with wake-via-command\n"); + } else { + /* The versions which boot up this way don't seem to + work even if we set it to the command interrupt */ + priv->fwcapinfo &= ~FW_CAPINFO_PS; + netdev_info(priv->dev, + "Firmware doesn't wake via command interrupt; disabling PS mode\n"); + } + } +} + +static void if_usb_fw_timeo(unsigned long priv) +{ + struct if_usb_card *cardp = (void *)priv; + + if (cardp->fwdnldover) { + lbs_deb_usb("Download complete, no event. Assuming success\n"); + } else { + pr_err("Download timed out\n"); + cardp->surprise_removed = 1; + } + wake_up(&cardp->fw_wq); +} + +#ifdef CONFIG_OLPC +static void if_usb_reset_olpc_card(struct lbs_private *priv) +{ + printk(KERN_CRIT "Resetting OLPC wireless via EC...\n"); + olpc_ec_cmd(0x25, NULL, 0, NULL, 0); +} +#endif + +/** + * if_usb_probe - sets the configuration values + * @intf: &usb_interface pointer + * @id: pointer to usb_device_id + * returns: 0 on success, error code on failure + */ +static int if_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct lbs_private *priv; + struct if_usb_card *cardp; + int r = -ENOMEM; + int i; + + udev = interface_to_usbdev(intf); + + cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); + if (!cardp) + goto error; + + setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp); + init_waitqueue_head(&cardp->fw_wq); + + cardp->udev = udev; + cardp->model = (uint32_t) id->driver_info; + iface_desc = intf->cur_altsetting; + + lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" + " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", + le16_to_cpu(udev->descriptor.bcdUSB), + udev->descriptor.bDeviceClass, + udev->descriptor.bDeviceSubClass, + udev->descriptor.bDeviceProtocol); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (usb_endpoint_is_bulk_in(endpoint)) { + cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); + cardp->ep_in = usb_endpoint_num(endpoint); + + lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); + lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); + + } else if (usb_endpoint_is_bulk_out(endpoint)) { + cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); + cardp->ep_out = usb_endpoint_num(endpoint); + + lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); + lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); + } + } + if (!cardp->ep_out_size || !cardp->ep_in_size) { + lbs_deb_usbd(&udev->dev, "Endpoints not found\n"); + goto dealloc; + } + if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) { + lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); + goto dealloc; + } + if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) { + lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n"); + goto dealloc; + } + cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL); + if (!cardp->ep_out_buf) { + lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n"); + goto dealloc; + } + + if (!(priv = lbs_add_card(cardp, &intf->dev))) + goto err_add_card; + + cardp->priv = priv; + + priv->hw_host_to_card = if_usb_host_to_card; + priv->enter_deep_sleep = NULL; + priv->exit_deep_sleep = NULL; + priv->reset_deep_sleep_wakeup = NULL; +#ifdef CONFIG_OLPC + if (machine_is_olpc()) + priv->reset_card = if_usb_reset_olpc_card; +#endif + + cardp->boot2_version = udev->descriptor.bcdDevice; + + usb_get_dev(udev); + usb_set_intfdata(intf, cardp); + + r = lbs_get_firmware_async(priv, &udev->dev, cardp->model, + fw_table, if_usb_prog_firmware); + if (r) + goto err_get_fw; + + return 0; + +err_get_fw: + lbs_remove_card(priv); +err_add_card: + if_usb_reset_device(cardp); +dealloc: + if_usb_free(cardp); + +error: + return r; +} + +/** + * if_usb_disconnect - free resource and cleanup + * @intf: USB interface structure + * returns: N/A + */ +static void if_usb_disconnect(struct usb_interface *intf) +{ + struct if_usb_card *cardp = usb_get_intfdata(intf); + struct lbs_private *priv = cardp->priv; + + lbs_deb_enter(LBS_DEB_MAIN); + + cardp->surprise_removed = 1; + + if (priv) { + lbs_stop_card(priv); + lbs_remove_card(priv); + } + + /* Unlink and free urb */ + if_usb_free(cardp); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); + + lbs_deb_leave(LBS_DEB_MAIN); +} + +/** + * if_usb_send_fw_pkt - download FW + * @cardp: pointer to &struct if_usb_card + * returns: 0 + */ +static int if_usb_send_fw_pkt(struct if_usb_card *cardp) +{ + struct fwdata *fwdata = cardp->ep_out_buf; + const uint8_t *firmware = cardp->fw->data; + + /* If we got a CRC failure on the last block, back + up and retry it */ + if (!cardp->CRC_OK) { + cardp->totalbytes = cardp->fwlastblksent; + cardp->fwseqnum--; + } + + lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n", + cardp->totalbytes); + + /* struct fwdata (which we sent to the card) has an + extra __le32 field in between the header and the data, + which is not in the struct fwheader in the actual + firmware binary. Insert the seqnum in the middle... */ + memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], + sizeof(struct fwheader)); + + cardp->fwlastblksent = cardp->totalbytes; + cardp->totalbytes += sizeof(struct fwheader); + + memcpy(fwdata->data, &firmware[cardp->totalbytes], + le32_to_cpu(fwdata->hdr.datalength)); + + lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n", + le32_to_cpu(fwdata->hdr.datalength)); + + fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); + cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); + + usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + + le32_to_cpu(fwdata->hdr.datalength)); + + if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) { + lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n"); + lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n", + cardp->fwseqnum, cardp->totalbytes); + } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { + lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n"); + lbs_deb_usb2(&cardp->udev->dev, "Donwloading FW JUMP BLOCK\n"); + + cardp->fwfinalblk = 1; + } + + lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n", + cardp->totalbytes); + + return 0; +} + +static int if_usb_reset_device(struct if_usb_card *cardp) +{ + struct cmd_header *cmd = cardp->ep_out_buf + 4; + int ret; + + lbs_deb_enter(LBS_DEB_USB); + + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); + + cmd->command = cpu_to_le16(CMD_802_11_RESET); + cmd->size = cpu_to_le16(sizeof(cmd)); + cmd->result = cpu_to_le16(0); + cmd->seqnum = cpu_to_le16(0x5a5a); + usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_header)); + + msleep(100); + ret = usb_reset_device(cardp->udev); + msleep(100); + +#ifdef CONFIG_OLPC + if (ret && machine_is_olpc()) + if_usb_reset_olpc_card(NULL); +#endif + + lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret); + + return ret; +} + +/** + * usb_tx_block - transfer the data to the device + * @cardp: pointer to &struct if_usb_card + * @payload: pointer to payload data + * @nb: data length + * returns: 0 for success or negative error code + */ +static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb) +{ + int ret; + + /* check if device is removed */ + if (cardp->surprise_removed) { + lbs_deb_usbd(&cardp->udev->dev, "Device removed\n"); + ret = -ENODEV; + goto tx_ret; + } + + usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, + usb_sndbulkpipe(cardp->udev, + cardp->ep_out), + payload, nb, if_usb_write_bulk_callback, cardp); + + cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; + + if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { + lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret); + } else { + lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n"); + ret = 0; + } + +tx_ret: + return ret; +} + +static int __if_usb_submit_rx_urb(struct if_usb_card *cardp, + void (*callbackfn)(struct urb *urb)) +{ + struct sk_buff *skb; + int ret = -1; + + if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { + pr_err("No free skb\n"); + goto rx_ret; + } + + cardp->rx_skb = skb; + + /* Fill the receive configuration URB and initialise the Rx call back */ + usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, + usb_rcvbulkpipe(cardp->udev, cardp->ep_in), + skb->data + IPFIELD_ALIGN_OFFSET, + MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, + cardp); + + cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; + + lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); + if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { + lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret); + kfree_skb(skb); + cardp->rx_skb = NULL; + ret = -1; + } else { + lbs_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n"); + ret = 0; + } + +rx_ret: + return ret; +} + +static int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) +{ + return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); +} + +static int if_usb_submit_rx_urb(struct if_usb_card *cardp) +{ + return __if_usb_submit_rx_urb(cardp, &if_usb_receive); +} + +static void if_usb_receive_fwload(struct urb *urb) +{ + struct if_usb_card *cardp = urb->context; + struct sk_buff *skb = cardp->rx_skb; + struct fwsyncheader *syncfwheader; + struct bootcmdresp bootcmdresp; + + if (urb->status) { + lbs_deb_usbd(&cardp->udev->dev, + "URB status is failed during fw load\n"); + kfree_skb(skb); + return; + } + + if (cardp->fwdnldover) { + __le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); + + if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && + tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) { + pr_info("Firmware ready event received\n"); + wake_up(&cardp->fw_wq); + } else { + lbs_deb_usb("Waiting for confirmation; got %x %x\n", + le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1])); + if_usb_submit_rx_urb_fwload(cardp); + } + kfree_skb(skb); + return; + } + if (cardp->bootcmdresp <= 0) { + memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(bootcmdresp)); + + if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(cardp); + cardp->bootcmdresp = BOOT_CMD_RESP_OK; + lbs_deb_usbd(&cardp->udev->dev, + "Received valid boot command response\n"); + return; + } + if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { + if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || + bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || + bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) { + if (!cardp->bootcmdresp) + pr_info("Firmware already seems alive; resetting\n"); + cardp->bootcmdresp = -1; + } else { + pr_info("boot cmd response wrong magic number (0x%x)\n", + le32_to_cpu(bootcmdresp.magic)); + } + } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) && + (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) && + (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) { + pr_info("boot cmd response cmd_tag error (%d)\n", + bootcmdresp.cmd); + } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { + pr_info("boot cmd response result error (%d)\n", + bootcmdresp.result); + } else { + cardp->bootcmdresp = 1; + lbs_deb_usbd(&cardp->udev->dev, + "Received valid boot command response\n"); + } + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(cardp); + return; + } + + syncfwheader = kmemdup(skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(struct fwsyncheader), GFP_ATOMIC); + if (!syncfwheader) { + lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n"); + kfree_skb(skb); + return; + } + + if (!syncfwheader->cmd) { + lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n"); + lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n", + le32_to_cpu(syncfwheader->seqnum)); + cardp->CRC_OK = 1; + } else { + lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n"); + cardp->CRC_OK = 0; + } + + kfree_skb(skb); + + /* Give device 5s to either write firmware to its RAM or eeprom */ + mod_timer(&cardp->fw_timeout, jiffies + (HZ*5)); + + if (cardp->fwfinalblk) { + cardp->fwdnldover = 1; + goto exit; + } + + if_usb_send_fw_pkt(cardp); + + exit: + if_usb_submit_rx_urb_fwload(cardp); + + kfree(syncfwheader); +} + +#define MRVDRV_MIN_PKT_LEN 30 + +static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, + struct if_usb_card *cardp, + struct lbs_private *priv) +{ + if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN + || recvlength < MRVDRV_MIN_PKT_LEN) { + lbs_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n"); + kfree_skb(skb); + return; + } + + skb_reserve(skb, IPFIELD_ALIGN_OFFSET); + skb_put(skb, recvlength); + skb_pull(skb, MESSAGE_HEADER_LEN); + + lbs_process_rxed_packet(priv, skb); +} + +static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, + struct sk_buff *skb, + struct if_usb_card *cardp, + struct lbs_private *priv) +{ + u8 i; + + if (recvlength > LBS_CMD_BUFFER_SIZE) { + lbs_deb_usbd(&cardp->udev->dev, + "The receive buffer is too large\n"); + kfree_skb(skb); + return; + } + + BUG_ON(!in_interrupt()); + + spin_lock(&priv->driver_lock); + + i = (priv->resp_idx == 0) ? 1 : 0; + BUG_ON(priv->resp_len[i]); + priv->resp_len[i] = (recvlength - MESSAGE_HEADER_LEN); + memcpy(priv->resp_buf[i], recvbuff + MESSAGE_HEADER_LEN, + priv->resp_len[i]); + kfree_skb(skb); + lbs_notify_command_response(priv, i); + + spin_unlock(&priv->driver_lock); + + lbs_deb_usbd(&cardp->udev->dev, + "Wake up main thread to handle cmd response\n"); +} + +/** + * if_usb_receive - read the packet into the upload buffer, + * wake up the main thread and initialise the Rx callack + * + * @urb: pointer to &struct urb + * returns: N/A + */ +static void if_usb_receive(struct urb *urb) +{ + struct if_usb_card *cardp = urb->context; + struct sk_buff *skb = cardp->rx_skb; + struct lbs_private *priv = cardp->priv; + int recvlength = urb->actual_length; + uint8_t *recvbuff = NULL; + uint32_t recvtype = 0; + __le32 *pkt = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); + uint32_t event; + + lbs_deb_enter(LBS_DEB_USB); + + if (recvlength) { + if (urb->status) { + lbs_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n", + urb->status); + kfree_skb(skb); + goto setup_for_next; + } + + recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; + recvtype = le32_to_cpu(pkt[0]); + lbs_deb_usbd(&cardp->udev->dev, + "Recv length = 0x%x, Recv type = 0x%X\n", + recvlength, recvtype); + } else if (urb->status) { + kfree_skb(skb); + goto rx_exit; + } + + switch (recvtype) { + case CMD_TYPE_DATA: + process_cmdtypedata(recvlength, skb, cardp, priv); + break; + + case CMD_TYPE_REQUEST: + process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); + break; + + case CMD_TYPE_INDICATION: + /* Event handling */ + event = le32_to_cpu(pkt[1]); + lbs_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event); + kfree_skb(skb); + + /* Icky undocumented magic special case */ + if (event & 0xffff0000) { + u32 trycount = (event & 0xffff0000) >> 16; + + lbs_send_tx_feedback(priv, trycount); + } else + lbs_queue_event(priv, event & 0xFF); + break; + + default: + lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n", + recvtype); + kfree_skb(skb); + break; + } + +setup_for_next: + if_usb_submit_rx_urb(cardp); +rx_exit: + lbs_deb_leave(LBS_DEB_USB); +} + +/** + * if_usb_host_to_card - downloads data to FW + * @priv: pointer to &struct lbs_private structure + * @type: type of data + * @payload: pointer to data buffer + * @nb: number of bytes + * returns: 0 for success or negative error code + */ +static int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, + uint8_t *payload, uint16_t nb) +{ + struct if_usb_card *cardp = priv->card; + + lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type); + lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb); + + if (type == MVMS_CMD) { + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); + priv->dnld_sent = DNLD_CMD_SENT; + } else { + *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); + priv->dnld_sent = DNLD_DATA_SENT; + } + + memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); + + return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN); +} + +/** + * if_usb_issue_boot_command - issues Boot command to the Boot2 code + * @cardp: pointer to &if_usb_card + * @ivalue: 1:Boot from FW by USB-Download + * 2:Boot from FW in EEPROM + * returns: 0 for success or negative error code + */ +static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) +{ + struct bootcmd *bootcmd = cardp->ep_out_buf; + + /* Prepare command */ + bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); + bootcmd->cmd = ivalue; + memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); + + /* Issue command */ + usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd)); + + return 0; +} + + +/** + * check_fwfile_format - check the validity of Boot2/FW image + * + * @data: pointer to image + * @totlen: image length + * returns: 0 (good) or 1 (failure) + */ +static int check_fwfile_format(const uint8_t *data, uint32_t totlen) +{ + uint32_t bincmd, exit; + uint32_t blksize, offset, len; + int ret; + + ret = 1; + exit = len = 0; + + do { + struct fwheader *fwh = (void *)data; + + bincmd = le32_to_cpu(fwh->dnldcmd); + blksize = le32_to_cpu(fwh->datalength); + switch (bincmd) { + case FW_HAS_DATA_TO_RECV: + offset = sizeof(struct fwheader) + blksize; + data += offset; + len += offset; + if (len >= totlen) + exit = 1; + break; + case FW_HAS_LAST_BLOCK: + exit = 1; + ret = 0; + break; + default: + exit = 1; + break; + } + } while (!exit); + + if (ret) + pr_err("firmware file format check FAIL\n"); + else + lbs_deb_fw("firmware file format check PASS\n"); + + return ret; +} + +static void if_usb_prog_firmware(struct lbs_private *priv, int ret, + const struct firmware *fw, + const struct firmware *unused) +{ + struct if_usb_card *cardp = priv->card; + int i = 0; + static int reset_count = 10; + + lbs_deb_enter(LBS_DEB_USB); + + if (ret) { + pr_err("failed to find firmware (%d)\n", ret); + goto done; + } + + cardp->fw = fw; + if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { + ret = -EINVAL; + goto done; + } + + /* Cancel any pending usb business */ + usb_kill_urb(cardp->rx_urb); + usb_kill_urb(cardp->tx_urb); + + cardp->fwlastblksent = 0; + cardp->fwdnldover = 0; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + cardp->bootcmdresp = 0; + +restart: + if (if_usb_submit_rx_urb_fwload(cardp) < 0) { + lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); + ret = -EIO; + goto done; + } + + cardp->bootcmdresp = 0; + do { + int j = 0; + i++; + if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); + /* wait for command response */ + do { + j++; + msleep_interruptible(100); + } while (cardp->bootcmdresp == 0 && j < 10); + } while (cardp->bootcmdresp == 0 && i < 5); + + if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) { + /* Return to normal operation */ + ret = -EOPNOTSUPP; + usb_kill_urb(cardp->rx_urb); + usb_kill_urb(cardp->tx_urb); + if (if_usb_submit_rx_urb(cardp) < 0) + ret = -EIO; + goto done; + } else if (cardp->bootcmdresp <= 0) { + if (--reset_count >= 0) { + if_usb_reset_device(cardp); + goto restart; + } + ret = -EIO; + goto done; + } + + i = 0; + + cardp->totalbytes = 0; + cardp->fwlastblksent = 0; + cardp->CRC_OK = 1; + cardp->fwdnldover = 0; + cardp->fwseqnum = -1; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + + /* Send the first firmware packet... */ + if_usb_send_fw_pkt(cardp); + + /* ... and wait for the process to complete */ + wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover); + + del_timer_sync(&cardp->fw_timeout); + usb_kill_urb(cardp->rx_urb); + + if (!cardp->fwdnldover) { + pr_info("failed to load fw, resetting device!\n"); + if (--reset_count >= 0) { + if_usb_reset_device(cardp); + goto restart; + } + + pr_info("FW download failure, time = %d ms\n", i * 100); + ret = -EIO; + goto done; + } + + cardp->priv->fw_ready = 1; + if_usb_submit_rx_urb(cardp); + + if (lbs_start_card(priv)) + goto done; + + if_usb_setup_firmware(priv); + + /* + * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware. + */ + priv->wol_criteria = EHS_REMOVE_WAKEUP; + if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) + priv->ehs_remove_supported = false; + + done: + cardp->fw = NULL; + lbs_deb_leave(LBS_DEB_USB); +} + + +#ifdef CONFIG_PM +static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct if_usb_card *cardp = usb_get_intfdata(intf); + struct lbs_private *priv = cardp->priv; + int ret; + + lbs_deb_enter(LBS_DEB_USB); + + if (priv->psstate != PS_STATE_FULL_POWER) { + ret = -1; + goto out; + } + +#ifdef CONFIG_OLPC + if (machine_is_olpc()) { + if (priv->wol_criteria == EHS_REMOVE_WAKEUP) + olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN); + else + olpc_ec_wakeup_set(EC_SCI_SRC_WLAN); + } +#endif + + ret = lbs_suspend(priv); + if (ret) + goto out; + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + out: + lbs_deb_leave(LBS_DEB_USB); + return ret; +} + +static int if_usb_resume(struct usb_interface *intf) +{ + struct if_usb_card *cardp = usb_get_intfdata(intf); + struct lbs_private *priv = cardp->priv; + + lbs_deb_enter(LBS_DEB_USB); + + if_usb_submit_rx_urb(cardp); + + lbs_resume(priv); + + lbs_deb_leave(LBS_DEB_USB); + return 0; +} +#else +#define if_usb_suspend NULL +#define if_usb_resume NULL +#endif + +static struct usb_driver if_usb_driver = { + .name = DRV_NAME, + .probe = if_usb_probe, + .disconnect = if_usb_disconnect, + .id_table = if_usb_table, + .suspend = if_usb_suspend, + .resume = if_usb_resume, + .reset_resume = if_usb_resume, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(if_usb_driver); + +MODULE_DESCRIPTION("8388 USB WLAN Driver"); +MODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/marvell/libertas/if_usb.h b/drivers/net/wireless/marvell/libertas/if_usb.h new file mode 100644 index 000000000000..6e42eac331de --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/if_usb.h @@ -0,0 +1,106 @@ +#ifndef _LBS_IF_USB_H +#define _LBS_IF_USB_H + +#include +#include + +struct lbs_private; + +/* + * This file contains definition for USB interface. + */ +#define CMD_TYPE_REQUEST 0xF00DFACE +#define CMD_TYPE_DATA 0xBEADC0DE +#define CMD_TYPE_INDICATION 0xBEEFFACE + +#define IPFIELD_ALIGN_OFFSET 2 + +#define BOOT_CMD_FW_BY_USB 0x01 +#define BOOT_CMD_FW_IN_EEPROM 0x02 +#define BOOT_CMD_UPDATE_BOOT2 0x03 +#define BOOT_CMD_UPDATE_FW 0x04 +#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* LVRM */ + +struct bootcmd +{ + __le32 magic; + uint8_t cmd; + uint8_t pad[11]; +}; + +#define BOOT_CMD_RESP_OK 0x0001 +#define BOOT_CMD_RESP_FAIL 0x0000 +#define BOOT_CMD_RESP_NOT_SUPPORTED 0x0002 + +struct bootcmdresp +{ + __le32 magic; + uint8_t cmd; + uint8_t result; + uint8_t pad[2]; +}; + +/* USB card description structure*/ +struct if_usb_card { + struct usb_device *udev; + uint32_t model; /* MODEL_* */ + struct urb *rx_urb, *tx_urb; + struct lbs_private *priv; + + struct sk_buff *rx_skb; + + uint8_t ep_in; + uint8_t ep_out; + + /* bootcmdresp == 0 means command is pending + * bootcmdresp < 0 means error + * bootcmdresp > 0 is a BOOT_CMD_RESP_* from firmware + */ + int8_t bootcmdresp; + + int ep_in_size; + + void *ep_out_buf; + int ep_out_size; + + const struct firmware *fw; + struct timer_list fw_timeout; + wait_queue_head_t fw_wq; + uint32_t fwseqnum; + uint32_t totalbytes; + uint32_t fwlastblksent; + uint8_t CRC_OK; + uint8_t fwdnldover; + uint8_t fwfinalblk; + uint8_t surprise_removed; + + __le16 boot2_version; +}; + +/* fwheader */ +struct fwheader { + __le32 dnldcmd; + __le32 baseaddr; + __le32 datalength; + __le32 CRC; +}; + +#define FW_MAX_DATA_BLK_SIZE 600 +/* FWData */ +struct fwdata { + struct fwheader hdr; + __le32 seqnum; + uint8_t data[0]; +}; + +/* fwsyncheader */ +struct fwsyncheader { + __le32 cmd; + __le32 seqnum; +}; + +#define FW_HAS_DATA_TO_RECV 0x00000001 +#define FW_HAS_LAST_BLOCK 0x00000004 + + +#endif diff --git a/drivers/net/wireless/marvell/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c new file mode 100644 index 000000000000..8079560f4965 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/main.c @@ -0,0 +1,1225 @@ +/* + * This file contains the major functions in WLAN + * driver. It includes init, exit, open, close and main + * thread etc.. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "host.h" +#include "decl.h" +#include "dev.h" +#include "cfg.h" +#include "debugfs.h" +#include "cmd.h" +#include "mesh.h" + +#define DRIVER_RELEASE_VERSION "323.p0" +const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION +#ifdef DEBUG + "-dbg" +#endif + ""; + + +/* Module parameters */ +unsigned int lbs_debug; +EXPORT_SYMBOL_GPL(lbs_debug); +module_param_named(libertas_debug, lbs_debug, int, 0644); + +unsigned int lbs_disablemesh; +EXPORT_SYMBOL_GPL(lbs_disablemesh); +module_param_named(libertas_disablemesh, lbs_disablemesh, int, 0644); + + +/* + * This global structure is used to send the confirm_sleep command as + * fast as possible down to the firmware. + */ +struct cmd_confirm_sleep confirm_sleep; + + +/* + * the table to keep region code + */ +u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] = + { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; + +/* + * FW rate table. FW refers to rates by their index in this table, not by the + * rate value itself. Values of 0x00 are + * reserved positions. + */ +static u8 fw_data_rates[MAX_RATES] = + { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, + 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 +}; + +/** + * lbs_fw_index_to_data_rate - use index to get the data rate + * + * @idx: The index of data rate + * returns: data rate or 0 + */ +u32 lbs_fw_index_to_data_rate(u8 idx) +{ + if (idx >= sizeof(fw_data_rates)) + idx = 0; + return fw_data_rates[idx]; +} + +/** + * lbs_data_rate_to_fw_index - use rate to get the index + * + * @rate: data rate + * returns: index or 0 + */ +u8 lbs_data_rate_to_fw_index(u32 rate) +{ + u8 i; + + if (!rate) + return 0; + + for (i = 0; i < sizeof(fw_data_rates); i++) { + if (rate == fw_data_rates[i]) + return i; + } + return 0; +} + +int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type) +{ + int ret = 0; + + switch (type) { + case NL80211_IFTYPE_MONITOR: + ret = lbs_set_monitor_mode(priv, 1); + break; + case NL80211_IFTYPE_STATION: + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) + ret = lbs_set_monitor_mode(priv, 0); + if (!ret) + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1); + break; + case NL80211_IFTYPE_ADHOC: + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) + ret = lbs_set_monitor_mode(priv, 0); + if (!ret) + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2); + break; + default: + ret = -ENOTSUPP; + } + return ret; +} + +int lbs_start_iface(struct lbs_private *priv) +{ + struct cmd_ds_802_11_mac_address cmd; + int ret; + + if (priv->power_restore) { + ret = priv->power_restore(priv); + if (ret) + return ret; + } + + cmd.hdr.size = cpu_to_le16(sizeof(cmd)); + cmd.action = cpu_to_le16(CMD_ACT_SET); + memcpy(cmd.macadd, priv->current_addr, ETH_ALEN); + + ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd); + if (ret) { + lbs_deb_net("set MAC address failed\n"); + goto err; + } + + ret = lbs_set_iface_type(priv, priv->wdev->iftype); + if (ret) { + lbs_deb_net("set iface type failed\n"); + goto err; + } + + ret = lbs_set_11d_domain_info(priv); + if (ret) { + lbs_deb_net("set 11d domain info failed\n"); + goto err; + } + + lbs_update_channel(priv); + + priv->iface_running = true; + return 0; + +err: + if (priv->power_save) + priv->power_save(priv); + return ret; +} + +/** + * lbs_dev_open - open the ethX interface + * + * @dev: A pointer to &net_device structure + * returns: 0 or -EBUSY if monitor mode active + */ +static int lbs_dev_open(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + int ret = 0; + + lbs_deb_enter(LBS_DEB_NET); + if (!priv->iface_running) { + ret = lbs_start_iface(priv); + if (ret) + goto out; + } + + spin_lock_irq(&priv->driver_lock); + + netif_carrier_off(dev); + + if (!priv->tx_pending_len) + netif_wake_queue(dev); + + spin_unlock_irq(&priv->driver_lock); + +out: + lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); + return ret; +} + +static bool lbs_command_queue_empty(struct lbs_private *priv) +{ + unsigned long flags; + bool ret; + spin_lock_irqsave(&priv->driver_lock, flags); + ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq); + spin_unlock_irqrestore(&priv->driver_lock, flags); + return ret; +} + +int lbs_stop_iface(struct lbs_private *priv) +{ + unsigned long flags; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MAIN); + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->iface_running = false; + kfree_skb(priv->currenttxskb); + priv->currenttxskb = NULL; + priv->tx_pending_len = 0; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + cancel_work_sync(&priv->mcast_work); + del_timer_sync(&priv->tx_lockup_timer); + + /* Disable command processing, and wait for all commands to complete */ + lbs_deb_main("waiting for commands to complete\n"); + wait_event(priv->waitq, lbs_command_queue_empty(priv)); + lbs_deb_main("all commands completed\n"); + + if (priv->power_save) + ret = priv->power_save(priv); + + lbs_deb_leave(LBS_DEB_MAIN); + return ret; +} + +/** + * lbs_eth_stop - close the ethX interface + * + * @dev: A pointer to &net_device structure + * returns: 0 + */ +static int lbs_eth_stop(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + + lbs_deb_enter(LBS_DEB_NET); + + if (priv->connect_status == LBS_CONNECTED) + lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING); + + spin_lock_irq(&priv->driver_lock); + netif_stop_queue(dev); + spin_unlock_irq(&priv->driver_lock); + + lbs_update_mcast(priv); + cancel_delayed_work_sync(&priv->scan_work); + if (priv->scan_req) + lbs_scan_done(priv); + + netif_carrier_off(priv->dev); + + if (!lbs_iface_active(priv)) + lbs_stop_iface(priv); + + lbs_deb_leave(LBS_DEB_NET); + return 0; +} + +void lbs_host_to_card_done(struct lbs_private *priv) +{ + unsigned long flags; + + lbs_deb_enter(LBS_DEB_THREAD); + + spin_lock_irqsave(&priv->driver_lock, flags); + del_timer(&priv->tx_lockup_timer); + + priv->dnld_sent = DNLD_RES_RECEIVED; + + /* Wake main thread if commands are pending */ + if (!priv->cur_cmd || priv->tx_pending_len > 0) { + if (!priv->wakeup_dev_required) + wake_up(&priv->waitq); + } + + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_THREAD); +} +EXPORT_SYMBOL_GPL(lbs_host_to_card_done); + +int lbs_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + struct lbs_private *priv = dev->ml_priv; + struct sockaddr *phwaddr = addr; + + lbs_deb_enter(LBS_DEB_NET); + + /* + * Can only set MAC address when all interfaces are down, to be written + * to the hardware when one of them is brought up. + */ + if (lbs_iface_active(priv)) + return -EBUSY; + + /* In case it was called from the mesh device */ + dev = priv->dev; + + memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN); + memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN); + if (priv->mesh_dev) + memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN); + + lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); + return ret; +} + + +static inline int mac_in_list(unsigned char *list, int list_len, + unsigned char *mac) +{ + while (list_len) { + if (!memcmp(list, mac, ETH_ALEN)) + return 1; + list += ETH_ALEN; + list_len--; + } + return 0; +} + + +static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, + struct net_device *dev, int nr_addrs) +{ + int i = nr_addrs; + struct netdev_hw_addr *ha; + int cnt; + + if ((dev->flags & (IFF_UP|IFF_MULTICAST)) != (IFF_UP|IFF_MULTICAST)) + return nr_addrs; + + netif_addr_lock_bh(dev); + cnt = netdev_mc_count(dev); + netdev_for_each_mc_addr(ha, dev) { + if (mac_in_list(cmd->maclist, nr_addrs, ha->addr)) { + lbs_deb_net("mcast address %s:%pM skipped\n", dev->name, + ha->addr); + cnt--; + continue; + } + + if (i == MRVDRV_MAX_MULTICAST_LIST_SIZE) + break; + memcpy(&cmd->maclist[6*i], ha->addr, ETH_ALEN); + lbs_deb_net("mcast address %s:%pM added to filter\n", dev->name, + ha->addr); + i++; + cnt--; + } + netif_addr_unlock_bh(dev); + if (cnt) + return -EOVERFLOW; + + return i; +} + +void lbs_update_mcast(struct lbs_private *priv) +{ + struct cmd_ds_mac_multicast_adr mcast_cmd; + int dev_flags = 0; + int nr_addrs; + int old_mac_control = priv->mac_control; + + lbs_deb_enter(LBS_DEB_NET); + + if (netif_running(priv->dev)) + dev_flags |= priv->dev->flags; + if (priv->mesh_dev && netif_running(priv->mesh_dev)) + dev_flags |= priv->mesh_dev->flags; + + if (dev_flags & IFF_PROMISC) { + priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE; + priv->mac_control &= ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE | + CMD_ACT_MAC_MULTICAST_ENABLE); + goto out_set_mac_control; + } else if (dev_flags & IFF_ALLMULTI) { + do_allmulti: + priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; + priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | + CMD_ACT_MAC_MULTICAST_ENABLE); + goto out_set_mac_control; + } + + /* Once for priv->dev, again for priv->mesh_dev if it exists */ + nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->dev, 0); + if (nr_addrs >= 0 && priv->mesh_dev) + nr_addrs = lbs_add_mcast_addrs(&mcast_cmd, priv->mesh_dev, nr_addrs); + if (nr_addrs < 0) + goto do_allmulti; + + if (nr_addrs) { + int size = offsetof(struct cmd_ds_mac_multicast_adr, + maclist[6*nr_addrs]); + + mcast_cmd.action = cpu_to_le16(CMD_ACT_SET); + mcast_cmd.hdr.size = cpu_to_le16(size); + mcast_cmd.nr_of_adrs = cpu_to_le16(nr_addrs); + + lbs_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &mcast_cmd.hdr, size); + + priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; + } else + priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; + + priv->mac_control &= ~(CMD_ACT_MAC_PROMISCUOUS_ENABLE | + CMD_ACT_MAC_ALL_MULTICAST_ENABLE); + out_set_mac_control: + if (priv->mac_control != old_mac_control) + lbs_set_mac_control(priv); + + lbs_deb_leave(LBS_DEB_NET); +} + +static void lbs_set_mcast_worker(struct work_struct *work) +{ + struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work); + lbs_update_mcast(priv); +} + +void lbs_set_multicast_list(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + + schedule_work(&priv->mcast_work); +} + +/** + * lbs_thread - handles the major jobs in the LBS driver. + * It handles all events generated by firmware, RX data received + * from firmware and TX data sent from kernel. + * + * @data: A pointer to &lbs_thread structure + * returns: 0 + */ +static int lbs_thread(void *data) +{ + struct net_device *dev = data; + struct lbs_private *priv = dev->ml_priv; + wait_queue_t wait; + + lbs_deb_enter(LBS_DEB_THREAD); + + init_waitqueue_entry(&wait, current); + + for (;;) { + int shouldsleep; + u8 resp_idx; + + lbs_deb_thread("1: currenttxskb %p, dnld_sent %d\n", + priv->currenttxskb, priv->dnld_sent); + + add_wait_queue(&priv->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irq(&priv->driver_lock); + + if (kthread_should_stop()) + shouldsleep = 0; /* Bye */ + else if (priv->surpriseremoved) + shouldsleep = 1; /* We need to wait until we're _told_ to die */ + else if (priv->psstate == PS_STATE_SLEEP) + shouldsleep = 1; /* Sleep mode. Nothing we can do till it wakes */ + else if (priv->cmd_timed_out) + shouldsleep = 0; /* Command timed out. Recover */ + else if (!priv->fw_ready) + shouldsleep = 1; /* Firmware not ready. We're waiting for it */ + else if (priv->dnld_sent) + shouldsleep = 1; /* Something is en route to the device already */ + else if (priv->tx_pending_len > 0) + shouldsleep = 0; /* We've a packet to send */ + else if (priv->resp_len[priv->resp_idx]) + shouldsleep = 0; /* We have a command response */ + else if (priv->cur_cmd) + shouldsleep = 1; /* Can't send a command; one already running */ + else if (!list_empty(&priv->cmdpendingq) && + !(priv->wakeup_dev_required)) + shouldsleep = 0; /* We have a command to send */ + else if (kfifo_len(&priv->event_fifo)) + shouldsleep = 0; /* We have an event to process */ + else + shouldsleep = 1; /* No command */ + + if (shouldsleep) { + lbs_deb_thread("sleeping, connect_status %d, " + "psmode %d, psstate %d\n", + priv->connect_status, + priv->psmode, priv->psstate); + spin_unlock_irq(&priv->driver_lock); + schedule(); + } else + spin_unlock_irq(&priv->driver_lock); + + lbs_deb_thread("2: currenttxskb %p, dnld_send %d\n", + priv->currenttxskb, priv->dnld_sent); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&priv->waitq, &wait); + + lbs_deb_thread("3: currenttxskb %p, dnld_sent %d\n", + priv->currenttxskb, priv->dnld_sent); + + if (kthread_should_stop()) { + lbs_deb_thread("break from main thread\n"); + break; + } + + if (priv->surpriseremoved) { + lbs_deb_thread("adapter removed; waiting to die...\n"); + continue; + } + + lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n", + priv->currenttxskb, priv->dnld_sent); + + /* Process any pending command response */ + spin_lock_irq(&priv->driver_lock); + resp_idx = priv->resp_idx; + if (priv->resp_len[resp_idx]) { + spin_unlock_irq(&priv->driver_lock); + lbs_process_command_response(priv, + priv->resp_buf[resp_idx], + priv->resp_len[resp_idx]); + spin_lock_irq(&priv->driver_lock); + priv->resp_len[resp_idx] = 0; + } + spin_unlock_irq(&priv->driver_lock); + + /* Process hardware events, e.g. card removed, link lost */ + spin_lock_irq(&priv->driver_lock); + while (kfifo_len(&priv->event_fifo)) { + u32 event; + + if (kfifo_out(&priv->event_fifo, + (unsigned char *) &event, sizeof(event)) != + sizeof(event)) + break; + spin_unlock_irq(&priv->driver_lock); + lbs_process_event(priv, event); + spin_lock_irq(&priv->driver_lock); + } + spin_unlock_irq(&priv->driver_lock); + + if (priv->wakeup_dev_required) { + lbs_deb_thread("Waking up device...\n"); + /* Wake up device */ + if (priv->exit_deep_sleep(priv)) + lbs_deb_thread("Wakeup device failed\n"); + continue; + } + + /* command timeout stuff */ + if (priv->cmd_timed_out && priv->cur_cmd) { + struct cmd_ctrl_node *cmdnode = priv->cur_cmd; + + netdev_info(dev, "Timeout submitting command 0x%04x\n", + le16_to_cpu(cmdnode->cmdbuf->command)); + lbs_complete_command(priv, cmdnode, -ETIMEDOUT); + + /* Reset card, but only when it isn't in the process + * of being shutdown anyway. */ + if (!dev->dismantle && priv->reset_card) + priv->reset_card(priv); + } + priv->cmd_timed_out = 0; + + if (!priv->fw_ready) + continue; + + /* Check if we need to confirm Sleep Request received previously */ + if (priv->psstate == PS_STATE_PRE_SLEEP && + !priv->dnld_sent && !priv->cur_cmd) { + if (priv->connect_status == LBS_CONNECTED) { + lbs_deb_thread("pre-sleep, currenttxskb %p, " + "dnld_sent %d, cur_cmd %p\n", + priv->currenttxskb, priv->dnld_sent, + priv->cur_cmd); + + lbs_ps_confirm_sleep(priv); + } else { + /* workaround for firmware sending + * deauth/linkloss event immediately + * after sleep request; remove this + * after firmware fixes it + */ + priv->psstate = PS_STATE_AWAKE; + netdev_alert(dev, + "ignore PS_SleepConfirm in non-connected state\n"); + } + } + + /* The PS state is changed during processing of Sleep Request + * event above + */ + if ((priv->psstate == PS_STATE_SLEEP) || + (priv->psstate == PS_STATE_PRE_SLEEP)) + continue; + + if (priv->is_deep_sleep) + continue; + + /* Execute the next command */ + if (!priv->dnld_sent && !priv->cur_cmd) + lbs_execute_next_command(priv); + + spin_lock_irq(&priv->driver_lock); + if (!priv->dnld_sent && priv->tx_pending_len > 0) { + int ret = priv->hw_host_to_card(priv, MVMS_DAT, + priv->tx_pending_buf, + priv->tx_pending_len); + if (ret) { + lbs_deb_tx("host_to_card failed %d\n", ret); + priv->dnld_sent = DNLD_RES_RECEIVED; + } else { + mod_timer(&priv->tx_lockup_timer, + jiffies + (HZ * 5)); + } + priv->tx_pending_len = 0; + if (!priv->currenttxskb) { + /* We can wake the queues immediately if we aren't + waiting for TX feedback */ + if (priv->connect_status == LBS_CONNECTED) + netif_wake_queue(priv->dev); + if (priv->mesh_dev && + netif_running(priv->mesh_dev)) + netif_wake_queue(priv->mesh_dev); + } + } + spin_unlock_irq(&priv->driver_lock); + } + + del_timer(&priv->command_timer); + del_timer(&priv->tx_lockup_timer); + del_timer(&priv->auto_deepsleep_timer); + + lbs_deb_leave(LBS_DEB_THREAD); + return 0; +} + +/** + * lbs_setup_firmware - gets the HW spec from the firmware and sets + * some basic parameters + * + * @priv: A pointer to &struct lbs_private structure + * returns: 0 or -1 + */ +static int lbs_setup_firmware(struct lbs_private *priv) +{ + int ret = -1; + s16 curlevel = 0, minlevel = 0, maxlevel = 0; + + lbs_deb_enter(LBS_DEB_FW); + + /* Read MAC address from firmware */ + eth_broadcast_addr(priv->current_addr); + ret = lbs_update_hw_spec(priv); + if (ret) + goto done; + + /* Read power levels if available */ + ret = lbs_get_tx_power(priv, &curlevel, &minlevel, &maxlevel); + if (ret == 0) { + priv->txpower_cur = curlevel; + priv->txpower_min = minlevel; + priv->txpower_max = maxlevel; + } + + /* Send cmd to FW to enable 11D function */ + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); + if (ret) + goto done; + + ret = lbs_set_mac_control_sync(priv); +done: + lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); + return ret; +} + +int lbs_suspend(struct lbs_private *priv) +{ + int ret; + + lbs_deb_enter(LBS_DEB_FW); + + if (priv->is_deep_sleep) { + ret = lbs_set_deep_sleep(priv, 0); + if (ret) { + netdev_err(priv->dev, + "deep sleep cancellation failed: %d\n", ret); + return ret; + } + priv->deep_sleep_required = 1; + } + + ret = lbs_set_host_sleep(priv, 1); + + netif_device_detach(priv->dev); + if (priv->mesh_dev) + netif_device_detach(priv->mesh_dev); + + lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(lbs_suspend); + +int lbs_resume(struct lbs_private *priv) +{ + int ret; + + lbs_deb_enter(LBS_DEB_FW); + + ret = lbs_set_host_sleep(priv, 0); + + netif_device_attach(priv->dev); + if (priv->mesh_dev) + netif_device_attach(priv->mesh_dev); + + if (priv->deep_sleep_required) { + priv->deep_sleep_required = 0; + ret = lbs_set_deep_sleep(priv, 1); + if (ret) + netdev_err(priv->dev, + "deep sleep activation failed: %d\n", ret); + } + + if (priv->setup_fw_on_resume) + ret = lbs_setup_firmware(priv); + + lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(lbs_resume); + +/** + * lbs_cmd_timeout_handler - handles the timeout of command sending. + * It will re-send the same command again. + * + * @data: &struct lbs_private pointer + */ +static void lbs_cmd_timeout_handler(unsigned long data) +{ + struct lbs_private *priv = (struct lbs_private *)data; + unsigned long flags; + + lbs_deb_enter(LBS_DEB_CMD); + spin_lock_irqsave(&priv->driver_lock, flags); + + if (!priv->cur_cmd) + goto out; + + netdev_info(priv->dev, "command 0x%04x timed out\n", + le16_to_cpu(priv->cur_cmd->cmdbuf->command)); + + priv->cmd_timed_out = 1; + + /* + * If the device didn't even acknowledge the command, reset the state + * so that we don't block all future commands due to this one timeout. + */ + if (priv->dnld_sent == DNLD_CMD_SENT) + priv->dnld_sent = DNLD_RES_RECEIVED; + + wake_up(&priv->waitq); +out: + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_CMD); +} + +/** + * lbs_tx_lockup_handler - handles the timeout of the passing of TX frames + * to the hardware. This is known to frequently happen with SD8686 when + * waking up after a Wake-on-WLAN-triggered resume. + * + * @data: &struct lbs_private pointer + */ +static void lbs_tx_lockup_handler(unsigned long data) +{ + struct lbs_private *priv = (struct lbs_private *)data; + unsigned long flags; + + lbs_deb_enter(LBS_DEB_TX); + spin_lock_irqsave(&priv->driver_lock, flags); + + netdev_info(priv->dev, "TX lockup detected\n"); + if (priv->reset_card) + priv->reset_card(priv); + + priv->dnld_sent = DNLD_RES_RECEIVED; + wake_up_interruptible(&priv->waitq); + + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_TX); +} + +/** + * auto_deepsleep_timer_fn - put the device back to deep sleep mode when + * timer expires and no activity (command, event, data etc.) is detected. + * @data: &struct lbs_private pointer + * returns: N/A + */ +static void auto_deepsleep_timer_fn(unsigned long data) +{ + struct lbs_private *priv = (struct lbs_private *)data; + + lbs_deb_enter(LBS_DEB_CMD); + + if (priv->is_activity_detected) { + priv->is_activity_detected = 0; + } else { + if (priv->is_auto_deep_sleep_enabled && + (!priv->wakeup_dev_required) && + (priv->connect_status != LBS_CONNECTED)) { + struct cmd_header cmd; + + lbs_deb_main("Entering auto deep sleep mode...\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.size = cpu_to_le16(sizeof(cmd)); + lbs_cmd_async(priv, CMD_802_11_DEEP_SLEEP, &cmd, + sizeof(cmd)); + } + } + mod_timer(&priv->auto_deepsleep_timer , jiffies + + (priv->auto_deep_sleep_timeout * HZ)/1000); + lbs_deb_leave(LBS_DEB_CMD); +} + +int lbs_enter_auto_deep_sleep(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_SDIO); + + priv->is_auto_deep_sleep_enabled = 1; + if (priv->is_deep_sleep) + priv->wakeup_dev_required = 1; + mod_timer(&priv->auto_deepsleep_timer , + jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000); + + lbs_deb_leave(LBS_DEB_SDIO); + return 0; +} + +int lbs_exit_auto_deep_sleep(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_SDIO); + + priv->is_auto_deep_sleep_enabled = 0; + priv->auto_deep_sleep_timeout = 0; + del_timer(&priv->auto_deepsleep_timer); + + lbs_deb_leave(LBS_DEB_SDIO); + return 0; +} + +static int lbs_init_adapter(struct lbs_private *priv) +{ + int ret; + + lbs_deb_enter(LBS_DEB_MAIN); + + eth_broadcast_addr(priv->current_addr); + + priv->connect_status = LBS_DISCONNECTED; + priv->channel = DEFAULT_AD_HOC_CHANNEL; + priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON; + priv->radio_on = 1; + priv->psmode = LBS802_11POWERMODECAM; + priv->psstate = PS_STATE_FULL_POWER; + priv->is_deep_sleep = 0; + priv->is_auto_deep_sleep_enabled = 0; + priv->deep_sleep_required = 0; + priv->wakeup_dev_required = 0; + init_waitqueue_head(&priv->ds_awake_q); + init_waitqueue_head(&priv->scan_q); + priv->authtype_auto = 1; + priv->is_host_sleep_configured = 0; + priv->is_host_sleep_activated = 0; + init_waitqueue_head(&priv->host_sleep_q); + init_waitqueue_head(&priv->fw_waitq); + mutex_init(&priv->lock); + + setup_timer(&priv->command_timer, lbs_cmd_timeout_handler, + (unsigned long)priv); + setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler, + (unsigned long)priv); + setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, + (unsigned long)priv); + + INIT_LIST_HEAD(&priv->cmdfreeq); + INIT_LIST_HEAD(&priv->cmdpendingq); + + spin_lock_init(&priv->driver_lock); + + /* Allocate the command buffers */ + if (lbs_allocate_cmd_buffer(priv)) { + pr_err("Out of memory allocating command buffers\n"); + ret = -ENOMEM; + goto out; + } + priv->resp_idx = 0; + priv->resp_len[0] = priv->resp_len[1] = 0; + + /* Create the event FIFO */ + ret = kfifo_alloc(&priv->event_fifo, sizeof(u32) * 16, GFP_KERNEL); + if (ret) { + pr_err("Out of memory allocating event FIFO buffer\n"); + goto out; + } + +out: + lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); + + return ret; +} + +static void lbs_free_adapter(struct lbs_private *priv) +{ + lbs_deb_enter(LBS_DEB_MAIN); + + lbs_free_cmd_buffer(priv); + kfifo_free(&priv->event_fifo); + del_timer(&priv->command_timer); + del_timer(&priv->tx_lockup_timer); + del_timer(&priv->auto_deepsleep_timer); + + lbs_deb_leave(LBS_DEB_MAIN); +} + +static const struct net_device_ops lbs_netdev_ops = { + .ndo_open = lbs_dev_open, + .ndo_stop = lbs_eth_stop, + .ndo_start_xmit = lbs_hard_start_xmit, + .ndo_set_mac_address = lbs_set_mac_address, + .ndo_set_rx_mode = lbs_set_multicast_list, + .ndo_change_mtu = eth_change_mtu, + .ndo_validate_addr = eth_validate_addr, +}; + +/** + * lbs_add_card - adds the card. It will probe the + * card, allocate the lbs_priv and initialize the device. + * + * @card: A pointer to card + * @dmdev: A pointer to &struct device + * returns: A pointer to &struct lbs_private structure + */ +struct lbs_private *lbs_add_card(void *card, struct device *dmdev) +{ + struct net_device *dev; + struct wireless_dev *wdev; + struct lbs_private *priv = NULL; + + lbs_deb_enter(LBS_DEB_MAIN); + + /* Allocate an Ethernet device and register it */ + wdev = lbs_cfg_alloc(dmdev); + if (IS_ERR(wdev)) { + pr_err("cfg80211 init failed\n"); + goto done; + } + + wdev->iftype = NL80211_IFTYPE_STATION; + priv = wdev_priv(wdev); + priv->wdev = wdev; + + if (lbs_init_adapter(priv)) { + pr_err("failed to initialize adapter structure\n"); + goto err_wdev; + } + + dev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup); + if (!dev) { + dev_err(dmdev, "no memory for network device instance\n"); + goto err_adapter; + } + + dev->ieee80211_ptr = wdev; + dev->ml_priv = priv; + SET_NETDEV_DEV(dev, dmdev); + wdev->netdev = dev; + priv->dev = dev; + + dev->netdev_ops = &lbs_netdev_ops; + dev->watchdog_timeo = 5 * HZ; + dev->ethtool_ops = &lbs_ethtool_ops; + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + + priv->card = card; + + strcpy(dev->name, "wlan%d"); + + lbs_deb_thread("Starting main thread...\n"); + init_waitqueue_head(&priv->waitq); + priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); + if (IS_ERR(priv->main_thread)) { + lbs_deb_thread("Error creating main thread.\n"); + goto err_ndev; + } + + priv->work_thread = create_singlethread_workqueue("lbs_worker"); + INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); + + priv->wol_criteria = EHS_REMOVE_WAKEUP; + priv->wol_gpio = 0xff; + priv->wol_gap = 20; + priv->ehs_remove_supported = true; + + goto done; + + err_ndev: + free_netdev(dev); + + err_adapter: + lbs_free_adapter(priv); + + err_wdev: + lbs_cfg_free(priv); + + priv = NULL; + +done: + lbs_deb_leave_args(LBS_DEB_MAIN, "priv %p", priv); + return priv; +} +EXPORT_SYMBOL_GPL(lbs_add_card); + + +void lbs_remove_card(struct lbs_private *priv) +{ + struct net_device *dev = priv->dev; + + lbs_deb_enter(LBS_DEB_MAIN); + + lbs_remove_mesh(priv); + + if (priv->wiphy_registered) + lbs_scan_deinit(priv); + + lbs_wait_for_firmware_load(priv); + + /* worker thread destruction blocks on the in-flight command which + * should have been cleared already in lbs_stop_card(). + */ + lbs_deb_main("destroying worker thread\n"); + destroy_workqueue(priv->work_thread); + lbs_deb_main("done destroying worker thread\n"); + + if (priv->psmode == LBS802_11POWERMODEMAX_PSP) { + priv->psmode = LBS802_11POWERMODECAM; + lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, true); + } + + if (priv->is_deep_sleep) { + priv->is_deep_sleep = 0; + wake_up_interruptible(&priv->ds_awake_q); + } + + priv->is_host_sleep_configured = 0; + priv->is_host_sleep_activated = 0; + wake_up_interruptible(&priv->host_sleep_q); + + /* Stop the thread servicing the interrupts */ + priv->surpriseremoved = 1; + kthread_stop(priv->main_thread); + + lbs_free_adapter(priv); + lbs_cfg_free(priv); + free_netdev(dev); + + lbs_deb_leave(LBS_DEB_MAIN); +} +EXPORT_SYMBOL_GPL(lbs_remove_card); + + +int lbs_rtap_supported(struct lbs_private *priv) +{ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) + return 1; + + /* newer firmware use a capability mask */ + return ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && + (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)); +} + + +int lbs_start_card(struct lbs_private *priv) +{ + struct net_device *dev = priv->dev; + int ret = -1; + + lbs_deb_enter(LBS_DEB_MAIN); + + /* poke the firmware */ + ret = lbs_setup_firmware(priv); + if (ret) + goto done; + + if (!lbs_disablemesh) + lbs_init_mesh(priv); + else + pr_info("%s: mesh disabled\n", dev->name); + + if (lbs_cfg_register(priv)) { + pr_err("cannot register device\n"); + goto done; + } + + if (lbs_mesh_activated(priv)) + lbs_start_mesh(priv); + + lbs_debugfs_init_one(priv, dev); + + netdev_info(dev, "Marvell WLAN 802.11 adapter\n"); + + ret = 0; + +done: + lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(lbs_start_card); + + +void lbs_stop_card(struct lbs_private *priv) +{ + struct net_device *dev; + + lbs_deb_enter(LBS_DEB_MAIN); + + if (!priv) + goto out; + dev = priv->dev; + + /* If the netdev isn't registered, it means that lbs_start_card() was + * never called so we have nothing to do here. */ + if (dev->reg_state != NETREG_REGISTERED) + goto out; + + netif_stop_queue(dev); + netif_carrier_off(dev); + + lbs_debugfs_remove_one(priv); + lbs_deinit_mesh(priv); + unregister_netdev(dev); + +out: + lbs_deb_leave(LBS_DEB_MAIN); +} +EXPORT_SYMBOL_GPL(lbs_stop_card); + + +void lbs_queue_event(struct lbs_private *priv, u32 event) +{ + unsigned long flags; + + lbs_deb_enter(LBS_DEB_THREAD); + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->psstate == PS_STATE_SLEEP) + priv->psstate = PS_STATE_AWAKE; + + kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32)); + + wake_up(&priv->waitq); + + spin_unlock_irqrestore(&priv->driver_lock, flags); + lbs_deb_leave(LBS_DEB_THREAD); +} +EXPORT_SYMBOL_GPL(lbs_queue_event); + +void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx) +{ + lbs_deb_enter(LBS_DEB_THREAD); + + if (priv->psstate == PS_STATE_SLEEP) + priv->psstate = PS_STATE_AWAKE; + + /* Swap buffers by flipping the response index */ + BUG_ON(resp_idx > 1); + priv->resp_idx = resp_idx; + + wake_up(&priv->waitq); + + lbs_deb_leave(LBS_DEB_THREAD); +} +EXPORT_SYMBOL_GPL(lbs_notify_command_response); + +static int __init lbs_init_module(void) +{ + lbs_deb_enter(LBS_DEB_MAIN); + memset(&confirm_sleep, 0, sizeof(confirm_sleep)); + confirm_sleep.hdr.command = cpu_to_le16(CMD_802_11_PS_MODE); + confirm_sleep.hdr.size = cpu_to_le16(sizeof(confirm_sleep)); + confirm_sleep.action = cpu_to_le16(PS_MODE_ACTION_SLEEP_CONFIRMED); + lbs_debugfs_init(); + lbs_deb_leave(LBS_DEB_MAIN); + return 0; +} + +static void __exit lbs_exit_module(void) +{ + lbs_deb_enter(LBS_DEB_MAIN); + lbs_debugfs_remove(); + lbs_deb_leave(LBS_DEB_MAIN); +} + +module_init(lbs_init_module); +module_exit(lbs_exit_module); + +MODULE_DESCRIPTION("Libertas WLAN Driver Library"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/marvell/libertas/mesh.c b/drivers/net/wireless/marvell/libertas/mesh.c new file mode 100644 index 000000000000..d0c881dd5846 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/mesh.c @@ -0,0 +1,1187 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mesh.h" +#include "decl.h" +#include "cmd.h" + + +static int lbs_add_mesh(struct lbs_private *priv); + +/*************************************************************************** + * Mesh command handling + */ + +static int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action, + struct cmd_ds_mesh_access *cmd) +{ + int ret; + + lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action); + + cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS); + cmd->hdr.size = cpu_to_le16(sizeof(*cmd)); + cmd->hdr.result = 0; + + cmd->action = cpu_to_le16(cmd_action); + + ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +static int __lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type) +{ + int ret; + u16 command = CMD_MESH_CONFIG_OLD; + + lbs_deb_enter(LBS_DEB_CMD); + + /* + * Command id is 0xac for v10 FW along with mesh interface + * id in bits 14-13-12. + */ + if (priv->mesh_tlv == TLV_TYPE_MESH_ID) + command = CMD_MESH_CONFIG | + (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET); + + cmd->hdr.command = cpu_to_le16(command); + cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config)); + cmd->hdr.result = 0; + + cmd->type = cpu_to_le16(type); + cmd->action = cpu_to_le16(action); + + ret = lbs_cmd_with_response(priv, command, cmd); + + lbs_deb_leave(LBS_DEB_CMD); + return ret; +} + +static int lbs_mesh_config_send(struct lbs_private *priv, + struct cmd_ds_mesh_config *cmd, + uint16_t action, uint16_t type) +{ + int ret; + + if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) + return -EOPNOTSUPP; + + ret = __lbs_mesh_config_send(priv, cmd, action, type); + return ret; +} + +/* This function is the CMD_MESH_CONFIG legacy function. It only handles the + * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG + * are all handled by preparing a struct cmd_ds_mesh_config and passing it to + * lbs_mesh_config_send. + */ +static int lbs_mesh_config(struct lbs_private *priv, uint16_t action, + uint16_t chan) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_meshie *ie; + + memset(&cmd, 0, sizeof(cmd)); + cmd.channel = cpu_to_le16(chan); + ie = (struct mrvl_meshie *)cmd.data; + + switch (action) { + case CMD_ACT_MESH_CONFIG_START: + ie->id = WLAN_EID_VENDOR_SPECIFIC; + ie->val.oui[0] = 0x00; + ie->val.oui[1] = 0x50; + ie->val.oui[2] = 0x43; + ie->val.type = MARVELL_MESH_IE_TYPE; + ie->val.subtype = MARVELL_MESH_IE_SUBTYPE; + ie->val.version = MARVELL_MESH_IE_VERSION; + ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP; + ie->val.active_metric_id = MARVELL_MESH_METRIC_ID; + ie->val.mesh_capability = MARVELL_MESH_CAPABILITY; + ie->val.mesh_id_len = priv->mesh_ssid_len; + memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len); + ie->len = sizeof(struct mrvl_meshie_val) - + IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len; + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val)); + break; + case CMD_ACT_MESH_CONFIG_STOP: + break; + default: + return -1; + } + lbs_deb_cmd("mesh config action %d type %x channel %d SSID %*pE\n", + action, priv->mesh_tlv, chan, priv->mesh_ssid_len, + priv->mesh_ssid); + + return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv); +} + +int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel) +{ + priv->mesh_channel = channel; + return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel); +} + +static uint16_t lbs_mesh_get_channel(struct lbs_private *priv) +{ + return priv->mesh_channel ?: 1; +} + +/*************************************************************************** + * Mesh sysfs support + */ + +/* + * Attributes exported through sysfs + */ + +/** + * lbs_anycast_get - Get function for sysfs attribute anycast_mask + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t lbs_anycast_get(struct device *dev, + struct device_attribute *attr, char * buf) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + + memset(&mesh_access, 0, sizeof(mesh_access)); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access); + if (ret) + return ret; + + return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0])); +} + +/** + * lbs_anycast_set - Set function for sysfs attribute anycast_mask + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t lbs_anycast_set(struct device *dev, + struct device_attribute *attr, const char * buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + uint32_t datum; + int ret; + + memset(&mesh_access, 0, sizeof(mesh_access)); + sscanf(buf, "%x", &datum); + mesh_access.data[0] = cpu_to_le32(datum); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * lbs_prb_rsp_limit_get - Get function for sysfs attribute prb_rsp_limit + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t lbs_prb_rsp_limit_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + u32 retry_limit; + + memset(&mesh_access, 0, sizeof(mesh_access)); + mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, + &mesh_access); + if (ret) + return ret; + + retry_limit = le32_to_cpu(mesh_access.data[1]); + return snprintf(buf, 10, "%d\n", retry_limit); +} + +/** + * lbs_prb_rsp_limit_set - Set function for sysfs attribute prb_rsp_limit + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t lbs_prb_rsp_limit_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + unsigned long retry_limit; + + memset(&mesh_access, 0, sizeof(mesh_access)); + mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET); + + if (!kstrtoul(buf, 10, &retry_limit)) + return -ENOTSUPP; + if (retry_limit > 15) + return -ENOTSUPP; + + mesh_access.data[1] = cpu_to_le32(retry_limit); + + ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT, + &mesh_access); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * lbs_mesh_get - Get function for sysfs attribute mesh + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t lbs_mesh_get(struct device *dev, + struct device_attribute *attr, char * buf) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev); +} + +/** + * lbs_mesh_set - Set function for sysfs attribute mesh + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t lbs_mesh_set(struct device *dev, + struct device_attribute *attr, const char * buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + int enable; + + sscanf(buf, "%x", &enable); + enable = !!enable; + if (enable == !!priv->mesh_dev) + return count; + + if (enable) + lbs_add_mesh(priv); + else + lbs_remove_mesh(priv); + + return count; +} + +/* + * lbs_mesh attribute to be exported per ethX interface + * through sysfs (/sys/class/net/ethX/lbs_mesh) + */ +static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set); + +/* + * anycast_mask attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/anycast_mask) + */ +static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set); + +/* + * prb_rsp_limit attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/prb_rsp_limit) + */ +static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get, + lbs_prb_rsp_limit_set); + +static struct attribute *lbs_mesh_sysfs_entries[] = { + &dev_attr_anycast_mask.attr, + &dev_attr_prb_rsp_limit.attr, + NULL, +}; + +static const struct attribute_group lbs_mesh_attr_group = { + .attrs = lbs_mesh_sysfs_entries, +}; + + +/*************************************************************************** + * Persistent configuration support + */ + +static int mesh_get_default_parameters(struct device *dev, + struct mrvl_mesh_defaults *defs) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + int ret; + + memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET, + CMD_TYPE_MESH_GET_DEFAULTS); + + if (ret) + return -EOPNOTSUPP; + + memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults)); + + return 0; +} + +/** + * bootflag_get - Get function for sysfs attribute bootflag + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t bootflag_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag)); +} + +/** + * bootflag_set - Set function for sysfs attribute bootflag + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 1)) + return -EINVAL; + + *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum); + cmd.length = cpu_to_le16(sizeof(uint32_t)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_BOOTFLAG); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * boottime_get - Get function for sysfs attribute boottime + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t boottime_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 12, "%d\n", defs.boottime); +} + +/** + * boottime_set - Set function for sysfs attribute boottime + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t boottime_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* A too small boot time will result in the device booting into + * standalone (no-host) mode before the host can take control of it, + * so the change will be hard to revert. This may be a desired + * feature (e.g to configure a very fast boot time for devices that + * will not be attached to a host), but dangerous. So I'm enforcing a + * lower limit of 20 seconds: remove and recompile the driver if this + * does not work for you. + */ + datum = (datum < 20) ? 20 : datum; + cmd.data[0] = datum; + cmd.length = cpu_to_le16(sizeof(uint8_t)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_BOOTTIME); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * channel_get - Get function for sysfs attribute channel + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t channel_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel)); +} + +/** + * channel_set - Set function for sysfs attribute channel + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t channel_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + struct cmd_ds_mesh_config cmd; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if (ret != 1 || datum < 1 || datum > 11) + return -EINVAL; + + *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum); + cmd.length = cpu_to_le16(sizeof(uint16_t)); + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_DEF_CHANNEL); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * mesh_id_get - Get function for sysfs attribute mesh_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) { + dev_err(dev, "inconsistent mesh ID length\n"); + defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN; + } + + memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len); + buf[defs.meshie.val.mesh_id_len] = '\n'; + buf[defs.meshie.val.mesh_id_len + 1] = '\0'; + + return defs.meshie.val.mesh_id_len + 1; +} + +/** + * mesh_id_set - Set function for sysfs attribute mesh_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + int len; + int ret; + + if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1) + return -EINVAL; + + memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); + ie = (struct mrvl_meshie *) &cmd.data[0]; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + + len = count - 1; + memcpy(ie->val.mesh_id, buf, len); + /* SSID len */ + ie->val.mesh_id_len = len; + /* IE len */ + ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * protocol_id_get - Get function for sysfs attribute protocol_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t protocol_id_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id); +} + +/** + * protocol_id_set - Set function for sysfs attribute protocol_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t protocol_id_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + ie = (struct mrvl_meshie *) &cmd.data[0]; + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + /* update protocol id */ + ie->val.active_protocol_id = datum; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * metric_id_get - Get function for sysfs attribute metric_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t metric_id_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id); +} + +/** + * metric_id_set - Set function for sysfs attribute metric_id + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + ie = (struct mrvl_meshie *) &cmd.data[0]; + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + /* update metric id */ + ie->val.active_metric_id = datum; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + +/** + * capability_get - Get function for sysfs attribute capability + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer where data will be returned + */ +static ssize_t capability_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mrvl_mesh_defaults defs; + int ret; + + ret = mesh_get_default_parameters(dev, &defs); + + if (ret) + return ret; + + return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability); +} + +/** + * capability_set - Set function for sysfs attribute capability + * @dev: the &struct device + * @attr: device attributes + * @buf: buffer that contains new attribute value + * @count: size of buffer + */ +static ssize_t capability_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cmd_ds_mesh_config cmd; + struct mrvl_mesh_defaults defs; + struct mrvl_meshie *ie; + struct lbs_private *priv = to_net_dev(dev)->ml_priv; + uint32_t datum; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) + return -EINVAL; + + /* fetch all other Information Element parameters */ + ret = mesh_get_default_parameters(dev, &defs); + + cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); + + /* transfer IE elements */ + ie = (struct mrvl_meshie *) &cmd.data[0]; + memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); + /* update value */ + ie->val.mesh_capability = datum; + + ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, + CMD_TYPE_MESH_SET_MESH_IE); + if (ret) + return ret; + + return strlen(buf); +} + + +static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set); +static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set); +static DEVICE_ATTR(channel, 0644, channel_get, channel_set); +static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set); +static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set); +static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set); +static DEVICE_ATTR(capability, 0644, capability_get, capability_set); + +static struct attribute *boot_opts_attrs[] = { + &dev_attr_bootflag.attr, + &dev_attr_boottime.attr, + &dev_attr_channel.attr, + NULL +}; + +static const struct attribute_group boot_opts_group = { + .name = "boot_options", + .attrs = boot_opts_attrs, +}; + +static struct attribute *mesh_ie_attrs[] = { + &dev_attr_mesh_id.attr, + &dev_attr_protocol_id.attr, + &dev_attr_metric_id.attr, + &dev_attr_capability.attr, + NULL +}; + +static const struct attribute_group mesh_ie_group = { + .name = "mesh_ie", + .attrs = mesh_ie_attrs, +}; + +static void lbs_persist_config_init(struct net_device *dev) +{ + int ret; + ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group); + ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group); +} + +static void lbs_persist_config_remove(struct net_device *dev) +{ + sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group); + sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group); +} + + +/*************************************************************************** + * Initializing and starting, stopping mesh + */ + +/* + * Check mesh FW version and appropriately send the mesh start + * command + */ +int lbs_init_mesh(struct lbs_private *priv) +{ + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + /* Determine mesh_fw_ver from fwrelease and fwcapinfo */ + /* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */ + /* 5.110.22 have mesh command with 0xa3 command id */ + /* 10.0.0.p0 FW brings in mesh config command with different id */ + /* Check FW version MSB and initialize mesh_fw_ver */ + if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) { + /* Enable mesh, if supported, and work out which TLV it uses. + 0x100 + 291 is an unofficial value used in 5.110.20.pXX + 0x100 + 37 is the official value used in 5.110.21.pXX + but we check them in that order because 20.pXX doesn't + give an error -- it just silently fails. */ + + /* 5.110.20.pXX firmware will fail the command if the channel + doesn't match the existing channel. But only if the TLV + is correct. If the channel is wrong, _BOTH_ versions will + give an error to 0x100+291, and allow 0x100+37 to succeed. + It's just that 5.110.20.pXX will not have done anything + useful */ + + priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID; + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) { + priv->mesh_tlv = TLV_TYPE_MESH_ID; + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) + priv->mesh_tlv = 0; + } + } else + if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) && + (priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) { + /* 10.0.0.pXX new firmwares should succeed with TLV + * 0x100+37; Do not invoke command with old TLV. + */ + priv->mesh_tlv = TLV_TYPE_MESH_ID; + if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, 1)) + priv->mesh_tlv = 0; + } + + /* Stop meshing until interface is brought up */ + lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, 1); + + if (priv->mesh_tlv) { + sprintf(priv->mesh_ssid, "mesh"); + priv->mesh_ssid_len = 4; + ret = 1; + } + + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + return ret; +} + +void lbs_start_mesh(struct lbs_private *priv) +{ + lbs_add_mesh(priv); + + if (device_create_file(&priv->dev->dev, &dev_attr_lbs_mesh)) + netdev_err(priv->dev, "cannot register lbs_mesh attribute\n"); +} + +int lbs_deinit_mesh(struct lbs_private *priv) +{ + struct net_device *dev = priv->dev; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + if (priv->mesh_tlv) { + device_remove_file(&dev->dev, &dev_attr_lbs_mesh); + ret = 1; + } + + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + return ret; +} + + +/** + * lbs_mesh_stop - close the mshX interface + * + * @dev: A pointer to &net_device structure + * returns: 0 + */ +static int lbs_mesh_stop(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + + lbs_deb_enter(LBS_DEB_MESH); + lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_STOP, + lbs_mesh_get_channel(priv)); + + spin_lock_irq(&priv->driver_lock); + + netif_stop_queue(dev); + netif_carrier_off(dev); + + spin_unlock_irq(&priv->driver_lock); + + lbs_update_mcast(priv); + if (!lbs_iface_active(priv)) + lbs_stop_iface(priv); + + lbs_deb_leave(LBS_DEB_MESH); + return 0; +} + +/** + * lbs_mesh_dev_open - open the mshX interface + * + * @dev: A pointer to &net_device structure + * returns: 0 or -EBUSY if monitor mode active + */ +static int lbs_mesh_dev_open(struct net_device *dev) +{ + struct lbs_private *priv = dev->ml_priv; + int ret = 0; + + lbs_deb_enter(LBS_DEB_NET); + if (!priv->iface_running) { + ret = lbs_start_iface(priv); + if (ret) + goto out; + } + + spin_lock_irq(&priv->driver_lock); + + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + ret = -EBUSY; + spin_unlock_irq(&priv->driver_lock); + goto out; + } + + netif_carrier_on(dev); + + if (!priv->tx_pending_len) + netif_wake_queue(dev); + + spin_unlock_irq(&priv->driver_lock); + + ret = lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, + lbs_mesh_get_channel(priv)); + +out: + lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret); + return ret; +} + +static const struct net_device_ops mesh_netdev_ops = { + .ndo_open = lbs_mesh_dev_open, + .ndo_stop = lbs_mesh_stop, + .ndo_start_xmit = lbs_hard_start_xmit, + .ndo_set_mac_address = lbs_set_mac_address, + .ndo_set_rx_mode = lbs_set_multicast_list, +}; + +/** + * lbs_add_mesh - add mshX interface + * + * @priv: A pointer to the &struct lbs_private structure + * returns: 0 if successful, -X otherwise + */ +static int lbs_add_mesh(struct lbs_private *priv) +{ + struct net_device *mesh_dev = NULL; + struct wireless_dev *mesh_wdev; + int ret = 0; + + lbs_deb_enter(LBS_DEB_MESH); + + /* Allocate a virtual mesh device */ + mesh_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); + if (!mesh_wdev) { + lbs_deb_mesh("init mshX wireless device failed\n"); + ret = -ENOMEM; + goto done; + } + + mesh_dev = alloc_netdev(0, "msh%d", NET_NAME_UNKNOWN, ether_setup); + if (!mesh_dev) { + lbs_deb_mesh("init mshX device failed\n"); + ret = -ENOMEM; + goto err_free_wdev; + } + + mesh_wdev->iftype = NL80211_IFTYPE_MESH_POINT; + mesh_wdev->wiphy = priv->wdev->wiphy; + mesh_wdev->netdev = mesh_dev; + + mesh_dev->ml_priv = priv; + mesh_dev->ieee80211_ptr = mesh_wdev; + priv->mesh_dev = mesh_dev; + + mesh_dev->netdev_ops = &mesh_netdev_ops; + mesh_dev->ethtool_ops = &lbs_ethtool_ops; + eth_hw_addr_inherit(mesh_dev, priv->dev); + + SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent); + + mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + /* Register virtual mesh interface */ + ret = register_netdev(mesh_dev); + if (ret) { + pr_err("cannot register mshX virtual interface\n"); + goto err_free_netdev; + } + + ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); + if (ret) + goto err_unregister; + + lbs_persist_config_init(mesh_dev); + + /* Everything successful */ + ret = 0; + goto done; + +err_unregister: + unregister_netdev(mesh_dev); + +err_free_netdev: + free_netdev(mesh_dev); + +err_free_wdev: + kfree(mesh_wdev); + +done: + lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret); + return ret; +} + +void lbs_remove_mesh(struct lbs_private *priv) +{ + struct net_device *mesh_dev; + + mesh_dev = priv->mesh_dev; + if (!mesh_dev) + return; + + lbs_deb_enter(LBS_DEB_MESH); + netif_stop_queue(mesh_dev); + netif_carrier_off(mesh_dev); + sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group); + lbs_persist_config_remove(mesh_dev); + unregister_netdev(mesh_dev); + priv->mesh_dev = NULL; + kfree(mesh_dev->ieee80211_ptr); + free_netdev(mesh_dev); + lbs_deb_leave(LBS_DEB_MESH); +} + + +/*************************************************************************** + * Sending and receiving + */ +struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, + struct net_device *dev, struct rxpd *rxpd) +{ + if (priv->mesh_dev) { + if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) { + if (rxpd->rx_control & RxPD_MESH_FRAME) + dev = priv->mesh_dev; + } else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) { + if (rxpd->u.bss.bss_num == MESH_IFACE_ID) + dev = priv->mesh_dev; + } + } + return dev; +} + + +void lbs_mesh_set_txpd(struct lbs_private *priv, + struct net_device *dev, struct txpd *txpd) +{ + if (dev == priv->mesh_dev) { + if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) + txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME); + else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) + txpd->u.bss.bss_num = MESH_IFACE_ID; + } +} + + +/*************************************************************************** + * Ethtool related + */ + +static const char * const mesh_stat_strings[] = { + "drop_duplicate_bcast", + "drop_ttl_zero", + "drop_no_fwd_route", + "drop_no_buffers", + "fwded_unicast_cnt", + "fwded_bcast_cnt", + "drop_blind_table", + "tx_failed_cnt" +}; + +void lbs_mesh_ethtool_get_stats(struct net_device *dev, + struct ethtool_stats *stats, uint64_t *data) +{ + struct lbs_private *priv = dev->ml_priv; + struct cmd_ds_mesh_access mesh_access; + int ret; + + lbs_deb_enter(LBS_DEB_ETHTOOL); + + /* Get Mesh Statistics */ + ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access); + + if (ret) { + memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t))); + return; + } + + priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]); + priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]); + priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]); + priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]); + priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]); + priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]); + priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]); + priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]); + + data[0] = priv->mstats.fwd_drop_rbt; + data[1] = priv->mstats.fwd_drop_ttl; + data[2] = priv->mstats.fwd_drop_noroute; + data[3] = priv->mstats.fwd_drop_nobuf; + data[4] = priv->mstats.fwd_unicast_cnt; + data[5] = priv->mstats.fwd_bcast_cnt; + data[6] = priv->mstats.drop_blind; + data[7] = priv->mstats.tx_failed_cnt; + + lbs_deb_enter(LBS_DEB_ETHTOOL); +} + +int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset) +{ + struct lbs_private *priv = dev->ml_priv; + + if (sset == ETH_SS_STATS && dev == priv->mesh_dev) + return MESH_STATS_NUM; + + return -EOPNOTSUPP; +} + +void lbs_mesh_ethtool_get_strings(struct net_device *dev, + uint32_t stringset, uint8_t *s) +{ + int i; + + lbs_deb_enter(LBS_DEB_ETHTOOL); + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < MESH_STATS_NUM; i++) { + memcpy(s + i * ETH_GSTRING_LEN, + mesh_stat_strings[i], + ETH_GSTRING_LEN); + } + break; + } + lbs_deb_enter(LBS_DEB_ETHTOOL); +} diff --git a/drivers/net/wireless/marvell/libertas/mesh.h b/drivers/net/wireless/marvell/libertas/mesh.h new file mode 100644 index 000000000000..6603f341c874 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/mesh.h @@ -0,0 +1,77 @@ +/* + * Contains all definitions needed for the Libertas' MESH implementation. + */ +#ifndef _LBS_MESH_H_ +#define _LBS_MESH_H_ + + +#include +#include + +#include "host.h" +#include "dev.h" + +#ifdef CONFIG_LIBERTAS_MESH + +struct net_device; + +int lbs_init_mesh(struct lbs_private *priv); +void lbs_start_mesh(struct lbs_private *priv); +int lbs_deinit_mesh(struct lbs_private *priv); + +void lbs_remove_mesh(struct lbs_private *priv); + +static inline bool lbs_mesh_activated(struct lbs_private *priv) +{ + /* Mesh SSID is only programmed after successful init */ + return priv->mesh_ssid_len != 0; +} + +int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel); + +/* Sending / Receiving */ + +struct rxpd; +struct txpd; + +struct net_device *lbs_mesh_set_dev(struct lbs_private *priv, + struct net_device *dev, struct rxpd *rxpd); +void lbs_mesh_set_txpd(struct lbs_private *priv, + struct net_device *dev, struct txpd *txpd); + + +/* Command handling */ + +struct cmd_ds_command; +struct cmd_ds_mesh_access; +struct cmd_ds_mesh_config; + + +/* Ethtool statistics */ + +struct ethtool_stats; + +void lbs_mesh_ethtool_get_stats(struct net_device *dev, + struct ethtool_stats *stats, uint64_t *data); +int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset); +void lbs_mesh_ethtool_get_strings(struct net_device *dev, + uint32_t stringset, uint8_t *s); + + +#else + +#define lbs_init_mesh(priv) +#define lbs_deinit_mesh(priv) +#define lbs_start_mesh(priv) +#define lbs_add_mesh(priv) +#define lbs_remove_mesh(priv) +#define lbs_mesh_set_dev(priv, dev, rxpd) (dev) +#define lbs_mesh_set_txpd(priv, dev, txpd) +#define lbs_mesh_set_channel(priv, channel) (0) +#define lbs_mesh_activated(priv) (false) + +#endif + + + +#endif diff --git a/drivers/net/wireless/marvell/libertas/radiotap.h b/drivers/net/wireless/marvell/libertas/radiotap.h new file mode 100644 index 000000000000..b3c8ea6d610e --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/radiotap.h @@ -0,0 +1,44 @@ +#include + +struct tx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 rate; + u8 txpower; + u8 rts_retries; + u8 data_retries; +} __packed; + +#define TX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ + (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \ + (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \ + 0) + +#define IEEE80211_FC_VERSION_MASK 0x0003 +#define IEEE80211_FC_TYPE_MASK 0x000c +#define IEEE80211_FC_TYPE_MGT 0x0000 +#define IEEE80211_FC_TYPE_CTL 0x0004 +#define IEEE80211_FC_TYPE_DATA 0x0008 +#define IEEE80211_FC_SUBTYPE_MASK 0x00f0 +#define IEEE80211_FC_TOFROMDS_MASK 0x0300 +#define IEEE80211_FC_TODS_MASK 0x0100 +#define IEEE80211_FC_FROMDS_MASK 0x0200 +#define IEEE80211_FC_NODS 0x0000 +#define IEEE80211_FC_TODS 0x0100 +#define IEEE80211_FC_FROMDS 0x0200 +#define IEEE80211_FC_DSTODS 0x0300 + +struct rx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 flags; + u8 rate; + u8 antsignal; +} __packed; + +#define RX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ + 0) + diff --git a/drivers/net/wireless/marvell/libertas/rx.c b/drivers/net/wireless/marvell/libertas/rx.c new file mode 100644 index 000000000000..e446fed7b345 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/rx.c @@ -0,0 +1,286 @@ +/* + * This file contains the handling of RX in wlan driver. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "dev.h" +#include "mesh.h" + +struct eth803hdr { + u8 dest_addr[6]; + u8 src_addr[6]; + u16 h803_len; +} __packed; + +struct rfc1042hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + u16 snap_type; +} __packed; + +struct rxpackethdr { + struct eth803hdr eth803_hdr; + struct rfc1042hdr rfc1042_hdr; +} __packed; + +struct rx80211packethdr { + struct rxpd rx_pd; + void *eth80211_hdr; +} __packed; + +static int process_rxed_802_11_packet(struct lbs_private *priv, + struct sk_buff *skb); + +/** + * lbs_process_rxed_packet - processes received packet and forwards it + * to kernel/upper layer + * + * @priv: A pointer to &struct lbs_private + * @skb: A pointer to skb which includes the received packet + * returns: 0 or -1 + */ +int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) +{ + int ret = 0; + struct net_device *dev = priv->dev; + struct rxpackethdr *p_rx_pkt; + struct rxpd *p_rx_pd; + int hdrchop; + struct ethhdr *p_ethhdr; + static const u8 rfc1042_eth_hdr[] = { + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 + }; + + lbs_deb_enter(LBS_DEB_RX); + + BUG_ON(!skb); + + skb->ip_summed = CHECKSUM_NONE; + + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + ret = process_rxed_802_11_packet(priv, skb); + goto done; + } + + p_rx_pd = (struct rxpd *) skb->data; + p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd + + le32_to_cpu(p_rx_pd->pkt_ptr)); + + dev = lbs_mesh_set_dev(priv, dev, p_rx_pd); + + lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_deb_rx("rx err: frame received with bad length\n"); + dev->stats.rx_length_errors++; + ret = -EINVAL; + dev_kfree_skb(skb); + goto done; + } + + lbs_deb_rx("rx data: skb->len - pkt_ptr = %d-%zd = %zd\n", + skb->len, (size_t)le32_to_cpu(p_rx_pd->pkt_ptr), + skb->len - (size_t)le32_to_cpu(p_rx_pd->pkt_ptr)); + + lbs_deb_hex(LBS_DEB_RX, "RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_rx_pkt->eth803_hdr.dest_addr)); + lbs_deb_hex(LBS_DEB_RX, "RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_rx_pkt->eth803_hdr.src_addr)); + + if (memcmp(&p_rx_pkt->rfc1042_hdr, + rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { + /* + * Replace the 803 header and rfc1042 header (llc/snap) with an + * EthernetII header, keep the src/dst and snap_type (ethertype) + * + * The firmware only passes up SNAP frames converting + * all RX Data from 802.11 to 802.2/LLC/SNAP frames. + * + * To create the Ethernet II, just move the src, dst address right + * before the snap_type. + */ + p_ethhdr = (struct ethhdr *) + ((u8 *) &p_rx_pkt->eth803_hdr + + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) + - sizeof(p_rx_pkt->eth803_hdr.dest_addr) + - sizeof(p_rx_pkt->eth803_hdr.src_addr) + - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); + + memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_ethhdr->h_source)); + memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_ethhdr->h_dest)); + + /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header + * that was removed + */ + hdrchop = (u8 *)p_ethhdr - (u8 *)p_rx_pd; + } else { + lbs_deb_hex(LBS_DEB_RX, "RX Data: LLC/SNAP", + (u8 *) &p_rx_pkt->rfc1042_hdr, + sizeof(p_rx_pkt->rfc1042_hdr)); + + /* Chop off the rxpd */ + hdrchop = (u8 *)&p_rx_pkt->eth803_hdr - (u8 *)p_rx_pd; + } + + /* Chop off the leading header bytes so the skb points to the start of + * either the reconstructed EthII frame or the 802.2/llc/snap frame + */ + skb_pull(skb, hdrchop); + + priv->cur_rate = lbs_fw_index_to_data_rate(p_rx_pd->rx_rate); + + lbs_deb_rx("rx data: size of actual packet %d\n", skb->len); + dev->stats.rx_bytes += skb->len; + dev->stats.rx_packets++; + + skb->protocol = eth_type_trans(skb, dev); + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + + ret = 0; +done: + lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(lbs_process_rxed_packet); + +/** + * convert_mv_rate_to_radiotap - converts Tx/Rx rates from Marvell WLAN format + * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) + * + * @rate: Input rate + * returns: Output Rate (0 if invalid) + */ +static u8 convert_mv_rate_to_radiotap(u8 rate) +{ + switch (rate) { + case 0: /* 1 Mbps */ + return 2; + case 1: /* 2 Mbps */ + return 4; + case 2: /* 5.5 Mbps */ + return 11; + case 3: /* 11 Mbps */ + return 22; + /* case 4: reserved */ + case 5: /* 6 Mbps */ + return 12; + case 6: /* 9 Mbps */ + return 18; + case 7: /* 12 Mbps */ + return 24; + case 8: /* 18 Mbps */ + return 36; + case 9: /* 24 Mbps */ + return 48; + case 10: /* 36 Mbps */ + return 72; + case 11: /* 48 Mbps */ + return 96; + case 12: /* 54 Mbps */ + return 108; + } + pr_alert("Invalid Marvell WLAN rate %i\n", rate); + return 0; +} + +/** + * process_rxed_802_11_packet - processes a received 802.11 packet and forwards + * it to kernel/upper layer + * + * @priv: A pointer to &struct lbs_private + * @skb: A pointer to skb which includes the received packet + * returns: 0 or -1 + */ +static int process_rxed_802_11_packet(struct lbs_private *priv, + struct sk_buff *skb) +{ + int ret = 0; + struct net_device *dev = priv->dev; + struct rx80211packethdr *p_rx_pkt; + struct rxpd *prxpd; + struct rx_radiotap_hdr radiotap_hdr; + struct rx_radiotap_hdr *pradiotap_hdr; + + lbs_deb_enter(LBS_DEB_RX); + + p_rx_pkt = (struct rx80211packethdr *) skb->data; + prxpd = &p_rx_pkt->rx_pd; + + /* lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); */ + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_deb_rx("rx err: frame received with bad length\n"); + dev->stats.rx_length_errors++; + ret = -EINVAL; + kfree_skb(skb); + goto done; + } + + lbs_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + + /* create the exported radio header */ + + /* radiotap header */ + memset(&radiotap_hdr, 0, sizeof(radiotap_hdr)); + /* XXX must check radiotap_hdr.hdr.it_pad for pad */ + radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr)); + radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT); + radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); + /* XXX must check no carryout */ + radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; + + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + + /* add space for the new radio header */ + if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && + pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, GFP_ATOMIC)) { + netdev_alert(dev, "%s: couldn't pskb_expand_head\n", __func__); + ret = -ENOMEM; + kfree_skb(skb); + goto done; + } + + pradiotap_hdr = (void *)skb_push(skb, sizeof(struct rx_radiotap_hdr)); + memcpy(pradiotap_hdr, &radiotap_hdr, sizeof(struct rx_radiotap_hdr)); + + priv->cur_rate = lbs_fw_index_to_data_rate(prxpd->rx_rate); + + lbs_deb_rx("rx data: size of actual packet %d\n", skb->len); + dev->stats.rx_bytes += skb->len; + dev->stats.rx_packets++; + + skb->protocol = eth_type_trans(skb, priv->dev); + + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + + ret = 0; + +done: + lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret); + return ret; +} diff --git a/drivers/net/wireless/marvell/libertas/tx.c b/drivers/net/wireless/marvell/libertas/tx.c new file mode 100644 index 000000000000..c025f9c18282 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/tx.c @@ -0,0 +1,207 @@ +/* + * This file contains the handling of TX in wlan driver. + */ +#include +#include +#include +#include +#include +#include + +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "mesh.h" + +/** + * convert_radiotap_rate_to_mv - converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE + * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1) + * + * @rate: Input rate + * returns: Output Rate (0 if invalid) + */ +static u32 convert_radiotap_rate_to_mv(u8 rate) +{ + switch (rate) { + case 2: /* 1 Mbps */ + return 0 | (1 << 4); + case 4: /* 2 Mbps */ + return 1 | (1 << 4); + case 11: /* 5.5 Mbps */ + return 2 | (1 << 4); + case 22: /* 11 Mbps */ + return 3 | (1 << 4); + case 12: /* 6 Mbps */ + return 4 | (1 << 4); + case 18: /* 9 Mbps */ + return 5 | (1 << 4); + case 24: /* 12 Mbps */ + return 6 | (1 << 4); + case 36: /* 18 Mbps */ + return 7 | (1 << 4); + case 48: /* 24 Mbps */ + return 8 | (1 << 4); + case 72: /* 36 Mbps */ + return 9 | (1 << 4); + case 96: /* 48 Mbps */ + return 10 | (1 << 4); + case 108: /* 54 Mbps */ + return 11 | (1 << 4); + } + return 0; +} + +/** + * lbs_hard_start_xmit - checks the conditions and sends packet to IF + * layer if everything is ok + * + * @skb: A pointer to skb which includes TX packet + * @dev: A pointer to the &struct net_device + * returns: 0 or -1 + */ +netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned long flags; + struct lbs_private *priv = dev->ml_priv; + struct txpd *txpd; + char *p802x_hdr; + uint16_t pkt_len; + netdev_tx_t ret = NETDEV_TX_OK; + + lbs_deb_enter(LBS_DEB_TX); + + /* We need to protect against the queues being restarted before + we get round to stopping them */ + spin_lock_irqsave(&priv->driver_lock, flags); + + if (priv->surpriseremoved) + goto free; + + if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { + lbs_deb_tx("tx err: skb length %d 0 or > %zd\n", + skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); + /* We'll never manage to send this one; drop it and return 'OK' */ + + dev->stats.tx_dropped++; + dev->stats.tx_errors++; + goto free; + } + + + netif_stop_queue(priv->dev); + if (priv->mesh_dev) + netif_stop_queue(priv->mesh_dev); + + if (priv->tx_pending_len) { + /* This can happen if packets come in on the mesh and eth + device simultaneously -- there's no mutual exclusion on + hard_start_xmit() calls between devices. */ + lbs_deb_tx("Packet on %s while busy\n", dev->name); + ret = NETDEV_TX_BUSY; + goto unlock; + } + + priv->tx_pending_len = -1; + spin_unlock_irqrestore(&priv->driver_lock, flags); + + lbs_deb_hex(LBS_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100)); + + txpd = (void *)priv->tx_pending_buf; + memset(txpd, 0, sizeof(struct txpd)); + + p802x_hdr = skb->data; + pkt_len = skb->len; + + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + struct tx_radiotap_hdr *rtap_hdr = (void *)skb->data; + + /* set txpd fields from the radiotap header */ + txpd->tx_control = cpu_to_le32(convert_radiotap_rate_to_mv(rtap_hdr->rate)); + + /* skip the radiotap header */ + p802x_hdr += sizeof(*rtap_hdr); + pkt_len -= sizeof(*rtap_hdr); + + /* copy destination address from 802.11 header */ + memcpy(txpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN); + } else { + /* copy destination address from 802.3 header */ + memcpy(txpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN); + } + + txpd->tx_packet_length = cpu_to_le16(pkt_len); + txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd)); + + lbs_mesh_set_txpd(priv, dev, txpd); + + lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd)); + + lbs_deb_hex(LBS_DEB_TX, "Tx Data", (u8 *) p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); + + memcpy(&txpd[1], p802x_hdr, le16_to_cpu(txpd->tx_packet_length)); + + spin_lock_irqsave(&priv->driver_lock, flags); + priv->tx_pending_len = pkt_len + sizeof(struct txpd); + + lbs_deb_tx("%s lined up packet\n", __func__); + + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + /* Keep the skb to echo it back once Tx feedback is + received from FW */ + skb_orphan(skb); + + /* Keep the skb around for when we get feedback */ + priv->currenttxskb = skb; + } else { + free: + dev_kfree_skb_any(skb); + } + + unlock: + spin_unlock_irqrestore(&priv->driver_lock, flags); + wake_up(&priv->waitq); + + lbs_deb_leave_args(LBS_DEB_TX, "ret %d", ret); + return ret; +} + +/** + * lbs_send_tx_feedback - sends to the host the last transmitted packet, + * filling the radiotap headers with transmission information. + * + * @priv: A pointer to &struct lbs_private structure + * @try_count: A 32-bit value containing transmission retry status. + * + * returns: void + */ +void lbs_send_tx_feedback(struct lbs_private *priv, u32 try_count) +{ + struct tx_radiotap_hdr *radiotap_hdr; + + if (priv->wdev->iftype != NL80211_IFTYPE_MONITOR || + priv->currenttxskb == NULL) + return; + + radiotap_hdr = (struct tx_radiotap_hdr *)priv->currenttxskb->data; + + radiotap_hdr->data_retries = try_count ? + (1 + priv->txretrycount - try_count) : 0; + + priv->currenttxskb->protocol = eth_type_trans(priv->currenttxskb, + priv->dev); + netif_rx(priv->currenttxskb); + + priv->currenttxskb = NULL; + + if (priv->connect_status == LBS_CONNECTED) + netif_wake_queue(priv->dev); + + if (priv->mesh_dev && netif_running(priv->mesh_dev)) + netif_wake_queue(priv->mesh_dev); +} +EXPORT_SYMBOL_GPL(lbs_send_tx_feedback); diff --git a/drivers/net/wireless/marvell/libertas/types.h b/drivers/net/wireless/marvell/libertas/types.h new file mode 100644 index 000000000000..cf1d9b047ee6 --- /dev/null +++ b/drivers/net/wireless/marvell/libertas/types.h @@ -0,0 +1,268 @@ +/* + * This header file contains definition for global types + */ +#ifndef _LBS_TYPES_H_ +#define _LBS_TYPES_H_ + +#include +#include +#include + +struct ieee_ie_header { + u8 id; + u8 len; +} __packed; + +struct ieee_ie_cf_param_set { + struct ieee_ie_header header; + + u8 cfpcnt; + u8 cfpperiod; + __le16 cfpmaxduration; + __le16 cfpdurationremaining; +} __packed; + + +struct ieee_ie_ibss_param_set { + struct ieee_ie_header header; + + __le16 atimwindow; +} __packed; + +union ieee_ss_param_set { + struct ieee_ie_cf_param_set cf; + struct ieee_ie_ibss_param_set ibss; +} __packed; + +struct ieee_ie_fh_param_set { + struct ieee_ie_header header; + + __le16 dwelltime; + u8 hopset; + u8 hoppattern; + u8 hopindex; +} __packed; + +struct ieee_ie_ds_param_set { + struct ieee_ie_header header; + + u8 channel; +} __packed; + +union ieee_phy_param_set { + struct ieee_ie_fh_param_set fh; + struct ieee_ie_ds_param_set ds; +} __packed; + +/* TLV type ID definition */ +#define PROPRIETARY_TLV_BASE_ID 0x0100 + +/* Terminating TLV type */ +#define MRVL_TERMINATE_TLV_ID 0xffff + +#define TLV_TYPE_SSID 0x0000 +#define TLV_TYPE_RATES 0x0001 +#define TLV_TYPE_PHY_FH 0x0002 +#define TLV_TYPE_PHY_DS 0x0003 +#define TLV_TYPE_CF 0x0004 +#define TLV_TYPE_IBSS 0x0006 + +#define TLV_TYPE_DOMAIN 0x0007 + +#define TLV_TYPE_POWER_CAPABILITY 0x0021 + +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) +#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) +#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) +#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) +#define TLV_TYPE_LED_GPIO (PROPRIETARY_TLV_BASE_ID + 8) +#define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) +#define TLV_TYPE_REASSOCAP (PROPRIETARY_TLV_BASE_ID + 11) +#define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) +#define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) +#define TLV_TYPE_BCASTPROBE (PROPRIETARY_TLV_BASE_ID + 14) +#define TLV_TYPE_NUMSSID_PROBE (PROPRIETARY_TLV_BASE_ID + 15) +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) +#define TLV_TYPE_CRYPTO_DATA (PROPRIETARY_TLV_BASE_ID + 17) +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) +#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) +#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) +#define TLV_TYPE_MESH_ID (PROPRIETARY_TLV_BASE_ID + 37) +#define TLV_TYPE_OLD_MESH_ID (PROPRIETARY_TLV_BASE_ID + 291) + +/* TLV related data structures */ +struct mrvl_ie_header { + __le16 type; + __le16 len; +} __packed; + +struct mrvl_ie_data { + struct mrvl_ie_header header; + u8 Data[1]; +} __packed; + +struct mrvl_ie_rates_param_set { + struct mrvl_ie_header header; + u8 rates[1]; +} __packed; + +struct mrvl_ie_ssid_param_set { + struct mrvl_ie_header header; + u8 ssid[1]; +} __packed; + +struct mrvl_ie_wildcard_ssid_param_set { + struct mrvl_ie_header header; + u8 MaxSsidlength; + u8 ssid[1]; +} __packed; + +struct chanscanmode { +#ifdef __BIG_ENDIAN_BITFIELD + u8 reserved_2_7:6; + u8 disablechanfilt:1; + u8 passivescan:1; +#else + u8 passivescan:1; + u8 disablechanfilt:1; + u8 reserved_2_7:6; +#endif +} __packed; + +struct chanscanparamset { + u8 radiotype; + u8 channumber; + struct chanscanmode chanscanmode; + __le16 minscantime; + __le16 maxscantime; +} __packed; + +struct mrvl_ie_chanlist_param_set { + struct mrvl_ie_header header; + struct chanscanparamset chanscanparam[1]; +} __packed; + +struct mrvl_ie_cf_param_set { + struct mrvl_ie_header header; + u8 cfpcnt; + u8 cfpperiod; + __le16 cfpmaxduration; + __le16 cfpdurationremaining; +} __packed; + +struct mrvl_ie_ds_param_set { + struct mrvl_ie_header header; + u8 channel; +} __packed; + +struct mrvl_ie_rsn_param_set { + struct mrvl_ie_header header; + u8 rsnie[1]; +} __packed; + +struct mrvl_ie_tsf_timestamp { + struct mrvl_ie_header header; + __le64 tsftable[1]; +} __packed; + +/* v9 and later firmware only */ +struct mrvl_ie_auth_type { + struct mrvl_ie_header header; + __le16 auth; +} __packed; + +/* Local Power capability */ +struct mrvl_ie_power_capability { + struct mrvl_ie_header header; + s8 minpower; + s8 maxpower; +} __packed; + +/* used in CMD_802_11_SUBSCRIBE_EVENT for SNR, RSSI and Failure */ +struct mrvl_ie_thresholds { + struct mrvl_ie_header header; + u8 value; + u8 freq; +} __packed; + +struct mrvl_ie_beacons_missed { + struct mrvl_ie_header header; + u8 beaconmissed; + u8 reserved; +} __packed; + +struct mrvl_ie_num_probes { + struct mrvl_ie_header header; + __le16 numprobes; +} __packed; + +struct mrvl_ie_bcast_probe { + struct mrvl_ie_header header; + __le16 bcastprobe; +} __packed; + +struct mrvl_ie_num_ssid_probe { + struct mrvl_ie_header header; + __le16 numssidprobe; +} __packed; + +struct led_pin { + u8 led; + u8 pin; +} __packed; + +struct mrvl_ie_ledgpio { + struct mrvl_ie_header header; + struct led_pin ledpin[1]; +} __packed; + +struct led_bhv { + uint8_t firmwarestate; + uint8_t led; + uint8_t ledstate; + uint8_t ledarg; +} __packed; + + +struct mrvl_ie_ledbhv { + struct mrvl_ie_header header; + struct led_bhv ledbhv[1]; +} __packed; + +/* + * Meant to be packed as the value member of a struct ieee80211_info_element. + * Note that the len member of the ieee80211_info_element varies depending on + * the mesh_id_len + */ +struct mrvl_meshie_val { + uint8_t oui[3]; + uint8_t type; + uint8_t subtype; + uint8_t version; + uint8_t active_protocol_id; + uint8_t active_metric_id; + uint8_t mesh_capability; + uint8_t mesh_id_len; + uint8_t mesh_id[IEEE80211_MAX_SSID_LEN]; +} __packed; + +struct mrvl_meshie { + u8 id, len; + struct mrvl_meshie_val val; +} __packed; + +struct mrvl_mesh_defaults { + __le32 bootflag; + uint8_t boottime; + uint8_t reserved; + __le16 channel; + struct mrvl_meshie meshie; +} __packed; + +#endif