diff options
author | John W. Linville <linville@tuxdriver.com> | 2012-04-12 15:02:19 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-04-12 15:02:19 -0400 |
commit | c2786e4a173f89ae81d6ba45d5bf129d77733eea (patch) | |
tree | ab83a1c7658262314548236a96fabce88be37cb5 /drivers/net | |
parent | 8065248069097dddf9945acfb2081025e9618c16 (diff) | |
parent | d97c121bb23d32ef631c553d2656f8ccf8349507 (diff) |
Merge branch 'for-linville' of git://github.com/kvalo/ath6kl
Diffstat (limited to 'drivers/net')
20 files changed, 3370 insertions, 165 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index 85746c3eb027..8cae8886f17d 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile | |||
@@ -25,7 +25,8 @@ | |||
25 | obj-$(CONFIG_ATH6KL) += ath6kl_core.o | 25 | obj-$(CONFIG_ATH6KL) += ath6kl_core.o |
26 | ath6kl_core-y += debug.o | 26 | ath6kl_core-y += debug.o |
27 | ath6kl_core-y += hif.o | 27 | ath6kl_core-y += hif.o |
28 | ath6kl_core-y += htc.o | 28 | ath6kl_core-y += htc_mbox.o |
29 | ath6kl_core-y += htc_pipe.o | ||
29 | ath6kl_core-y += bmi.o | 30 | ath6kl_core-y += bmi.o |
30 | ath6kl_core-y += cfg80211.o | 31 | ath6kl_core-y += cfg80211.o |
31 | ath6kl_core-y += init.o | 32 | ath6kl_core-y += init.o |
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index bdcc68fb1e37..28a65d3a03d0 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c | |||
@@ -51,6 +51,8 @@ | |||
51 | .max_power = 30, \ | 51 | .max_power = 30, \ |
52 | } | 52 | } |
53 | 53 | ||
54 | #define DEFAULT_BG_SCAN_PERIOD 60 | ||
55 | |||
54 | static struct ieee80211_rate ath6kl_rates[] = { | 56 | static struct ieee80211_rate ath6kl_rates[] = { |
55 | RATETAB_ENT(10, 0x1, 0), | 57 | RATETAB_ENT(10, 0x1, 0), |
56 | RATETAB_ENT(20, 0x2, 0), | 58 | RATETAB_ENT(20, 0x2, 0), |
@@ -71,7 +73,8 @@ static struct ieee80211_rate ath6kl_rates[] = { | |||
71 | #define ath6kl_g_rates (ath6kl_rates + 0) | 73 | #define ath6kl_g_rates (ath6kl_rates + 0) |
72 | #define ath6kl_g_rates_size 12 | 74 | #define ath6kl_g_rates_size 12 |
73 | 75 | ||
74 | #define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ | 76 | #define ath6kl_g_htcap IEEE80211_HT_CAP_SGI_20 |
77 | #define ath6kl_a_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ | ||
75 | IEEE80211_HT_CAP_SGI_20 | \ | 78 | IEEE80211_HT_CAP_SGI_20 | \ |
76 | IEEE80211_HT_CAP_SGI_40) | 79 | IEEE80211_HT_CAP_SGI_40) |
77 | 80 | ||
@@ -128,7 +131,7 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = { | |||
128 | .channels = ath6kl_5ghz_a_channels, | 131 | .channels = ath6kl_5ghz_a_channels, |
129 | .n_bitrates = ath6kl_a_rates_size, | 132 | .n_bitrates = ath6kl_a_rates_size, |
130 | .bitrates = ath6kl_a_rates, | 133 | .bitrates = ath6kl_a_rates, |
131 | .ht_cap.cap = ath6kl_g_htcap, | 134 | .ht_cap.cap = ath6kl_a_htcap, |
132 | .ht_cap.ht_supported = true, | 135 | .ht_cap.ht_supported = true, |
133 | }; | 136 | }; |
134 | 137 | ||
@@ -609,6 +612,17 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, | |||
609 | vif->req_bssid, vif->ch_hint, | 612 | vif->req_bssid, vif->ch_hint, |
610 | ar->connect_ctrl_flags, nw_subtype); | 613 | ar->connect_ctrl_flags, nw_subtype); |
611 | 614 | ||
615 | /* disable background scan if period is 0 */ | ||
616 | if (sme->bg_scan_period == 0) | ||
617 | sme->bg_scan_period = 0xffff; | ||
618 | |||
619 | /* configure default value if not specified */ | ||
620 | if (sme->bg_scan_period == -1) | ||
621 | sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD; | ||
622 | |||
623 | ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0, | ||
624 | sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0); | ||
625 | |||
612 | up(&ar->sem); | 626 | up(&ar->sem); |
613 | 627 | ||
614 | if (status == -EINVAL) { | 628 | if (status == -EINVAL) { |
@@ -943,6 +957,8 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, | |||
943 | if (test_bit(CONNECTED, &vif->flags)) | 957 | if (test_bit(CONNECTED, &vif->flags)) |
944 | force_fg_scan = 1; | 958 | force_fg_scan = 1; |
945 | 959 | ||
960 | vif->scan_req = request; | ||
961 | |||
946 | if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, | 962 | if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, |
947 | ar->fw_capabilities)) { | 963 | ar->fw_capabilities)) { |
948 | /* | 964 | /* |
@@ -965,10 +981,10 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, | |||
965 | ATH6KL_FG_SCAN_INTERVAL, | 981 | ATH6KL_FG_SCAN_INTERVAL, |
966 | n_channels, channels); | 982 | n_channels, channels); |
967 | } | 983 | } |
968 | if (ret) | 984 | if (ret) { |
969 | ath6kl_err("wmi_startscan_cmd failed\n"); | 985 | ath6kl_err("wmi_startscan_cmd failed\n"); |
970 | else | 986 | vif->scan_req = NULL; |
971 | vif->scan_req = request; | 987 | } |
972 | 988 | ||
973 | kfree(channels); | 989 | kfree(channels); |
974 | 990 | ||
@@ -1438,9 +1454,38 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, | |||
1438 | struct vif_params *params) | 1454 | struct vif_params *params) |
1439 | { | 1455 | { |
1440 | struct ath6kl_vif *vif = netdev_priv(ndev); | 1456 | struct ath6kl_vif *vif = netdev_priv(ndev); |
1457 | int i; | ||
1441 | 1458 | ||
1442 | ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type); | 1459 | ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type); |
1443 | 1460 | ||
1461 | /* | ||
1462 | * Don't bring up p2p on an interface which is not initialized | ||
1463 | * for p2p operation where fw does not have capability to switch | ||
1464 | * dynamically between non-p2p and p2p type interface. | ||
1465 | */ | ||
1466 | if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, | ||
1467 | vif->ar->fw_capabilities) && | ||
1468 | (type == NL80211_IFTYPE_P2P_CLIENT || | ||
1469 | type == NL80211_IFTYPE_P2P_GO)) { | ||
1470 | if (vif->ar->vif_max == 1) { | ||
1471 | if (vif->fw_vif_idx != 0) | ||
1472 | return -EINVAL; | ||
1473 | else | ||
1474 | goto set_iface_type; | ||
1475 | } | ||
1476 | |||
1477 | for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) { | ||
1478 | if (i == vif->fw_vif_idx) | ||
1479 | break; | ||
1480 | } | ||
1481 | |||
1482 | if (i == vif->ar->vif_max) { | ||
1483 | ath6kl_err("Invalid interface to bring up P2P\n"); | ||
1484 | return -EINVAL; | ||
1485 | } | ||
1486 | } | ||
1487 | |||
1488 | set_iface_type: | ||
1444 | switch (type) { | 1489 | switch (type) { |
1445 | case NL80211_IFTYPE_STATION: | 1490 | case NL80211_IFTYPE_STATION: |
1446 | vif->next_mode = INFRA_NETWORK; | 1491 | vif->next_mode = INFRA_NETWORK; |
@@ -1926,12 +1971,61 @@ static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif) | |||
1926 | return 0; | 1971 | return 0; |
1927 | } | 1972 | } |
1928 | 1973 | ||
1974 | static int is_hsleep_mode_procsed(struct ath6kl_vif *vif) | ||
1975 | { | ||
1976 | return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); | ||
1977 | } | ||
1978 | |||
1979 | static bool is_ctrl_ep_empty(struct ath6kl *ar) | ||
1980 | { | ||
1981 | return !ar->tx_pending[ar->ctrl_ep]; | ||
1982 | } | ||
1983 | |||
1984 | static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif) | ||
1985 | { | ||
1986 | int ret, left; | ||
1987 | |||
1988 | clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); | ||
1989 | |||
1990 | ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, | ||
1991 | ATH6KL_HOST_MODE_ASLEEP); | ||
1992 | if (ret) | ||
1993 | return ret; | ||
1994 | |||
1995 | left = wait_event_interruptible_timeout(ar->event_wq, | ||
1996 | is_hsleep_mode_procsed(vif), | ||
1997 | WMI_TIMEOUT); | ||
1998 | if (left == 0) { | ||
1999 | ath6kl_warn("timeout, didn't get host sleep cmd processed event\n"); | ||
2000 | ret = -ETIMEDOUT; | ||
2001 | } else if (left < 0) { | ||
2002 | ath6kl_warn("error while waiting for host sleep cmd processed event %d\n", | ||
2003 | left); | ||
2004 | ret = left; | ||
2005 | } | ||
2006 | |||
2007 | if (ar->tx_pending[ar->ctrl_ep]) { | ||
2008 | left = wait_event_interruptible_timeout(ar->event_wq, | ||
2009 | is_ctrl_ep_empty(ar), | ||
2010 | WMI_TIMEOUT); | ||
2011 | if (left == 0) { | ||
2012 | ath6kl_warn("clear wmi ctrl data timeout\n"); | ||
2013 | ret = -ETIMEDOUT; | ||
2014 | } else if (left < 0) { | ||
2015 | ath6kl_warn("clear wmi ctrl data failed: %d\n", left); | ||
2016 | ret = left; | ||
2017 | } | ||
2018 | } | ||
2019 | |||
2020 | return ret; | ||
2021 | } | ||
2022 | |||
1929 | static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) | 2023 | static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) |
1930 | { | 2024 | { |
1931 | struct in_device *in_dev; | 2025 | struct in_device *in_dev; |
1932 | struct in_ifaddr *ifa; | 2026 | struct in_ifaddr *ifa; |
1933 | struct ath6kl_vif *vif; | 2027 | struct ath6kl_vif *vif; |
1934 | int ret, left; | 2028 | int ret; |
1935 | u32 filter = 0; | 2029 | u32 filter = 0; |
1936 | u16 i, bmiss_time; | 2030 | u16 i, bmiss_time; |
1937 | u8 index = 0; | 2031 | u8 index = 0; |
@@ -2032,39 +2126,11 @@ skip_arp: | |||
2032 | if (ret) | 2126 | if (ret) |
2033 | return ret; | 2127 | return ret; |
2034 | 2128 | ||
2035 | clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); | 2129 | ret = ath6kl_cfg80211_host_sleep(ar, vif); |
2036 | |||
2037 | ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, | ||
2038 | ATH6KL_HOST_MODE_ASLEEP); | ||
2039 | if (ret) | 2130 | if (ret) |
2040 | return ret; | 2131 | return ret; |
2041 | 2132 | ||
2042 | left = wait_event_interruptible_timeout(ar->event_wq, | 2133 | return 0; |
2043 | test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags), | ||
2044 | WMI_TIMEOUT); | ||
2045 | if (left == 0) { | ||
2046 | ath6kl_warn("timeout, didn't get host sleep cmd " | ||
2047 | "processed event\n"); | ||
2048 | ret = -ETIMEDOUT; | ||
2049 | } else if (left < 0) { | ||
2050 | ath6kl_warn("error while waiting for host sleep cmd " | ||
2051 | "processed event %d\n", left); | ||
2052 | ret = left; | ||
2053 | } | ||
2054 | |||
2055 | if (ar->tx_pending[ar->ctrl_ep]) { | ||
2056 | left = wait_event_interruptible_timeout(ar->event_wq, | ||
2057 | ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT); | ||
2058 | if (left == 0) { | ||
2059 | ath6kl_warn("clear wmi ctrl data timeout\n"); | ||
2060 | ret = -ETIMEDOUT; | ||
2061 | } else if (left < 0) { | ||
2062 | ath6kl_warn("clear wmi ctrl data failed: %d\n", left); | ||
2063 | ret = left; | ||
2064 | } | ||
2065 | } | ||
2066 | |||
2067 | return ret; | ||
2068 | } | 2134 | } |
2069 | 2135 | ||
2070 | static int ath6kl_wow_resume(struct ath6kl *ar) | 2136 | static int ath6kl_wow_resume(struct ath6kl *ar) |
@@ -2111,10 +2177,82 @@ static int ath6kl_wow_resume(struct ath6kl *ar) | |||
2111 | return 0; | 2177 | return 0; |
2112 | } | 2178 | } |
2113 | 2179 | ||
2180 | static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar) | ||
2181 | { | ||
2182 | struct ath6kl_vif *vif; | ||
2183 | int ret; | ||
2184 | |||
2185 | vif = ath6kl_vif_first(ar); | ||
2186 | if (!vif) | ||
2187 | return -EIO; | ||
2188 | |||
2189 | if (!ath6kl_cfg80211_ready(vif)) | ||
2190 | return -EIO; | ||
2191 | |||
2192 | ath6kl_cfg80211_stop_all(ar); | ||
2193 | |||
2194 | /* Save the current power mode before enabling power save */ | ||
2195 | ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; | ||
2196 | |||
2197 | ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER); | ||
2198 | if (ret) | ||
2199 | return ret; | ||
2200 | |||
2201 | /* Disable WOW mode */ | ||
2202 | ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, | ||
2203 | ATH6KL_WOW_MODE_DISABLE, | ||
2204 | 0, 0); | ||
2205 | if (ret) | ||
2206 | return ret; | ||
2207 | |||
2208 | /* Flush all non control pkts in TX path */ | ||
2209 | ath6kl_tx_data_cleanup(ar); | ||
2210 | |||
2211 | ret = ath6kl_cfg80211_host_sleep(ar, vif); | ||
2212 | if (ret) | ||
2213 | return ret; | ||
2214 | |||
2215 | return 0; | ||
2216 | } | ||
2217 | |||
2218 | static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar) | ||
2219 | { | ||
2220 | struct ath6kl_vif *vif; | ||
2221 | int ret; | ||
2222 | |||
2223 | vif = ath6kl_vif_first(ar); | ||
2224 | |||
2225 | if (!vif) | ||
2226 | return -EIO; | ||
2227 | |||
2228 | if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) { | ||
2229 | ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, | ||
2230 | ar->wmi->saved_pwr_mode); | ||
2231 | if (ret) | ||
2232 | return ret; | ||
2233 | } | ||
2234 | |||
2235 | ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, | ||
2236 | ATH6KL_HOST_MODE_AWAKE); | ||
2237 | if (ret) | ||
2238 | return ret; | ||
2239 | |||
2240 | ar->state = ATH6KL_STATE_ON; | ||
2241 | |||
2242 | /* Reset scan parameter to default values */ | ||
2243 | ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, | ||
2244 | 0, 0, 0, 0, 0, 0, 3, 0, 0, 0); | ||
2245 | if (ret) | ||
2246 | return ret; | ||
2247 | |||
2248 | return 0; | ||
2249 | } | ||
2250 | |||
2114 | int ath6kl_cfg80211_suspend(struct ath6kl *ar, | 2251 | int ath6kl_cfg80211_suspend(struct ath6kl *ar, |
2115 | enum ath6kl_cfg_suspend_mode mode, | 2252 | enum ath6kl_cfg_suspend_mode mode, |
2116 | struct cfg80211_wowlan *wow) | 2253 | struct cfg80211_wowlan *wow) |
2117 | { | 2254 | { |
2255 | struct ath6kl_vif *vif; | ||
2118 | enum ath6kl_state prev_state; | 2256 | enum ath6kl_state prev_state; |
2119 | int ret; | 2257 | int ret; |
2120 | 2258 | ||
@@ -2139,15 +2277,12 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, | |||
2139 | 2277 | ||
2140 | case ATH6KL_CFG_SUSPEND_DEEPSLEEP: | 2278 | case ATH6KL_CFG_SUSPEND_DEEPSLEEP: |
2141 | 2279 | ||
2142 | ath6kl_cfg80211_stop_all(ar); | 2280 | ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n"); |
2143 | |||
2144 | /* save the current power mode before enabling power save */ | ||
2145 | ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; | ||
2146 | 2281 | ||
2147 | ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER); | 2282 | ret = ath6kl_cfg80211_deepsleep_suspend(ar); |
2148 | if (ret) { | 2283 | if (ret) { |
2149 | ath6kl_warn("wmi powermode command failed during suspend: %d\n", | 2284 | ath6kl_err("deepsleep suspend failed: %d\n", ret); |
2150 | ret); | 2285 | return ret; |
2151 | } | 2286 | } |
2152 | 2287 | ||
2153 | ar->state = ATH6KL_STATE_DEEPSLEEP; | 2288 | ar->state = ATH6KL_STATE_DEEPSLEEP; |
@@ -2187,6 +2322,9 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, | |||
2187 | break; | 2322 | break; |
2188 | } | 2323 | } |
2189 | 2324 | ||
2325 | list_for_each_entry(vif, &ar->vif_list, list) | ||
2326 | ath6kl_cfg80211_scan_complete_event(vif, true); | ||
2327 | |||
2190 | return 0; | 2328 | return 0; |
2191 | } | 2329 | } |
2192 | EXPORT_SYMBOL(ath6kl_cfg80211_suspend); | 2330 | EXPORT_SYMBOL(ath6kl_cfg80211_suspend); |
@@ -2208,17 +2346,13 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) | |||
2208 | break; | 2346 | break; |
2209 | 2347 | ||
2210 | case ATH6KL_STATE_DEEPSLEEP: | 2348 | case ATH6KL_STATE_DEEPSLEEP: |
2211 | if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) { | 2349 | ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n"); |
2212 | ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, | ||
2213 | ar->wmi->saved_pwr_mode); | ||
2214 | if (ret) { | ||
2215 | ath6kl_warn("wmi powermode command failed during resume: %d\n", | ||
2216 | ret); | ||
2217 | } | ||
2218 | } | ||
2219 | |||
2220 | ar->state = ATH6KL_STATE_ON; | ||
2221 | 2350 | ||
2351 | ret = ath6kl_cfg80211_deepsleep_resume(ar); | ||
2352 | if (ret) { | ||
2353 | ath6kl_warn("deep sleep resume failed: %d\n", ret); | ||
2354 | return ret; | ||
2355 | } | ||
2222 | break; | 2356 | break; |
2223 | 2357 | ||
2224 | case ATH6KL_STATE_CUTPOWER: | 2358 | case ATH6KL_STATE_CUTPOWER: |
@@ -2292,31 +2426,25 @@ void ath6kl_check_wow_status(struct ath6kl *ar) | |||
2292 | } | 2426 | } |
2293 | #endif | 2427 | #endif |
2294 | 2428 | ||
2295 | static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, | 2429 | static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band, |
2296 | struct ieee80211_channel *chan, | 2430 | bool ht_enable) |
2297 | enum nl80211_channel_type channel_type) | ||
2298 | { | 2431 | { |
2299 | struct ath6kl_vif *vif; | 2432 | struct ath6kl_htcap *htcap = &vif->htcap; |
2300 | |||
2301 | /* | ||
2302 | * 'dev' could be NULL if a channel change is required for the hardware | ||
2303 | * device itself, instead of a particular VIF. | ||
2304 | * | ||
2305 | * FIXME: To be handled properly when monitor mode is supported. | ||
2306 | */ | ||
2307 | if (!dev) | ||
2308 | return -EBUSY; | ||
2309 | |||
2310 | vif = netdev_priv(dev); | ||
2311 | 2433 | ||
2312 | if (!ath6kl_cfg80211_ready(vif)) | 2434 | if (htcap->ht_enable == ht_enable) |
2313 | return -EIO; | 2435 | return 0; |
2314 | 2436 | ||
2315 | ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n", | 2437 | if (ht_enable) { |
2316 | __func__, chan->center_freq, chan->hw_value); | 2438 | /* Set default ht capabilities */ |
2317 | vif->next_chan = chan->center_freq; | 2439 | htcap->ht_enable = true; |
2440 | htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ? | ||
2441 | ath6kl_g_htcap : ath6kl_a_htcap; | ||
2442 | htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K; | ||
2443 | } else /* Disable ht */ | ||
2444 | memset(htcap, 0, sizeof(*htcap)); | ||
2318 | 2445 | ||
2319 | return 0; | 2446 | return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx, |
2447 | band, htcap); | ||
2320 | } | 2448 | } |
2321 | 2449 | ||
2322 | static bool ath6kl_is_p2p_ie(const u8 *pos) | 2450 | static bool ath6kl_is_p2p_ie(const u8 *pos) |
@@ -2393,6 +2521,81 @@ static int ath6kl_set_ies(struct ath6kl_vif *vif, | |||
2393 | return 0; | 2521 | return 0; |
2394 | } | 2522 | } |
2395 | 2523 | ||
2524 | static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, | ||
2525 | struct ieee80211_channel *chan, | ||
2526 | enum nl80211_channel_type channel_type) | ||
2527 | { | ||
2528 | struct ath6kl_vif *vif; | ||
2529 | |||
2530 | /* | ||
2531 | * 'dev' could be NULL if a channel change is required for the hardware | ||
2532 | * device itself, instead of a particular VIF. | ||
2533 | * | ||
2534 | * FIXME: To be handled properly when monitor mode is supported. | ||
2535 | */ | ||
2536 | if (!dev) | ||
2537 | return -EBUSY; | ||
2538 | |||
2539 | vif = netdev_priv(dev); | ||
2540 | |||
2541 | if (!ath6kl_cfg80211_ready(vif)) | ||
2542 | return -EIO; | ||
2543 | |||
2544 | ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n", | ||
2545 | __func__, chan->center_freq, chan->hw_value); | ||
2546 | vif->next_chan = chan->center_freq; | ||
2547 | vif->next_ch_type = channel_type; | ||
2548 | vif->next_ch_band = chan->band; | ||
2549 | |||
2550 | return 0; | ||
2551 | } | ||
2552 | |||
2553 | static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon, | ||
2554 | u8 *rsn_capab) | ||
2555 | { | ||
2556 | const u8 *rsn_ie; | ||
2557 | size_t rsn_ie_len; | ||
2558 | u16 cnt; | ||
2559 | |||
2560 | if (!beacon->tail) | ||
2561 | return -EINVAL; | ||
2562 | |||
2563 | rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len); | ||
2564 | if (!rsn_ie) | ||
2565 | return -EINVAL; | ||
2566 | |||
2567 | rsn_ie_len = *(rsn_ie + 1); | ||
2568 | /* skip element id and length */ | ||
2569 | rsn_ie += 2; | ||
2570 | |||
2571 | /* skip version, group cipher */ | ||
2572 | if (rsn_ie_len < 6) | ||
2573 | return -EINVAL; | ||
2574 | rsn_ie += 6; | ||
2575 | rsn_ie_len -= 6; | ||
2576 | |||
2577 | /* skip pairwise cipher suite */ | ||
2578 | if (rsn_ie_len < 2) | ||
2579 | return -EINVAL; | ||
2580 | cnt = *((u16 *) rsn_ie); | ||
2581 | rsn_ie += (2 + cnt * 4); | ||
2582 | rsn_ie_len -= (2 + cnt * 4); | ||
2583 | |||
2584 | /* skip akm suite */ | ||
2585 | if (rsn_ie_len < 2) | ||
2586 | return -EINVAL; | ||
2587 | cnt = *((u16 *) rsn_ie); | ||
2588 | rsn_ie += (2 + cnt * 4); | ||
2589 | rsn_ie_len -= (2 + cnt * 4); | ||
2590 | |||
2591 | if (rsn_ie_len < 2) | ||
2592 | return -EINVAL; | ||
2593 | |||
2594 | memcpy(rsn_capab, rsn_ie, 2); | ||
2595 | |||
2596 | return 0; | ||
2597 | } | ||
2598 | |||
2396 | static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, | 2599 | static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, |
2397 | struct cfg80211_ap_settings *info) | 2600 | struct cfg80211_ap_settings *info) |
2398 | { | 2601 | { |
@@ -2405,6 +2608,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, | |||
2405 | struct wmi_connect_cmd p; | 2608 | struct wmi_connect_cmd p; |
2406 | int res; | 2609 | int res; |
2407 | int i, ret; | 2610 | int i, ret; |
2611 | u16 rsn_capab = 0; | ||
2408 | 2612 | ||
2409 | ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__); | 2613 | ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__); |
2410 | 2614 | ||
@@ -2534,6 +2738,34 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, | |||
2534 | p.nw_subtype = SUBTYPE_NONE; | 2738 | p.nw_subtype = SUBTYPE_NONE; |
2535 | } | 2739 | } |
2536 | 2740 | ||
2741 | if (info->inactivity_timeout) { | ||
2742 | res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx, | ||
2743 | info->inactivity_timeout); | ||
2744 | if (res < 0) | ||
2745 | return res; | ||
2746 | } | ||
2747 | |||
2748 | if (ath6kl_set_htcap(vif, vif->next_ch_band, | ||
2749 | vif->next_ch_type != NL80211_CHAN_NO_HT)) | ||
2750 | return -EIO; | ||
2751 | |||
2752 | /* | ||
2753 | * Get the PTKSA replay counter in the RSN IE. Supplicant | ||
2754 | * will use the RSN IE in M3 message and firmware has to | ||
2755 | * advertise the same in beacon/probe response. Send | ||
2756 | * the complete RSN IE capability field to firmware | ||
2757 | */ | ||
2758 | if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) && | ||
2759 | test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, | ||
2760 | ar->fw_capabilities)) { | ||
2761 | res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx, | ||
2762 | WLAN_EID_RSN, WMI_RSN_IE_CAPB, | ||
2763 | (const u8 *) &rsn_capab, | ||
2764 | sizeof(rsn_capab)); | ||
2765 | if (res < 0) | ||
2766 | return res; | ||
2767 | } | ||
2768 | |||
2537 | res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); | 2769 | res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); |
2538 | if (res < 0) | 2770 | if (res < 0) |
2539 | return res; | 2771 | return res; |
@@ -2568,6 +2800,13 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) | |||
2568 | ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); | 2800 | ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); |
2569 | clear_bit(CONNECTED, &vif->flags); | 2801 | clear_bit(CONNECTED, &vif->flags); |
2570 | 2802 | ||
2803 | /* Restore ht setting in firmware */ | ||
2804 | if (ath6kl_set_htcap(vif, IEEE80211_BAND_2GHZ, true)) | ||
2805 | return -EIO; | ||
2806 | |||
2807 | if (ath6kl_set_htcap(vif, IEEE80211_BAND_5GHZ, true)) | ||
2808 | return -EIO; | ||
2809 | |||
2571 | return 0; | 2810 | return 0; |
2572 | } | 2811 | } |
2573 | 2812 | ||
@@ -2749,6 +2988,21 @@ static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif, | |||
2749 | return false; | 2988 | return false; |
2750 | } | 2989 | } |
2751 | 2990 | ||
2991 | /* Check if SSID length is greater than DIRECT- */ | ||
2992 | static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len) | ||
2993 | { | ||
2994 | const struct ieee80211_mgmt *mgmt; | ||
2995 | mgmt = (const struct ieee80211_mgmt *) buf; | ||
2996 | |||
2997 | /* variable[1] contains the SSID tag length */ | ||
2998 | if (buf + len >= &mgmt->u.probe_resp.variable[1] && | ||
2999 | (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) { | ||
3000 | return true; | ||
3001 | } | ||
3002 | |||
3003 | return false; | ||
3004 | } | ||
3005 | |||
2752 | static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, | 3006 | static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, |
2753 | struct ieee80211_channel *chan, bool offchan, | 3007 | struct ieee80211_channel *chan, bool offchan, |
2754 | enum nl80211_channel_type channel_type, | 3008 | enum nl80211_channel_type channel_type, |
@@ -2763,11 +3017,11 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, | |||
2763 | bool more_data, queued; | 3017 | bool more_data, queued; |
2764 | 3018 | ||
2765 | mgmt = (const struct ieee80211_mgmt *) buf; | 3019 | mgmt = (const struct ieee80211_mgmt *) buf; |
2766 | if (buf + len >= mgmt->u.probe_resp.variable && | 3020 | if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && |
2767 | vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && | 3021 | ieee80211_is_probe_resp(mgmt->frame_control) && |
2768 | ieee80211_is_probe_resp(mgmt->frame_control)) { | 3022 | ath6kl_is_p2p_go_ssid(buf, len)) { |
2769 | /* | 3023 | /* |
2770 | * Send Probe Response frame in AP mode using a separate WMI | 3024 | * Send Probe Response frame in GO mode using a separate WMI |
2771 | * command to allow the target to fill in the generic IEs. | 3025 | * command to allow the target to fill in the generic IEs. |
2772 | */ | 3026 | */ |
2773 | *cookie = 0; /* TX status not supported */ | 3027 | *cookie = 0; /* TX status not supported */ |
@@ -2835,6 +3089,8 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, | |||
2835 | if (vif->sme_state != SME_DISCONNECTED) | 3089 | if (vif->sme_state != SME_DISCONNECTED) |
2836 | return -EBUSY; | 3090 | return -EBUSY; |
2837 | 3091 | ||
3092 | ath6kl_cfg80211_scan_complete_event(vif, true); | ||
3093 | |||
2838 | for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) { | 3094 | for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) { |
2839 | ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, | 3095 | ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, |
2840 | i, DISABLE_SSID_FLAG, | 3096 | i, DISABLE_SSID_FLAG, |
@@ -3096,6 +3352,7 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, | |||
3096 | vif->next_mode = nw_type; | 3352 | vif->next_mode = nw_type; |
3097 | vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL; | 3353 | vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL; |
3098 | vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME; | 3354 | vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME; |
3355 | vif->htcap.ht_enable = true; | ||
3099 | 3356 | ||
3100 | memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); | 3357 | memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); |
3101 | if (fw_vif_idx != 0) | 3358 | if (fw_vif_idx != 0) |
@@ -3183,6 +3440,10 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) | |||
3183 | if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities)) | 3440 | if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities)) |
3184 | ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; | 3441 | ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; |
3185 | 3442 | ||
3443 | if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, | ||
3444 | ar->fw_capabilities)) | ||
3445 | ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER; | ||
3446 | |||
3186 | ar->wiphy->probe_resp_offload = | 3447 | ar->wiphy->probe_resp_offload = |
3187 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | | 3448 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | |
3188 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | | 3449 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | |
diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h index a60e78c0472f..98a886154d9c 100644 --- a/drivers/net/wireless/ath/ath6kl/common.h +++ b/drivers/net/wireless/ath/ath6kl/common.h | |||
@@ -22,7 +22,8 @@ | |||
22 | 22 | ||
23 | #define ATH6KL_MAX_IE 256 | 23 | #define ATH6KL_MAX_IE 256 |
24 | 24 | ||
25 | extern int ath6kl_printk(const char *level, const char *fmt, ...); | 25 | extern __printf(2, 3) |
26 | int ath6kl_printk(const char *level, const char *fmt, ...); | ||
26 | 27 | ||
27 | /* | 28 | /* |
28 | * Reflects the version of binary interface exposed by ATH6KL target | 29 | * Reflects the version of binary interface exposed by ATH6KL target |
@@ -77,6 +78,7 @@ enum crypto_type { | |||
77 | 78 | ||
78 | struct htc_endpoint_credit_dist; | 79 | struct htc_endpoint_credit_dist; |
79 | struct ath6kl; | 80 | struct ath6kl; |
81 | struct ath6kl_htcap; | ||
80 | enum htc_credit_dist_reason; | 82 | enum htc_credit_dist_reason; |
81 | struct ath6kl_htc_credit_info; | 83 | struct ath6kl_htc_credit_info; |
82 | 84 | ||
diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 45e641f3a41b..fdb3b1decc76 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c | |||
@@ -20,9 +20,11 @@ | |||
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/moduleparam.h> | 21 | #include <linux/moduleparam.h> |
22 | #include <linux/export.h> | 22 | #include <linux/export.h> |
23 | #include <linux/vmalloc.h> | ||
23 | 24 | ||
24 | #include "debug.h" | 25 | #include "debug.h" |
25 | #include "hif-ops.h" | 26 | #include "hif-ops.h" |
27 | #include "htc-ops.h" | ||
26 | #include "cfg80211.h" | 28 | #include "cfg80211.h" |
27 | 29 | ||
28 | unsigned int debug_mask; | 30 | unsigned int debug_mask; |
@@ -39,12 +41,36 @@ module_param(uart_debug, uint, 0644); | |||
39 | module_param(ath6kl_p2p, uint, 0644); | 41 | module_param(ath6kl_p2p, uint, 0644); |
40 | module_param(testmode, uint, 0644); | 42 | module_param(testmode, uint, 0644); |
41 | 43 | ||
42 | int ath6kl_core_init(struct ath6kl *ar) | 44 | void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) |
45 | { | ||
46 | ath6kl_htc_tx_complete(ar, skb); | ||
47 | } | ||
48 | EXPORT_SYMBOL(ath6kl_core_tx_complete); | ||
49 | |||
50 | void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe) | ||
51 | { | ||
52 | ath6kl_htc_rx_complete(ar, skb, pipe); | ||
53 | } | ||
54 | EXPORT_SYMBOL(ath6kl_core_rx_complete); | ||
55 | |||
56 | int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) | ||
43 | { | 57 | { |
44 | struct ath6kl_bmi_target_info targ_info; | 58 | struct ath6kl_bmi_target_info targ_info; |
45 | struct net_device *ndev; | 59 | struct net_device *ndev; |
46 | int ret = 0, i; | 60 | int ret = 0, i; |
47 | 61 | ||
62 | switch (htc_type) { | ||
63 | case ATH6KL_HTC_TYPE_MBOX: | ||
64 | ath6kl_htc_mbox_attach(ar); | ||
65 | break; | ||
66 | case ATH6KL_HTC_TYPE_PIPE: | ||
67 | ath6kl_htc_pipe_attach(ar); | ||
68 | break; | ||
69 | default: | ||
70 | WARN_ON(1); | ||
71 | return -ENOMEM; | ||
72 | } | ||
73 | |||
48 | ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); | 74 | ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); |
49 | if (!ar->ath6kl_wq) | 75 | if (!ar->ath6kl_wq) |
50 | return -ENOMEM; | 76 | return -ENOMEM; |
@@ -280,7 +306,7 @@ void ath6kl_core_cleanup(struct ath6kl *ar) | |||
280 | 306 | ||
281 | kfree(ar->fw_board); | 307 | kfree(ar->fw_board); |
282 | kfree(ar->fw_otp); | 308 | kfree(ar->fw_otp); |
283 | kfree(ar->fw); | 309 | vfree(ar->fw); |
284 | kfree(ar->fw_patch); | 310 | kfree(ar->fw_patch); |
285 | kfree(ar->fw_testscript); | 311 | kfree(ar->fw_testscript); |
286 | 312 | ||
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index f1dd8906be45..9d67964a51dd 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h | |||
@@ -91,6 +91,15 @@ enum ath6kl_fw_capability { | |||
91 | */ | 91 | */ |
92 | ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, | 92 | ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, |
93 | 93 | ||
94 | /* | ||
95 | * Firmware has support to cleanup inactive stations | ||
96 | * in AP mode. | ||
97 | */ | ||
98 | ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, | ||
99 | |||
100 | /* Firmware has support to override rsn cap of rsn ie */ | ||
101 | ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, | ||
102 | |||
94 | /* this needs to be last */ | 103 | /* this needs to be last */ |
95 | ATH6KL_FW_CAPABILITY_MAX, | 104 | ATH6KL_FW_CAPABILITY_MAX, |
96 | }; | 105 | }; |
@@ -205,6 +214,8 @@ struct ath6kl_fw_ie { | |||
205 | #define ATH6KL_CONF_ENABLE_TX_BURST BIT(3) | 214 | #define ATH6KL_CONF_ENABLE_TX_BURST BIT(3) |
206 | #define ATH6KL_CONF_UART_DEBUG BIT(4) | 215 | #define ATH6KL_CONF_UART_DEBUG BIT(4) |
207 | 216 | ||
217 | #define P2P_WILDCARD_SSID_LEN 7 /* DIRECT- */ | ||
218 | |||
208 | enum wlan_low_pwr_state { | 219 | enum wlan_low_pwr_state { |
209 | WLAN_POWER_STATE_ON, | 220 | WLAN_POWER_STATE_ON, |
210 | WLAN_POWER_STATE_CUT_PWR, | 221 | WLAN_POWER_STATE_CUT_PWR, |
@@ -454,6 +465,11 @@ enum ath6kl_hif_type { | |||
454 | ATH6KL_HIF_TYPE_USB, | 465 | ATH6KL_HIF_TYPE_USB, |
455 | }; | 466 | }; |
456 | 467 | ||
468 | enum ath6kl_htc_type { | ||
469 | ATH6KL_HTC_TYPE_MBOX, | ||
470 | ATH6KL_HTC_TYPE_PIPE, | ||
471 | }; | ||
472 | |||
457 | /* Max number of filters that hw supports */ | 473 | /* Max number of filters that hw supports */ |
458 | #define ATH6K_MAX_MC_FILTERS_PER_LIST 7 | 474 | #define ATH6K_MAX_MC_FILTERS_PER_LIST 7 |
459 | struct ath6kl_mc_filter { | 475 | struct ath6kl_mc_filter { |
@@ -461,6 +477,12 @@ struct ath6kl_mc_filter { | |||
461 | char hw_addr[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; | 477 | char hw_addr[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; |
462 | }; | 478 | }; |
463 | 479 | ||
480 | struct ath6kl_htcap { | ||
481 | bool ht_enable; | ||
482 | u8 ampdu_factor; | ||
483 | unsigned short cap_info; | ||
484 | }; | ||
485 | |||
464 | /* | 486 | /* |
465 | * Driver's maximum limit, note that some firmwares support only one vif | 487 | * Driver's maximum limit, note that some firmwares support only one vif |
466 | * and the runtime (current) limit must be checked from ar->vif_max. | 488 | * and the runtime (current) limit must be checked from ar->vif_max. |
@@ -509,6 +531,7 @@ struct ath6kl_vif { | |||
509 | struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1]; | 531 | struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1]; |
510 | struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1]; | 532 | struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1]; |
511 | struct aggr_info *aggr_cntxt; | 533 | struct aggr_info *aggr_cntxt; |
534 | struct ath6kl_htcap htcap; | ||
512 | 535 | ||
513 | struct timer_list disconnect_timer; | 536 | struct timer_list disconnect_timer; |
514 | struct timer_list sched_scan_timer; | 537 | struct timer_list sched_scan_timer; |
@@ -521,6 +544,8 @@ struct ath6kl_vif { | |||
521 | u32 send_action_id; | 544 | u32 send_action_id; |
522 | bool probe_req_report; | 545 | bool probe_req_report; |
523 | u16 next_chan; | 546 | u16 next_chan; |
547 | enum nl80211_channel_type next_ch_type; | ||
548 | enum ieee80211_band next_ch_band; | ||
524 | u16 assoc_bss_beacon_int; | 549 | u16 assoc_bss_beacon_int; |
525 | u16 listen_intvl_t; | 550 | u16 listen_intvl_t; |
526 | u16 bmiss_time_t; | 551 | u16 bmiss_time_t; |
@@ -568,6 +593,7 @@ struct ath6kl { | |||
568 | 593 | ||
569 | struct ath6kl_bmi bmi; | 594 | struct ath6kl_bmi bmi; |
570 | const struct ath6kl_hif_ops *hif_ops; | 595 | const struct ath6kl_hif_ops *hif_ops; |
596 | const struct ath6kl_htc_ops *htc_ops; | ||
571 | struct wmi *wmi; | 597 | struct wmi *wmi; |
572 | int tx_pending[ENDPOINT_MAX]; | 598 | int tx_pending[ENDPOINT_MAX]; |
573 | int total_tx_data_pend; | 599 | int total_tx_data_pend; |
@@ -746,7 +772,8 @@ void init_netdev(struct net_device *dev); | |||
746 | void ath6kl_cookie_init(struct ath6kl *ar); | 772 | void ath6kl_cookie_init(struct ath6kl *ar); |
747 | void ath6kl_cookie_cleanup(struct ath6kl *ar); | 773 | void ath6kl_cookie_cleanup(struct ath6kl *ar); |
748 | void ath6kl_rx(struct htc_target *target, struct htc_packet *packet); | 774 | void ath6kl_rx(struct htc_target *target, struct htc_packet *packet); |
749 | void ath6kl_tx_complete(void *context, struct list_head *packet_queue); | 775 | void ath6kl_tx_complete(struct htc_target *context, |
776 | struct list_head *packet_queue); | ||
750 | enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, | 777 | enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, |
751 | struct htc_packet *packet); | 778 | struct htc_packet *packet); |
752 | void ath6kl_stop_txrx(struct ath6kl *ar); | 779 | void ath6kl_stop_txrx(struct ath6kl *ar); |
@@ -821,8 +848,11 @@ int ath6kl_init_hw_params(struct ath6kl *ar); | |||
821 | 848 | ||
822 | void ath6kl_check_wow_status(struct ath6kl *ar); | 849 | void ath6kl_check_wow_status(struct ath6kl *ar); |
823 | 850 | ||
851 | void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb); | ||
852 | void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe); | ||
853 | |||
824 | struct ath6kl *ath6kl_core_create(struct device *dev); | 854 | struct ath6kl *ath6kl_core_create(struct device *dev); |
825 | int ath6kl_core_init(struct ath6kl *ar); | 855 | int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type); |
826 | void ath6kl_core_cleanup(struct ath6kl *ar); | 856 | void ath6kl_core_cleanup(struct ath6kl *ar); |
827 | void ath6kl_core_destroy(struct ath6kl *ar); | 857 | void ath6kl_core_destroy(struct ath6kl *ar); |
828 | 858 | ||
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index d01403a263ff..1b76aff78508 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c | |||
@@ -616,6 +616,12 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, | |||
616 | "Num disconnects", tgt_stats->cs_discon_cnt); | 616 | "Num disconnects", tgt_stats->cs_discon_cnt); |
617 | len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", | 617 | len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", |
618 | "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi); | 618 | "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi); |
619 | len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", | ||
620 | "ARP pkt received", tgt_stats->arp_received); | ||
621 | len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", | ||
622 | "ARP pkt matched", tgt_stats->arp_matched); | ||
623 | len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", | ||
624 | "ARP pkt replied", tgt_stats->arp_replied); | ||
619 | 625 | ||
620 | if (len > buf_len) | 626 | if (len > buf_len) |
621 | len = buf_len; | 627 | len = buf_len; |
diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 1803a0baae82..49639d8266c2 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h | |||
@@ -43,6 +43,7 @@ enum ATH6K_DEBUG_MASK { | |||
43 | ATH6KL_DBG_WMI_DUMP = BIT(19), | 43 | ATH6KL_DBG_WMI_DUMP = BIT(19), |
44 | ATH6KL_DBG_SUSPEND = BIT(20), | 44 | ATH6KL_DBG_SUSPEND = BIT(20), |
45 | ATH6KL_DBG_USB = BIT(21), | 45 | ATH6KL_DBG_USB = BIT(21), |
46 | ATH6KL_DBG_USB_BULK = BIT(22), | ||
46 | ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ | 47 | ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ |
47 | }; | 48 | }; |
48 | 49 | ||
diff --git a/drivers/net/wireless/ath/ath6kl/hif-ops.h b/drivers/net/wireless/ath/ath6kl/hif-ops.h index fd84086638e3..8c9e72d5250d 100644 --- a/drivers/net/wireless/ath/ath6kl/hif-ops.h +++ b/drivers/net/wireless/ath/ath6kl/hif-ops.h | |||
@@ -150,4 +150,38 @@ static inline void ath6kl_hif_stop(struct ath6kl *ar) | |||
150 | ar->hif_ops->stop(ar); | 150 | ar->hif_ops->stop(ar); |
151 | } | 151 | } |
152 | 152 | ||
153 | static inline int ath6kl_hif_pipe_send(struct ath6kl *ar, | ||
154 | u8 pipe, struct sk_buff *hdr_buf, | ||
155 | struct sk_buff *buf) | ||
156 | { | ||
157 | ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe send\n"); | ||
158 | |||
159 | return ar->hif_ops->pipe_send(ar, pipe, hdr_buf, buf); | ||
160 | } | ||
161 | |||
162 | static inline void ath6kl_hif_pipe_get_default(struct ath6kl *ar, | ||
163 | u8 *ul_pipe, u8 *dl_pipe) | ||
164 | { | ||
165 | ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n"); | ||
166 | |||
167 | ar->hif_ops->pipe_get_default(ar, ul_pipe, dl_pipe); | ||
168 | } | ||
169 | |||
170 | static inline int ath6kl_hif_pipe_map_service(struct ath6kl *ar, | ||
171 | u16 service_id, u8 *ul_pipe, | ||
172 | u8 *dl_pipe) | ||
173 | { | ||
174 | ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n"); | ||
175 | |||
176 | return ar->hif_ops->pipe_map_service(ar, service_id, ul_pipe, dl_pipe); | ||
177 | } | ||
178 | |||
179 | static inline u16 ath6kl_hif_pipe_get_free_queue_number(struct ath6kl *ar, | ||
180 | u8 pipe) | ||
181 | { | ||
182 | ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get free queue number\n"); | ||
183 | |||
184 | return ar->hif_ops->pipe_get_free_queue_number(ar, pipe); | ||
185 | } | ||
186 | |||
153 | #endif | 187 | #endif |
diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h index 20ed6b73517b..61f6b21fb0ae 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.h +++ b/drivers/net/wireless/ath/ath6kl/hif.h | |||
@@ -256,6 +256,12 @@ struct ath6kl_hif_ops { | |||
256 | int (*power_on)(struct ath6kl *ar); | 256 | int (*power_on)(struct ath6kl *ar); |
257 | int (*power_off)(struct ath6kl *ar); | 257 | int (*power_off)(struct ath6kl *ar); |
258 | void (*stop)(struct ath6kl *ar); | 258 | void (*stop)(struct ath6kl *ar); |
259 | int (*pipe_send)(struct ath6kl *ar, u8 pipe, struct sk_buff *hdr_buf, | ||
260 | struct sk_buff *buf); | ||
261 | void (*pipe_get_default)(struct ath6kl *ar, u8 *pipe_ul, u8 *pipe_dl); | ||
262 | int (*pipe_map_service)(struct ath6kl *ar, u16 service_id, u8 *pipe_ul, | ||
263 | u8 *pipe_dl); | ||
264 | u16 (*pipe_get_free_queue_number)(struct ath6kl *ar, u8 pipe); | ||
259 | }; | 265 | }; |
260 | 266 | ||
261 | int ath6kl_hif_setup(struct ath6kl_device *dev); | 267 | int ath6kl_hif_setup(struct ath6kl_device *dev); |
diff --git a/drivers/net/wireless/ath/ath6kl/htc-ops.h b/drivers/net/wireless/ath/ath6kl/htc-ops.h new file mode 100644 index 000000000000..2d4eed55cfd1 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/htc-ops.h | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2004-2011 Atheros Communications Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #ifndef HTC_OPS_H | ||
18 | #define HTC_OPS_H | ||
19 | |||
20 | #include "htc.h" | ||
21 | #include "debug.h" | ||
22 | |||
23 | static inline void *ath6kl_htc_create(struct ath6kl *ar) | ||
24 | { | ||
25 | return ar->htc_ops->create(ar); | ||
26 | } | ||
27 | |||
28 | static inline int ath6kl_htc_wait_target(struct htc_target *target) | ||
29 | { | ||
30 | return target->dev->ar->htc_ops->wait_target(target); | ||
31 | } | ||
32 | |||
33 | static inline int ath6kl_htc_start(struct htc_target *target) | ||
34 | { | ||
35 | return target->dev->ar->htc_ops->start(target); | ||
36 | } | ||
37 | |||
38 | static inline int ath6kl_htc_conn_service(struct htc_target *target, | ||
39 | struct htc_service_connect_req *req, | ||
40 | struct htc_service_connect_resp *resp) | ||
41 | { | ||
42 | return target->dev->ar->htc_ops->conn_service(target, req, resp); | ||
43 | } | ||
44 | |||
45 | static inline int ath6kl_htc_tx(struct htc_target *target, | ||
46 | struct htc_packet *packet) | ||
47 | { | ||
48 | return target->dev->ar->htc_ops->tx(target, packet); | ||
49 | } | ||
50 | |||
51 | static inline void ath6kl_htc_stop(struct htc_target *target) | ||
52 | { | ||
53 | return target->dev->ar->htc_ops->stop(target); | ||
54 | } | ||
55 | |||
56 | static inline void ath6kl_htc_cleanup(struct htc_target *target) | ||
57 | { | ||
58 | return target->dev->ar->htc_ops->cleanup(target); | ||
59 | } | ||
60 | |||
61 | static inline void ath6kl_htc_flush_txep(struct htc_target *target, | ||
62 | enum htc_endpoint_id endpoint, | ||
63 | u16 tag) | ||
64 | { | ||
65 | return target->dev->ar->htc_ops->flush_txep(target, endpoint, tag); | ||
66 | } | ||
67 | |||
68 | static inline void ath6kl_htc_flush_rx_buf(struct htc_target *target) | ||
69 | { | ||
70 | return target->dev->ar->htc_ops->flush_rx_buf(target); | ||
71 | } | ||
72 | |||
73 | static inline void ath6kl_htc_activity_changed(struct htc_target *target, | ||
74 | enum htc_endpoint_id endpoint, | ||
75 | bool active) | ||
76 | { | ||
77 | return target->dev->ar->htc_ops->activity_changed(target, endpoint, | ||
78 | active); | ||
79 | } | ||
80 | |||
81 | static inline int ath6kl_htc_get_rxbuf_num(struct htc_target *target, | ||
82 | enum htc_endpoint_id endpoint) | ||
83 | { | ||
84 | return target->dev->ar->htc_ops->get_rxbuf_num(target, endpoint); | ||
85 | } | ||
86 | |||
87 | static inline int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, | ||
88 | struct list_head *pktq) | ||
89 | { | ||
90 | return target->dev->ar->htc_ops->add_rxbuf_multiple(target, pktq); | ||
91 | } | ||
92 | |||
93 | static inline int ath6kl_htc_credit_setup(struct htc_target *target, | ||
94 | struct ath6kl_htc_credit_info *info) | ||
95 | { | ||
96 | return target->dev->ar->htc_ops->credit_setup(target, info); | ||
97 | } | ||
98 | |||
99 | static inline void ath6kl_htc_tx_complete(struct ath6kl *ar, | ||
100 | struct sk_buff *skb) | ||
101 | { | ||
102 | ar->htc_ops->tx_complete(ar, skb); | ||
103 | } | ||
104 | |||
105 | |||
106 | static inline void ath6kl_htc_rx_complete(struct ath6kl *ar, | ||
107 | struct sk_buff *skb, u8 pipe) | ||
108 | { | ||
109 | ar->htc_ops->rx_complete(ar, skb, pipe); | ||
110 | } | ||
111 | |||
112 | |||
113 | #endif | ||
diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h index 5027ccc36b62..a2c8ff809793 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.h +++ b/drivers/net/wireless/ath/ath6kl/htc.h | |||
@@ -25,6 +25,7 @@ | |||
25 | /* send direction */ | 25 | /* send direction */ |
26 | #define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0) | 26 | #define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0) |
27 | #define HTC_FLAGS_SEND_BUNDLE (1 << 1) | 27 | #define HTC_FLAGS_SEND_BUNDLE (1 << 1) |
28 | #define HTC_FLAGS_TX_FIXUP_NETBUF (1 << 2) | ||
28 | 29 | ||
29 | /* receive direction */ | 30 | /* receive direction */ |
30 | #define HTC_FLG_RX_UNUSED (1 << 0) | 31 | #define HTC_FLG_RX_UNUSED (1 << 0) |
@@ -56,6 +57,10 @@ | |||
56 | #define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT 0x2 | 57 | #define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT 0x2 |
57 | #define HTC_CONN_FLGS_REDUCE_CRED_DRIB 0x4 | 58 | #define HTC_CONN_FLGS_REDUCE_CRED_DRIB 0x4 |
58 | #define HTC_CONN_FLGS_THRESH_MASK 0x3 | 59 | #define HTC_CONN_FLGS_THRESH_MASK 0x3 |
60 | /* disable credit flow control on a specific service */ | ||
61 | #define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL (1 << 3) | ||
62 | #define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT 8 | ||
63 | #define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00 | ||
59 | 64 | ||
60 | /* connect response status codes */ | 65 | /* connect response status codes */ |
61 | #define HTC_SERVICE_SUCCESS 0 | 66 | #define HTC_SERVICE_SUCCESS 0 |
@@ -75,6 +80,7 @@ | |||
75 | #define HTC_RECORD_LOOKAHEAD_BUNDLE 3 | 80 | #define HTC_RECORD_LOOKAHEAD_BUNDLE 3 |
76 | 81 | ||
77 | #define HTC_SETUP_COMP_FLG_RX_BNDL_EN (1 << 0) | 82 | #define HTC_SETUP_COMP_FLG_RX_BNDL_EN (1 << 0) |
83 | #define HTC_SETUP_COMP_FLG_DISABLE_TX_CREDIT_FLOW (1 << 1) | ||
78 | 84 | ||
79 | #define MAKE_SERVICE_ID(group, index) \ | 85 | #define MAKE_SERVICE_ID(group, index) \ |
80 | (int)(((int)group << 8) | (int)(index)) | 86 | (int)(((int)group << 8) | (int)(index)) |
@@ -109,6 +115,8 @@ | |||
109 | 115 | ||
110 | /* HTC operational parameters */ | 116 | /* HTC operational parameters */ |
111 | #define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */ | 117 | #define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */ |
118 | #define HTC_TARGET_RESPONSE_POLL_WAIT 10 | ||
119 | #define HTC_TARGET_RESPONSE_POLL_COUNT 200 | ||
112 | #define HTC_TARGET_DEBUG_INTR_MASK 0x01 | 120 | #define HTC_TARGET_DEBUG_INTR_MASK 0x01 |
113 | #define HTC_TARGET_CREDIT_INTR_MASK 0xF0 | 121 | #define HTC_TARGET_CREDIT_INTR_MASK 0xF0 |
114 | 122 | ||
@@ -128,6 +136,7 @@ | |||
128 | 136 | ||
129 | #define HTC_RECV_WAIT_BUFFERS (1 << 0) | 137 | #define HTC_RECV_WAIT_BUFFERS (1 << 0) |
130 | #define HTC_OP_STATE_STOPPING (1 << 0) | 138 | #define HTC_OP_STATE_STOPPING (1 << 0) |
139 | #define HTC_OP_STATE_SETUP_COMPLETE (1 << 1) | ||
131 | 140 | ||
132 | /* | 141 | /* |
133 | * The frame header length and message formats defined herein were selected | 142 | * The frame header length and message formats defined herein were selected |
@@ -311,6 +320,14 @@ struct htc_packet { | |||
311 | 320 | ||
312 | void (*completion) (struct htc_target *, struct htc_packet *); | 321 | void (*completion) (struct htc_target *, struct htc_packet *); |
313 | struct htc_target *context; | 322 | struct htc_target *context; |
323 | |||
324 | /* | ||
325 | * optimization for network-oriented data, the HTC packet | ||
326 | * can pass the network buffer corresponding to the HTC packet | ||
327 | * lower layers may optimized the transfer knowing this is | ||
328 | * a network buffer | ||
329 | */ | ||
330 | struct sk_buff *skb; | ||
314 | }; | 331 | }; |
315 | 332 | ||
316 | enum htc_send_full_action { | 333 | enum htc_send_full_action { |
@@ -319,12 +336,14 @@ enum htc_send_full_action { | |||
319 | }; | 336 | }; |
320 | 337 | ||
321 | struct htc_ep_callbacks { | 338 | struct htc_ep_callbacks { |
339 | void (*tx_complete) (struct htc_target *, struct htc_packet *); | ||
322 | void (*rx) (struct htc_target *, struct htc_packet *); | 340 | void (*rx) (struct htc_target *, struct htc_packet *); |
323 | void (*rx_refill) (struct htc_target *, enum htc_endpoint_id endpoint); | 341 | void (*rx_refill) (struct htc_target *, enum htc_endpoint_id endpoint); |
324 | enum htc_send_full_action (*tx_full) (struct htc_target *, | 342 | enum htc_send_full_action (*tx_full) (struct htc_target *, |
325 | struct htc_packet *); | 343 | struct htc_packet *); |
326 | struct htc_packet *(*rx_allocthresh) (struct htc_target *, | 344 | struct htc_packet *(*rx_allocthresh) (struct htc_target *, |
327 | enum htc_endpoint_id, int); | 345 | enum htc_endpoint_id, int); |
346 | void (*tx_comp_multi) (struct htc_target *, struct list_head *); | ||
328 | int rx_alloc_thresh; | 347 | int rx_alloc_thresh; |
329 | int rx_refill_thresh; | 348 | int rx_refill_thresh; |
330 | }; | 349 | }; |
@@ -502,6 +521,13 @@ struct htc_endpoint { | |||
502 | u32 conn_flags; | 521 | u32 conn_flags; |
503 | struct htc_endpoint_stats ep_st; | 522 | struct htc_endpoint_stats ep_st; |
504 | u16 tx_drop_packet_threshold; | 523 | u16 tx_drop_packet_threshold; |
524 | |||
525 | struct { | ||
526 | u8 pipeid_ul; | ||
527 | u8 pipeid_dl; | ||
528 | struct list_head tx_lookup_queue; | ||
529 | bool tx_credit_flow_enabled; | ||
530 | } pipe; | ||
505 | }; | 531 | }; |
506 | 532 | ||
507 | struct htc_control_buffer { | 533 | struct htc_control_buffer { |
@@ -509,6 +535,42 @@ struct htc_control_buffer { | |||
509 | u8 *buf; | 535 | u8 *buf; |
510 | }; | 536 | }; |
511 | 537 | ||
538 | struct htc_pipe_txcredit_alloc { | ||
539 | u16 service_id; | ||
540 | u8 credit_alloc; | ||
541 | }; | ||
542 | |||
543 | enum htc_send_queue_result { | ||
544 | HTC_SEND_QUEUE_OK = 0, /* packet was queued */ | ||
545 | HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */ | ||
546 | }; | ||
547 | |||
548 | struct ath6kl_htc_ops { | ||
549 | void* (*create)(struct ath6kl *ar); | ||
550 | int (*wait_target)(struct htc_target *target); | ||
551 | int (*start)(struct htc_target *target); | ||
552 | int (*conn_service)(struct htc_target *target, | ||
553 | struct htc_service_connect_req *req, | ||
554 | struct htc_service_connect_resp *resp); | ||
555 | int (*tx)(struct htc_target *target, struct htc_packet *packet); | ||
556 | void (*stop)(struct htc_target *target); | ||
557 | void (*cleanup)(struct htc_target *target); | ||
558 | void (*flush_txep)(struct htc_target *target, | ||
559 | enum htc_endpoint_id endpoint, u16 tag); | ||
560 | void (*flush_rx_buf)(struct htc_target *target); | ||
561 | void (*activity_changed)(struct htc_target *target, | ||
562 | enum htc_endpoint_id endpoint, | ||
563 | bool active); | ||
564 | int (*get_rxbuf_num)(struct htc_target *target, | ||
565 | enum htc_endpoint_id endpoint); | ||
566 | int (*add_rxbuf_multiple)(struct htc_target *target, | ||
567 | struct list_head *pktq); | ||
568 | int (*credit_setup)(struct htc_target *target, | ||
569 | struct ath6kl_htc_credit_info *cred_info); | ||
570 | int (*tx_complete)(struct ath6kl *ar, struct sk_buff *skb); | ||
571 | int (*rx_complete)(struct ath6kl *ar, struct sk_buff *skb, u8 pipe); | ||
572 | }; | ||
573 | |||
512 | struct ath6kl_device; | 574 | struct ath6kl_device; |
513 | 575 | ||
514 | /* our HTC target state */ | 576 | /* our HTC target state */ |
@@ -557,36 +619,19 @@ struct htc_target { | |||
557 | 619 | ||
558 | /* counts the number of Tx without bundling continously per AC */ | 620 | /* counts the number of Tx without bundling continously per AC */ |
559 | u32 ac_tx_count[WMM_NUM_AC]; | 621 | u32 ac_tx_count[WMM_NUM_AC]; |
622 | |||
623 | struct { | ||
624 | struct htc_packet *htc_packet_pool; | ||
625 | u8 ctrl_response_buf[HTC_MAX_CTRL_MSG_LEN]; | ||
626 | int ctrl_response_len; | ||
627 | bool ctrl_response_valid; | ||
628 | struct htc_pipe_txcredit_alloc txcredit_alloc[ENDPOINT_MAX]; | ||
629 | } pipe; | ||
560 | }; | 630 | }; |
561 | 631 | ||
562 | void *ath6kl_htc_create(struct ath6kl *ar); | ||
563 | void ath6kl_htc_set_credit_dist(struct htc_target *target, | ||
564 | struct ath6kl_htc_credit_info *cred_info, | ||
565 | u16 svc_pri_order[], int len); | ||
566 | int ath6kl_htc_wait_target(struct htc_target *target); | ||
567 | int ath6kl_htc_start(struct htc_target *target); | ||
568 | int ath6kl_htc_conn_service(struct htc_target *target, | ||
569 | struct htc_service_connect_req *req, | ||
570 | struct htc_service_connect_resp *resp); | ||
571 | int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet); | ||
572 | void ath6kl_htc_stop(struct htc_target *target); | ||
573 | void ath6kl_htc_cleanup(struct htc_target *target); | ||
574 | void ath6kl_htc_flush_txep(struct htc_target *target, | ||
575 | enum htc_endpoint_id endpoint, u16 tag); | ||
576 | void ath6kl_htc_flush_rx_buf(struct htc_target *target); | ||
577 | void ath6kl_htc_indicate_activity_change(struct htc_target *target, | ||
578 | enum htc_endpoint_id endpoint, | ||
579 | bool active); | ||
580 | int ath6kl_htc_get_rxbuf_num(struct htc_target *target, | ||
581 | enum htc_endpoint_id endpoint); | ||
582 | int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, | ||
583 | struct list_head *pktq); | ||
584 | int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, | 632 | int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, |
585 | u32 msg_look_ahead, int *n_pkts); | 633 | u32 msg_look_ahead, int *n_pkts); |
586 | 634 | ||
587 | int ath6kl_credit_setup(void *htc_handle, | ||
588 | struct ath6kl_htc_credit_info *cred_info); | ||
589 | |||
590 | static inline void set_htc_pkt_info(struct htc_packet *packet, void *context, | 635 | static inline void set_htc_pkt_info(struct htc_packet *packet, void *context, |
591 | u8 *buf, unsigned int len, | 636 | u8 *buf, unsigned int len, |
592 | enum htc_endpoint_id eid, u16 tag) | 637 | enum htc_endpoint_id eid, u16 tag) |
@@ -626,4 +671,7 @@ static inline int get_queue_depth(struct list_head *queue) | |||
626 | return depth; | 671 | return depth; |
627 | } | 672 | } |
628 | 673 | ||
674 | void ath6kl_htc_pipe_attach(struct ath6kl *ar); | ||
675 | void ath6kl_htc_mbox_attach(struct ath6kl *ar); | ||
676 | |||
629 | #endif | 677 | #endif |
diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index 4849d99cce77..065e61516d7a 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c | |||
@@ -23,6 +23,14 @@ | |||
23 | 23 | ||
24 | #define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) | 24 | #define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) |
25 | 25 | ||
26 | static void ath6kl_htc_mbox_cleanup(struct htc_target *target); | ||
27 | static void ath6kl_htc_mbox_stop(struct htc_target *target); | ||
28 | static int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target, | ||
29 | struct list_head *pkt_queue); | ||
30 | static void ath6kl_htc_set_credit_dist(struct htc_target *target, | ||
31 | struct ath6kl_htc_credit_info *cred_info, | ||
32 | u16 svc_pri_order[], int len); | ||
33 | |||
26 | /* threshold to re-enable Tx bundling for an AC*/ | 34 | /* threshold to re-enable Tx bundling for an AC*/ |
27 | #define TX_RESUME_BUNDLE_THRESHOLD 1500 | 35 | #define TX_RESUME_BUNDLE_THRESHOLD 1500 |
28 | 36 | ||
@@ -130,8 +138,8 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, | |||
130 | } | 138 | } |
131 | 139 | ||
132 | /* initialize and setup credit distribution */ | 140 | /* initialize and setup credit distribution */ |
133 | int ath6kl_credit_setup(void *htc_handle, | 141 | static int ath6kl_htc_mbox_credit_setup(struct htc_target *htc_target, |
134 | struct ath6kl_htc_credit_info *cred_info) | 142 | struct ath6kl_htc_credit_info *cred_info) |
135 | { | 143 | { |
136 | u16 servicepriority[5]; | 144 | u16 servicepriority[5]; |
137 | 145 | ||
@@ -144,7 +152,7 @@ int ath6kl_credit_setup(void *htc_handle, | |||
144 | servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */ | 152 | servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */ |
145 | 153 | ||
146 | /* set priority list */ | 154 | /* set priority list */ |
147 | ath6kl_htc_set_credit_dist(htc_handle, cred_info, servicepriority, 5); | 155 | ath6kl_htc_set_credit_dist(htc_target, cred_info, servicepriority, 5); |
148 | 156 | ||
149 | return 0; | 157 | return 0; |
150 | } | 158 | } |
@@ -432,7 +440,7 @@ static void htc_tx_complete(struct htc_endpoint *endpoint, | |||
432 | "htc tx complete ep %d pkts %d\n", | 440 | "htc tx complete ep %d pkts %d\n", |
433 | endpoint->eid, get_queue_depth(txq)); | 441 | endpoint->eid, get_queue_depth(txq)); |
434 | 442 | ||
435 | ath6kl_tx_complete(endpoint->target->dev->ar, txq); | 443 | ath6kl_tx_complete(endpoint->target, txq); |
436 | } | 444 | } |
437 | 445 | ||
438 | static void htc_tx_comp_handler(struct htc_target *target, | 446 | static void htc_tx_comp_handler(struct htc_target *target, |
@@ -1065,7 +1073,7 @@ static int htc_setup_tx_complete(struct htc_target *target) | |||
1065 | return status; | 1073 | return status; |
1066 | } | 1074 | } |
1067 | 1075 | ||
1068 | void ath6kl_htc_set_credit_dist(struct htc_target *target, | 1076 | static void ath6kl_htc_set_credit_dist(struct htc_target *target, |
1069 | struct ath6kl_htc_credit_info *credit_info, | 1077 | struct ath6kl_htc_credit_info *credit_info, |
1070 | u16 srvc_pri_order[], int list_len) | 1078 | u16 srvc_pri_order[], int list_len) |
1071 | { | 1079 | { |
@@ -1093,7 +1101,8 @@ void ath6kl_htc_set_credit_dist(struct htc_target *target, | |||
1093 | } | 1101 | } |
1094 | } | 1102 | } |
1095 | 1103 | ||
1096 | int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet) | 1104 | static int ath6kl_htc_mbox_tx(struct htc_target *target, |
1105 | struct htc_packet *packet) | ||
1097 | { | 1106 | { |
1098 | struct htc_endpoint *endpoint; | 1107 | struct htc_endpoint *endpoint; |
1099 | struct list_head queue; | 1108 | struct list_head queue; |
@@ -1121,7 +1130,7 @@ int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet) | |||
1121 | } | 1130 | } |
1122 | 1131 | ||
1123 | /* flush endpoint TX queue */ | 1132 | /* flush endpoint TX queue */ |
1124 | void ath6kl_htc_flush_txep(struct htc_target *target, | 1133 | static void ath6kl_htc_mbox_flush_txep(struct htc_target *target, |
1125 | enum htc_endpoint_id eid, u16 tag) | 1134 | enum htc_endpoint_id eid, u16 tag) |
1126 | { | 1135 | { |
1127 | struct htc_packet *packet, *tmp_pkt; | 1136 | struct htc_packet *packet, *tmp_pkt; |
@@ -1173,12 +1182,13 @@ static void ath6kl_htc_flush_txep_all(struct htc_target *target) | |||
1173 | if (endpoint->svc_id == 0) | 1182 | if (endpoint->svc_id == 0) |
1174 | /* not in use.. */ | 1183 | /* not in use.. */ |
1175 | continue; | 1184 | continue; |
1176 | ath6kl_htc_flush_txep(target, i, HTC_TX_PACKET_TAG_ALL); | 1185 | ath6kl_htc_mbox_flush_txep(target, i, HTC_TX_PACKET_TAG_ALL); |
1177 | } | 1186 | } |
1178 | } | 1187 | } |
1179 | 1188 | ||
1180 | void ath6kl_htc_indicate_activity_change(struct htc_target *target, | 1189 | static void ath6kl_htc_mbox_activity_changed(struct htc_target *target, |
1181 | enum htc_endpoint_id eid, bool active) | 1190 | enum htc_endpoint_id eid, |
1191 | bool active) | ||
1182 | { | 1192 | { |
1183 | struct htc_endpoint *endpoint = &target->endpoint[eid]; | 1193 | struct htc_endpoint *endpoint = &target->endpoint[eid]; |
1184 | bool dist = false; | 1194 | bool dist = false; |
@@ -1246,7 +1256,7 @@ static int htc_add_rxbuf(struct htc_target *target, struct htc_packet *packet) | |||
1246 | 1256 | ||
1247 | INIT_LIST_HEAD(&queue); | 1257 | INIT_LIST_HEAD(&queue); |
1248 | list_add_tail(&packet->list, &queue); | 1258 | list_add_tail(&packet->list, &queue); |
1249 | return ath6kl_htc_add_rxbuf_multiple(target, &queue); | 1259 | return ath6kl_htc_mbox_add_rxbuf_multiple(target, &queue); |
1250 | } | 1260 | } |
1251 | 1261 | ||
1252 | static void htc_reclaim_rxbuf(struct htc_target *target, | 1262 | static void htc_reclaim_rxbuf(struct htc_target *target, |
@@ -1353,7 +1363,9 @@ static int ath6kl_htc_rx_setup(struct htc_target *target, | |||
1353 | sizeof(*htc_hdr)); | 1363 | sizeof(*htc_hdr)); |
1354 | 1364 | ||
1355 | if (!htc_valid_rx_frame_len(target, ep->eid, full_len)) { | 1365 | if (!htc_valid_rx_frame_len(target, ep->eid, full_len)) { |
1356 | ath6kl_warn("Rx buffer requested with invalid length\n"); | 1366 | ath6kl_warn("Rx buffer requested with invalid length htc_hdr:eid %d, flags 0x%x, len %d\n", |
1367 | htc_hdr->eid, htc_hdr->flags, | ||
1368 | le16_to_cpu(htc_hdr->payld_len)); | ||
1357 | return -EINVAL; | 1369 | return -EINVAL; |
1358 | } | 1370 | } |
1359 | 1371 | ||
@@ -2288,7 +2300,7 @@ fail_ctrl_rx: | |||
2288 | return NULL; | 2300 | return NULL; |
2289 | } | 2301 | } |
2290 | 2302 | ||
2291 | int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, | 2303 | static int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target, |
2292 | struct list_head *pkt_queue) | 2304 | struct list_head *pkt_queue) |
2293 | { | 2305 | { |
2294 | struct htc_endpoint *endpoint; | 2306 | struct htc_endpoint *endpoint; |
@@ -2350,7 +2362,7 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, | |||
2350 | return status; | 2362 | return status; |
2351 | } | 2363 | } |
2352 | 2364 | ||
2353 | void ath6kl_htc_flush_rx_buf(struct htc_target *target) | 2365 | static void ath6kl_htc_mbox_flush_rx_buf(struct htc_target *target) |
2354 | { | 2366 | { |
2355 | struct htc_endpoint *endpoint; | 2367 | struct htc_endpoint *endpoint; |
2356 | struct htc_packet *packet, *tmp_pkt; | 2368 | struct htc_packet *packet, *tmp_pkt; |
@@ -2392,7 +2404,7 @@ void ath6kl_htc_flush_rx_buf(struct htc_target *target) | |||
2392 | } | 2404 | } |
2393 | } | 2405 | } |
2394 | 2406 | ||
2395 | int ath6kl_htc_conn_service(struct htc_target *target, | 2407 | static int ath6kl_htc_mbox_conn_service(struct htc_target *target, |
2396 | struct htc_service_connect_req *conn_req, | 2408 | struct htc_service_connect_req *conn_req, |
2397 | struct htc_service_connect_resp *conn_resp) | 2409 | struct htc_service_connect_resp *conn_resp) |
2398 | { | 2410 | { |
@@ -2564,7 +2576,7 @@ static void reset_ep_state(struct htc_target *target) | |||
2564 | INIT_LIST_HEAD(&target->cred_dist_list); | 2576 | INIT_LIST_HEAD(&target->cred_dist_list); |
2565 | } | 2577 | } |
2566 | 2578 | ||
2567 | int ath6kl_htc_get_rxbuf_num(struct htc_target *target, | 2579 | static int ath6kl_htc_mbox_get_rxbuf_num(struct htc_target *target, |
2568 | enum htc_endpoint_id endpoint) | 2580 | enum htc_endpoint_id endpoint) |
2569 | { | 2581 | { |
2570 | int num; | 2582 | int num; |
@@ -2624,7 +2636,7 @@ static void htc_setup_msg_bndl(struct htc_target *target) | |||
2624 | } | 2636 | } |
2625 | } | 2637 | } |
2626 | 2638 | ||
2627 | int ath6kl_htc_wait_target(struct htc_target *target) | 2639 | static int ath6kl_htc_mbox_wait_target(struct htc_target *target) |
2628 | { | 2640 | { |
2629 | struct htc_packet *packet = NULL; | 2641 | struct htc_packet *packet = NULL; |
2630 | struct htc_ready_ext_msg *rdy_msg; | 2642 | struct htc_ready_ext_msg *rdy_msg; |
@@ -2693,12 +2705,12 @@ int ath6kl_htc_wait_target(struct htc_target *target) | |||
2693 | connect.svc_id = HTC_CTRL_RSVD_SVC; | 2705 | connect.svc_id = HTC_CTRL_RSVD_SVC; |
2694 | 2706 | ||
2695 | /* connect fake service */ | 2707 | /* connect fake service */ |
2696 | status = ath6kl_htc_conn_service((void *)target, &connect, &resp); | 2708 | status = ath6kl_htc_mbox_conn_service((void *)target, &connect, &resp); |
2697 | 2709 | ||
2698 | if (status) | 2710 | if (status) |
2699 | /* | 2711 | /* |
2700 | * FIXME: this call doesn't make sense, the caller should | 2712 | * FIXME: this call doesn't make sense, the caller should |
2701 | * call ath6kl_htc_cleanup() when it wants remove htc | 2713 | * call ath6kl_htc_mbox_cleanup() when it wants remove htc |
2702 | */ | 2714 | */ |
2703 | ath6kl_hif_cleanup_scatter(target->dev->ar); | 2715 | ath6kl_hif_cleanup_scatter(target->dev->ar); |
2704 | 2716 | ||
@@ -2715,7 +2727,7 @@ fail_wait_target: | |||
2715 | * Start HTC, enable interrupts and let the target know | 2727 | * Start HTC, enable interrupts and let the target know |
2716 | * host has finished setup. | 2728 | * host has finished setup. |
2717 | */ | 2729 | */ |
2718 | int ath6kl_htc_start(struct htc_target *target) | 2730 | static int ath6kl_htc_mbox_start(struct htc_target *target) |
2719 | { | 2731 | { |
2720 | struct htc_packet *packet; | 2732 | struct htc_packet *packet; |
2721 | int status; | 2733 | int status; |
@@ -2752,7 +2764,7 @@ int ath6kl_htc_start(struct htc_target *target) | |||
2752 | status = ath6kl_hif_unmask_intrs(target->dev); | 2764 | status = ath6kl_hif_unmask_intrs(target->dev); |
2753 | 2765 | ||
2754 | if (status) | 2766 | if (status) |
2755 | ath6kl_htc_stop(target); | 2767 | ath6kl_htc_mbox_stop(target); |
2756 | 2768 | ||
2757 | return status; | 2769 | return status; |
2758 | } | 2770 | } |
@@ -2796,7 +2808,7 @@ static int ath6kl_htc_reset(struct htc_target *target) | |||
2796 | } | 2808 | } |
2797 | 2809 | ||
2798 | /* htc_stop: stop interrupt reception, and flush all queued buffers */ | 2810 | /* htc_stop: stop interrupt reception, and flush all queued buffers */ |
2799 | void ath6kl_htc_stop(struct htc_target *target) | 2811 | static void ath6kl_htc_mbox_stop(struct htc_target *target) |
2800 | { | 2812 | { |
2801 | spin_lock_bh(&target->htc_lock); | 2813 | spin_lock_bh(&target->htc_lock); |
2802 | target->htc_flags |= HTC_OP_STATE_STOPPING; | 2814 | target->htc_flags |= HTC_OP_STATE_STOPPING; |
@@ -2811,12 +2823,12 @@ void ath6kl_htc_stop(struct htc_target *target) | |||
2811 | 2823 | ||
2812 | ath6kl_htc_flush_txep_all(target); | 2824 | ath6kl_htc_flush_txep_all(target); |
2813 | 2825 | ||
2814 | ath6kl_htc_flush_rx_buf(target); | 2826 | ath6kl_htc_mbox_flush_rx_buf(target); |
2815 | 2827 | ||
2816 | ath6kl_htc_reset(target); | 2828 | ath6kl_htc_reset(target); |
2817 | } | 2829 | } |
2818 | 2830 | ||
2819 | void *ath6kl_htc_create(struct ath6kl *ar) | 2831 | static void *ath6kl_htc_mbox_create(struct ath6kl *ar) |
2820 | { | 2832 | { |
2821 | struct htc_target *target = NULL; | 2833 | struct htc_target *target = NULL; |
2822 | int status = 0; | 2834 | int status = 0; |
@@ -2857,13 +2869,13 @@ void *ath6kl_htc_create(struct ath6kl *ar) | |||
2857 | return target; | 2869 | return target; |
2858 | 2870 | ||
2859 | err_htc_cleanup: | 2871 | err_htc_cleanup: |
2860 | ath6kl_htc_cleanup(target); | 2872 | ath6kl_htc_mbox_cleanup(target); |
2861 | 2873 | ||
2862 | return NULL; | 2874 | return NULL; |
2863 | } | 2875 | } |
2864 | 2876 | ||
2865 | /* cleanup the HTC instance */ | 2877 | /* cleanup the HTC instance */ |
2866 | void ath6kl_htc_cleanup(struct htc_target *target) | 2878 | static void ath6kl_htc_mbox_cleanup(struct htc_target *target) |
2867 | { | 2879 | { |
2868 | struct htc_packet *packet, *tmp_packet; | 2880 | struct htc_packet *packet, *tmp_packet; |
2869 | 2881 | ||
@@ -2888,3 +2900,24 @@ void ath6kl_htc_cleanup(struct htc_target *target) | |||
2888 | kfree(target->dev); | 2900 | kfree(target->dev); |
2889 | kfree(target); | 2901 | kfree(target); |
2890 | } | 2902 | } |
2903 | |||
2904 | static const struct ath6kl_htc_ops ath6kl_htc_mbox_ops = { | ||
2905 | .create = ath6kl_htc_mbox_create, | ||
2906 | .wait_target = ath6kl_htc_mbox_wait_target, | ||
2907 | .start = ath6kl_htc_mbox_start, | ||
2908 | .conn_service = ath6kl_htc_mbox_conn_service, | ||
2909 | .tx = ath6kl_htc_mbox_tx, | ||
2910 | .stop = ath6kl_htc_mbox_stop, | ||
2911 | .cleanup = ath6kl_htc_mbox_cleanup, | ||
2912 | .flush_txep = ath6kl_htc_mbox_flush_txep, | ||
2913 | .flush_rx_buf = ath6kl_htc_mbox_flush_rx_buf, | ||
2914 | .activity_changed = ath6kl_htc_mbox_activity_changed, | ||
2915 | .get_rxbuf_num = ath6kl_htc_mbox_get_rxbuf_num, | ||
2916 | .add_rxbuf_multiple = ath6kl_htc_mbox_add_rxbuf_multiple, | ||
2917 | .credit_setup = ath6kl_htc_mbox_credit_setup, | ||
2918 | }; | ||
2919 | |||
2920 | void ath6kl_htc_mbox_attach(struct ath6kl *ar) | ||
2921 | { | ||
2922 | ar->htc_ops = &ath6kl_htc_mbox_ops; | ||
2923 | } | ||
diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c new file mode 100644 index 000000000000..b277b3446882 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c | |||
@@ -0,0 +1,1713 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2007-2011 Atheros Communications Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include "core.h" | ||
18 | #include "debug.h" | ||
19 | #include "hif-ops.h" | ||
20 | |||
21 | #define HTC_PACKET_CONTAINER_ALLOCATION 32 | ||
22 | #define HTC_CONTROL_BUFFER_SIZE (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH) | ||
23 | |||
24 | static int ath6kl_htc_pipe_tx(struct htc_target *handle, | ||
25 | struct htc_packet *packet); | ||
26 | static void ath6kl_htc_pipe_cleanup(struct htc_target *handle); | ||
27 | |||
28 | /* htc pipe tx path */ | ||
29 | static inline void restore_tx_packet(struct htc_packet *packet) | ||
30 | { | ||
31 | if (packet->info.tx.flags & HTC_FLAGS_TX_FIXUP_NETBUF) { | ||
32 | skb_pull(packet->skb, sizeof(struct htc_frame_hdr)); | ||
33 | packet->info.tx.flags &= ~HTC_FLAGS_TX_FIXUP_NETBUF; | ||
34 | } | ||
35 | } | ||
36 | |||
37 | static void do_send_completion(struct htc_endpoint *ep, | ||
38 | struct list_head *queue_to_indicate) | ||
39 | { | ||
40 | struct htc_packet *packet; | ||
41 | |||
42 | if (list_empty(queue_to_indicate)) { | ||
43 | /* nothing to indicate */ | ||
44 | return; | ||
45 | } | ||
46 | |||
47 | if (ep->ep_cb.tx_comp_multi != NULL) { | ||
48 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
49 | "%s: calling ep %d, send complete multiple callback (%d pkts)\n", | ||
50 | __func__, ep->eid, | ||
51 | get_queue_depth(queue_to_indicate)); | ||
52 | /* | ||
53 | * a multiple send complete handler is being used, | ||
54 | * pass the queue to the handler | ||
55 | */ | ||
56 | ep->ep_cb.tx_comp_multi(ep->target, queue_to_indicate); | ||
57 | /* | ||
58 | * all packets are now owned by the callback, | ||
59 | * reset queue to be safe | ||
60 | */ | ||
61 | INIT_LIST_HEAD(queue_to_indicate); | ||
62 | } else { | ||
63 | /* using legacy EpTxComplete */ | ||
64 | do { | ||
65 | packet = list_first_entry(queue_to_indicate, | ||
66 | struct htc_packet, list); | ||
67 | |||
68 | list_del(&packet->list); | ||
69 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
70 | "%s: calling ep %d send complete callback on packet 0x%p\n", | ||
71 | __func__, ep->eid, packet); | ||
72 | ep->ep_cb.tx_complete(ep->target, packet); | ||
73 | } while (!list_empty(queue_to_indicate)); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | static void send_packet_completion(struct htc_target *target, | ||
78 | struct htc_packet *packet) | ||
79 | { | ||
80 | struct htc_endpoint *ep = &target->endpoint[packet->endpoint]; | ||
81 | struct list_head container; | ||
82 | |||
83 | restore_tx_packet(packet); | ||
84 | INIT_LIST_HEAD(&container); | ||
85 | list_add_tail(&packet->list, &container); | ||
86 | |||
87 | /* do completion */ | ||
88 | do_send_completion(ep, &container); | ||
89 | } | ||
90 | |||
91 | static void get_htc_packet_credit_based(struct htc_target *target, | ||
92 | struct htc_endpoint *ep, | ||
93 | struct list_head *queue) | ||
94 | { | ||
95 | int credits_required; | ||
96 | int remainder; | ||
97 | u8 send_flags; | ||
98 | struct htc_packet *packet; | ||
99 | unsigned int transfer_len; | ||
100 | |||
101 | /* NOTE : the TX lock is held when this function is called */ | ||
102 | |||
103 | /* loop until we can grab as many packets out of the queue as we can */ | ||
104 | while (true) { | ||
105 | send_flags = 0; | ||
106 | if (list_empty(&ep->txq)) | ||
107 | break; | ||
108 | |||
109 | /* get packet at head, but don't remove it */ | ||
110 | packet = list_first_entry(&ep->txq, struct htc_packet, list); | ||
111 | if (packet == NULL) | ||
112 | break; | ||
113 | |||
114 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
115 | "%s: got head packet:0x%p , queue depth: %d\n", | ||
116 | __func__, packet, get_queue_depth(&ep->txq)); | ||
117 | |||
118 | transfer_len = packet->act_len + HTC_HDR_LENGTH; | ||
119 | |||
120 | if (transfer_len <= target->tgt_cred_sz) { | ||
121 | credits_required = 1; | ||
122 | } else { | ||
123 | /* figure out how many credits this message requires */ | ||
124 | credits_required = transfer_len / target->tgt_cred_sz; | ||
125 | remainder = transfer_len % target->tgt_cred_sz; | ||
126 | |||
127 | if (remainder) | ||
128 | credits_required++; | ||
129 | } | ||
130 | |||
131 | ath6kl_dbg(ATH6KL_DBG_HTC, "%s: creds required:%d got:%d\n", | ||
132 | __func__, credits_required, ep->cred_dist.credits); | ||
133 | |||
134 | if (ep->eid == ENDPOINT_0) { | ||
135 | /* | ||
136 | * endpoint 0 is special, it always has a credit and | ||
137 | * does not require credit based flow control | ||
138 | */ | ||
139 | credits_required = 0; | ||
140 | |||
141 | } else { | ||
142 | |||
143 | if (ep->cred_dist.credits < credits_required) | ||
144 | break; | ||
145 | |||
146 | ep->cred_dist.credits -= credits_required; | ||
147 | ep->ep_st.cred_cosumd += credits_required; | ||
148 | |||
149 | /* check if we need credits back from the target */ | ||
150 | if (ep->cred_dist.credits < | ||
151 | ep->cred_dist.cred_per_msg) { | ||
152 | /* tell the target we need credits ASAP! */ | ||
153 | send_flags |= HTC_FLAGS_NEED_CREDIT_UPDATE; | ||
154 | ep->ep_st.cred_low_indicate += 1; | ||
155 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
156 | "%s: host needs credits\n", | ||
157 | __func__); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | /* now we can fully dequeue */ | ||
162 | packet = list_first_entry(&ep->txq, struct htc_packet, list); | ||
163 | |||
164 | list_del(&packet->list); | ||
165 | /* save the number of credits this packet consumed */ | ||
166 | packet->info.tx.cred_used = credits_required; | ||
167 | /* save send flags */ | ||
168 | packet->info.tx.flags = send_flags; | ||
169 | packet->info.tx.seqno = ep->seqno; | ||
170 | ep->seqno++; | ||
171 | /* queue this packet into the caller's queue */ | ||
172 | list_add_tail(&packet->list, queue); | ||
173 | } | ||
174 | |||
175 | } | ||
176 | |||
177 | static void get_htc_packet(struct htc_target *target, | ||
178 | struct htc_endpoint *ep, | ||
179 | struct list_head *queue, int resources) | ||
180 | { | ||
181 | struct htc_packet *packet; | ||
182 | |||
183 | /* NOTE : the TX lock is held when this function is called */ | ||
184 | |||
185 | /* loop until we can grab as many packets out of the queue as we can */ | ||
186 | while (resources) { | ||
187 | if (list_empty(&ep->txq)) | ||
188 | break; | ||
189 | |||
190 | packet = list_first_entry(&ep->txq, struct htc_packet, list); | ||
191 | list_del(&packet->list); | ||
192 | |||
193 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
194 | "%s: got packet:0x%p , new queue depth: %d\n", | ||
195 | __func__, packet, get_queue_depth(&ep->txq)); | ||
196 | packet->info.tx.seqno = ep->seqno; | ||
197 | packet->info.tx.flags = 0; | ||
198 | packet->info.tx.cred_used = 0; | ||
199 | ep->seqno++; | ||
200 | |||
201 | /* queue this packet into the caller's queue */ | ||
202 | list_add_tail(&packet->list, queue); | ||
203 | resources--; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | static int htc_issue_packets(struct htc_target *target, | ||
208 | struct htc_endpoint *ep, | ||
209 | struct list_head *pkt_queue) | ||
210 | { | ||
211 | int status = 0; | ||
212 | u16 payload_len; | ||
213 | struct sk_buff *skb; | ||
214 | struct htc_frame_hdr *htc_hdr; | ||
215 | struct htc_packet *packet; | ||
216 | |||
217 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
218 | "%s: queue: 0x%p, pkts %d\n", __func__, | ||
219 | pkt_queue, get_queue_depth(pkt_queue)); | ||
220 | |||
221 | while (!list_empty(pkt_queue)) { | ||
222 | packet = list_first_entry(pkt_queue, struct htc_packet, list); | ||
223 | list_del(&packet->list); | ||
224 | |||
225 | skb = packet->skb; | ||
226 | if (!skb) { | ||
227 | WARN_ON_ONCE(1); | ||
228 | status = -EINVAL; | ||
229 | break; | ||
230 | } | ||
231 | |||
232 | payload_len = packet->act_len; | ||
233 | |||
234 | /* setup HTC frame header */ | ||
235 | htc_hdr = (struct htc_frame_hdr *) skb_push(skb, | ||
236 | sizeof(*htc_hdr)); | ||
237 | if (!htc_hdr) { | ||
238 | WARN_ON_ONCE(1); | ||
239 | status = -EINVAL; | ||
240 | break; | ||
241 | } | ||
242 | |||
243 | packet->info.tx.flags |= HTC_FLAGS_TX_FIXUP_NETBUF; | ||
244 | |||
245 | /* Endianess? */ | ||
246 | put_unaligned((u16) payload_len, &htc_hdr->payld_len); | ||
247 | htc_hdr->flags = packet->info.tx.flags; | ||
248 | htc_hdr->eid = (u8) packet->endpoint; | ||
249 | htc_hdr->ctrl[0] = 0; | ||
250 | htc_hdr->ctrl[1] = (u8) packet->info.tx.seqno; | ||
251 | |||
252 | spin_lock_bh(&target->tx_lock); | ||
253 | |||
254 | /* store in look up queue to match completions */ | ||
255 | list_add_tail(&packet->list, &ep->pipe.tx_lookup_queue); | ||
256 | ep->ep_st.tx_issued += 1; | ||
257 | spin_unlock_bh(&target->tx_lock); | ||
258 | |||
259 | status = ath6kl_hif_pipe_send(target->dev->ar, | ||
260 | ep->pipe.pipeid_ul, NULL, skb); | ||
261 | |||
262 | if (status != 0) { | ||
263 | if (status != -ENOMEM) { | ||
264 | /* TODO: if more than 1 endpoint maps to the | ||
265 | * same PipeID, it is possible to run out of | ||
266 | * resources in the HIF layer. | ||
267 | * Don't emit the error | ||
268 | */ | ||
269 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
270 | "%s: failed status:%d\n", | ||
271 | __func__, status); | ||
272 | } | ||
273 | spin_lock_bh(&target->tx_lock); | ||
274 | list_del(&packet->list); | ||
275 | |||
276 | /* reclaim credits */ | ||
277 | ep->cred_dist.credits += packet->info.tx.cred_used; | ||
278 | spin_unlock_bh(&target->tx_lock); | ||
279 | |||
280 | /* put it back into the callers queue */ | ||
281 | list_add(&packet->list, pkt_queue); | ||
282 | break; | ||
283 | } | ||
284 | |||
285 | } | ||
286 | |||
287 | if (status != 0) { | ||
288 | while (!list_empty(pkt_queue)) { | ||
289 | if (status != -ENOMEM) { | ||
290 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
291 | "%s: failed pkt:0x%p status:%d\n", | ||
292 | __func__, packet, status); | ||
293 | } | ||
294 | |||
295 | packet = list_first_entry(pkt_queue, | ||
296 | struct htc_packet, list); | ||
297 | list_del(&packet->list); | ||
298 | packet->status = status; | ||
299 | send_packet_completion(target, packet); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | return status; | ||
304 | } | ||
305 | |||
306 | static enum htc_send_queue_result htc_try_send(struct htc_target *target, | ||
307 | struct htc_endpoint *ep, | ||
308 | struct list_head *txq) | ||
309 | { | ||
310 | struct list_head send_queue; /* temp queue to hold packets */ | ||
311 | struct htc_packet *packet, *tmp_pkt; | ||
312 | struct ath6kl *ar = target->dev->ar; | ||
313 | enum htc_send_full_action action; | ||
314 | int tx_resources, overflow, txqueue_depth, i, good_pkts; | ||
315 | u8 pipeid; | ||
316 | |||
317 | ath6kl_dbg(ATH6KL_DBG_HTC, "%s: (queue:0x%p depth:%d)\n", | ||
318 | __func__, txq, | ||
319 | (txq == NULL) ? 0 : get_queue_depth(txq)); | ||
320 | |||
321 | /* init the local send queue */ | ||
322 | INIT_LIST_HEAD(&send_queue); | ||
323 | |||
324 | /* | ||
325 | * txq equals to NULL means | ||
326 | * caller didn't provide a queue, just wants us to | ||
327 | * check queues and send | ||
328 | */ | ||
329 | if (txq != NULL) { | ||
330 | if (list_empty(txq)) { | ||
331 | /* empty queue */ | ||
332 | return HTC_SEND_QUEUE_DROP; | ||
333 | } | ||
334 | |||
335 | spin_lock_bh(&target->tx_lock); | ||
336 | txqueue_depth = get_queue_depth(&ep->txq); | ||
337 | spin_unlock_bh(&target->tx_lock); | ||
338 | |||
339 | if (txqueue_depth >= ep->max_txq_depth) { | ||
340 | /* we've already overflowed */ | ||
341 | overflow = get_queue_depth(txq); | ||
342 | } else { | ||
343 | /* get how much we will overflow by */ | ||
344 | overflow = txqueue_depth; | ||
345 | overflow += get_queue_depth(txq); | ||
346 | /* get how much we will overflow the TX queue by */ | ||
347 | overflow -= ep->max_txq_depth; | ||
348 | } | ||
349 | |||
350 | /* if overflow is negative or zero, we are okay */ | ||
351 | if (overflow > 0) { | ||
352 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
353 | "%s: Endpoint %d, TX queue will overflow :%d, Tx Depth:%d, Max:%d\n", | ||
354 | __func__, ep->eid, overflow, txqueue_depth, | ||
355 | ep->max_txq_depth); | ||
356 | } | ||
357 | if ((overflow <= 0) || | ||
358 | (ep->ep_cb.tx_full == NULL)) { | ||
359 | /* | ||
360 | * all packets will fit or caller did not provide send | ||
361 | * full indication handler -- just move all of them | ||
362 | * to the local send_queue object | ||
363 | */ | ||
364 | list_splice_tail_init(txq, &send_queue); | ||
365 | } else { | ||
366 | good_pkts = get_queue_depth(txq) - overflow; | ||
367 | if (good_pkts < 0) { | ||
368 | WARN_ON_ONCE(1); | ||
369 | return HTC_SEND_QUEUE_DROP; | ||
370 | } | ||
371 | |||
372 | /* we have overflowed, and a callback is provided */ | ||
373 | /* dequeue all non-overflow packets to the sendqueue */ | ||
374 | for (i = 0; i < good_pkts; i++) { | ||
375 | /* pop off caller's queue */ | ||
376 | packet = list_first_entry(txq, | ||
377 | struct htc_packet, | ||
378 | list); | ||
379 | list_del(&packet->list); | ||
380 | /* insert into local queue */ | ||
381 | list_add_tail(&packet->list, &send_queue); | ||
382 | } | ||
383 | |||
384 | /* | ||
385 | * the caller's queue has all the packets that won't fit | ||
386 | * walk through the caller's queue and indicate each to | ||
387 | * the send full handler | ||
388 | */ | ||
389 | list_for_each_entry_safe(packet, tmp_pkt, | ||
390 | txq, list) { | ||
391 | |||
392 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
393 | "%s: Indicat overflowed TX pkts: %p\n", | ||
394 | __func__, packet); | ||
395 | action = ep->ep_cb.tx_full(ep->target, packet); | ||
396 | if (action == HTC_SEND_FULL_DROP) { | ||
397 | /* callback wants the packet dropped */ | ||
398 | ep->ep_st.tx_dropped += 1; | ||
399 | |||
400 | /* leave this one in the caller's queue | ||
401 | * for cleanup */ | ||
402 | } else { | ||
403 | /* callback wants to keep this packet, | ||
404 | * remove from caller's queue */ | ||
405 | list_del(&packet->list); | ||
406 | /* put it in the send queue */ | ||
407 | list_add_tail(&packet->list, | ||
408 | &send_queue); | ||
409 | } | ||
410 | |||
411 | } | ||
412 | |||
413 | if (list_empty(&send_queue)) { | ||
414 | /* no packets made it in, caller will cleanup */ | ||
415 | return HTC_SEND_QUEUE_DROP; | ||
416 | } | ||
417 | } | ||
418 | } | ||
419 | |||
420 | if (!ep->pipe.tx_credit_flow_enabled) { | ||
421 | tx_resources = | ||
422 | ath6kl_hif_pipe_get_free_queue_number(ar, | ||
423 | ep->pipe.pipeid_ul); | ||
424 | } else { | ||
425 | tx_resources = 0; | ||
426 | } | ||
427 | |||
428 | spin_lock_bh(&target->tx_lock); | ||
429 | if (!list_empty(&send_queue)) { | ||
430 | /* transfer packets to tail */ | ||
431 | list_splice_tail_init(&send_queue, &ep->txq); | ||
432 | if (!list_empty(&send_queue)) { | ||
433 | WARN_ON_ONCE(1); | ||
434 | spin_unlock_bh(&target->tx_lock); | ||
435 | return HTC_SEND_QUEUE_DROP; | ||
436 | } | ||
437 | INIT_LIST_HEAD(&send_queue); | ||
438 | } | ||
439 | |||
440 | /* increment tx processing count on entry */ | ||
441 | ep->tx_proc_cnt++; | ||
442 | |||
443 | if (ep->tx_proc_cnt > 1) { | ||
444 | /* | ||
445 | * Another thread or task is draining the TX queues on this | ||
446 | * endpoint that thread will reset the tx processing count | ||
447 | * when the queue is drained. | ||
448 | */ | ||
449 | ep->tx_proc_cnt--; | ||
450 | spin_unlock_bh(&target->tx_lock); | ||
451 | return HTC_SEND_QUEUE_OK; | ||
452 | } | ||
453 | |||
454 | /***** beyond this point only 1 thread may enter ******/ | ||
455 | |||
456 | /* | ||
457 | * Now drain the endpoint TX queue for transmission as long as we have | ||
458 | * enough transmit resources. | ||
459 | */ | ||
460 | while (true) { | ||
461 | |||
462 | if (get_queue_depth(&ep->txq) == 0) | ||
463 | break; | ||
464 | |||
465 | if (ep->pipe.tx_credit_flow_enabled) { | ||
466 | /* | ||
467 | * Credit based mechanism provides flow control | ||
468 | * based on target transmit resource availability, | ||
469 | * we assume that the HIF layer will always have | ||
470 | * bus resources greater than target transmit | ||
471 | * resources. | ||
472 | */ | ||
473 | get_htc_packet_credit_based(target, ep, &send_queue); | ||
474 | } else { | ||
475 | /* | ||
476 | * Get all packets for this endpoint that we can | ||
477 | * for this pass. | ||
478 | */ | ||
479 | get_htc_packet(target, ep, &send_queue, tx_resources); | ||
480 | } | ||
481 | |||
482 | if (get_queue_depth(&send_queue) == 0) { | ||
483 | /* | ||
484 | * Didn't get packets due to out of resources or TX | ||
485 | * queue was drained. | ||
486 | */ | ||
487 | break; | ||
488 | } | ||
489 | |||
490 | spin_unlock_bh(&target->tx_lock); | ||
491 | |||
492 | /* send what we can */ | ||
493 | htc_issue_packets(target, ep, &send_queue); | ||
494 | |||
495 | if (!ep->pipe.tx_credit_flow_enabled) { | ||
496 | pipeid = ep->pipe.pipeid_ul; | ||
497 | tx_resources = | ||
498 | ath6kl_hif_pipe_get_free_queue_number(ar, pipeid); | ||
499 | } | ||
500 | |||
501 | spin_lock_bh(&target->tx_lock); | ||
502 | |||
503 | } | ||
504 | /* done with this endpoint, we can clear the count */ | ||
505 | ep->tx_proc_cnt = 0; | ||
506 | spin_unlock_bh(&target->tx_lock); | ||
507 | |||
508 | return HTC_SEND_QUEUE_OK; | ||
509 | } | ||
510 | |||
511 | /* htc control packet manipulation */ | ||
512 | static void destroy_htc_txctrl_packet(struct htc_packet *packet) | ||
513 | { | ||
514 | struct sk_buff *skb; | ||
515 | skb = packet->skb; | ||
516 | if (skb != NULL) | ||
517 | dev_kfree_skb(skb); | ||
518 | |||
519 | kfree(packet); | ||
520 | } | ||
521 | |||
522 | static struct htc_packet *build_htc_txctrl_packet(void) | ||
523 | { | ||
524 | struct htc_packet *packet = NULL; | ||
525 | struct sk_buff *skb; | ||
526 | |||
527 | packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL); | ||
528 | if (packet == NULL) | ||
529 | return NULL; | ||
530 | |||
531 | skb = __dev_alloc_skb(HTC_CONTROL_BUFFER_SIZE, GFP_KERNEL); | ||
532 | |||
533 | if (skb == NULL) { | ||
534 | kfree(packet); | ||
535 | return NULL; | ||
536 | } | ||
537 | packet->skb = skb; | ||
538 | |||
539 | return packet; | ||
540 | } | ||
541 | |||
542 | static void htc_free_txctrl_packet(struct htc_target *target, | ||
543 | struct htc_packet *packet) | ||
544 | { | ||
545 | destroy_htc_txctrl_packet(packet); | ||
546 | } | ||
547 | |||
548 | static struct htc_packet *htc_alloc_txctrl_packet(struct htc_target *target) | ||
549 | { | ||
550 | return build_htc_txctrl_packet(); | ||
551 | } | ||
552 | |||
553 | static void htc_txctrl_complete(struct htc_target *target, | ||
554 | struct htc_packet *packet) | ||
555 | { | ||
556 | htc_free_txctrl_packet(target, packet); | ||
557 | } | ||
558 | |||
559 | #define MAX_MESSAGE_SIZE 1536 | ||
560 | |||
561 | static int htc_setup_target_buffer_assignments(struct htc_target *target) | ||
562 | { | ||
563 | int status, credits, credit_per_maxmsg, i; | ||
564 | struct htc_pipe_txcredit_alloc *entry; | ||
565 | unsigned int hif_usbaudioclass = 0; | ||
566 | |||
567 | credit_per_maxmsg = MAX_MESSAGE_SIZE / target->tgt_cred_sz; | ||
568 | if (MAX_MESSAGE_SIZE % target->tgt_cred_sz) | ||
569 | credit_per_maxmsg++; | ||
570 | |||
571 | /* TODO, this should be configured by the caller! */ | ||
572 | |||
573 | credits = target->tgt_creds; | ||
574 | entry = &target->pipe.txcredit_alloc[0]; | ||
575 | |||
576 | status = -ENOMEM; | ||
577 | |||
578 | /* FIXME: hif_usbaudioclass is always zero */ | ||
579 | if (hif_usbaudioclass) { | ||
580 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
581 | "%s: For USB Audio Class- Total:%d\n", | ||
582 | __func__, credits); | ||
583 | entry++; | ||
584 | entry++; | ||
585 | /* Setup VO Service To have Max Credits */ | ||
586 | entry->service_id = WMI_DATA_VO_SVC; | ||
587 | entry->credit_alloc = (credits - 6); | ||
588 | if (entry->credit_alloc == 0) | ||
589 | entry->credit_alloc++; | ||
590 | |||
591 | credits -= (int) entry->credit_alloc; | ||
592 | if (credits <= 0) | ||
593 | return status; | ||
594 | |||
595 | entry++; | ||
596 | entry->service_id = WMI_CONTROL_SVC; | ||
597 | entry->credit_alloc = credit_per_maxmsg; | ||
598 | credits -= (int) entry->credit_alloc; | ||
599 | if (credits <= 0) | ||
600 | return status; | ||
601 | |||
602 | /* leftovers go to best effort */ | ||
603 | entry++; | ||
604 | entry++; | ||
605 | entry->service_id = WMI_DATA_BE_SVC; | ||
606 | entry->credit_alloc = (u8) credits; | ||
607 | status = 0; | ||
608 | } else { | ||
609 | entry++; | ||
610 | entry->service_id = WMI_DATA_VI_SVC; | ||
611 | entry->credit_alloc = credits / 4; | ||
612 | if (entry->credit_alloc == 0) | ||
613 | entry->credit_alloc++; | ||
614 | |||
615 | credits -= (int) entry->credit_alloc; | ||
616 | if (credits <= 0) | ||
617 | return status; | ||
618 | |||
619 | entry++; | ||
620 | entry->service_id = WMI_DATA_VO_SVC; | ||
621 | entry->credit_alloc = credits / 4; | ||
622 | if (entry->credit_alloc == 0) | ||
623 | entry->credit_alloc++; | ||
624 | |||
625 | credits -= (int) entry->credit_alloc; | ||
626 | if (credits <= 0) | ||
627 | return status; | ||
628 | |||
629 | entry++; | ||
630 | entry->service_id = WMI_CONTROL_SVC; | ||
631 | entry->credit_alloc = credit_per_maxmsg; | ||
632 | credits -= (int) entry->credit_alloc; | ||
633 | if (credits <= 0) | ||
634 | return status; | ||
635 | |||
636 | entry++; | ||
637 | entry->service_id = WMI_DATA_BK_SVC; | ||
638 | entry->credit_alloc = credit_per_maxmsg; | ||
639 | credits -= (int) entry->credit_alloc; | ||
640 | if (credits <= 0) | ||
641 | return status; | ||
642 | |||
643 | /* leftovers go to best effort */ | ||
644 | entry++; | ||
645 | entry->service_id = WMI_DATA_BE_SVC; | ||
646 | entry->credit_alloc = (u8) credits; | ||
647 | status = 0; | ||
648 | } | ||
649 | |||
650 | if (status == 0) { | ||
651 | for (i = 0; i < ENDPOINT_MAX; i++) { | ||
652 | if (target->pipe.txcredit_alloc[i].service_id != 0) { | ||
653 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
654 | "HTC Service Index : %d TX : 0x%2.2X : alloc:%d\n", | ||
655 | i, | ||
656 | target->pipe.txcredit_alloc[i]. | ||
657 | service_id, | ||
658 | target->pipe.txcredit_alloc[i]. | ||
659 | credit_alloc); | ||
660 | } | ||
661 | } | ||
662 | } | ||
663 | return status; | ||
664 | } | ||
665 | |||
666 | /* process credit reports and call distribution function */ | ||
667 | static void htc_process_credit_report(struct htc_target *target, | ||
668 | struct htc_credit_report *rpt, | ||
669 | int num_entries, | ||
670 | enum htc_endpoint_id from_ep) | ||
671 | { | ||
672 | int total_credits = 0, i; | ||
673 | struct htc_endpoint *ep; | ||
674 | |||
675 | /* lock out TX while we update credits */ | ||
676 | spin_lock_bh(&target->tx_lock); | ||
677 | |||
678 | for (i = 0; i < num_entries; i++, rpt++) { | ||
679 | if (rpt->eid >= ENDPOINT_MAX) { | ||
680 | WARN_ON_ONCE(1); | ||
681 | spin_unlock_bh(&target->tx_lock); | ||
682 | return; | ||
683 | } | ||
684 | |||
685 | ep = &target->endpoint[rpt->eid]; | ||
686 | ep->cred_dist.credits += rpt->credits; | ||
687 | |||
688 | if (ep->cred_dist.credits && get_queue_depth(&ep->txq)) { | ||
689 | spin_unlock_bh(&target->tx_lock); | ||
690 | htc_try_send(target, ep, NULL); | ||
691 | spin_lock_bh(&target->tx_lock); | ||
692 | } | ||
693 | |||
694 | total_credits += rpt->credits; | ||
695 | } | ||
696 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
697 | "Report indicated %d credits to distribute\n", | ||
698 | total_credits); | ||
699 | |||
700 | spin_unlock_bh(&target->tx_lock); | ||
701 | } | ||
702 | |||
703 | /* flush endpoint TX queue */ | ||
704 | static void htc_flush_tx_endpoint(struct htc_target *target, | ||
705 | struct htc_endpoint *ep, u16 tag) | ||
706 | { | ||
707 | struct htc_packet *packet; | ||
708 | |||
709 | spin_lock_bh(&target->tx_lock); | ||
710 | while (get_queue_depth(&ep->txq)) { | ||
711 | packet = list_first_entry(&ep->txq, struct htc_packet, list); | ||
712 | list_del(&packet->list); | ||
713 | packet->status = 0; | ||
714 | send_packet_completion(target, packet); | ||
715 | } | ||
716 | spin_unlock_bh(&target->tx_lock); | ||
717 | } | ||
718 | |||
719 | /* | ||
720 | * In the adapted HIF layer, struct sk_buff * are passed between HIF and HTC, | ||
721 | * since upper layers expects struct htc_packet containers we use the completed | ||
722 | * skb and lookup it's corresponding HTC packet buffer from a lookup list. | ||
723 | * This is extra overhead that can be fixed by re-aligning HIF interfaces with | ||
724 | * HTC. | ||
725 | */ | ||
726 | static struct htc_packet *htc_lookup_tx_packet(struct htc_target *target, | ||
727 | struct htc_endpoint *ep, | ||
728 | struct sk_buff *skb) | ||
729 | { | ||
730 | struct htc_packet *packet, *tmp_pkt, *found_packet = NULL; | ||
731 | |||
732 | spin_lock_bh(&target->tx_lock); | ||
733 | |||
734 | /* | ||
735 | * interate from the front of tx lookup queue | ||
736 | * this lookup should be fast since lower layers completes in-order and | ||
737 | * so the completed packet should be at the head of the list generally | ||
738 | */ | ||
739 | list_for_each_entry_safe(packet, tmp_pkt, &ep->pipe.tx_lookup_queue, | ||
740 | list) { | ||
741 | /* check for removal */ | ||
742 | if (skb == packet->skb) { | ||
743 | /* found it */ | ||
744 | list_del(&packet->list); | ||
745 | found_packet = packet; | ||
746 | break; | ||
747 | } | ||
748 | } | ||
749 | |||
750 | spin_unlock_bh(&target->tx_lock); | ||
751 | |||
752 | return found_packet; | ||
753 | } | ||
754 | |||
755 | static int ath6kl_htc_pipe_tx_complete(struct ath6kl *ar, struct sk_buff *skb) | ||
756 | { | ||
757 | struct htc_target *target = ar->htc_target; | ||
758 | struct htc_frame_hdr *htc_hdr; | ||
759 | struct htc_endpoint *ep; | ||
760 | struct htc_packet *packet; | ||
761 | u8 ep_id, *netdata; | ||
762 | u32 netlen; | ||
763 | |||
764 | netdata = skb->data; | ||
765 | netlen = skb->len; | ||
766 | |||
767 | htc_hdr = (struct htc_frame_hdr *) netdata; | ||
768 | |||
769 | ep_id = htc_hdr->eid; | ||
770 | ep = &target->endpoint[ep_id]; | ||
771 | |||
772 | packet = htc_lookup_tx_packet(target, ep, skb); | ||
773 | if (packet == NULL) { | ||
774 | /* may have already been flushed and freed */ | ||
775 | ath6kl_err("HTC TX lookup failed!\n"); | ||
776 | } else { | ||
777 | /* will be giving this buffer back to upper layers */ | ||
778 | packet->status = 0; | ||
779 | send_packet_completion(target, packet); | ||
780 | } | ||
781 | skb = NULL; | ||
782 | |||
783 | if (!ep->pipe.tx_credit_flow_enabled) { | ||
784 | /* | ||
785 | * note: when using TX credit flow, the re-checking of queues | ||
786 | * happens when credits flow back from the target. in the | ||
787 | * non-TX credit case, we recheck after the packet completes | ||
788 | */ | ||
789 | htc_try_send(target, ep, NULL); | ||
790 | } | ||
791 | |||
792 | return 0; | ||
793 | } | ||
794 | |||
795 | static int htc_send_packets_multiple(struct htc_target *target, | ||
796 | struct list_head *pkt_queue) | ||
797 | { | ||
798 | struct htc_endpoint *ep; | ||
799 | struct htc_packet *packet, *tmp_pkt; | ||
800 | |||
801 | if (list_empty(pkt_queue)) | ||
802 | return -EINVAL; | ||
803 | |||
804 | /* get first packet to find out which ep the packets will go into */ | ||
805 | packet = list_first_entry(pkt_queue, struct htc_packet, list); | ||
806 | if (packet == NULL) | ||
807 | return -EINVAL; | ||
808 | |||
809 | if (packet->endpoint >= ENDPOINT_MAX) { | ||
810 | WARN_ON_ONCE(1); | ||
811 | return -EINVAL; | ||
812 | } | ||
813 | ep = &target->endpoint[packet->endpoint]; | ||
814 | |||
815 | htc_try_send(target, ep, pkt_queue); | ||
816 | |||
817 | /* do completion on any packets that couldn't get in */ | ||
818 | if (!list_empty(pkt_queue)) { | ||
819 | list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { | ||
820 | packet->status = -ENOMEM; | ||
821 | } | ||
822 | |||
823 | do_send_completion(ep, pkt_queue); | ||
824 | } | ||
825 | |||
826 | return 0; | ||
827 | } | ||
828 | |||
829 | /* htc pipe rx path */ | ||
830 | static struct htc_packet *alloc_htc_packet_container(struct htc_target *target) | ||
831 | { | ||
832 | struct htc_packet *packet; | ||
833 | spin_lock_bh(&target->rx_lock); | ||
834 | |||
835 | if (target->pipe.htc_packet_pool == NULL) { | ||
836 | spin_unlock_bh(&target->rx_lock); | ||
837 | return NULL; | ||
838 | } | ||
839 | |||
840 | packet = target->pipe.htc_packet_pool; | ||
841 | target->pipe.htc_packet_pool = (struct htc_packet *) packet->list.next; | ||
842 | |||
843 | spin_unlock_bh(&target->rx_lock); | ||
844 | |||
845 | packet->list.next = NULL; | ||
846 | return packet; | ||
847 | } | ||
848 | |||
849 | static void free_htc_packet_container(struct htc_target *target, | ||
850 | struct htc_packet *packet) | ||
851 | { | ||
852 | struct list_head *lh; | ||
853 | |||
854 | spin_lock_bh(&target->rx_lock); | ||
855 | |||
856 | if (target->pipe.htc_packet_pool == NULL) { | ||
857 | target->pipe.htc_packet_pool = packet; | ||
858 | packet->list.next = NULL; | ||
859 | } else { | ||
860 | lh = (struct list_head *) target->pipe.htc_packet_pool; | ||
861 | packet->list.next = lh; | ||
862 | target->pipe.htc_packet_pool = packet; | ||
863 | } | ||
864 | |||
865 | spin_unlock_bh(&target->rx_lock); | ||
866 | } | ||
867 | |||
868 | static int htc_process_trailer(struct htc_target *target, u8 *buffer, | ||
869 | int len, enum htc_endpoint_id from_ep) | ||
870 | { | ||
871 | struct htc_credit_report *report; | ||
872 | struct htc_record_hdr *record; | ||
873 | u8 *record_buf, *orig_buf; | ||
874 | int orig_len, status; | ||
875 | |||
876 | orig_buf = buffer; | ||
877 | orig_len = len; | ||
878 | status = 0; | ||
879 | |||
880 | while (len > 0) { | ||
881 | if (len < sizeof(struct htc_record_hdr)) { | ||
882 | status = -EINVAL; | ||
883 | break; | ||
884 | } | ||
885 | |||
886 | /* these are byte aligned structs */ | ||
887 | record = (struct htc_record_hdr *) buffer; | ||
888 | len -= sizeof(struct htc_record_hdr); | ||
889 | buffer += sizeof(struct htc_record_hdr); | ||
890 | |||
891 | if (record->len > len) { | ||
892 | /* no room left in buffer for record */ | ||
893 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
894 | "invalid length: %d (id:%d) buffer has: %d bytes left\n", | ||
895 | record->len, record->rec_id, len); | ||
896 | status = -EINVAL; | ||
897 | break; | ||
898 | } | ||
899 | |||
900 | /* start of record follows the header */ | ||
901 | record_buf = buffer; | ||
902 | |||
903 | switch (record->rec_id) { | ||
904 | case HTC_RECORD_CREDITS: | ||
905 | if (record->len < sizeof(struct htc_credit_report)) { | ||
906 | WARN_ON_ONCE(1); | ||
907 | return -EINVAL; | ||
908 | } | ||
909 | |||
910 | report = (struct htc_credit_report *) record_buf; | ||
911 | htc_process_credit_report(target, report, | ||
912 | record->len / sizeof(*report), | ||
913 | from_ep); | ||
914 | break; | ||
915 | default: | ||
916 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
917 | "unhandled record: id:%d length:%d\n", | ||
918 | record->rec_id, record->len); | ||
919 | break; | ||
920 | } | ||
921 | |||
922 | if (status != 0) | ||
923 | break; | ||
924 | |||
925 | /* advance buffer past this record for next time around */ | ||
926 | buffer += record->len; | ||
927 | len -= record->len; | ||
928 | } | ||
929 | |||
930 | return status; | ||
931 | } | ||
932 | |||
933 | static void do_recv_completion(struct htc_endpoint *ep, | ||
934 | struct list_head *queue_to_indicate) | ||
935 | { | ||
936 | struct htc_packet *packet; | ||
937 | |||
938 | if (list_empty(queue_to_indicate)) { | ||
939 | /* nothing to indicate */ | ||
940 | return; | ||
941 | } | ||
942 | |||
943 | /* using legacy EpRecv */ | ||
944 | while (!list_empty(queue_to_indicate)) { | ||
945 | packet = list_first_entry(queue_to_indicate, | ||
946 | struct htc_packet, list); | ||
947 | list_del(&packet->list); | ||
948 | ep->ep_cb.rx(ep->target, packet); | ||
949 | } | ||
950 | |||
951 | return; | ||
952 | } | ||
953 | |||
954 | static void recv_packet_completion(struct htc_target *target, | ||
955 | struct htc_endpoint *ep, | ||
956 | struct htc_packet *packet) | ||
957 | { | ||
958 | struct list_head container; | ||
959 | INIT_LIST_HEAD(&container); | ||
960 | list_add_tail(&packet->list, &container); | ||
961 | |||
962 | /* do completion */ | ||
963 | do_recv_completion(ep, &container); | ||
964 | } | ||
965 | |||
966 | static int ath6kl_htc_pipe_rx_complete(struct ath6kl *ar, struct sk_buff *skb, | ||
967 | u8 pipeid) | ||
968 | { | ||
969 | struct htc_target *target = ar->htc_target; | ||
970 | u8 *netdata, *trailer, hdr_info; | ||
971 | struct htc_frame_hdr *htc_hdr; | ||
972 | u32 netlen, trailerlen = 0; | ||
973 | struct htc_packet *packet; | ||
974 | struct htc_endpoint *ep; | ||
975 | u16 payload_len; | ||
976 | int status = 0; | ||
977 | |||
978 | netdata = skb->data; | ||
979 | netlen = skb->len; | ||
980 | |||
981 | htc_hdr = (struct htc_frame_hdr *) netdata; | ||
982 | |||
983 | ep = &target->endpoint[htc_hdr->eid]; | ||
984 | |||
985 | if (htc_hdr->eid >= ENDPOINT_MAX) { | ||
986 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
987 | "HTC Rx: invalid EndpointID=%d\n", | ||
988 | htc_hdr->eid); | ||
989 | status = -EINVAL; | ||
990 | goto free_skb; | ||
991 | } | ||
992 | |||
993 | payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len)); | ||
994 | |||
995 | if (netlen < (payload_len + HTC_HDR_LENGTH)) { | ||
996 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
997 | "HTC Rx: insufficient length, got:%d expected =%u\n", | ||
998 | netlen, payload_len + HTC_HDR_LENGTH); | ||
999 | status = -EINVAL; | ||
1000 | goto free_skb; | ||
1001 | } | ||
1002 | |||
1003 | /* get flags to check for trailer */ | ||
1004 | hdr_info = htc_hdr->flags; | ||
1005 | if (hdr_info & HTC_FLG_RX_TRAILER) { | ||
1006 | /* extract the trailer length */ | ||
1007 | hdr_info = htc_hdr->ctrl[0]; | ||
1008 | if ((hdr_info < sizeof(struct htc_record_hdr)) || | ||
1009 | (hdr_info > payload_len)) { | ||
1010 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
1011 | "invalid header: payloadlen should be %d, CB[0]: %d\n", | ||
1012 | payload_len, hdr_info); | ||
1013 | status = -EINVAL; | ||
1014 | goto free_skb; | ||
1015 | } | ||
1016 | |||
1017 | trailerlen = hdr_info; | ||
1018 | /* process trailer after hdr/apps payload */ | ||
1019 | trailer = (u8 *) htc_hdr + HTC_HDR_LENGTH + | ||
1020 | payload_len - hdr_info; | ||
1021 | status = htc_process_trailer(target, trailer, hdr_info, | ||
1022 | htc_hdr->eid); | ||
1023 | if (status != 0) | ||
1024 | goto free_skb; | ||
1025 | } | ||
1026 | |||
1027 | if (((int) payload_len - (int) trailerlen) <= 0) { | ||
1028 | /* zero length packet with trailer, just drop these */ | ||
1029 | goto free_skb; | ||
1030 | } | ||
1031 | |||
1032 | if (htc_hdr->eid == ENDPOINT_0) { | ||
1033 | /* handle HTC control message */ | ||
1034 | if (target->htc_flags & HTC_OP_STATE_SETUP_COMPLETE) { | ||
1035 | /* | ||
1036 | * fatal: target should not send unsolicited | ||
1037 | * messageson the endpoint 0 | ||
1038 | */ | ||
1039 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
1040 | "HTC ignores Rx Ctrl after setup complete\n"); | ||
1041 | status = -EINVAL; | ||
1042 | goto free_skb; | ||
1043 | } | ||
1044 | |||
1045 | /* remove HTC header */ | ||
1046 | skb_pull(skb, HTC_HDR_LENGTH); | ||
1047 | |||
1048 | netdata = skb->data; | ||
1049 | netlen = skb->len; | ||
1050 | |||
1051 | spin_lock_bh(&target->rx_lock); | ||
1052 | |||
1053 | target->pipe.ctrl_response_valid = true; | ||
1054 | target->pipe.ctrl_response_len = min_t(int, netlen, | ||
1055 | HTC_MAX_CTRL_MSG_LEN); | ||
1056 | memcpy(target->pipe.ctrl_response_buf, netdata, | ||
1057 | target->pipe.ctrl_response_len); | ||
1058 | |||
1059 | spin_unlock_bh(&target->rx_lock); | ||
1060 | |||
1061 | dev_kfree_skb(skb); | ||
1062 | skb = NULL; | ||
1063 | goto free_skb; | ||
1064 | } | ||
1065 | |||
1066 | /* | ||
1067 | * TODO: the message based HIF architecture allocates net bufs | ||
1068 | * for recv packets since it bridges that HIF to upper layers, | ||
1069 | * which expects HTC packets, we form the packets here | ||
1070 | */ | ||
1071 | packet = alloc_htc_packet_container(target); | ||
1072 | if (packet == NULL) { | ||
1073 | status = -ENOMEM; | ||
1074 | goto free_skb; | ||
1075 | } | ||
1076 | |||
1077 | packet->status = 0; | ||
1078 | packet->endpoint = htc_hdr->eid; | ||
1079 | packet->pkt_cntxt = skb; | ||
1080 | |||
1081 | /* TODO: for backwards compatibility */ | ||
1082 | packet->buf = skb_push(skb, 0) + HTC_HDR_LENGTH; | ||
1083 | packet->act_len = netlen - HTC_HDR_LENGTH - trailerlen; | ||
1084 | |||
1085 | /* | ||
1086 | * TODO: this is a hack because the driver layer will set the | ||
1087 | * actual len of the skb again which will just double the len | ||
1088 | */ | ||
1089 | skb_trim(skb, 0); | ||
1090 | |||
1091 | recv_packet_completion(target, ep, packet); | ||
1092 | |||
1093 | /* recover the packet container */ | ||
1094 | free_htc_packet_container(target, packet); | ||
1095 | skb = NULL; | ||
1096 | |||
1097 | free_skb: | ||
1098 | if (skb != NULL) | ||
1099 | dev_kfree_skb(skb); | ||
1100 | |||
1101 | return status; | ||
1102 | |||
1103 | } | ||
1104 | |||
1105 | static void htc_flush_rx_queue(struct htc_target *target, | ||
1106 | struct htc_endpoint *ep) | ||
1107 | { | ||
1108 | struct list_head container; | ||
1109 | struct htc_packet *packet; | ||
1110 | |||
1111 | spin_lock_bh(&target->rx_lock); | ||
1112 | |||
1113 | while (1) { | ||
1114 | if (list_empty(&ep->rx_bufq)) | ||
1115 | break; | ||
1116 | |||
1117 | packet = list_first_entry(&ep->rx_bufq, | ||
1118 | struct htc_packet, list); | ||
1119 | list_del(&packet->list); | ||
1120 | |||
1121 | spin_unlock_bh(&target->rx_lock); | ||
1122 | packet->status = -ECANCELED; | ||
1123 | packet->act_len = 0; | ||
1124 | |||
1125 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
1126 | "Flushing RX packet:0x%p, length:%d, ep:%d\n", | ||
1127 | packet, packet->buf_len, | ||
1128 | packet->endpoint); | ||
1129 | |||
1130 | INIT_LIST_HEAD(&container); | ||
1131 | list_add_tail(&packet->list, &container); | ||
1132 | |||
1133 | /* give the packet back */ | ||
1134 | do_recv_completion(ep, &container); | ||
1135 | spin_lock_bh(&target->rx_lock); | ||
1136 | } | ||
1137 | |||
1138 | spin_unlock_bh(&target->rx_lock); | ||
1139 | } | ||
1140 | |||
1141 | /* polling routine to wait for a control packet to be received */ | ||
1142 | static int htc_wait_recv_ctrl_message(struct htc_target *target) | ||
1143 | { | ||
1144 | int count = HTC_TARGET_RESPONSE_POLL_COUNT; | ||
1145 | |||
1146 | while (count > 0) { | ||
1147 | spin_lock_bh(&target->rx_lock); | ||
1148 | |||
1149 | if (target->pipe.ctrl_response_valid) { | ||
1150 | target->pipe.ctrl_response_valid = false; | ||
1151 | spin_unlock_bh(&target->rx_lock); | ||
1152 | break; | ||
1153 | } | ||
1154 | |||
1155 | spin_unlock_bh(&target->rx_lock); | ||
1156 | |||
1157 | count--; | ||
1158 | |||
1159 | msleep_interruptible(HTC_TARGET_RESPONSE_POLL_WAIT); | ||
1160 | } | ||
1161 | |||
1162 | if (count <= 0) { | ||
1163 | ath6kl_dbg(ATH6KL_DBG_HTC, "%s: Timeout!\n", __func__); | ||
1164 | return -ECOMM; | ||
1165 | } | ||
1166 | |||
1167 | return 0; | ||
1168 | } | ||
1169 | |||
1170 | static void htc_rxctrl_complete(struct htc_target *context, | ||
1171 | struct htc_packet *packet) | ||
1172 | { | ||
1173 | /* TODO, can't really receive HTC control messages yet.... */ | ||
1174 | ath6kl_dbg(ATH6KL_DBG_HTC, "%s: invalid call function\n", __func__); | ||
1175 | } | ||
1176 | |||
1177 | /* htc pipe initialization */ | ||
1178 | static void reset_endpoint_states(struct htc_target *target) | ||
1179 | { | ||
1180 | struct htc_endpoint *ep; | ||
1181 | int i; | ||
1182 | |||
1183 | for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { | ||
1184 | ep = &target->endpoint[i]; | ||
1185 | ep->svc_id = 0; | ||
1186 | ep->len_max = 0; | ||
1187 | ep->max_txq_depth = 0; | ||
1188 | ep->eid = i; | ||
1189 | INIT_LIST_HEAD(&ep->txq); | ||
1190 | INIT_LIST_HEAD(&ep->pipe.tx_lookup_queue); | ||
1191 | INIT_LIST_HEAD(&ep->rx_bufq); | ||
1192 | ep->target = target; | ||
1193 | ep->pipe.tx_credit_flow_enabled = (bool) 1; /* FIXME */ | ||
1194 | } | ||
1195 | } | ||
1196 | |||
1197 | /* start HTC, this is called after all services are connected */ | ||
1198 | static int htc_config_target_hif_pipe(struct htc_target *target) | ||
1199 | { | ||
1200 | return 0; | ||
1201 | } | ||
1202 | |||
1203 | /* htc service functions */ | ||
1204 | static u8 htc_get_credit_alloc(struct htc_target *target, u16 service_id) | ||
1205 | { | ||
1206 | u8 allocation = 0; | ||
1207 | int i; | ||
1208 | |||
1209 | for (i = 0; i < ENDPOINT_MAX; i++) { | ||
1210 | if (target->pipe.txcredit_alloc[i].service_id == service_id) | ||
1211 | allocation = | ||
1212 | target->pipe.txcredit_alloc[i].credit_alloc; | ||
1213 | } | ||
1214 | |||
1215 | if (allocation == 0) { | ||
1216 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
1217 | "HTC Service TX : 0x%2.2X : allocation is zero!\n", | ||
1218 | service_id); | ||
1219 | } | ||
1220 | |||
1221 | return allocation; | ||
1222 | } | ||
1223 | |||
1224 | static int ath6kl_htc_pipe_conn_service(struct htc_target *target, | ||
1225 | struct htc_service_connect_req *conn_req, | ||
1226 | struct htc_service_connect_resp *conn_resp) | ||
1227 | { | ||
1228 | struct ath6kl *ar = target->dev->ar; | ||
1229 | struct htc_packet *packet = NULL; | ||
1230 | struct htc_conn_service_resp *resp_msg; | ||
1231 | struct htc_conn_service_msg *conn_msg; | ||
1232 | enum htc_endpoint_id assigned_epid = ENDPOINT_MAX; | ||
1233 | bool disable_credit_flowctrl = false; | ||
1234 | unsigned int max_msg_size = 0; | ||
1235 | struct htc_endpoint *ep; | ||
1236 | int length, status = 0; | ||
1237 | struct sk_buff *skb; | ||
1238 | u8 tx_alloc; | ||
1239 | u16 flags; | ||
1240 | |||
1241 | if (conn_req->svc_id == 0) { | ||
1242 | WARN_ON_ONCE(1); | ||
1243 | status = -EINVAL; | ||
1244 | goto free_packet; | ||
1245 | } | ||
1246 | |||
1247 | if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) { | ||
1248 | /* special case for pseudo control service */ | ||
1249 | assigned_epid = ENDPOINT_0; | ||
1250 | max_msg_size = HTC_MAX_CTRL_MSG_LEN; | ||
1251 | tx_alloc = 0; | ||
1252 | |||
1253 | } else { | ||
1254 | |||
1255 | tx_alloc = htc_get_credit_alloc(target, conn_req->svc_id); | ||
1256 | if (tx_alloc == 0) { | ||
1257 | status = -ENOMEM; | ||
1258 | goto free_packet; | ||
1259 | } | ||
1260 | |||
1261 | /* allocate a packet to send to the target */ | ||
1262 | packet = htc_alloc_txctrl_packet(target); | ||
1263 | |||
1264 | if (packet == NULL) { | ||
1265 | WARN_ON_ONCE(1); | ||
1266 | status = -ENOMEM; | ||
1267 | goto free_packet; | ||
1268 | } | ||
1269 | |||
1270 | skb = packet->skb; | ||
1271 | length = sizeof(struct htc_conn_service_msg); | ||
1272 | |||
1273 | /* assemble connect service message */ | ||
1274 | conn_msg = (struct htc_conn_service_msg *) skb_put(skb, | ||
1275 | length); | ||
1276 | if (conn_msg == NULL) { | ||
1277 | WARN_ON_ONCE(1); | ||
1278 | status = -EINVAL; | ||
1279 | goto free_packet; | ||
1280 | } | ||
1281 | |||
1282 | memset(conn_msg, 0, | ||
1283 | sizeof(struct htc_conn_service_msg)); | ||
1284 | conn_msg->msg_id = cpu_to_le16(HTC_MSG_CONN_SVC_ID); | ||
1285 | conn_msg->svc_id = cpu_to_le16(conn_req->svc_id); | ||
1286 | conn_msg->conn_flags = cpu_to_le16(conn_req->conn_flags & | ||
1287 | ~HTC_CONN_FLGS_SET_RECV_ALLOC_MASK); | ||
1288 | |||
1289 | /* tell target desired recv alloc for this ep */ | ||
1290 | flags = tx_alloc << HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT; | ||
1291 | conn_msg->conn_flags |= cpu_to_le16(flags); | ||
1292 | |||
1293 | if (conn_req->conn_flags & | ||
1294 | HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL) { | ||
1295 | disable_credit_flowctrl = true; | ||
1296 | } | ||
1297 | |||
1298 | set_htc_pkt_info(packet, NULL, (u8 *) conn_msg, | ||
1299 | length, | ||
1300 | ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); | ||
1301 | |||
1302 | status = ath6kl_htc_pipe_tx(target, packet); | ||
1303 | |||
1304 | /* we don't own it anymore */ | ||
1305 | packet = NULL; | ||
1306 | if (status != 0) | ||
1307 | goto free_packet; | ||
1308 | |||
1309 | /* wait for response */ | ||
1310 | status = htc_wait_recv_ctrl_message(target); | ||
1311 | if (status != 0) | ||
1312 | goto free_packet; | ||
1313 | |||
1314 | /* we controlled the buffer creation so it has to be | ||
1315 | * properly aligned | ||
1316 | */ | ||
1317 | resp_msg = (struct htc_conn_service_resp *) | ||
1318 | target->pipe.ctrl_response_buf; | ||
1319 | |||
1320 | if (resp_msg->msg_id != cpu_to_le16(HTC_MSG_CONN_SVC_RESP_ID) || | ||
1321 | (target->pipe.ctrl_response_len < sizeof(*resp_msg))) { | ||
1322 | /* this message is not valid */ | ||
1323 | WARN_ON_ONCE(1); | ||
1324 | status = -EINVAL; | ||
1325 | goto free_packet; | ||
1326 | } | ||
1327 | |||
1328 | ath6kl_dbg(ATH6KL_DBG_TRC, | ||
1329 | "%s: service 0x%X conn resp: status: %d ep: %d\n", | ||
1330 | __func__, resp_msg->svc_id, resp_msg->status, | ||
1331 | resp_msg->eid); | ||
1332 | |||
1333 | conn_resp->resp_code = resp_msg->status; | ||
1334 | /* check response status */ | ||
1335 | if (resp_msg->status != HTC_SERVICE_SUCCESS) { | ||
1336 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
1337 | "Target failed service 0x%X connect request (status:%d)\n", | ||
1338 | resp_msg->svc_id, resp_msg->status); | ||
1339 | status = -EINVAL; | ||
1340 | goto free_packet; | ||
1341 | } | ||
1342 | |||
1343 | assigned_epid = (enum htc_endpoint_id) resp_msg->eid; | ||
1344 | max_msg_size = le16_to_cpu(resp_msg->max_msg_sz); | ||
1345 | } | ||
1346 | |||
1347 | /* the rest are parameter checks so set the error status */ | ||
1348 | status = -EINVAL; | ||
1349 | |||
1350 | if (assigned_epid >= ENDPOINT_MAX) { | ||
1351 | WARN_ON_ONCE(1); | ||
1352 | goto free_packet; | ||
1353 | } | ||
1354 | |||
1355 | if (max_msg_size == 0) { | ||
1356 | WARN_ON_ONCE(1); | ||
1357 | goto free_packet; | ||
1358 | } | ||
1359 | |||
1360 | ep = &target->endpoint[assigned_epid]; | ||
1361 | ep->eid = assigned_epid; | ||
1362 | if (ep->svc_id != 0) { | ||
1363 | /* endpoint already in use! */ | ||
1364 | WARN_ON_ONCE(1); | ||
1365 | goto free_packet; | ||
1366 | } | ||
1367 | |||
1368 | /* return assigned endpoint to caller */ | ||
1369 | conn_resp->endpoint = assigned_epid; | ||
1370 | conn_resp->len_max = max_msg_size; | ||
1371 | |||
1372 | /* setup the endpoint */ | ||
1373 | ep->svc_id = conn_req->svc_id; /* this marks ep in use */ | ||
1374 | ep->max_txq_depth = conn_req->max_txq_depth; | ||
1375 | ep->len_max = max_msg_size; | ||
1376 | ep->cred_dist.credits = tx_alloc; | ||
1377 | ep->cred_dist.cred_sz = target->tgt_cred_sz; | ||
1378 | ep->cred_dist.cred_per_msg = max_msg_size / target->tgt_cred_sz; | ||
1379 | if (max_msg_size % target->tgt_cred_sz) | ||
1380 | ep->cred_dist.cred_per_msg++; | ||
1381 | |||
1382 | /* copy all the callbacks */ | ||
1383 | ep->ep_cb = conn_req->ep_cb; | ||
1384 | |||
1385 | status = ath6kl_hif_pipe_map_service(ar, ep->svc_id, | ||
1386 | &ep->pipe.pipeid_ul, | ||
1387 | &ep->pipe.pipeid_dl); | ||
1388 | if (status != 0) | ||
1389 | goto free_packet; | ||
1390 | |||
1391 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
1392 | "SVC Ready: 0x%4.4X: ULpipe:%d DLpipe:%d id:%d\n", | ||
1393 | ep->svc_id, ep->pipe.pipeid_ul, | ||
1394 | ep->pipe.pipeid_dl, ep->eid); | ||
1395 | |||
1396 | if (disable_credit_flowctrl && ep->pipe.tx_credit_flow_enabled) { | ||
1397 | ep->pipe.tx_credit_flow_enabled = false; | ||
1398 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
1399 | "SVC: 0x%4.4X ep:%d TX flow control off\n", | ||
1400 | ep->svc_id, assigned_epid); | ||
1401 | } | ||
1402 | |||
1403 | free_packet: | ||
1404 | if (packet != NULL) | ||
1405 | htc_free_txctrl_packet(target, packet); | ||
1406 | return status; | ||
1407 | } | ||
1408 | |||
1409 | /* htc export functions */ | ||
1410 | static void *ath6kl_htc_pipe_create(struct ath6kl *ar) | ||
1411 | { | ||
1412 | int status = 0; | ||
1413 | struct htc_endpoint *ep = NULL; | ||
1414 | struct htc_target *target = NULL; | ||
1415 | struct htc_packet *packet; | ||
1416 | int i; | ||
1417 | |||
1418 | target = kzalloc(sizeof(struct htc_target), GFP_KERNEL); | ||
1419 | if (target == NULL) { | ||
1420 | ath6kl_err("htc create unable to allocate memory\n"); | ||
1421 | status = -ENOMEM; | ||
1422 | goto fail_htc_create; | ||
1423 | } | ||
1424 | |||
1425 | spin_lock_init(&target->htc_lock); | ||
1426 | spin_lock_init(&target->rx_lock); | ||
1427 | spin_lock_init(&target->tx_lock); | ||
1428 | |||
1429 | reset_endpoint_states(target); | ||
1430 | |||
1431 | for (i = 0; i < HTC_PACKET_CONTAINER_ALLOCATION; i++) { | ||
1432 | packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL); | ||
1433 | |||
1434 | if (packet != NULL) | ||
1435 | free_htc_packet_container(target, packet); | ||
1436 | } | ||
1437 | |||
1438 | target->dev = kzalloc(sizeof(*target->dev), GFP_KERNEL); | ||
1439 | if (!target->dev) { | ||
1440 | ath6kl_err("unable to allocate memory\n"); | ||
1441 | status = -ENOMEM; | ||
1442 | goto fail_htc_create; | ||
1443 | } | ||
1444 | target->dev->ar = ar; | ||
1445 | target->dev->htc_cnxt = target; | ||
1446 | |||
1447 | /* Get HIF default pipe for HTC message exchange */ | ||
1448 | ep = &target->endpoint[ENDPOINT_0]; | ||
1449 | |||
1450 | ath6kl_hif_pipe_get_default(ar, &ep->pipe.pipeid_ul, | ||
1451 | &ep->pipe.pipeid_dl); | ||
1452 | |||
1453 | return target; | ||
1454 | |||
1455 | fail_htc_create: | ||
1456 | if (status != 0) { | ||
1457 | if (target != NULL) | ||
1458 | ath6kl_htc_pipe_cleanup(target); | ||
1459 | |||
1460 | target = NULL; | ||
1461 | } | ||
1462 | return target; | ||
1463 | } | ||
1464 | |||
1465 | /* cleanup the HTC instance */ | ||
1466 | static void ath6kl_htc_pipe_cleanup(struct htc_target *target) | ||
1467 | { | ||
1468 | struct htc_packet *packet; | ||
1469 | |||
1470 | while (true) { | ||
1471 | packet = alloc_htc_packet_container(target); | ||
1472 | if (packet == NULL) | ||
1473 | break; | ||
1474 | kfree(packet); | ||
1475 | } | ||
1476 | |||
1477 | kfree(target->dev); | ||
1478 | |||
1479 | /* kfree our instance */ | ||
1480 | kfree(target); | ||
1481 | } | ||
1482 | |||
1483 | static int ath6kl_htc_pipe_start(struct htc_target *target) | ||
1484 | { | ||
1485 | struct sk_buff *skb; | ||
1486 | struct htc_setup_comp_ext_msg *setup; | ||
1487 | struct htc_packet *packet; | ||
1488 | |||
1489 | htc_config_target_hif_pipe(target); | ||
1490 | |||
1491 | /* allocate a buffer to send */ | ||
1492 | packet = htc_alloc_txctrl_packet(target); | ||
1493 | if (packet == NULL) { | ||
1494 | WARN_ON_ONCE(1); | ||
1495 | return -ENOMEM; | ||
1496 | } | ||
1497 | |||
1498 | skb = packet->skb; | ||
1499 | |||
1500 | /* assemble setup complete message */ | ||
1501 | setup = (struct htc_setup_comp_ext_msg *) skb_put(skb, | ||
1502 | sizeof(*setup)); | ||
1503 | memset(setup, 0, sizeof(struct htc_setup_comp_ext_msg)); | ||
1504 | setup->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID); | ||
1505 | |||
1506 | ath6kl_dbg(ATH6KL_DBG_HTC, "HTC using TX credit flow control\n"); | ||
1507 | |||
1508 | set_htc_pkt_info(packet, NULL, (u8 *) setup, | ||
1509 | sizeof(struct htc_setup_comp_ext_msg), | ||
1510 | ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); | ||
1511 | |||
1512 | target->htc_flags |= HTC_OP_STATE_SETUP_COMPLETE; | ||
1513 | |||
1514 | return ath6kl_htc_pipe_tx(target, packet); | ||
1515 | } | ||
1516 | |||
1517 | static void ath6kl_htc_pipe_stop(struct htc_target *target) | ||
1518 | { | ||
1519 | int i; | ||
1520 | struct htc_endpoint *ep; | ||
1521 | |||
1522 | /* cleanup endpoints */ | ||
1523 | for (i = 0; i < ENDPOINT_MAX; i++) { | ||
1524 | ep = &target->endpoint[i]; | ||
1525 | htc_flush_rx_queue(target, ep); | ||
1526 | htc_flush_tx_endpoint(target, ep, HTC_TX_PACKET_TAG_ALL); | ||
1527 | } | ||
1528 | |||
1529 | reset_endpoint_states(target); | ||
1530 | target->htc_flags &= ~HTC_OP_STATE_SETUP_COMPLETE; | ||
1531 | } | ||
1532 | |||
1533 | static int ath6kl_htc_pipe_get_rxbuf_num(struct htc_target *target, | ||
1534 | enum htc_endpoint_id endpoint) | ||
1535 | { | ||
1536 | int num; | ||
1537 | |||
1538 | spin_lock_bh(&target->rx_lock); | ||
1539 | num = get_queue_depth(&(target->endpoint[endpoint].rx_bufq)); | ||
1540 | spin_unlock_bh(&target->rx_lock); | ||
1541 | |||
1542 | return num; | ||
1543 | } | ||
1544 | |||
1545 | static int ath6kl_htc_pipe_tx(struct htc_target *target, | ||
1546 | struct htc_packet *packet) | ||
1547 | { | ||
1548 | struct list_head queue; | ||
1549 | |||
1550 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
1551 | "%s: endPointId: %d, buffer: 0x%p, length: %d\n", | ||
1552 | __func__, packet->endpoint, packet->buf, | ||
1553 | packet->act_len); | ||
1554 | |||
1555 | INIT_LIST_HEAD(&queue); | ||
1556 | list_add_tail(&packet->list, &queue); | ||
1557 | |||
1558 | return htc_send_packets_multiple(target, &queue); | ||
1559 | } | ||
1560 | |||
1561 | static int ath6kl_htc_pipe_wait_target(struct htc_target *target) | ||
1562 | { | ||
1563 | struct htc_ready_ext_msg *ready_msg; | ||
1564 | struct htc_service_connect_req connect; | ||
1565 | struct htc_service_connect_resp resp; | ||
1566 | int status = 0; | ||
1567 | |||
1568 | status = htc_wait_recv_ctrl_message(target); | ||
1569 | |||
1570 | if (status != 0) | ||
1571 | return status; | ||
1572 | |||
1573 | if (target->pipe.ctrl_response_len < sizeof(*ready_msg)) { | ||
1574 | ath6kl_dbg(ATH6KL_DBG_HTC, "invalid htc ready msg len:%d!\n", | ||
1575 | target->pipe.ctrl_response_len); | ||
1576 | return -ECOMM; | ||
1577 | } | ||
1578 | |||
1579 | ready_msg = (struct htc_ready_ext_msg *) target->pipe.ctrl_response_buf; | ||
1580 | |||
1581 | if (ready_msg->ver2_0_info.msg_id != cpu_to_le16(HTC_MSG_READY_ID)) { | ||
1582 | ath6kl_dbg(ATH6KL_DBG_HTC, "invalid htc ready msg : 0x%X !\n", | ||
1583 | ready_msg->ver2_0_info.msg_id); | ||
1584 | return -ECOMM; | ||
1585 | } | ||
1586 | |||
1587 | ath6kl_dbg(ATH6KL_DBG_HTC, | ||
1588 | "Target Ready! : transmit resources : %d size:%d\n", | ||
1589 | ready_msg->ver2_0_info.cred_cnt, | ||
1590 | ready_msg->ver2_0_info.cred_sz); | ||
1591 | |||
1592 | target->tgt_creds = le16_to_cpu(ready_msg->ver2_0_info.cred_cnt); | ||
1593 | target->tgt_cred_sz = le16_to_cpu(ready_msg->ver2_0_info.cred_sz); | ||
1594 | |||
1595 | if ((target->tgt_creds == 0) || (target->tgt_cred_sz == 0)) | ||
1596 | return -ECOMM; | ||
1597 | |||
1598 | htc_setup_target_buffer_assignments(target); | ||
1599 | |||
1600 | /* setup our pseudo HTC control endpoint connection */ | ||
1601 | memset(&connect, 0, sizeof(connect)); | ||
1602 | memset(&resp, 0, sizeof(resp)); | ||
1603 | connect.ep_cb.tx_complete = htc_txctrl_complete; | ||
1604 | connect.ep_cb.rx = htc_rxctrl_complete; | ||
1605 | connect.max_txq_depth = NUM_CONTROL_TX_BUFFERS; | ||
1606 | connect.svc_id = HTC_CTRL_RSVD_SVC; | ||
1607 | |||
1608 | /* connect fake service */ | ||
1609 | status = ath6kl_htc_pipe_conn_service(target, &connect, &resp); | ||
1610 | |||
1611 | return status; | ||
1612 | } | ||
1613 | |||
1614 | static void ath6kl_htc_pipe_flush_txep(struct htc_target *target, | ||
1615 | enum htc_endpoint_id endpoint, u16 tag) | ||
1616 | { | ||
1617 | struct htc_endpoint *ep = &target->endpoint[endpoint]; | ||
1618 | |||
1619 | if (ep->svc_id == 0) { | ||
1620 | WARN_ON_ONCE(1); | ||
1621 | /* not in use.. */ | ||
1622 | return; | ||
1623 | } | ||
1624 | |||
1625 | htc_flush_tx_endpoint(target, ep, tag); | ||
1626 | } | ||
1627 | |||
1628 | static int ath6kl_htc_pipe_add_rxbuf_multiple(struct htc_target *target, | ||
1629 | struct list_head *pkt_queue) | ||
1630 | { | ||
1631 | struct htc_packet *packet, *tmp_pkt, *first; | ||
1632 | struct htc_endpoint *ep; | ||
1633 | int status = 0; | ||
1634 | |||
1635 | if (list_empty(pkt_queue)) | ||
1636 | return -EINVAL; | ||
1637 | |||
1638 | first = list_first_entry(pkt_queue, struct htc_packet, list); | ||
1639 | if (first == NULL) { | ||
1640 | WARN_ON_ONCE(1); | ||
1641 | return -EINVAL; | ||
1642 | } | ||
1643 | |||
1644 | if (first->endpoint >= ENDPOINT_MAX) { | ||
1645 | WARN_ON_ONCE(1); | ||
1646 | return -EINVAL; | ||
1647 | } | ||
1648 | |||
1649 | ath6kl_dbg(ATH6KL_DBG_HTC, "%s: epid: %d, cnt:%d, len: %d\n", | ||
1650 | __func__, first->endpoint, get_queue_depth(pkt_queue), | ||
1651 | first->buf_len); | ||
1652 | |||
1653 | ep = &target->endpoint[first->endpoint]; | ||
1654 | |||
1655 | spin_lock_bh(&target->rx_lock); | ||
1656 | |||
1657 | /* store receive packets */ | ||
1658 | list_splice_tail_init(pkt_queue, &ep->rx_bufq); | ||
1659 | |||
1660 | spin_unlock_bh(&target->rx_lock); | ||
1661 | |||
1662 | if (status != 0) { | ||
1663 | /* walk through queue and mark each one canceled */ | ||
1664 | list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { | ||
1665 | packet->status = -ECANCELED; | ||
1666 | } | ||
1667 | |||
1668 | do_recv_completion(ep, pkt_queue); | ||
1669 | } | ||
1670 | |||
1671 | return status; | ||
1672 | } | ||
1673 | |||
1674 | static void ath6kl_htc_pipe_activity_changed(struct htc_target *target, | ||
1675 | enum htc_endpoint_id ep, | ||
1676 | bool active) | ||
1677 | { | ||
1678 | /* TODO */ | ||
1679 | } | ||
1680 | |||
1681 | static void ath6kl_htc_pipe_flush_rx_buf(struct htc_target *target) | ||
1682 | { | ||
1683 | /* TODO */ | ||
1684 | } | ||
1685 | |||
1686 | static int ath6kl_htc_pipe_credit_setup(struct htc_target *target, | ||
1687 | struct ath6kl_htc_credit_info *info) | ||
1688 | { | ||
1689 | return 0; | ||
1690 | } | ||
1691 | |||
1692 | static const struct ath6kl_htc_ops ath6kl_htc_pipe_ops = { | ||
1693 | .create = ath6kl_htc_pipe_create, | ||
1694 | .wait_target = ath6kl_htc_pipe_wait_target, | ||
1695 | .start = ath6kl_htc_pipe_start, | ||
1696 | .conn_service = ath6kl_htc_pipe_conn_service, | ||
1697 | .tx = ath6kl_htc_pipe_tx, | ||
1698 | .stop = ath6kl_htc_pipe_stop, | ||
1699 | .cleanup = ath6kl_htc_pipe_cleanup, | ||
1700 | .flush_txep = ath6kl_htc_pipe_flush_txep, | ||
1701 | .flush_rx_buf = ath6kl_htc_pipe_flush_rx_buf, | ||
1702 | .activity_changed = ath6kl_htc_pipe_activity_changed, | ||
1703 | .get_rxbuf_num = ath6kl_htc_pipe_get_rxbuf_num, | ||
1704 | .add_rxbuf_multiple = ath6kl_htc_pipe_add_rxbuf_multiple, | ||
1705 | .credit_setup = ath6kl_htc_pipe_credit_setup, | ||
1706 | .tx_complete = ath6kl_htc_pipe_tx_complete, | ||
1707 | .rx_complete = ath6kl_htc_pipe_rx_complete, | ||
1708 | }; | ||
1709 | |||
1710 | void ath6kl_htc_pipe_attach(struct ath6kl *ar) | ||
1711 | { | ||
1712 | ar->htc_ops = &ath6kl_htc_pipe_ops; | ||
1713 | } | ||
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index eb7cc2f5b96f..29ef50ea07d5 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c | |||
@@ -23,12 +23,14 @@ | |||
23 | #include <linux/export.h> | 23 | #include <linux/export.h> |
24 | #include <linux/of.h> | 24 | #include <linux/of.h> |
25 | #include <linux/mmc/sdio_func.h> | 25 | #include <linux/mmc/sdio_func.h> |
26 | #include <linux/vmalloc.h> | ||
26 | 27 | ||
27 | #include "core.h" | 28 | #include "core.h" |
28 | #include "cfg80211.h" | 29 | #include "cfg80211.h" |
29 | #include "target.h" | 30 | #include "target.h" |
30 | #include "debug.h" | 31 | #include "debug.h" |
31 | #include "hif-ops.h" | 32 | #include "hif-ops.h" |
33 | #include "htc-ops.h" | ||
32 | 34 | ||
33 | static const struct ath6kl_hw hw_list[] = { | 35 | static const struct ath6kl_hw hw_list[] = { |
34 | { | 36 | { |
@@ -258,6 +260,7 @@ static int ath6kl_init_service_ep(struct ath6kl *ar) | |||
258 | memset(&connect, 0, sizeof(connect)); | 260 | memset(&connect, 0, sizeof(connect)); |
259 | 261 | ||
260 | /* these fields are the same for all service endpoints */ | 262 | /* these fields are the same for all service endpoints */ |
263 | connect.ep_cb.tx_comp_multi = ath6kl_tx_complete; | ||
261 | connect.ep_cb.rx = ath6kl_rx; | 264 | connect.ep_cb.rx = ath6kl_rx; |
262 | connect.ep_cb.rx_refill = ath6kl_rx_refill; | 265 | connect.ep_cb.rx_refill = ath6kl_rx_refill; |
263 | connect.ep_cb.tx_full = ath6kl_tx_queue_full; | 266 | connect.ep_cb.tx_full = ath6kl_tx_queue_full; |
@@ -487,22 +490,31 @@ int ath6kl_configure_target(struct ath6kl *ar) | |||
487 | fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS); | 490 | fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS); |
488 | 491 | ||
489 | /* | 492 | /* |
490 | * By default, submodes : | 493 | * Submodes when fw does not support dynamic interface |
494 | * switching: | ||
491 | * vif[0] - AP/STA/IBSS | 495 | * vif[0] - AP/STA/IBSS |
492 | * vif[1] - "P2P dev"/"P2P GO"/"P2P Client" | 496 | * vif[1] - "P2P dev"/"P2P GO"/"P2P Client" |
493 | * vif[2] - "P2P dev"/"P2P GO"/"P2P Client" | 497 | * vif[2] - "P2P dev"/"P2P GO"/"P2P Client" |
498 | * Otherwise, All the interface are initialized to p2p dev. | ||
494 | */ | 499 | */ |
495 | 500 | ||
496 | for (i = 0; i < ar->max_norm_iface; i++) | 501 | if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, |
497 | fw_submode |= HI_OPTION_FW_SUBMODE_NONE << | 502 | ar->fw_capabilities)) { |
498 | (i * HI_OPTION_FW_SUBMODE_BITS); | 503 | for (i = 0; i < ar->vif_max; i++) |
504 | fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << | ||
505 | (i * HI_OPTION_FW_SUBMODE_BITS); | ||
506 | } else { | ||
507 | for (i = 0; i < ar->max_norm_iface; i++) | ||
508 | fw_submode |= HI_OPTION_FW_SUBMODE_NONE << | ||
509 | (i * HI_OPTION_FW_SUBMODE_BITS); | ||
499 | 510 | ||
500 | for (i = ar->max_norm_iface; i < ar->vif_max; i++) | 511 | for (i = ar->max_norm_iface; i < ar->vif_max; i++) |
501 | fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << | 512 | fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << |
502 | (i * HI_OPTION_FW_SUBMODE_BITS); | 513 | (i * HI_OPTION_FW_SUBMODE_BITS); |
503 | 514 | ||
504 | if (ar->p2p && ar->vif_max == 1) | 515 | if (ar->p2p && ar->vif_max == 1) |
505 | fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; | 516 | fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; |
517 | } | ||
506 | 518 | ||
507 | if (ath6kl_bmi_write_hi32(ar, hi_app_host_interest, | 519 | if (ath6kl_bmi_write_hi32(ar, hi_app_host_interest, |
508 | HTC_PROTOCOL_VERSION) != 0) { | 520 | HTC_PROTOCOL_VERSION) != 0) { |
@@ -541,18 +553,20 @@ int ath6kl_configure_target(struct ath6kl *ar) | |||
541 | * but possible in theory. | 553 | * but possible in theory. |
542 | */ | 554 | */ |
543 | 555 | ||
544 | param = ar->hw.board_ext_data_addr; | 556 | if (ar->target_type == TARGET_TYPE_AR6003) { |
545 | ram_reserved_size = ar->hw.reserved_ram_size; | 557 | param = ar->hw.board_ext_data_addr; |
558 | ram_reserved_size = ar->hw.reserved_ram_size; | ||
546 | 559 | ||
547 | if (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) { | 560 | if (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) { |
548 | ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); | 561 | ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); |
549 | return -EIO; | 562 | return -EIO; |
550 | } | 563 | } |
551 | 564 | ||
552 | if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, | 565 | if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, |
553 | ram_reserved_size) != 0) { | 566 | ram_reserved_size) != 0) { |
554 | ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); | 567 | ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); |
555 | return -EIO; | 568 | return -EIO; |
569 | } | ||
556 | } | 570 | } |
557 | 571 | ||
558 | /* set the block size for the target */ | 572 | /* set the block size for the target */ |
@@ -926,13 +940,14 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) | |||
926 | if (ar->fw != NULL) | 940 | if (ar->fw != NULL) |
927 | break; | 941 | break; |
928 | 942 | ||
929 | ar->fw = kmemdup(data, ie_len, GFP_KERNEL); | 943 | ar->fw = vmalloc(ie_len); |
930 | 944 | ||
931 | if (ar->fw == NULL) { | 945 | if (ar->fw == NULL) { |
932 | ret = -ENOMEM; | 946 | ret = -ENOMEM; |
933 | goto out; | 947 | goto out; |
934 | } | 948 | } |
935 | 949 | ||
950 | memcpy(ar->fw, data, ie_len); | ||
936 | ar->fw_len = ie_len; | 951 | ar->fw_len = ie_len; |
937 | break; | 952 | break; |
938 | case ATH6KL_FW_IE_PATCH_IMAGE: | 953 | case ATH6KL_FW_IE_PATCH_IMAGE: |
@@ -1509,7 +1524,7 @@ int ath6kl_init_hw_start(struct ath6kl *ar) | |||
1509 | } | 1524 | } |
1510 | 1525 | ||
1511 | /* setup credit distribution */ | 1526 | /* setup credit distribution */ |
1512 | ath6kl_credit_setup(ar->htc_target, &ar->credit_state_info); | 1527 | ath6kl_htc_credit_setup(ar->htc_target, &ar->credit_state_info); |
1513 | 1528 | ||
1514 | /* start HTC */ | 1529 | /* start HTC */ |
1515 | ret = ath6kl_htc_start(ar->htc_target); | 1530 | ret = ath6kl_htc_start(ar->htc_target); |
diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 07071fce8a0e..4d818f96c415 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c | |||
@@ -758,6 +758,10 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) | |||
758 | stats->wow_evt_discarded += | 758 | stats->wow_evt_discarded += |
759 | le16_to_cpu(tgt_stats->wow_stats.wow_evt_discarded); | 759 | le16_to_cpu(tgt_stats->wow_stats.wow_evt_discarded); |
760 | 760 | ||
761 | stats->arp_received = le32_to_cpu(tgt_stats->arp_stats.arp_received); | ||
762 | stats->arp_replied = le32_to_cpu(tgt_stats->arp_stats.arp_replied); | ||
763 | stats->arp_matched = le32_to_cpu(tgt_stats->arp_stats.arp_matched); | ||
764 | |||
761 | if (test_bit(STATS_UPDATE_PEND, &vif->flags)) { | 765 | if (test_bit(STATS_UPDATE_PEND, &vif->flags)) { |
762 | clear_bit(STATS_UPDATE_PEND, &vif->flags); | 766 | clear_bit(STATS_UPDATE_PEND, &vif->flags); |
763 | wake_up(&ar->event_wq); | 767 | wake_up(&ar->event_wq); |
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 53528648b425..44ea7a742101 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c | |||
@@ -1362,7 +1362,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func, | |||
1362 | goto err_core_alloc; | 1362 | goto err_core_alloc; |
1363 | } | 1363 | } |
1364 | 1364 | ||
1365 | ret = ath6kl_core_init(ar); | 1365 | ret = ath6kl_core_init(ar, ATH6KL_HTC_TYPE_MBOX); |
1366 | if (ret) { | 1366 | if (ret) { |
1367 | ath6kl_err("Failed to init ath6kl core\n"); | 1367 | ath6kl_err("Failed to init ath6kl core\n"); |
1368 | goto err_core_alloc; | 1368 | goto err_core_alloc; |
diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 521f0be990f1..82f2f5cb475b 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c | |||
@@ -19,6 +19,7 @@ | |||
19 | 19 | ||
20 | #include "core.h" | 20 | #include "core.h" |
21 | #include "debug.h" | 21 | #include "debug.h" |
22 | #include "htc-ops.h" | ||
22 | 23 | ||
23 | /* | 24 | /* |
24 | * tid - tid_mux0..tid_mux3 | 25 | * tid - tid_mux0..tid_mux3 |
@@ -324,6 +325,7 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, | |||
324 | cookie->map_no = 0; | 325 | cookie->map_no = 0; |
325 | set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, | 326 | set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, |
326 | eid, ATH6KL_CONTROL_PKT_TAG); | 327 | eid, ATH6KL_CONTROL_PKT_TAG); |
328 | cookie->htc_pkt.skb = skb; | ||
327 | 329 | ||
328 | /* | 330 | /* |
329 | * This interface is asynchronous, if there is an error, cleanup | 331 | * This interface is asynchronous, if there is an error, cleanup |
@@ -492,6 +494,7 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) | |||
492 | cookie->map_no = map_no; | 494 | cookie->map_no = map_no; |
493 | set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, | 495 | set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, |
494 | eid, htc_tag); | 496 | eid, htc_tag); |
497 | cookie->htc_pkt.skb = skb; | ||
495 | 498 | ||
496 | ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ", | 499 | ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ", |
497 | skb->data, skb->len); | 500 | skb->data, skb->len); |
@@ -572,7 +575,7 @@ void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active) | |||
572 | 575 | ||
573 | notify_htc: | 576 | notify_htc: |
574 | /* notify HTC, this may cause credit distribution changes */ | 577 | /* notify HTC, this may cause credit distribution changes */ |
575 | ath6kl_htc_indicate_activity_change(ar->htc_target, eid, active); | 578 | ath6kl_htc_activity_changed(ar->htc_target, eid, active); |
576 | } | 579 | } |
577 | 580 | ||
578 | enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, | 581 | enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, |
@@ -668,9 +671,10 @@ static void ath6kl_tx_clear_node_map(struct ath6kl_vif *vif, | |||
668 | } | 671 | } |
669 | } | 672 | } |
670 | 673 | ||
671 | void ath6kl_tx_complete(void *context, struct list_head *packet_queue) | 674 | void ath6kl_tx_complete(struct htc_target *target, |
675 | struct list_head *packet_queue) | ||
672 | { | 676 | { |
673 | struct ath6kl *ar = context; | 677 | struct ath6kl *ar = target->dev->ar; |
674 | struct sk_buff_head skb_queue; | 678 | struct sk_buff_head skb_queue; |
675 | struct htc_packet *packet; | 679 | struct htc_packet *packet; |
676 | struct sk_buff *skb; | 680 | struct sk_buff *skb; |
@@ -889,6 +893,7 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) | |||
889 | skb->data = PTR_ALIGN(skb->data - 4, 4); | 893 | skb->data = PTR_ALIGN(skb->data - 4, 4); |
890 | set_htc_rxpkt_info(packet, skb, skb->data, | 894 | set_htc_rxpkt_info(packet, skb, skb->data, |
891 | ATH6KL_BUFFER_SIZE, endpoint); | 895 | ATH6KL_BUFFER_SIZE, endpoint); |
896 | packet->skb = skb; | ||
892 | list_add_tail(&packet->list, &queue); | 897 | list_add_tail(&packet->list, &queue); |
893 | } | 898 | } |
894 | 899 | ||
@@ -911,6 +916,8 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count) | |||
911 | skb->data = PTR_ALIGN(skb->data - 4, 4); | 916 | skb->data = PTR_ALIGN(skb->data - 4, 4); |
912 | set_htc_rxpkt_info(packet, skb, skb->data, | 917 | set_htc_rxpkt_info(packet, skb, skb->data, |
913 | ATH6KL_AMSDU_BUFFER_SIZE, 0); | 918 | ATH6KL_AMSDU_BUFFER_SIZE, 0); |
919 | packet->skb = skb; | ||
920 | |||
914 | spin_lock_bh(&ar->lock); | 921 | spin_lock_bh(&ar->lock); |
915 | list_add_tail(&packet->list, &ar->amsdu_rx_buffer_queue); | 922 | list_add_tail(&packet->list, &ar->amsdu_rx_buffer_queue); |
916 | spin_unlock_bh(&ar->lock); | 923 | spin_unlock_bh(&ar->lock); |
@@ -1283,6 +1290,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) | |||
1283 | struct wmi_data_hdr *dhdr; | 1290 | struct wmi_data_hdr *dhdr; |
1284 | int min_hdr_len; | 1291 | int min_hdr_len; |
1285 | u8 meta_type, dot11_hdr = 0; | 1292 | u8 meta_type, dot11_hdr = 0; |
1293 | u8 pad_before_data_start; | ||
1286 | int status = packet->status; | 1294 | int status = packet->status; |
1287 | enum htc_endpoint_id ept = packet->endpoint; | 1295 | enum htc_endpoint_id ept = packet->endpoint; |
1288 | bool is_amsdu, prev_ps, ps_state = false; | 1296 | bool is_amsdu, prev_ps, ps_state = false; |
@@ -1494,6 +1502,10 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) | |||
1494 | seq_no = wmi_data_hdr_get_seqno(dhdr); | 1502 | seq_no = wmi_data_hdr_get_seqno(dhdr); |
1495 | meta_type = wmi_data_hdr_get_meta(dhdr); | 1503 | meta_type = wmi_data_hdr_get_meta(dhdr); |
1496 | dot11_hdr = wmi_data_hdr_get_dot11(dhdr); | 1504 | dot11_hdr = wmi_data_hdr_get_dot11(dhdr); |
1505 | pad_before_data_start = | ||
1506 | (le16_to_cpu(dhdr->info3) >> WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT) | ||
1507 | & WMI_DATA_HDR_PAD_BEFORE_DATA_MASK; | ||
1508 | |||
1497 | skb_pull(skb, sizeof(struct wmi_data_hdr)); | 1509 | skb_pull(skb, sizeof(struct wmi_data_hdr)); |
1498 | 1510 | ||
1499 | switch (meta_type) { | 1511 | switch (meta_type) { |
@@ -1512,6 +1524,8 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) | |||
1512 | break; | 1524 | break; |
1513 | } | 1525 | } |
1514 | 1526 | ||
1527 | skb_pull(skb, pad_before_data_start); | ||
1528 | |||
1515 | if (dot11_hdr) | 1529 | if (dot11_hdr) |
1516 | status = ath6kl_wmi_dot11_hdr_remove(ar->wmi, skb); | 1530 | status = ath6kl_wmi_dot11_hdr_remove(ar->wmi, skb); |
1517 | else if (!is_amsdu) | 1531 | else if (!is_amsdu) |
@@ -1581,7 +1595,8 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) | |||
1581 | /* aggregation code will handle the skb */ | 1595 | /* aggregation code will handle the skb */ |
1582 | return; | 1596 | return; |
1583 | } | 1597 | } |
1584 | } | 1598 | } else if (!is_broadcast_ether_addr(datap->h_dest)) |
1599 | vif->net_stats.multicast++; | ||
1585 | 1600 | ||
1586 | ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb); | 1601 | ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb); |
1587 | } | 1602 | } |
diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 325b1224c2b1..ec7f1f5fd1ca 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c | |||
@@ -21,15 +21,77 @@ | |||
21 | #include "debug.h" | 21 | #include "debug.h" |
22 | #include "core.h" | 22 | #include "core.h" |
23 | 23 | ||
24 | /* constants */ | ||
25 | #define TX_URB_COUNT 32 | ||
26 | #define RX_URB_COUNT 32 | ||
27 | #define ATH6KL_USB_RX_BUFFER_SIZE 1700 | ||
28 | |||
29 | /* tx/rx pipes for usb */ | ||
30 | enum ATH6KL_USB_PIPE_ID { | ||
31 | ATH6KL_USB_PIPE_TX_CTRL = 0, | ||
32 | ATH6KL_USB_PIPE_TX_DATA_LP, | ||
33 | ATH6KL_USB_PIPE_TX_DATA_MP, | ||
34 | ATH6KL_USB_PIPE_TX_DATA_HP, | ||
35 | ATH6KL_USB_PIPE_RX_CTRL, | ||
36 | ATH6KL_USB_PIPE_RX_DATA, | ||
37 | ATH6KL_USB_PIPE_RX_DATA2, | ||
38 | ATH6KL_USB_PIPE_RX_INT, | ||
39 | ATH6KL_USB_PIPE_MAX | ||
40 | }; | ||
41 | |||
42 | #define ATH6KL_USB_PIPE_INVALID ATH6KL_USB_PIPE_MAX | ||
43 | |||
44 | struct ath6kl_usb_pipe { | ||
45 | struct list_head urb_list_head; | ||
46 | struct usb_anchor urb_submitted; | ||
47 | u32 urb_alloc; | ||
48 | u32 urb_cnt; | ||
49 | u32 urb_cnt_thresh; | ||
50 | unsigned int usb_pipe_handle; | ||
51 | u32 flags; | ||
52 | u8 ep_address; | ||
53 | u8 logical_pipe_num; | ||
54 | struct ath6kl_usb *ar_usb; | ||
55 | u16 max_packet_size; | ||
56 | struct work_struct io_complete_work; | ||
57 | struct sk_buff_head io_comp_queue; | ||
58 | struct usb_endpoint_descriptor *ep_desc; | ||
59 | }; | ||
60 | |||
61 | #define ATH6KL_USB_PIPE_FLAG_TX (1 << 0) | ||
62 | |||
24 | /* usb device object */ | 63 | /* usb device object */ |
25 | struct ath6kl_usb { | 64 | struct ath6kl_usb { |
65 | /* protects pipe->urb_list_head and pipe->urb_cnt */ | ||
66 | spinlock_t cs_lock; | ||
67 | |||
26 | struct usb_device *udev; | 68 | struct usb_device *udev; |
27 | struct usb_interface *interface; | 69 | struct usb_interface *interface; |
70 | struct ath6kl_usb_pipe pipes[ATH6KL_USB_PIPE_MAX]; | ||
28 | u8 *diag_cmd_buffer; | 71 | u8 *diag_cmd_buffer; |
29 | u8 *diag_resp_buffer; | 72 | u8 *diag_resp_buffer; |
30 | struct ath6kl *ar; | 73 | struct ath6kl *ar; |
31 | }; | 74 | }; |
32 | 75 | ||
76 | /* usb urb object */ | ||
77 | struct ath6kl_urb_context { | ||
78 | struct list_head link; | ||
79 | struct ath6kl_usb_pipe *pipe; | ||
80 | struct sk_buff *skb; | ||
81 | struct ath6kl *ar; | ||
82 | }; | ||
83 | |||
84 | /* USB endpoint definitions */ | ||
85 | #define ATH6KL_USB_EP_ADDR_APP_CTRL_IN 0x81 | ||
86 | #define ATH6KL_USB_EP_ADDR_APP_DATA_IN 0x82 | ||
87 | #define ATH6KL_USB_EP_ADDR_APP_DATA2_IN 0x83 | ||
88 | #define ATH6KL_USB_EP_ADDR_APP_INT_IN 0x84 | ||
89 | |||
90 | #define ATH6KL_USB_EP_ADDR_APP_CTRL_OUT 0x01 | ||
91 | #define ATH6KL_USB_EP_ADDR_APP_DATA_LP_OUT 0x02 | ||
92 | #define ATH6KL_USB_EP_ADDR_APP_DATA_MP_OUT 0x03 | ||
93 | #define ATH6KL_USB_EP_ADDR_APP_DATA_HP_OUT 0x04 | ||
94 | |||
33 | /* diagnostic command defnitions */ | 95 | /* diagnostic command defnitions */ |
34 | #define ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD 1 | 96 | #define ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD 1 |
35 | #define ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP 2 | 97 | #define ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP 2 |
@@ -55,11 +117,493 @@ struct ath6kl_usb_ctrl_diag_resp_read { | |||
55 | __le32 value; | 117 | __le32 value; |
56 | } __packed; | 118 | } __packed; |
57 | 119 | ||
120 | /* function declarations */ | ||
121 | static void ath6kl_usb_recv_complete(struct urb *urb); | ||
122 | |||
123 | #define ATH6KL_USB_IS_BULK_EP(attr) (((attr) & 3) == 0x02) | ||
124 | #define ATH6KL_USB_IS_INT_EP(attr) (((attr) & 3) == 0x03) | ||
125 | #define ATH6KL_USB_IS_ISOC_EP(attr) (((attr) & 3) == 0x01) | ||
126 | #define ATH6KL_USB_IS_DIR_IN(addr) ((addr) & 0x80) | ||
127 | |||
128 | /* pipe/urb operations */ | ||
129 | static struct ath6kl_urb_context * | ||
130 | ath6kl_usb_alloc_urb_from_pipe(struct ath6kl_usb_pipe *pipe) | ||
131 | { | ||
132 | struct ath6kl_urb_context *urb_context = NULL; | ||
133 | unsigned long flags; | ||
134 | |||
135 | spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); | ||
136 | if (!list_empty(&pipe->urb_list_head)) { | ||
137 | urb_context = | ||
138 | list_first_entry(&pipe->urb_list_head, | ||
139 | struct ath6kl_urb_context, link); | ||
140 | list_del(&urb_context->link); | ||
141 | pipe->urb_cnt--; | ||
142 | } | ||
143 | spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); | ||
144 | |||
145 | return urb_context; | ||
146 | } | ||
147 | |||
148 | static void ath6kl_usb_free_urb_to_pipe(struct ath6kl_usb_pipe *pipe, | ||
149 | struct ath6kl_urb_context *urb_context) | ||
150 | { | ||
151 | unsigned long flags; | ||
152 | |||
153 | spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); | ||
154 | pipe->urb_cnt++; | ||
155 | |||
156 | list_add(&urb_context->link, &pipe->urb_list_head); | ||
157 | spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); | ||
158 | } | ||
159 | |||
160 | static void ath6kl_usb_cleanup_recv_urb(struct ath6kl_urb_context *urb_context) | ||
161 | { | ||
162 | if (urb_context->skb != NULL) { | ||
163 | dev_kfree_skb(urb_context->skb); | ||
164 | urb_context->skb = NULL; | ||
165 | } | ||
166 | |||
167 | ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); | ||
168 | } | ||
169 | |||
170 | static inline struct ath6kl_usb *ath6kl_usb_priv(struct ath6kl *ar) | ||
171 | { | ||
172 | return ar->hif_priv; | ||
173 | } | ||
174 | |||
175 | /* pipe resource allocation/cleanup */ | ||
176 | static int ath6kl_usb_alloc_pipe_resources(struct ath6kl_usb_pipe *pipe, | ||
177 | int urb_cnt) | ||
178 | { | ||
179 | struct ath6kl_urb_context *urb_context; | ||
180 | int status = 0, i; | ||
181 | |||
182 | INIT_LIST_HEAD(&pipe->urb_list_head); | ||
183 | init_usb_anchor(&pipe->urb_submitted); | ||
184 | |||
185 | for (i = 0; i < urb_cnt; i++) { | ||
186 | urb_context = kzalloc(sizeof(struct ath6kl_urb_context), | ||
187 | GFP_KERNEL); | ||
188 | if (urb_context == NULL) | ||
189 | /* FIXME: set status to -ENOMEM */ | ||
190 | break; | ||
191 | |||
192 | urb_context->pipe = pipe; | ||
193 | |||
194 | /* | ||
195 | * we are only allocate the urb contexts here, the actual URB | ||
196 | * is allocated from the kernel as needed to do a transaction | ||
197 | */ | ||
198 | pipe->urb_alloc++; | ||
199 | ath6kl_usb_free_urb_to_pipe(pipe, urb_context); | ||
200 | } | ||
201 | |||
202 | ath6kl_dbg(ATH6KL_DBG_USB, | ||
203 | "ath6kl usb: alloc resources lpipe:%d hpipe:0x%X urbs:%d\n", | ||
204 | pipe->logical_pipe_num, pipe->usb_pipe_handle, | ||
205 | pipe->urb_alloc); | ||
206 | |||
207 | return status; | ||
208 | } | ||
209 | |||
210 | static void ath6kl_usb_free_pipe_resources(struct ath6kl_usb_pipe *pipe) | ||
211 | { | ||
212 | struct ath6kl_urb_context *urb_context; | ||
213 | |||
214 | if (pipe->ar_usb == NULL) { | ||
215 | /* nothing allocated for this pipe */ | ||
216 | return; | ||
217 | } | ||
218 | |||
219 | ath6kl_dbg(ATH6KL_DBG_USB, | ||
220 | "ath6kl usb: free resources lpipe:%d" | ||
221 | "hpipe:0x%X urbs:%d avail:%d\n", | ||
222 | pipe->logical_pipe_num, pipe->usb_pipe_handle, | ||
223 | pipe->urb_alloc, pipe->urb_cnt); | ||
224 | |||
225 | if (pipe->urb_alloc != pipe->urb_cnt) { | ||
226 | ath6kl_dbg(ATH6KL_DBG_USB, | ||
227 | "ath6kl usb: urb leak! lpipe:%d" | ||
228 | "hpipe:0x%X urbs:%d avail:%d\n", | ||
229 | pipe->logical_pipe_num, pipe->usb_pipe_handle, | ||
230 | pipe->urb_alloc, pipe->urb_cnt); | ||
231 | } | ||
232 | |||
233 | while (true) { | ||
234 | urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe); | ||
235 | if (urb_context == NULL) | ||
236 | break; | ||
237 | kfree(urb_context); | ||
238 | } | ||
239 | |||
240 | } | ||
241 | |||
242 | static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *ar_usb) | ||
243 | { | ||
244 | int i; | ||
245 | |||
246 | for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) | ||
247 | ath6kl_usb_free_pipe_resources(&ar_usb->pipes[i]); | ||
248 | |||
249 | } | ||
250 | |||
251 | static u8 ath6kl_usb_get_logical_pipe_num(struct ath6kl_usb *ar_usb, | ||
252 | u8 ep_address, int *urb_count) | ||
253 | { | ||
254 | u8 pipe_num = ATH6KL_USB_PIPE_INVALID; | ||
255 | |||
256 | switch (ep_address) { | ||
257 | case ATH6KL_USB_EP_ADDR_APP_CTRL_IN: | ||
258 | pipe_num = ATH6KL_USB_PIPE_RX_CTRL; | ||
259 | *urb_count = RX_URB_COUNT; | ||
260 | break; | ||
261 | case ATH6KL_USB_EP_ADDR_APP_DATA_IN: | ||
262 | pipe_num = ATH6KL_USB_PIPE_RX_DATA; | ||
263 | *urb_count = RX_URB_COUNT; | ||
264 | break; | ||
265 | case ATH6KL_USB_EP_ADDR_APP_INT_IN: | ||
266 | pipe_num = ATH6KL_USB_PIPE_RX_INT; | ||
267 | *urb_count = RX_URB_COUNT; | ||
268 | break; | ||
269 | case ATH6KL_USB_EP_ADDR_APP_DATA2_IN: | ||
270 | pipe_num = ATH6KL_USB_PIPE_RX_DATA2; | ||
271 | *urb_count = RX_URB_COUNT; | ||
272 | break; | ||
273 | case ATH6KL_USB_EP_ADDR_APP_CTRL_OUT: | ||
274 | pipe_num = ATH6KL_USB_PIPE_TX_CTRL; | ||
275 | *urb_count = TX_URB_COUNT; | ||
276 | break; | ||
277 | case ATH6KL_USB_EP_ADDR_APP_DATA_LP_OUT: | ||
278 | pipe_num = ATH6KL_USB_PIPE_TX_DATA_LP; | ||
279 | *urb_count = TX_URB_COUNT; | ||
280 | break; | ||
281 | case ATH6KL_USB_EP_ADDR_APP_DATA_MP_OUT: | ||
282 | pipe_num = ATH6KL_USB_PIPE_TX_DATA_MP; | ||
283 | *urb_count = TX_URB_COUNT; | ||
284 | break; | ||
285 | case ATH6KL_USB_EP_ADDR_APP_DATA_HP_OUT: | ||
286 | pipe_num = ATH6KL_USB_PIPE_TX_DATA_HP; | ||
287 | *urb_count = TX_URB_COUNT; | ||
288 | break; | ||
289 | default: | ||
290 | /* note: there may be endpoints not currently used */ | ||
291 | break; | ||
292 | } | ||
293 | |||
294 | return pipe_num; | ||
295 | } | ||
296 | |||
297 | static int ath6kl_usb_setup_pipe_resources(struct ath6kl_usb *ar_usb) | ||
298 | { | ||
299 | struct usb_interface *interface = ar_usb->interface; | ||
300 | struct usb_host_interface *iface_desc = interface->cur_altsetting; | ||
301 | struct usb_endpoint_descriptor *endpoint; | ||
302 | struct ath6kl_usb_pipe *pipe; | ||
303 | int i, urbcount, status = 0; | ||
304 | u8 pipe_num; | ||
305 | |||
306 | ath6kl_dbg(ATH6KL_DBG_USB, "setting up USB Pipes using interface\n"); | ||
307 | |||
308 | /* walk decriptors and setup pipes */ | ||
309 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | ||
310 | endpoint = &iface_desc->endpoint[i].desc; | ||
311 | |||
312 | if (ATH6KL_USB_IS_BULK_EP(endpoint->bmAttributes)) { | ||
313 | ath6kl_dbg(ATH6KL_DBG_USB, | ||
314 | "%s Bulk Ep:0x%2.2X maxpktsz:%d\n", | ||
315 | ATH6KL_USB_IS_DIR_IN | ||
316 | (endpoint->bEndpointAddress) ? | ||
317 | "RX" : "TX", endpoint->bEndpointAddress, | ||
318 | le16_to_cpu(endpoint->wMaxPacketSize)); | ||
319 | } else if (ATH6KL_USB_IS_INT_EP(endpoint->bmAttributes)) { | ||
320 | ath6kl_dbg(ATH6KL_DBG_USB, | ||
321 | "%s Int Ep:0x%2.2X maxpktsz:%d interval:%d\n", | ||
322 | ATH6KL_USB_IS_DIR_IN | ||
323 | (endpoint->bEndpointAddress) ? | ||
324 | "RX" : "TX", endpoint->bEndpointAddress, | ||
325 | le16_to_cpu(endpoint->wMaxPacketSize), | ||
326 | endpoint->bInterval); | ||
327 | } else if (ATH6KL_USB_IS_ISOC_EP(endpoint->bmAttributes)) { | ||
328 | /* TODO for ISO */ | ||
329 | ath6kl_dbg(ATH6KL_DBG_USB, | ||
330 | "%s ISOC Ep:0x%2.2X maxpktsz:%d interval:%d\n", | ||
331 | ATH6KL_USB_IS_DIR_IN | ||
332 | (endpoint->bEndpointAddress) ? | ||
333 | "RX" : "TX", endpoint->bEndpointAddress, | ||
334 | le16_to_cpu(endpoint->wMaxPacketSize), | ||
335 | endpoint->bInterval); | ||
336 | } | ||
337 | urbcount = 0; | ||
338 | |||
339 | pipe_num = | ||
340 | ath6kl_usb_get_logical_pipe_num(ar_usb, | ||
341 | endpoint->bEndpointAddress, | ||
342 | &urbcount); | ||
343 | if (pipe_num == ATH6KL_USB_PIPE_INVALID) | ||
344 | continue; | ||
345 | |||
346 | pipe = &ar_usb->pipes[pipe_num]; | ||
347 | if (pipe->ar_usb != NULL) { | ||
348 | /* hmmm..pipe was already setup */ | ||
349 | continue; | ||
350 | } | ||
351 | |||
352 | pipe->ar_usb = ar_usb; | ||
353 | pipe->logical_pipe_num = pipe_num; | ||
354 | pipe->ep_address = endpoint->bEndpointAddress; | ||
355 | pipe->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize); | ||
356 | |||
357 | if (ATH6KL_USB_IS_BULK_EP(endpoint->bmAttributes)) { | ||
358 | if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { | ||
359 | pipe->usb_pipe_handle = | ||
360 | usb_rcvbulkpipe(ar_usb->udev, | ||
361 | pipe->ep_address); | ||
362 | } else { | ||
363 | pipe->usb_pipe_handle = | ||
364 | usb_sndbulkpipe(ar_usb->udev, | ||
365 | pipe->ep_address); | ||
366 | } | ||
367 | } else if (ATH6KL_USB_IS_INT_EP(endpoint->bmAttributes)) { | ||
368 | if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { | ||
369 | pipe->usb_pipe_handle = | ||
370 | usb_rcvintpipe(ar_usb->udev, | ||
371 | pipe->ep_address); | ||
372 | } else { | ||
373 | pipe->usb_pipe_handle = | ||
374 | usb_sndintpipe(ar_usb->udev, | ||
375 | pipe->ep_address); | ||
376 | } | ||
377 | } else if (ATH6KL_USB_IS_ISOC_EP(endpoint->bmAttributes)) { | ||
378 | /* TODO for ISO */ | ||
379 | if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { | ||
380 | pipe->usb_pipe_handle = | ||
381 | usb_rcvisocpipe(ar_usb->udev, | ||
382 | pipe->ep_address); | ||
383 | } else { | ||
384 | pipe->usb_pipe_handle = | ||
385 | usb_sndisocpipe(ar_usb->udev, | ||
386 | pipe->ep_address); | ||
387 | } | ||
388 | } | ||
389 | |||
390 | pipe->ep_desc = endpoint; | ||
391 | |||
392 | if (!ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) | ||
393 | pipe->flags |= ATH6KL_USB_PIPE_FLAG_TX; | ||
394 | |||
395 | status = ath6kl_usb_alloc_pipe_resources(pipe, urbcount); | ||
396 | if (status != 0) | ||
397 | break; | ||
398 | } | ||
399 | |||
400 | return status; | ||
401 | } | ||
402 | |||
403 | /* pipe operations */ | ||
404 | static void ath6kl_usb_post_recv_transfers(struct ath6kl_usb_pipe *recv_pipe, | ||
405 | int buffer_length) | ||
406 | { | ||
407 | struct ath6kl_urb_context *urb_context; | ||
408 | struct urb *urb; | ||
409 | int usb_status; | ||
410 | |||
411 | while (true) { | ||
412 | urb_context = ath6kl_usb_alloc_urb_from_pipe(recv_pipe); | ||
413 | if (urb_context == NULL) | ||
414 | break; | ||
415 | |||
416 | urb_context->skb = dev_alloc_skb(buffer_length); | ||
417 | if (urb_context->skb == NULL) | ||
418 | goto err_cleanup_urb; | ||
419 | |||
420 | urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
421 | if (urb == NULL) | ||
422 | goto err_cleanup_urb; | ||
423 | |||
424 | usb_fill_bulk_urb(urb, | ||
425 | recv_pipe->ar_usb->udev, | ||
426 | recv_pipe->usb_pipe_handle, | ||
427 | urb_context->skb->data, | ||
428 | buffer_length, | ||
429 | ath6kl_usb_recv_complete, urb_context); | ||
430 | |||
431 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
432 | "ath6kl usb: bulk recv submit:%d, 0x%X (ep:0x%2.2X), %d bytes buf:0x%p\n", | ||
433 | recv_pipe->logical_pipe_num, | ||
434 | recv_pipe->usb_pipe_handle, recv_pipe->ep_address, | ||
435 | buffer_length, urb_context->skb); | ||
436 | |||
437 | usb_anchor_urb(urb, &recv_pipe->urb_submitted); | ||
438 | usb_status = usb_submit_urb(urb, GFP_ATOMIC); | ||
439 | |||
440 | if (usb_status) { | ||
441 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
442 | "ath6kl usb : usb bulk recv failed %d\n", | ||
443 | usb_status); | ||
444 | usb_unanchor_urb(urb); | ||
445 | usb_free_urb(urb); | ||
446 | goto err_cleanup_urb; | ||
447 | } | ||
448 | usb_free_urb(urb); | ||
449 | } | ||
450 | return; | ||
451 | |||
452 | err_cleanup_urb: | ||
453 | ath6kl_usb_cleanup_recv_urb(urb_context); | ||
454 | return; | ||
455 | } | ||
456 | |||
457 | static void ath6kl_usb_flush_all(struct ath6kl_usb *ar_usb) | ||
458 | { | ||
459 | int i; | ||
460 | |||
461 | for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) { | ||
462 | if (ar_usb->pipes[i].ar_usb != NULL) | ||
463 | usb_kill_anchored_urbs(&ar_usb->pipes[i].urb_submitted); | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * Flushing any pending I/O may schedule work this call will block | ||
468 | * until all scheduled work runs to completion. | ||
469 | */ | ||
470 | flush_scheduled_work(); | ||
471 | } | ||
472 | |||
473 | static void ath6kl_usb_start_recv_pipes(struct ath6kl_usb *ar_usb) | ||
474 | { | ||
475 | /* | ||
476 | * note: control pipe is no longer used | ||
477 | * ar_usb->pipes[ATH6KL_USB_PIPE_RX_CTRL].urb_cnt_thresh = | ||
478 | * ar_usb->pipes[ATH6KL_USB_PIPE_RX_CTRL].urb_alloc/2; | ||
479 | * ath6kl_usb_post_recv_transfers(&ar_usb-> | ||
480 | * pipes[ATH6KL_USB_PIPE_RX_CTRL], | ||
481 | * ATH6KL_USB_RX_BUFFER_SIZE); | ||
482 | */ | ||
483 | |||
484 | ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = | ||
485 | ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_alloc / 2; | ||
486 | ath6kl_usb_post_recv_transfers(&ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA], | ||
487 | ATH6KL_USB_RX_BUFFER_SIZE); | ||
488 | } | ||
489 | |||
490 | /* hif usb rx/tx completion functions */ | ||
491 | static void ath6kl_usb_recv_complete(struct urb *urb) | ||
492 | { | ||
493 | struct ath6kl_urb_context *urb_context = urb->context; | ||
494 | struct ath6kl_usb_pipe *pipe = urb_context->pipe; | ||
495 | struct sk_buff *skb = NULL; | ||
496 | int status = 0; | ||
497 | |||
498 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
499 | "%s: recv pipe: %d, stat:%d, len:%d urb:0x%p\n", __func__, | ||
500 | pipe->logical_pipe_num, urb->status, urb->actual_length, | ||
501 | urb); | ||
502 | |||
503 | if (urb->status != 0) { | ||
504 | status = -EIO; | ||
505 | switch (urb->status) { | ||
506 | case -ECONNRESET: | ||
507 | case -ENOENT: | ||
508 | case -ESHUTDOWN: | ||
509 | /* | ||
510 | * no need to spew these errors when device | ||
511 | * removed or urb killed due to driver shutdown | ||
512 | */ | ||
513 | status = -ECANCELED; | ||
514 | break; | ||
515 | default: | ||
516 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
517 | "%s recv pipe: %d (ep:0x%2.2X), failed:%d\n", | ||
518 | __func__, pipe->logical_pipe_num, | ||
519 | pipe->ep_address, urb->status); | ||
520 | break; | ||
521 | } | ||
522 | goto cleanup_recv_urb; | ||
523 | } | ||
524 | |||
525 | if (urb->actual_length == 0) | ||
526 | goto cleanup_recv_urb; | ||
527 | |||
528 | skb = urb_context->skb; | ||
529 | |||
530 | /* we are going to pass it up */ | ||
531 | urb_context->skb = NULL; | ||
532 | skb_put(skb, urb->actual_length); | ||
533 | |||
534 | /* note: queue implements a lock */ | ||
535 | skb_queue_tail(&pipe->io_comp_queue, skb); | ||
536 | schedule_work(&pipe->io_complete_work); | ||
537 | |||
538 | cleanup_recv_urb: | ||
539 | ath6kl_usb_cleanup_recv_urb(urb_context); | ||
540 | |||
541 | if (status == 0 && | ||
542 | pipe->urb_cnt >= pipe->urb_cnt_thresh) { | ||
543 | /* our free urbs are piling up, post more transfers */ | ||
544 | ath6kl_usb_post_recv_transfers(pipe, ATH6KL_USB_RX_BUFFER_SIZE); | ||
545 | } | ||
546 | } | ||
547 | |||
548 | static void ath6kl_usb_usb_transmit_complete(struct urb *urb) | ||
549 | { | ||
550 | struct ath6kl_urb_context *urb_context = urb->context; | ||
551 | struct ath6kl_usb_pipe *pipe = urb_context->pipe; | ||
552 | struct sk_buff *skb; | ||
553 | |||
554 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
555 | "%s: pipe: %d, stat:%d, len:%d\n", | ||
556 | __func__, pipe->logical_pipe_num, urb->status, | ||
557 | urb->actual_length); | ||
558 | |||
559 | if (urb->status != 0) { | ||
560 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
561 | "%s: pipe: %d, failed:%d\n", | ||
562 | __func__, pipe->logical_pipe_num, urb->status); | ||
563 | } | ||
564 | |||
565 | skb = urb_context->skb; | ||
566 | urb_context->skb = NULL; | ||
567 | ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); | ||
568 | |||
569 | /* note: queue implements a lock */ | ||
570 | skb_queue_tail(&pipe->io_comp_queue, skb); | ||
571 | schedule_work(&pipe->io_complete_work); | ||
572 | } | ||
573 | |||
574 | static void ath6kl_usb_io_comp_work(struct work_struct *work) | ||
575 | { | ||
576 | struct ath6kl_usb_pipe *pipe = container_of(work, | ||
577 | struct ath6kl_usb_pipe, | ||
578 | io_complete_work); | ||
579 | struct ath6kl_usb *ar_usb; | ||
580 | struct sk_buff *skb; | ||
581 | |||
582 | ar_usb = pipe->ar_usb; | ||
583 | |||
584 | while ((skb = skb_dequeue(&pipe->io_comp_queue))) { | ||
585 | if (pipe->flags & ATH6KL_USB_PIPE_FLAG_TX) { | ||
586 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
587 | "ath6kl usb xmit callback buf:0x%p\n", skb); | ||
588 | ath6kl_core_tx_complete(ar_usb->ar, skb); | ||
589 | } else { | ||
590 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
591 | "ath6kl usb recv callback buf:0x%p\n", skb); | ||
592 | ath6kl_core_rx_complete(ar_usb->ar, skb, | ||
593 | pipe->logical_pipe_num); | ||
594 | } | ||
595 | } | ||
596 | } | ||
597 | |||
58 | #define ATH6KL_USB_MAX_DIAG_CMD (sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)) | 598 | #define ATH6KL_USB_MAX_DIAG_CMD (sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)) |
59 | #define ATH6KL_USB_MAX_DIAG_RESP (sizeof(struct ath6kl_usb_ctrl_diag_resp_read)) | 599 | #define ATH6KL_USB_MAX_DIAG_RESP (sizeof(struct ath6kl_usb_ctrl_diag_resp_read)) |
60 | 600 | ||
61 | static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb) | 601 | static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb) |
62 | { | 602 | { |
603 | ath6kl_usb_flush_all(ar_usb); | ||
604 | |||
605 | ath6kl_usb_cleanup_pipe_resources(ar_usb); | ||
606 | |||
63 | usb_set_intfdata(ar_usb->interface, NULL); | 607 | usb_set_intfdata(ar_usb->interface, NULL); |
64 | 608 | ||
65 | kfree(ar_usb->diag_cmd_buffer); | 609 | kfree(ar_usb->diag_cmd_buffer); |
@@ -70,19 +614,28 @@ static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb) | |||
70 | 614 | ||
71 | static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface) | 615 | static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface) |
72 | { | 616 | { |
73 | struct ath6kl_usb *ar_usb = NULL; | ||
74 | struct usb_device *dev = interface_to_usbdev(interface); | 617 | struct usb_device *dev = interface_to_usbdev(interface); |
618 | struct ath6kl_usb *ar_usb; | ||
619 | struct ath6kl_usb_pipe *pipe; | ||
75 | int status = 0; | 620 | int status = 0; |
621 | int i; | ||
76 | 622 | ||
77 | ar_usb = kzalloc(sizeof(struct ath6kl_usb), GFP_KERNEL); | 623 | ar_usb = kzalloc(sizeof(struct ath6kl_usb), GFP_KERNEL); |
78 | if (ar_usb == NULL) | 624 | if (ar_usb == NULL) |
79 | goto fail_ath6kl_usb_create; | 625 | goto fail_ath6kl_usb_create; |
80 | 626 | ||
81 | memset(ar_usb, 0, sizeof(struct ath6kl_usb)); | ||
82 | usb_set_intfdata(interface, ar_usb); | 627 | usb_set_intfdata(interface, ar_usb); |
628 | spin_lock_init(&(ar_usb->cs_lock)); | ||
83 | ar_usb->udev = dev; | 629 | ar_usb->udev = dev; |
84 | ar_usb->interface = interface; | 630 | ar_usb->interface = interface; |
85 | 631 | ||
632 | for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) { | ||
633 | pipe = &ar_usb->pipes[i]; | ||
634 | INIT_WORK(&pipe->io_complete_work, | ||
635 | ath6kl_usb_io_comp_work); | ||
636 | skb_queue_head_init(&pipe->io_comp_queue); | ||
637 | } | ||
638 | |||
86 | ar_usb->diag_cmd_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_CMD, GFP_KERNEL); | 639 | ar_usb->diag_cmd_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_CMD, GFP_KERNEL); |
87 | if (ar_usb->diag_cmd_buffer == NULL) { | 640 | if (ar_usb->diag_cmd_buffer == NULL) { |
88 | status = -ENOMEM; | 641 | status = -ENOMEM; |
@@ -96,6 +649,8 @@ static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface) | |||
96 | goto fail_ath6kl_usb_create; | 649 | goto fail_ath6kl_usb_create; |
97 | } | 650 | } |
98 | 651 | ||
652 | status = ath6kl_usb_setup_pipe_resources(ar_usb); | ||
653 | |||
99 | fail_ath6kl_usb_create: | 654 | fail_ath6kl_usb_create: |
100 | if (status != 0) { | 655 | if (status != 0) { |
101 | ath6kl_usb_destroy(ar_usb); | 656 | ath6kl_usb_destroy(ar_usb); |
@@ -114,11 +669,177 @@ static void ath6kl_usb_device_detached(struct usb_interface *interface) | |||
114 | 669 | ||
115 | ath6kl_stop_txrx(ar_usb->ar); | 670 | ath6kl_stop_txrx(ar_usb->ar); |
116 | 671 | ||
672 | /* Delay to wait for the target to reboot */ | ||
673 | mdelay(20); | ||
117 | ath6kl_core_cleanup(ar_usb->ar); | 674 | ath6kl_core_cleanup(ar_usb->ar); |
118 | |||
119 | ath6kl_usb_destroy(ar_usb); | 675 | ath6kl_usb_destroy(ar_usb); |
120 | } | 676 | } |
121 | 677 | ||
678 | /* exported hif usb APIs for htc pipe */ | ||
679 | static void hif_start(struct ath6kl *ar) | ||
680 | { | ||
681 | struct ath6kl_usb *device = ath6kl_usb_priv(ar); | ||
682 | int i; | ||
683 | |||
684 | ath6kl_usb_start_recv_pipes(device); | ||
685 | |||
686 | /* set the TX resource avail threshold for each TX pipe */ | ||
687 | for (i = ATH6KL_USB_PIPE_TX_CTRL; | ||
688 | i <= ATH6KL_USB_PIPE_TX_DATA_HP; i++) { | ||
689 | device->pipes[i].urb_cnt_thresh = | ||
690 | device->pipes[i].urb_alloc / 2; | ||
691 | } | ||
692 | } | ||
693 | |||
694 | static int ath6kl_usb_send(struct ath6kl *ar, u8 PipeID, | ||
695 | struct sk_buff *hdr_skb, struct sk_buff *skb) | ||
696 | { | ||
697 | struct ath6kl_usb *device = ath6kl_usb_priv(ar); | ||
698 | struct ath6kl_usb_pipe *pipe = &device->pipes[PipeID]; | ||
699 | struct ath6kl_urb_context *urb_context; | ||
700 | int usb_status, status = 0; | ||
701 | struct urb *urb; | ||
702 | u8 *data; | ||
703 | u32 len; | ||
704 | |||
705 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, "+%s pipe : %d, buf:0x%p\n", | ||
706 | __func__, PipeID, skb); | ||
707 | |||
708 | urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe); | ||
709 | |||
710 | if (urb_context == NULL) { | ||
711 | /* | ||
712 | * TODO: it is possible to run out of urbs if | ||
713 | * 2 endpoints map to the same pipe ID | ||
714 | */ | ||
715 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
716 | "%s pipe:%d no urbs left. URB Cnt : %d\n", | ||
717 | __func__, PipeID, pipe->urb_cnt); | ||
718 | status = -ENOMEM; | ||
719 | goto fail_hif_send; | ||
720 | } | ||
721 | |||
722 | urb_context->skb = skb; | ||
723 | |||
724 | data = skb->data; | ||
725 | len = skb->len; | ||
726 | |||
727 | urb = usb_alloc_urb(0, GFP_ATOMIC); | ||
728 | if (urb == NULL) { | ||
729 | status = -ENOMEM; | ||
730 | ath6kl_usb_free_urb_to_pipe(urb_context->pipe, | ||
731 | urb_context); | ||
732 | goto fail_hif_send; | ||
733 | } | ||
734 | |||
735 | usb_fill_bulk_urb(urb, | ||
736 | device->udev, | ||
737 | pipe->usb_pipe_handle, | ||
738 | data, | ||
739 | len, | ||
740 | ath6kl_usb_usb_transmit_complete, urb_context); | ||
741 | |||
742 | if ((len % pipe->max_packet_size) == 0) { | ||
743 | /* hit a max packet boundary on this pipe */ | ||
744 | urb->transfer_flags |= URB_ZERO_PACKET; | ||
745 | } | ||
746 | |||
747 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
748 | "athusb bulk send submit:%d, 0x%X (ep:0x%2.2X), %d bytes\n", | ||
749 | pipe->logical_pipe_num, pipe->usb_pipe_handle, | ||
750 | pipe->ep_address, len); | ||
751 | |||
752 | usb_anchor_urb(urb, &pipe->urb_submitted); | ||
753 | usb_status = usb_submit_urb(urb, GFP_ATOMIC); | ||
754 | |||
755 | if (usb_status) { | ||
756 | ath6kl_dbg(ATH6KL_DBG_USB_BULK, | ||
757 | "ath6kl usb : usb bulk transmit failed %d\n", | ||
758 | usb_status); | ||
759 | usb_unanchor_urb(urb); | ||
760 | ath6kl_usb_free_urb_to_pipe(urb_context->pipe, | ||
761 | urb_context); | ||
762 | status = -EINVAL; | ||
763 | } | ||
764 | usb_free_urb(urb); | ||
765 | |||
766 | fail_hif_send: | ||
767 | return status; | ||
768 | } | ||
769 | |||
770 | static void hif_stop(struct ath6kl *ar) | ||
771 | { | ||
772 | struct ath6kl_usb *device = ath6kl_usb_priv(ar); | ||
773 | |||
774 | ath6kl_usb_flush_all(device); | ||
775 | } | ||
776 | |||
777 | static void ath6kl_usb_get_default_pipe(struct ath6kl *ar, | ||
778 | u8 *ul_pipe, u8 *dl_pipe) | ||
779 | { | ||
780 | *ul_pipe = ATH6KL_USB_PIPE_TX_CTRL; | ||
781 | *dl_pipe = ATH6KL_USB_PIPE_RX_CTRL; | ||
782 | } | ||
783 | |||
784 | static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id, | ||
785 | u8 *ul_pipe, u8 *dl_pipe) | ||
786 | { | ||
787 | int status = 0; | ||
788 | |||
789 | switch (svc_id) { | ||
790 | case HTC_CTRL_RSVD_SVC: | ||
791 | case WMI_CONTROL_SVC: | ||
792 | *ul_pipe = ATH6KL_USB_PIPE_TX_CTRL; | ||
793 | /* due to large control packets, shift to data pipe */ | ||
794 | *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; | ||
795 | break; | ||
796 | case WMI_DATA_BE_SVC: | ||
797 | case WMI_DATA_BK_SVC: | ||
798 | *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP; | ||
799 | /* | ||
800 | * Disable rxdata2 directly, it will be enabled | ||
801 | * if FW enable rxdata2 | ||
802 | */ | ||
803 | *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; | ||
804 | break; | ||
805 | case WMI_DATA_VI_SVC: | ||
806 | *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; | ||
807 | /* | ||
808 | * Disable rxdata2 directly, it will be enabled | ||
809 | * if FW enable rxdata2 | ||
810 | */ | ||
811 | *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; | ||
812 | break; | ||
813 | case WMI_DATA_VO_SVC: | ||
814 | *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_HP; | ||
815 | /* | ||
816 | * Disable rxdata2 directly, it will be enabled | ||
817 | * if FW enable rxdata2 | ||
818 | */ | ||
819 | *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; | ||
820 | break; | ||
821 | default: | ||
822 | status = -EPERM; | ||
823 | break; | ||
824 | } | ||
825 | |||
826 | return status; | ||
827 | } | ||
828 | |||
829 | static u16 ath6kl_usb_get_free_queue_number(struct ath6kl *ar, u8 pipe_id) | ||
830 | { | ||
831 | struct ath6kl_usb *device = ath6kl_usb_priv(ar); | ||
832 | |||
833 | return device->pipes[pipe_id].urb_cnt; | ||
834 | } | ||
835 | |||
836 | static void hif_detach_htc(struct ath6kl *ar) | ||
837 | { | ||
838 | struct ath6kl_usb *device = ath6kl_usb_priv(ar); | ||
839 | |||
840 | ath6kl_usb_flush_all(device); | ||
841 | } | ||
842 | |||
122 | static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb, | 843 | static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb, |
123 | u8 req, u16 value, u16 index, void *data, | 844 | u8 req, u16 value, u16 index, void *data, |
124 | u32 size) | 845 | u32 size) |
@@ -301,14 +1022,21 @@ static int ath6kl_usb_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) | |||
301 | 1022 | ||
302 | static int ath6kl_usb_power_on(struct ath6kl *ar) | 1023 | static int ath6kl_usb_power_on(struct ath6kl *ar) |
303 | { | 1024 | { |
1025 | hif_start(ar); | ||
304 | return 0; | 1026 | return 0; |
305 | } | 1027 | } |
306 | 1028 | ||
307 | static int ath6kl_usb_power_off(struct ath6kl *ar) | 1029 | static int ath6kl_usb_power_off(struct ath6kl *ar) |
308 | { | 1030 | { |
1031 | hif_detach_htc(ar); | ||
309 | return 0; | 1032 | return 0; |
310 | } | 1033 | } |
311 | 1034 | ||
1035 | static void ath6kl_usb_stop(struct ath6kl *ar) | ||
1036 | { | ||
1037 | hif_stop(ar); | ||
1038 | } | ||
1039 | |||
312 | static const struct ath6kl_hif_ops ath6kl_usb_ops = { | 1040 | static const struct ath6kl_hif_ops ath6kl_usb_ops = { |
313 | .diag_read32 = ath6kl_usb_diag_read32, | 1041 | .diag_read32 = ath6kl_usb_diag_read32, |
314 | .diag_write32 = ath6kl_usb_diag_write32, | 1042 | .diag_write32 = ath6kl_usb_diag_write32, |
@@ -316,6 +1044,11 @@ static const struct ath6kl_hif_ops ath6kl_usb_ops = { | |||
316 | .bmi_write = ath6kl_usb_bmi_write, | 1044 | .bmi_write = ath6kl_usb_bmi_write, |
317 | .power_on = ath6kl_usb_power_on, | 1045 | .power_on = ath6kl_usb_power_on, |
318 | .power_off = ath6kl_usb_power_off, | 1046 | .power_off = ath6kl_usb_power_off, |
1047 | .stop = ath6kl_usb_stop, | ||
1048 | .pipe_send = ath6kl_usb_send, | ||
1049 | .pipe_get_default = ath6kl_usb_get_default_pipe, | ||
1050 | .pipe_map_service = ath6kl_usb_map_service_pipe, | ||
1051 | .pipe_get_free_queue_number = ath6kl_usb_get_free_queue_number, | ||
319 | }; | 1052 | }; |
320 | 1053 | ||
321 | /* ath6kl usb driver registered functions */ | 1054 | /* ath6kl usb driver registered functions */ |
@@ -368,7 +1101,7 @@ static int ath6kl_usb_probe(struct usb_interface *interface, | |||
368 | 1101 | ||
369 | ar_usb->ar = ar; | 1102 | ar_usb->ar = ar; |
370 | 1103 | ||
371 | ret = ath6kl_core_init(ar); | 1104 | ret = ath6kl_core_init(ar, ATH6KL_HTC_TYPE_PIPE); |
372 | if (ret) { | 1105 | if (ret) { |
373 | ath6kl_err("Failed to init ath6kl core: %d\n", ret); | 1106 | ath6kl_err("Failed to init ath6kl core: %d\n", ret); |
374 | goto err_core_free; | 1107 | goto err_core_free; |
@@ -392,6 +1125,46 @@ static void ath6kl_usb_remove(struct usb_interface *interface) | |||
392 | ath6kl_usb_device_detached(interface); | 1125 | ath6kl_usb_device_detached(interface); |
393 | } | 1126 | } |
394 | 1127 | ||
1128 | #ifdef CONFIG_PM | ||
1129 | |||
1130 | static int ath6kl_usb_suspend(struct usb_interface *interface, | ||
1131 | pm_message_t message) | ||
1132 | { | ||
1133 | struct ath6kl_usb *device; | ||
1134 | device = usb_get_intfdata(interface); | ||
1135 | |||
1136 | ath6kl_usb_flush_all(device); | ||
1137 | return 0; | ||
1138 | } | ||
1139 | |||
1140 | static int ath6kl_usb_resume(struct usb_interface *interface) | ||
1141 | { | ||
1142 | struct ath6kl_usb *device; | ||
1143 | device = usb_get_intfdata(interface); | ||
1144 | |||
1145 | ath6kl_usb_post_recv_transfers(&device->pipes[ATH6KL_USB_PIPE_RX_DATA], | ||
1146 | ATH6KL_USB_RX_BUFFER_SIZE); | ||
1147 | ath6kl_usb_post_recv_transfers(&device->pipes[ATH6KL_USB_PIPE_RX_DATA2], | ||
1148 | ATH6KL_USB_RX_BUFFER_SIZE); | ||
1149 | |||
1150 | return 0; | ||
1151 | } | ||
1152 | |||
1153 | static int ath6kl_usb_reset_resume(struct usb_interface *intf) | ||
1154 | { | ||
1155 | if (usb_get_intfdata(intf)) | ||
1156 | ath6kl_usb_remove(intf); | ||
1157 | return 0; | ||
1158 | } | ||
1159 | |||
1160 | #else | ||
1161 | |||
1162 | #define ath6kl_usb_suspend NULL | ||
1163 | #define ath6kl_usb_resume NULL | ||
1164 | #define ath6kl_usb_reset_resume NULL | ||
1165 | |||
1166 | #endif | ||
1167 | |||
395 | /* table of devices that work with this driver */ | 1168 | /* table of devices that work with this driver */ |
396 | static struct usb_device_id ath6kl_usb_ids[] = { | 1169 | static struct usb_device_id ath6kl_usb_ids[] = { |
397 | {USB_DEVICE(0x0cf3, 0x9374)}, | 1170 | {USB_DEVICE(0x0cf3, 0x9374)}, |
@@ -403,8 +1176,12 @@ MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids); | |||
403 | static struct usb_driver ath6kl_usb_driver = { | 1176 | static struct usb_driver ath6kl_usb_driver = { |
404 | .name = "ath6kl_usb", | 1177 | .name = "ath6kl_usb", |
405 | .probe = ath6kl_usb_probe, | 1178 | .probe = ath6kl_usb_probe, |
1179 | .suspend = ath6kl_usb_suspend, | ||
1180 | .resume = ath6kl_usb_resume, | ||
1181 | .reset_resume = ath6kl_usb_reset_resume, | ||
406 | .disconnect = ath6kl_usb_remove, | 1182 | .disconnect = ath6kl_usb_remove, |
407 | .id_table = ath6kl_usb_ids, | 1183 | .id_table = ath6kl_usb_ids, |
1184 | .supports_autosuspend = true, | ||
408 | }; | 1185 | }; |
409 | 1186 | ||
410 | static int ath6kl_usb_init(void) | 1187 | static int ath6kl_usb_init(void) |
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 2b442332cd0f..7c8a9977faf5 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c | |||
@@ -2882,6 +2882,43 @@ int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, | |||
2882 | return ret; | 2882 | return ret; |
2883 | } | 2883 | } |
2884 | 2884 | ||
2885 | int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx, | ||
2886 | enum ieee80211_band band, | ||
2887 | struct ath6kl_htcap *htcap) | ||
2888 | { | ||
2889 | struct sk_buff *skb; | ||
2890 | struct wmi_set_htcap_cmd *cmd; | ||
2891 | |||
2892 | skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); | ||
2893 | if (!skb) | ||
2894 | return -ENOMEM; | ||
2895 | |||
2896 | cmd = (struct wmi_set_htcap_cmd *) skb->data; | ||
2897 | |||
2898 | /* | ||
2899 | * NOTE: Band in firmware matches enum ieee80211_band, it is unlikely | ||
2900 | * this will be changed in firmware. If at all there is any change in | ||
2901 | * band value, the host needs to be fixed. | ||
2902 | */ | ||
2903 | cmd->band = band; | ||
2904 | cmd->ht_enable = !!htcap->ht_enable; | ||
2905 | cmd->ht20_sgi = !!(htcap->cap_info & IEEE80211_HT_CAP_SGI_20); | ||
2906 | cmd->ht40_supported = | ||
2907 | !!(htcap->cap_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40); | ||
2908 | cmd->ht40_sgi = !!(htcap->cap_info & IEEE80211_HT_CAP_SGI_40); | ||
2909 | cmd->intolerant_40mhz = | ||
2910 | !!(htcap->cap_info & IEEE80211_HT_CAP_40MHZ_INTOLERANT); | ||
2911 | cmd->max_ampdu_len_exp = htcap->ampdu_factor; | ||
2912 | |||
2913 | ath6kl_dbg(ATH6KL_DBG_WMI, | ||
2914 | "Set htcap: band:%d ht_enable:%d 40mhz:%d sgi_20mhz:%d sgi_40mhz:%d 40mhz_intolerant:%d ampdu_len_exp:%d\n", | ||
2915 | cmd->band, cmd->ht_enable, cmd->ht40_supported, | ||
2916 | cmd->ht20_sgi, cmd->ht40_sgi, cmd->intolerant_40mhz, | ||
2917 | cmd->max_ampdu_len_exp); | ||
2918 | return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_HT_CAP_CMDID, | ||
2919 | NO_SYNC_WMIFLAG); | ||
2920 | } | ||
2921 | |||
2885 | int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len) | 2922 | int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len) |
2886 | { | 2923 | { |
2887 | struct sk_buff *skb; | 2924 | struct sk_buff *skb; |
@@ -3032,6 +3069,9 @@ int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, const u8 *mac, | |||
3032 | cm->reason = cpu_to_le16(reason); | 3069 | cm->reason = cpu_to_le16(reason); |
3033 | cm->cmd = cmd; | 3070 | cm->cmd = cmd; |
3034 | 3071 | ||
3072 | ath6kl_dbg(ATH6KL_DBG_WMI, "ap_set_mlme: cmd=%d reason=%d\n", cm->cmd, | ||
3073 | cm->reason); | ||
3074 | |||
3035 | return ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_SET_MLME_CMDID, | 3075 | return ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_SET_MLME_CMDID, |
3036 | NO_SYNC_WMIFLAG); | 3076 | NO_SYNC_WMIFLAG); |
3037 | } | 3077 | } |
@@ -3181,6 +3221,29 @@ int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, | |||
3181 | NO_SYNC_WMIFLAG); | 3221 | NO_SYNC_WMIFLAG); |
3182 | } | 3222 | } |
3183 | 3223 | ||
3224 | int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field, | ||
3225 | const u8 *ie_info, u8 ie_len) | ||
3226 | { | ||
3227 | struct sk_buff *skb; | ||
3228 | struct wmi_set_ie_cmd *p; | ||
3229 | |||
3230 | skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len); | ||
3231 | if (!skb) | ||
3232 | return -ENOMEM; | ||
3233 | |||
3234 | ath6kl_dbg(ATH6KL_DBG_WMI, "set_ie_cmd: ie_id=%u ie_ie_field=%u ie_len=%u\n", | ||
3235 | ie_id, ie_field, ie_len); | ||
3236 | p = (struct wmi_set_ie_cmd *) skb->data; | ||
3237 | p->ie_id = ie_id; | ||
3238 | p->ie_field = ie_field; | ||
3239 | p->ie_len = ie_len; | ||
3240 | if (ie_info && ie_len > 0) | ||
3241 | memcpy(p->ie_info, ie_info, ie_len); | ||
3242 | |||
3243 | return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IE_CMDID, | ||
3244 | NO_SYNC_WMIFLAG); | ||
3245 | } | ||
3246 | |||
3184 | int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable) | 3247 | int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable) |
3185 | { | 3248 | { |
3186 | struct sk_buff *skb; | 3249 | struct sk_buff *skb; |
@@ -3392,6 +3455,23 @@ int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx) | |||
3392 | WMI_CANCEL_REMAIN_ON_CHNL_CMDID); | 3455 | WMI_CANCEL_REMAIN_ON_CHNL_CMDID); |
3393 | } | 3456 | } |
3394 | 3457 | ||
3458 | int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout) | ||
3459 | { | ||
3460 | struct sk_buff *skb; | ||
3461 | struct wmi_set_inact_period_cmd *cmd; | ||
3462 | |||
3463 | skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); | ||
3464 | if (!skb) | ||
3465 | return -ENOMEM; | ||
3466 | |||
3467 | cmd = (struct wmi_set_inact_period_cmd *) skb->data; | ||
3468 | cmd->inact_period = cpu_to_le32(inact_timeout); | ||
3469 | cmd->num_null_func = 0; | ||
3470 | |||
3471 | return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_CONN_INACT_CMDID, | ||
3472 | NO_SYNC_WMIFLAG); | ||
3473 | } | ||
3474 | |||
3395 | static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) | 3475 | static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) |
3396 | { | 3476 | { |
3397 | struct wmix_cmd_hdr *cmd; | 3477 | struct wmix_cmd_hdr *cmd; |
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 4092e3e80790..d3d2ab5c1689 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h | |||
@@ -182,6 +182,9 @@ enum wmi_data_hdr_flags { | |||
182 | #define WMI_DATA_HDR_META_MASK 0x7 | 182 | #define WMI_DATA_HDR_META_MASK 0x7 |
183 | #define WMI_DATA_HDR_META_SHIFT 13 | 183 | #define WMI_DATA_HDR_META_SHIFT 13 |
184 | 184 | ||
185 | #define WMI_DATA_HDR_PAD_BEFORE_DATA_MASK 0xFF | ||
186 | #define WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT 0x8 | ||
187 | |||
185 | /* Macros for operating on WMI_DATA_HDR (info3) field */ | 188 | /* Macros for operating on WMI_DATA_HDR (info3) field */ |
186 | #define WMI_DATA_HDR_IF_IDX_MASK 0xF | 189 | #define WMI_DATA_HDR_IF_IDX_MASK 0xF |
187 | 190 | ||
@@ -423,6 +426,7 @@ enum wmi_cmd_id { | |||
423 | WMI_SET_FRAMERATES_CMDID, | 426 | WMI_SET_FRAMERATES_CMDID, |
424 | WMI_SET_AP_PS_CMDID, | 427 | WMI_SET_AP_PS_CMDID, |
425 | WMI_SET_QOS_SUPP_CMDID, | 428 | WMI_SET_QOS_SUPP_CMDID, |
429 | WMI_SET_IE_CMDID, | ||
426 | 430 | ||
427 | /* WMI_THIN_RESERVED_... mark the start and end | 431 | /* WMI_THIN_RESERVED_... mark the start and end |
428 | * values for WMI_THIN_RESERVED command IDs. These | 432 | * values for WMI_THIN_RESERVED command IDs. These |
@@ -629,6 +633,11 @@ enum wmi_mgmt_frame_type { | |||
629 | WMI_NUM_MGMT_FRAME | 633 | WMI_NUM_MGMT_FRAME |
630 | }; | 634 | }; |
631 | 635 | ||
636 | enum wmi_ie_field_type { | ||
637 | WMI_RSN_IE_CAPB = 0x1, | ||
638 | WMI_IE_FULL = 0xFF, /* indicats full IE */ | ||
639 | }; | ||
640 | |||
632 | /* WMI_CONNECT_CMDID */ | 641 | /* WMI_CONNECT_CMDID */ |
633 | enum network_type { | 642 | enum network_type { |
634 | INFRA_NETWORK = 0x01, | 643 | INFRA_NETWORK = 0x01, |
@@ -1268,6 +1277,16 @@ struct wmi_mcast_filter_add_del_cmd { | |||
1268 | u8 mcast_mac[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; | 1277 | u8 mcast_mac[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; |
1269 | } __packed; | 1278 | } __packed; |
1270 | 1279 | ||
1280 | struct wmi_set_htcap_cmd { | ||
1281 | u8 band; | ||
1282 | u8 ht_enable; | ||
1283 | u8 ht40_supported; | ||
1284 | u8 ht20_sgi; | ||
1285 | u8 ht40_sgi; | ||
1286 | u8 intolerant_40mhz; | ||
1287 | u8 max_ampdu_len_exp; | ||
1288 | } __packed; | ||
1289 | |||
1271 | /* Command Replies */ | 1290 | /* Command Replies */ |
1272 | 1291 | ||
1273 | /* WMI_GET_CHANNEL_LIST_CMDID reply */ | 1292 | /* WMI_GET_CHANNEL_LIST_CMDID reply */ |
@@ -1913,6 +1932,14 @@ struct wmi_set_appie_cmd { | |||
1913 | u8 ie_info[0]; | 1932 | u8 ie_info[0]; |
1914 | } __packed; | 1933 | } __packed; |
1915 | 1934 | ||
1935 | struct wmi_set_ie_cmd { | ||
1936 | u8 ie_id; | ||
1937 | u8 ie_field; /* enum wmi_ie_field_type */ | ||
1938 | u8 ie_len; | ||
1939 | u8 reserved; | ||
1940 | u8 ie_info[0]; | ||
1941 | } __packed; | ||
1942 | |||
1916 | /* Notify the WSC registration status to the target */ | 1943 | /* Notify the WSC registration status to the target */ |
1917 | #define WSC_REG_ACTIVE 1 | 1944 | #define WSC_REG_ACTIVE 1 |
1918 | #define WSC_REG_INACTIVE 0 | 1945 | #define WSC_REG_INACTIVE 0 |
@@ -2141,6 +2168,11 @@ struct wmi_ap_hidden_ssid_cmd { | |||
2141 | u8 hidden_ssid; | 2168 | u8 hidden_ssid; |
2142 | } __packed; | 2169 | } __packed; |
2143 | 2170 | ||
2171 | struct wmi_set_inact_period_cmd { | ||
2172 | __le32 inact_period; | ||
2173 | u8 num_null_func; | ||
2174 | } __packed; | ||
2175 | |||
2144 | /* AP mode events */ | 2176 | /* AP mode events */ |
2145 | struct wmi_ap_set_apsd_cmd { | 2177 | struct wmi_ap_set_apsd_cmd { |
2146 | u8 enable; | 2178 | u8 enable; |
@@ -2465,6 +2497,9 @@ int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi); | |||
2465 | int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg); | 2497 | int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg); |
2466 | int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, | 2498 | int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, |
2467 | u8 keep_alive_intvl); | 2499 | u8 keep_alive_intvl); |
2500 | int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx, | ||
2501 | enum ieee80211_band band, | ||
2502 | struct ath6kl_htcap *htcap); | ||
2468 | int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len); | 2503 | int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len); |
2469 | 2504 | ||
2470 | s32 ath6kl_wmi_get_rate(s8 rate_index); | 2505 | s32 ath6kl_wmi_get_rate(s8 rate_index); |
@@ -2515,6 +2550,9 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx, | |||
2515 | int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, | 2550 | int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, |
2516 | const u8 *ie, u8 ie_len); | 2551 | const u8 *ie, u8 ie_len); |
2517 | 2552 | ||
2553 | int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field, | ||
2554 | const u8 *ie_info, u8 ie_len); | ||
2555 | |||
2518 | /* P2P */ | 2556 | /* P2P */ |
2519 | int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable); | 2557 | int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable); |
2520 | 2558 | ||
@@ -2538,6 +2576,8 @@ int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx); | |||
2538 | int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, | 2576 | int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, |
2539 | const u8 *ie, u8 ie_len); | 2577 | const u8 *ie, u8 ie_len); |
2540 | 2578 | ||
2579 | int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout); | ||
2580 | |||
2541 | void ath6kl_wmi_sscan_timer(unsigned long ptr); | 2581 | void ath6kl_wmi_sscan_timer(unsigned long ptr); |
2542 | 2582 | ||
2543 | struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx); | 2583 | struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx); |