From 9b1cb21c36b39057ec28a8b551f301449e5c51bb Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Mon, 7 Dec 2009 16:37:42 -0500 Subject: iwlwifi: fix warning from ieee80211_stop_tx_ba_cb_irqsafe argument change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CC [M] drivers/net/wireless/iwlwifi/iwl-tx.o drivers/net/wireless/iwlwifi/iwl-tx.c: In function ‘iwl_tx_agg_stop’: drivers/net/wireless/iwlwifi/iwl-tx.c:1356: warning: passing argument 1 of ‘ieee80211_stop_tx_ba_cb_irqsafe’ from incompatible pointer type include/net/mac80211.h:2128: note: expected ‘struct ieee80211_vif *’ but argument is of type ‘struct ieee80211_hw *’ Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 58b132f9cf28..00da5e152d46 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -1353,7 +1353,7 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid) if (priv->stations[sta_id].tid[tid].agg.state == IWL_EMPTYING_HW_QUEUE_ADDBA) { IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); - ieee80211_stop_tx_ba_cb_irqsafe(priv->hw, ra, tid); + ieee80211_stop_tx_ba_cb_irqsafe(priv->vif, ra, tid); priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF; return 0; } -- cgit v1.2.3 From e84217a9fc6264fe4e73fc85cdfff185b71b7443 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 26 Nov 2009 10:55:55 +0200 Subject: wl1251: remove false warning messages There was a warning from wl1251_op_bss_info_changed(): wl1251: WARNING Set ctsprotect failed 0 It was printed always, it's completely false and can be removed. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1251_main.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index ff4be7bf5d36..8353139d85df 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -1181,8 +1181,6 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, wl->beacon_int, wl->dtim_period); if (ret < 0) goto out_sleep; - wl1251_warning("Set ctsprotect failed %d", ret); - goto out_sleep; } } -- cgit v1.2.3 From de8df1ea489d80106ea82d4a6323e83d376913fb Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 26 Nov 2009 10:56:06 +0200 Subject: wl1251: fix bssid handling bssid needs to be copied first in wl1251_op_bss_info_changed(), otherwise templates will have incorrect bssid and power save will not work correctly. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1251_main.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 8353139d85df..50d17fc00785 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -1110,6 +1110,21 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out; + if (changed & BSS_CHANGED_BSSID) { + memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); + + ret = wl1251_build_null_data(wl); + if (ret < 0) + goto out; + + if (wl->bss_type != BSS_TYPE_IBSS) { + ret = wl1251_join(wl, wl->bss_type, wl->channel, + wl->beacon_int, wl->dtim_period); + if (ret < 0) + goto out_sleep; + } + } + if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { wl->beacon_int = bss_conf->beacon_int; @@ -1169,21 +1184,6 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, } } - if (changed & BSS_CHANGED_BSSID) { - memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); - - ret = wl1251_build_null_data(wl); - if (ret < 0) - goto out; - - if (wl->bss_type != BSS_TYPE_IBSS) { - ret = wl1251_join(wl, wl->bss_type, wl->channel, - wl->beacon_int, wl->dtim_period); - if (ret < 0) - goto out_sleep; - } - } - if (changed & BSS_CHANGED_BEACON) { beacon = ieee80211_beacon_get(hw, vif); ret = wl1251_cmd_template_set(wl, CMD_BEACON, beacon->data, -- cgit v1.2.3 From c14589eb3080636a2f71ebaf21ab9fd70ffc20cc Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 26 Nov 2009 10:56:13 +0200 Subject: wl1251: don't build null data template in wl1251_op_config() The bssid can be zero when null data template is set in wl1251_op_config(). It's enough, and especially safe, to set it once after association. Signed-off-by: Kalle Valo Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1251_main.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 50d17fc00785..2f50a256efa5 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -629,10 +629,6 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) goto out_sleep; } - ret = wl1251_build_null_data(wl); - if (ret < 0) - goto out_sleep; - if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { wl1251_debug(DEBUG_PSM, "psm enabled"); -- cgit v1.2.3 From 0b5351a8e86292dfac1ca1451deaadb416a33cb8 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:11:18 +0100 Subject: mwl8k: fix MCS bitmap size in SET_RATE command The MCS bitmaps in the SET_RATE command structure were of the wrong size, due to use of the wrong define for the array length. Just hardcode the lengths as 16, and do the same for the MCS bitmaps in other command structures. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 0cb5ecc822a8..f93eddda9c0f 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -634,7 +634,6 @@ struct ewc_ht_info { #define MWL8K_PEER_TYPE_ACCESSPOINT 2 #define MWL8K_IEEE_LEGACY_DATA_RATES 13 -#define MWL8K_MCS_BITMAP_SIZE 16 struct peer_capability_info { /* Peer type - AP vs. STA. */ @@ -655,7 +654,7 @@ struct peer_capability_info { __u8 legacy_rates[MWL8K_IEEE_LEGACY_DATA_RATES]; /* HT rate table. Intersection of our rates and peer rates. */ - __u8 ht_rates[MWL8K_MCS_BITMAP_SIZE]; + __u8 ht_rates[16]; __u8 pad[16]; /* If set, interoperability mode, no proprietary extensions. */ @@ -2638,8 +2637,8 @@ struct mwl8k_cmd_update_rateset { __u8 legacy_rates[MWL8K_RATE_INDEX_MAX_ARRAY]; /* Bitmap for supported MCS codes. */ - __u8 mcs_set[MWL8K_IEEE_LEGACY_DATA_RATES]; - __u8 reserved[MWL8K_IEEE_LEGACY_DATA_RATES]; + __u8 mcs_set[16]; + __u8 reserved[16]; } __attribute__((packed)); static int mwl8k_update_rateset(struct ieee80211_hw *hw, -- cgit v1.2.3 From 140eb5e2c1978622d7cd979d59a1c0586fe3bbdb Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:11:44 +0100 Subject: mwl8k: fix UPDATE_STADB command struct legacy_rates array length There exist 12 802.11b/g rates, but mwl8k supports two additional (non-standard) rates, and includes those rates in rate bitmasks and in its internal rate table that hardware rate indices index. Commit "mwl8k: report rate and other information for received frames" added one of the nonstandard rates to the mwl8k_rates table to make the OFDM rates in the table line up with the rate indices that are reported in the receive descriptor (so that we can just simply copy the receive descriptor rate index into ieee80211_rx_status::rate_idx) and bumped MWL8K_IEEE_LEGACY_DATA_RATES from 12 to 13, but this screwed up the UPDATE_STADB command struct layout, as it also uses that define, for its legacy_rates array. To avoid having to convert rate indices and legacy rate bitmaps (e.g. ieee80211_bss_conf::basic_rates) between the 12-rate mac80211 format and the 14-rate mwl8k format, we'll report all 14 rates in our wiphy's band, but filter out the nonstandard ones e.g. in the case of the UPDATE_STADB command which only accepts 12 rates. In the commands that accept 14 rates (SET_AID, SET_RATE), replace the use of the MWL8K_RATE_INDEX_MAX_ARRAY define in the command struct by the constant 14, to make it clearer that these commands accept 14 rates. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 51 +++++++++++--------------------------------- 1 file changed, 13 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f93eddda9c0f..0251b6144f57 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -184,7 +184,7 @@ struct mwl8k_priv { /* PHY parameters */ struct ieee80211_supported_band band; struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[13]; + struct ieee80211_rate rates[14]; bool radio_on; bool radio_short_preamble; @@ -220,15 +220,6 @@ struct mwl8k_vif { u8 bssid[ETH_ALEN]; u8 mac_addr[ETH_ALEN]; - /* - * Subset of supported legacy rates. - * Intersection of AP and STA supported rates. - */ - struct ieee80211_rate legacy_rates[13]; - - /* number of supported legacy rates */ - u8 legacy_nrates; - /* Index into station database.Returned by update_sta_db call */ u8 peer_id; @@ -266,6 +257,11 @@ static const struct ieee80211_rate mwl8k_rates[] = { { .bitrate = 360, .hw_value = 72, }, { .bitrate = 480, .hw_value = 96, }, { .bitrate = 540, .hw_value = 108, }, + { .bitrate = 720, .hw_value = 144, }, +}; + +static const u8 mwl8k_rateids[12] = { + 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108, }; /* Set or get info from Firmware */ @@ -633,8 +629,6 @@ struct ewc_ht_info { /* Peer Entry flags - used to define the type of the peer node */ #define MWL8K_PEER_TYPE_ACCESSPOINT 2 -#define MWL8K_IEEE_LEGACY_DATA_RATES 13 - struct peer_capability_info { /* Peer type - AP vs. STA. */ __u8 peer_type; @@ -651,7 +645,7 @@ struct peer_capability_info { struct ewc_ht_info ewc_info; /* Legacy rate table. Intersection of our rates and peer rates. */ - __u8 legacy_rates[MWL8K_IEEE_LEGACY_DATA_RATES]; + __u8 legacy_rates[12]; /* HT rate table. Intersection of our rates and peer rates. */ __u8 ht_rates[16]; @@ -2514,9 +2508,7 @@ static int mwl8k_cmd_update_sta_db(struct ieee80211_hw *hw, struct ieee80211_bss_conf *info = &mv_vif->bss_info; struct mwl8k_cmd_update_sta_db *cmd; struct peer_capability_info *peer_info; - struct ieee80211_rate *bitrates = mv_vif->legacy_rates; int rc; - __u8 count, *rates; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) @@ -2535,13 +2527,11 @@ static int mwl8k_cmd_update_sta_db(struct ieee80211_hw *hw, /* Build peer_info block */ peer_info->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; peer_info->basic_caps = cpu_to_le16(info->assoc_capability); + memcpy(peer_info->legacy_rates, mwl8k_rateids, + sizeof(mwl8k_rateids)); peer_info->interop = 1; peer_info->amsdu_enabled = 0; - rates = peer_info->legacy_rates; - for (count = 0; count < mv_vif->legacy_nrates; count++) - rates[count] = bitrates[count].hw_value; - rc = mwl8k_post_cmd(hw, &cmd->header); if (rc == 0) mv_vif->peer_id = peer_info->station_id; @@ -2564,8 +2554,6 @@ static int mwl8k_cmd_update_sta_db(struct ieee80211_hw *hw, /* * CMD_SET_AID. */ -#define MWL8K_RATE_INDEX_MAX_ARRAY 14 - #define MWL8K_FRAME_PROT_DISABLED 0x00 #define MWL8K_FRAME_PROT_11G 0x07 #define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02 @@ -2578,7 +2566,7 @@ struct mwl8k_cmd_update_set_aid { /* AP's MAC address (BSSID) */ __u8 bssid[ETH_ALEN]; __le16 protection_mode; - __u8 supp_rates[MWL8K_RATE_INDEX_MAX_ARRAY]; + __u8 supp_rates[14]; } __attribute__((packed)); static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, @@ -2587,8 +2575,6 @@ static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); struct ieee80211_bss_conf *info = &mv_vif->bss_info; struct mwl8k_cmd_update_set_aid *cmd; - struct ieee80211_rate *bitrates = mv_vif->legacy_rates; - int count; u16 prot_mode; int rc; @@ -2620,8 +2606,7 @@ static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, } cmd->protection_mode = cpu_to_le16(prot_mode); - for (count = 0; count < mv_vif->legacy_nrates; count++) - cmd->supp_rates[count] = bitrates[count].hw_value; + memcpy(cmd->supp_rates, mwl8k_rateids, sizeof(mwl8k_rateids)); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2634,7 +2619,7 @@ static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, */ struct mwl8k_cmd_update_rateset { struct mwl8k_cmd_pkt header; - __u8 legacy_rates[MWL8K_RATE_INDEX_MAX_ARRAY]; + __u8 legacy_rates[14]; /* Bitmap for supported MCS codes. */ __u8 mcs_set[16]; @@ -2644,10 +2629,7 @@ struct mwl8k_cmd_update_rateset { static int mwl8k_update_rateset(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); struct mwl8k_cmd_update_rateset *cmd; - struct ieee80211_rate *bitrates = mv_vif->legacy_rates; - int count; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -2656,9 +2638,7 @@ static int mwl8k_update_rateset(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - for (count = 0; count < mv_vif->legacy_nrates; count++) - cmd->legacy_rates[count] = bitrates[count].hw_value; + memcpy(cmd->legacy_rates, mwl8k_rateids, sizeof(mwl8k_rateids)); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2931,11 +2911,6 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, /* Back pointer to parent config block */ mwl8k_vif->priv = priv; - /* Setup initial PHY parameters */ - memcpy(mwl8k_vif->legacy_rates, - priv->rates, sizeof(mwl8k_vif->legacy_rates)); - mwl8k_vif->legacy_nrates = ARRAY_SIZE(priv->rates); - /* Set Initial sequence number to zero */ mwl8k_vif->seqno = 0; -- cgit v1.2.3 From 20f09c3df7a8a623c290f62596c1a6b0da088030 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:12:08 +0100 Subject: mwl8k: prevent corruption of QoS field on receive Packets exchanged between the mwl8k driver and the firmware always have a 4-address header without QoS field. For QoS packets, the QoS field is passed to/from the firmware via the tx/rx descriptors. We were handling this correctly on transmit, but not on receive -- if a QoS packet was received, we would leave garbage in the QoS field in the packet passed up to the stack, which is Bad(tm). Also, if the packet received on the air was a 4-address without QoS packet, we would forget to skb_pull the 2-byte DMA length prefix off. This patch adds an argument to the ->rxd_process() receive descriptor operation to retrieve the QoS field from the receive descriptor, and extends mwl8k_remove_dma_header() to insert this field back into the packet if the packet received is a QoS packet. It also fixes mwl8k_remove_dma_header() to strip off the length prefix in all cases. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 46 +++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 0251b6144f57..6b12d81ba94b 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -84,7 +84,8 @@ struct rxd_ops { int rxd_size; void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); void (*rxd_refill)(void *rxd, dma_addr_t addr, int len); - int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status); + int (*rxd_process)(void *rxd, struct ieee80211_rx_status *status, + __le16 *qos); }; struct mwl8k_device_info { @@ -699,21 +700,29 @@ static inline u16 mwl8k_qos_setbit_qlen(u16 qos, u8 len) struct mwl8k_dma_data { __le16 fwlen; struct ieee80211_hdr wh; + char data[0]; } __attribute__((packed)); /* Routines to add/remove DMA header from skb. */ -static inline void mwl8k_remove_dma_header(struct sk_buff *skb) +static inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos) { - struct mwl8k_dma_data *tr = (struct mwl8k_dma_data *)skb->data; - void *dst, *src = &tr->wh; - int hdrlen = ieee80211_hdrlen(tr->wh.frame_control); - u16 space = sizeof(struct mwl8k_dma_data) - hdrlen; + struct mwl8k_dma_data *tr; + int hdrlen; + + tr = (struct mwl8k_dma_data *)skb->data; + hdrlen = ieee80211_hdrlen(tr->wh.frame_control); - dst = (void *)tr + space; - if (dst != src) { - memmove(dst, src, hdrlen); - skb_pull(skb, space); + if (hdrlen != sizeof(tr->wh)) { + if (ieee80211_is_data_qos(tr->wh.frame_control)) { + memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2); + *((__le16 *)(tr->data - 2)) = qos; + } else { + memmove(tr->data - hdrlen, &tr->wh, hdrlen); + } } + + if (hdrlen != sizeof(*tr)) + skb_pull(skb, sizeof(*tr) - hdrlen); } static inline void mwl8k_add_dma_header(struct sk_buff *skb) @@ -793,7 +802,8 @@ static void mwl8k_rxd_8366_refill(void *_rxd, dma_addr_t addr, int len) } static int -mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status) +mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status, + __le16 *qos) { struct mwl8k_rxd_8366 *rxd = _rxd; @@ -823,6 +833,8 @@ mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status) status->band = IEEE80211_BAND_2GHZ; status->freq = ieee80211_channel_to_frequency(rxd->channel); + *qos = rxd->qos_control; + return le16_to_cpu(rxd->pkt_len); } @@ -881,7 +893,8 @@ static void mwl8k_rxd_8687_refill(void *_rxd, dma_addr_t addr, int len) } static int -mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status) +mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status, + __le16 *qos) { struct mwl8k_rxd_8687 *rxd = _rxd; u16 rate_info; @@ -912,6 +925,8 @@ mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status) status->band = IEEE80211_BAND_2GHZ; status->freq = ieee80211_channel_to_frequency(rxd->channel); + *qos = rxd->qos_control; + return le16_to_cpu(rxd->pkt_len); } @@ -1083,6 +1098,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) void *rxd; int pkt_len; struct ieee80211_rx_status status; + __le16 qos; skb = rxq->buf[rxq->head].skb; if (skb == NULL) @@ -1090,7 +1106,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) rxd = rxq->rxd + (rxq->head * priv->rxd_ops->rxd_size); - pkt_len = priv->rxd_ops->rxd_process(rxd, &status); + pkt_len = priv->rxd_ops->rxd_process(rxd, &status, &qos); if (pkt_len < 0) break; @@ -1108,7 +1124,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) rxq->rxd_count--; skb_put(skb, pkt_len); - mwl8k_remove_dma_header(skb); + mwl8k_remove_dma_header(skb, qos); /* * Check for a pending join operation. Save a @@ -1354,7 +1370,7 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) BUG_ON(skb == NULL); pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE); - mwl8k_remove_dma_header(skb); + mwl8k_remove_dma_header(skb, tx_desc->qos_control); /* Mark descriptor as unused */ tx_desc->pkt_phys_addr = 0; -- cgit v1.2.3 From ca00930153c14b323c31b97623ac5c4f7855ed6a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:12:20 +0100 Subject: mwl8k: fix addr4 zeroing and payload overwrite on DMA header creation When inserting a DMA header into a packet for transmission, mwl8k_add_dma_header() would blindly zero the addr4 field, which is not a good idea if the packet being transmitted is actually a 4-address packet. Also, if the transmitted packet was a 4-address with QoS packet, the memmove() to do the needed header reshuffling would inadvertently overwrite the first two bytes of the packet payload with the QoS field. This fixes both of these issues. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 6b12d81ba94b..c1cb20fceaff 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -728,35 +728,36 @@ static inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos) static inline void mwl8k_add_dma_header(struct sk_buff *skb) { struct ieee80211_hdr *wh; - u32 hdrlen, pktlen; + int hdrlen; struct mwl8k_dma_data *tr; + /* + * Add a firmware DMA header; the firmware requires that we + * present a 2-byte payload length followed by a 4-address + * header (without QoS field), followed (optionally) by any + * WEP/ExtIV header (but only filled in for CCMP). + */ wh = (struct ieee80211_hdr *)skb->data; + hdrlen = ieee80211_hdrlen(wh->frame_control); - pktlen = skb->len; + if (hdrlen != sizeof(*tr)) + skb_push(skb, sizeof(*tr) - hdrlen); - /* - * Copy up/down the 802.11 header; the firmware requires - * we present a 2-byte payload length followed by a - * 4-address header (w/o QoS), followed (optionally) by - * any WEP/ExtIV header (but only filled in for CCMP). - */ - if (hdrlen != sizeof(struct mwl8k_dma_data)) - skb_push(skb, sizeof(struct mwl8k_dma_data) - hdrlen); + if (ieee80211_is_data_qos(wh->frame_control)) + hdrlen -= 2; tr = (struct mwl8k_dma_data *)skb->data; if (wh != &tr->wh) memmove(&tr->wh, wh, hdrlen); - - /* Clear addr4 */ - memset(tr->wh.addr4, 0, ETH_ALEN); + if (hdrlen != sizeof(tr->wh)) + memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen); /* * Firmware length is the length of the fully formed "802.11 * payload". That is, everything except for the 802.11 header. * This includes all crypto material including the MIC. */ - tr->fwlen = cpu_to_le16(pktlen - hdrlen); + tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr)); } -- cgit v1.2.3 From 8e9f33f0ced82a797d285b233e1c956cbd5c7de3 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:12:35 +0100 Subject: mwl8k: properly report rate on received 40MHz packets On 8366, bit 6 in the rx descriptor rate field indicates whether the packet was received on a 20MHz or 40MHz channel, and is not part of the MCS index. Handle this properly, which then prevents hitting the WARN_ON and being dropped in ieee80211_rx(). Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index c1cb20fceaff..f1566f93696e 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -782,6 +782,10 @@ struct mwl8k_rxd_8366 { __u8 rx_ctrl; } __attribute__((packed)); +#define MWL8K_8366_RATE_INFO_MCS_FORMAT 0x80 +#define MWL8K_8366_RATE_INFO_40MHZ 0x40 +#define MWL8K_8366_RATE_INFO_RATEID(x) ((x) & 0x3f) + #define MWL8K_8366_RX_CTRL_OWNED_BY_HOST 0x80 static void mwl8k_rxd_8366_init(void *_rxd, dma_addr_t next_dma_addr) @@ -817,9 +821,11 @@ mwl8k_rxd_8366_process(void *_rxd, struct ieee80211_rx_status *status, status->signal = -rxd->rssi; status->noise = -rxd->noise_floor; - if (rxd->rate & 0x80) { + if (rxd->rate & MWL8K_8366_RATE_INFO_MCS_FORMAT) { status->flag |= RX_FLAG_HT; - status->rate_idx = rxd->rate & 0x7f; + if (rxd->rate & MWL8K_8366_RATE_INFO_40MHZ) + status->flag |= RX_FLAG_40MHZ; + status->rate_idx = MWL8K_8366_RATE_INFO_RATEID(rxd->rate); } else { int i; -- cgit v1.2.3 From 0c9cc640225f4bd7c9aad87b1431bd8d9a29b338 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:12:49 +0100 Subject: mwl8k: allow more time for firmware commands to complete Some firmware commands can under some circumstances take more than 2 seconds to complete. This patch bumps the timeout up to 10 seconds, and prints a message whenever a command takes more than 2 seconds. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f1566f93696e..793a83e10e16 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1579,8 +1579,8 @@ static void mwl8k_fw_unlock(struct ieee80211_hw *hw) * Command processing. */ -/* Timeout firmware commands after 2000ms */ -#define MWL8K_CMD_TIMEOUT_MS 2000 +/* Timeout firmware commands after 10s */ +#define MWL8K_CMD_TIMEOUT_MS 10000 static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) { @@ -1631,12 +1631,21 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) MWL8K_CMD_TIMEOUT_MS); rc = -ETIMEDOUT; } else { + int ms; + + ms = MWL8K_CMD_TIMEOUT_MS - jiffies_to_msecs(timeout); + rc = cmd->result ? -EINVAL : 0; if (rc) printk(KERN_ERR "%s: Command %s error 0x%x\n", wiphy_name(hw->wiphy), mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), le16_to_cpu(cmd->result)); + else if (ms > 2000) + printk(KERN_NOTICE "%s: Command %s took %d ms\n", + wiphy_name(hw->wiphy), + mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), + ms); } return rc; -- cgit v1.2.3 From 7e1112d34aea10fdd689422e6bdc918309043bf3 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:13:04 +0100 Subject: mwl8k: allow more time for transmit rings to drain Before issuing any firmware commands, we wait for the transmit rings to drain, to prevent control versus data path synchronization issues. In some cases, this can end up taking longer than the current hardcoded limit of 5 seconds, for example if the transmit rings are filled with packets for a host that has dropped off the air and we end up retransmitting every pending packet at the lowest rate a couple of times. This patch changes mwl8k_tx_wait_empty() to only bail out on timeout expiry if there was no change in the number of packets pending in the transmit rings during the waiting period. If at least one transmit ring entry was reclaimed while we were waiting, we are apparently still making progress, and we'll allow waiting for another timeout period. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 127 +++++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 60 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 793a83e10e16..a16255810d15 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1237,99 +1237,106 @@ static inline void mwl8k_tx_start(struct mwl8k_priv *priv) ioread32(priv->regs + MWL8K_HIU_INT_CODE); } -struct mwl8k_txq_info { - u32 fw_owned; - u32 drv_owned; - u32 unused; - u32 len; - u32 head; - u32 tail; -}; - -static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv, - struct mwl8k_txq_info *txinfo) +static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw) { - int count, desc, status; - struct mwl8k_tx_queue *txq; - struct mwl8k_tx_desc *tx_desc; - int ndescs = 0; + struct mwl8k_priv *priv = hw->priv; + int i; - memset(txinfo, 0, MWL8K_TX_QUEUES * sizeof(struct mwl8k_txq_info)); + for (i = 0; i < MWL8K_TX_QUEUES; i++) { + struct mwl8k_tx_queue *txq = priv->txq + i; + int fw_owned = 0; + int drv_owned = 0; + int unused = 0; + int desc; - for (count = 0; count < MWL8K_TX_QUEUES; count++) { - txq = priv->txq + count; - txinfo[count].len = txq->stats.len; - txinfo[count].head = txq->head; - txinfo[count].tail = txq->tail; for (desc = 0; desc < MWL8K_TX_DESCS; desc++) { - tx_desc = txq->txd + desc; - status = le32_to_cpu(tx_desc->status); + struct mwl8k_tx_desc *tx_desc = txq->txd + desc; + u32 status; + status = le32_to_cpu(tx_desc->status); if (status & MWL8K_TXD_STATUS_FW_OWNED) - txinfo[count].fw_owned++; + fw_owned++; else - txinfo[count].drv_owned++; + drv_owned++; if (tx_desc->pkt_len == 0) - txinfo[count].unused++; + unused++; } - } - return ndescs; + printk(KERN_ERR "%s: txq[%d] len=%d head=%d tail=%d " + "fw_owned=%d drv_owned=%d unused=%d\n", + wiphy_name(hw->wiphy), i, + txq->stats.len, txq->head, txq->tail, + fw_owned, drv_owned, unused); + } } /* * Must be called with priv->fw_mutex held and tx queues stopped. */ +#define MWL8K_TX_WAIT_TIMEOUT_MS 1000 + static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; DECLARE_COMPLETION_ONSTACK(tx_wait); - u32 count; - unsigned long timeout; + int retry; + int rc; might_sleep(); + /* + * The TX queues are stopped at this point, so this test + * doesn't need to take ->tx_lock. + */ + if (!priv->pending_tx_pkts) + return 0; + + retry = 0; + rc = 0; + spin_lock_bh(&priv->tx_lock); - count = priv->pending_tx_pkts; - if (count) - priv->tx_wait = &tx_wait; - spin_unlock_bh(&priv->tx_lock); + priv->tx_wait = &tx_wait; + while (!rc) { + int oldcount; + unsigned long timeout; - if (count) { - struct mwl8k_txq_info txinfo[MWL8K_TX_QUEUES]; - int index; - int newcount; + oldcount = priv->pending_tx_pkts; + spin_unlock_bh(&priv->tx_lock); timeout = wait_for_completion_timeout(&tx_wait, - msecs_to_jiffies(5000)); - if (timeout) - return 0; - + msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS)); spin_lock_bh(&priv->tx_lock); - priv->tx_wait = NULL; - newcount = priv->pending_tx_pkts; - mwl8k_scan_tx_ring(priv, txinfo); - spin_unlock_bh(&priv->tx_lock); - printk(KERN_ERR "%s(%u) TIMEDOUT:5000ms Pend:%u-->%u\n", - __func__, __LINE__, count, newcount); + if (timeout) { + WARN_ON(priv->pending_tx_pkts); + if (retry) { + printk(KERN_NOTICE "%s: tx rings drained\n", + wiphy_name(hw->wiphy)); + } + break; + } - for (index = 0; index < MWL8K_TX_QUEUES; index++) - printk(KERN_ERR "TXQ:%u L:%u H:%u T:%u FW:%u " - "DRV:%u U:%u\n", - index, - txinfo[index].len, - txinfo[index].head, - txinfo[index].tail, - txinfo[index].fw_owned, - txinfo[index].drv_owned, - txinfo[index].unused); + if (priv->pending_tx_pkts < oldcount) { + printk(KERN_NOTICE "%s: timeout waiting for tx " + "rings to drain (%d -> %d pkts), retrying\n", + wiphy_name(hw->wiphy), oldcount, + priv->pending_tx_pkts); + retry = 1; + continue; + } + + priv->tx_wait = NULL; + + printk(KERN_ERR "%s: tx rings stuck for %d ms\n", + wiphy_name(hw->wiphy), MWL8K_TX_WAIT_TIMEOUT_MS); + mwl8k_dump_tx_rings(hw); - return -ETIMEDOUT; + rc = -ETIMEDOUT; } + spin_unlock_bh(&priv->tx_lock); - return 0; + return rc; } #define MWL8K_TXD_SUCCESS(status) \ -- cgit v1.2.3 From 89b872e2e476833cde8aaac658c75817f67e8f81 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:13:20 +0100 Subject: mwl8k: increase firmware loading timeouts The time between loading the helper image and starting to upload the main firmware image should be at least 5 ms or so. We were doing an msleep(1) before, and 1 ms appears to not be enough in almost all cases, but building with HZ=100 has always masked this so far. Bumping the msleep argument to 5 fixes firmware loading e.g. when HZ=1000. Some firmware images need more than 200ms to initialize. Bump the ready code timeout to 500ms to accommodate for this. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index a16255810d15..e59a92618807 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -571,7 +571,7 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw) "helper image\n", pci_name(priv->pdev)); return rc; } - msleep(1); + msleep(5); rc = mwl8k_feed_fw_image(priv, fw->data, fw->size); } else { @@ -588,9 +588,8 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw) iowrite32(MWL8K_MODE_AP, priv->regs + MWL8K_HIU_GEN_PTR); else iowrite32(MWL8K_MODE_STA, priv->regs + MWL8K_HIU_GEN_PTR); - msleep(1); - loops = 200000; + loops = 500000; do { u32 ready_code; -- cgit v1.2.3 From 3db95e50c8813d8ed04a1ec7cd7b77dba7c81c80 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:13:34 +0100 Subject: mwl8k: don't forget to call pci_disable_device() Don't forget to call pci_disable_device() if pci_request_regions() fails during probe. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index e59a92618807..76a6071aac8a 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3378,7 +3378,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, if (rc) { printk(KERN_ERR "%s: Cannot obtain PCI resources\n", MWL8K_NAME); - return rc; + goto err_disable_device; } pci_set_master(pdev); @@ -3609,6 +3609,8 @@ err_iounmap: err_free_reg: pci_release_regions(pdev); + +err_disable_device: pci_disable_device(pdev); return rc; -- cgit v1.2.3 From d8a8dd8f07aed82492c089ad13fd3d7476b692fd Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:13:42 +0100 Subject: mwl8k: struct ieee80211_rx_status::qual is deprecated Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 76a6071aac8a..2e9da9aa38e7 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -915,7 +915,6 @@ mwl8k_rxd_8687_process(void *_rxd, struct ieee80211_rx_status *status, status->signal = -rxd->rssi; status->noise = -rxd->noise_level; - status->qual = rxd->link_quality; status->antenna = MWL8K_8687_RATE_INFO_ANTSELECT(rate_info); status->rate_idx = MWL8K_8687_RATE_INFO_RATEID(rate_info); -- cgit v1.2.3 From d1844d77692de3158ad458ed4c7b86d78ab4085e Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:13:56 +0100 Subject: mwl8k: don't overwrite mwl8k_vif::bssid until after disassociation When disassociating, mac80211 zeroes vif->bss_info.bssid before calling our ->bss_info_changed(), but we need the BSSID to remove the hardware station database entry for our AP, so we can't clear our local copy of the BSSID until after we've done that. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 2e9da9aa38e7..ea1173fd598c 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3025,9 +3025,6 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); int rc; - if (changed & BSS_CHANGED_BSSID) - memcpy(mwl8k_vif->bssid, info->bssid, ETH_ALEN); - if ((changed & BSS_CHANGED_ASSOC) == 0) return; @@ -3041,6 +3038,8 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, memcpy(&mwl8k_vif->bss_info, info, sizeof(struct ieee80211_bss_conf)); + memcpy(mwl8k_vif->bssid, info->bssid, ETH_ALEN); + /* Install rates */ rc = mwl8k_update_rateset(hw, vif); if (rc) -- cgit v1.2.3 From 16cec43da50c4b4702653ca710549fd3457a4e6c Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 30 Nov 2009 18:14:23 +0100 Subject: mwl8k: don't complain about oversized beacons in FINALIZE_JOIN The FINALIZE_JOIN firmware command only looks at the first couple of fields in the beacon, and therefore it's not necessary to complain if the beacon is longer than 128 bytes. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index ea1173fd598c..59d49159cf2a 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2469,8 +2469,6 @@ mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, /* * CMD_FINALIZE_JOIN. */ - -/* FJ beacon buffer size is compiled into the firmware. */ #define MWL8K_FJ_BEACON_MAXLEN 128 struct mwl8k_cmd_finalize_join { @@ -2480,17 +2478,13 @@ struct mwl8k_cmd_finalize_join { } __attribute__((packed)); static int mwl8k_finalize_join(struct ieee80211_hw *hw, void *frame, - __u16 framelen, __u16 dtim) + int framelen, int dtim) { struct mwl8k_cmd_finalize_join *cmd; struct ieee80211_mgmt *payload = frame; - u16 hdrlen; - u32 payload_len; + int payload_len; int rc; - if (frame == NULL) - return -EINVAL; - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; @@ -2499,24 +2493,17 @@ static int mwl8k_finalize_join(struct ieee80211_hw *hw, void *frame, cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1); - hdrlen = ieee80211_hdrlen(payload->frame_control); - - payload_len = framelen > hdrlen ? framelen - hdrlen : 0; - - /* XXX TBD Might just have to abort and return an error */ - if (payload_len > MWL8K_FJ_BEACON_MAXLEN) - printk(KERN_ERR "%s(): WARNING: Incomplete beacon " - "sent to firmware. Sz=%u MAX=%u\n", __func__, - payload_len, MWL8K_FJ_BEACON_MAXLEN); - - if (payload_len > MWL8K_FJ_BEACON_MAXLEN) + payload_len = framelen - ieee80211_hdrlen(payload->frame_control); + if (payload_len < 0) + payload_len = 0; + else if (payload_len > MWL8K_FJ_BEACON_MAXLEN) payload_len = MWL8K_FJ_BEACON_MAXLEN; - if (payload && payload_len) - memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); + memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); + return rc; } -- cgit v1.2.3 From 815833e7ecf0b9a017315cae6aef4d7cd9517681 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 5 Dec 2009 18:08:05 +0100 Subject: ath9k: fix tx status reporting This patch fixes a bug in ath9k's tx status check, which caused mac80211 to consider regularly transmitted unicast frames as un-acked. When checking the ts_status field for errors, it needs to be masked with ATH9K_TXERR_FILT, because this field also contains other fields like ATH9K_TX_ACKED. Without this patch, AP mode is pretty much unusable, as hostapd checks the ACK status for the frames that it injects. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/xmit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 564c6cb1c2b4..2a11cc57ceea 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2078,7 +2078,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) &txq->axq_q, lastbf->list.prev); txq->axq_depth--; - txok = (ds->ds_txstat.ts_status == 0); + txok = !(ds->ds_txstat.ts_status & ATH9K_TXERR_FILT); txq->axq_tx_inprogress = false; spin_unlock_bh(&txq->axq_lock); -- cgit v1.2.3 From 70d57139f932b9ca21026253d02af71cf53d764a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sat, 5 Dec 2009 19:25:22 -0600 Subject: rtl8187: Fix wrong rfkill switch mask for some models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are different bits used to convey the setting of the rfkill switch to the driver. The current driver only supports one of these possibilities. These changes were derived from the latest version of the vendor driver. This patch fixes the regression noted in kernel Bugzilla #14743. Signed-off-by: Larry Finger Reported-and-tested-by: Antti Kaijanmäki Tested-by: Hin-Tak Leung Cc: Stable Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8187.h | 5 +++++ drivers/net/wireless/rtl818x/rtl8187_dev.c | 12 ++++++++++-- drivers/net/wireless/rtl818x/rtl8187_rfkill.c | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index abb4907cf296..b1a24de0b7b0 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h @@ -23,6 +23,7 @@ #define RTL8187_EEPROM_TXPWR_CHAN_1 0x16 /* 3 channels */ #define RTL8187_EEPROM_TXPWR_CHAN_6 0x1B /* 2 channels */ #define RTL8187_EEPROM_TXPWR_CHAN_4 0x3D /* 2 channels */ +#define RTL8187_EEPROM_SELECT_GPIO 0x3B #define RTL8187_REQT_READ 0xC0 #define RTL8187_REQT_WRITE 0x40 @@ -31,6 +32,9 @@ #define RTL8187_MAX_RX 0x9C4 +#define RFKILL_MASK_8187_89_97 0x2 +#define RFKILL_MASK_8198 0x4 + struct rtl8187_rx_info { struct urb *urb; struct ieee80211_hw *dev; @@ -122,6 +126,7 @@ struct rtl8187_priv { u8 noise; u8 slot_time; u8 aifsn[4]; + u8 rfkill_mask; struct { __le64 buf; struct sk_buff_head queue; diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 76973b8c7099..bc5726dd5fe4 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -1322,6 +1322,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, struct ieee80211_channel *channel; const char *chip_name; u16 txpwr, reg; + u16 product_id = le16_to_cpu(udev->descriptor.idProduct); int err, i; dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops); @@ -1481,6 +1482,13 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, (*channel++).hw_value = txpwr & 0xFF; (*channel++).hw_value = txpwr >> 8; } + /* Handle the differing rfkill GPIO bit in different models */ + priv->rfkill_mask = RFKILL_MASK_8187_89_97; + if (product_id == 0x8197 || product_id == 0x8198) { + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_SELECT_GPIO, ®); + if (reg & 0xFF00) + priv->rfkill_mask = RFKILL_MASK_8198; + } /* * XXX: Once this driver supports anything that requires @@ -1509,9 +1517,9 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, mutex_init(&priv->conf_mutex); skb_queue_head_init(&priv->b_tx_status.queue); - printk(KERN_INFO "%s: hwaddr %pM, %s V%d + %s\n", + printk(KERN_INFO "%s: hwaddr %pM, %s V%d + %s, rfkill mask %d\n", wiphy_name(dev->wiphy), dev->wiphy->perm_addr, - chip_name, priv->asic_rev, priv->rf->name); + chip_name, priv->asic_rev, priv->rf->name, priv->rfkill_mask); #ifdef CONFIG_RTL8187_LEDS eeprom_93cx6_read(&eeprom, 0x3F, ®); diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c index cad8037ab2af..03555e1e0cab 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c +++ b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c @@ -25,10 +25,10 @@ static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) u8 gpio; gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); - rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~0x02); + rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~priv->rfkill_mask); gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); - return gpio & 0x02; + return gpio & priv->rfkill_mask; } void rtl8187_rfkill_init(struct ieee80211_hw *hw) -- cgit v1.2.3 From a589296a3592c8879cef8fd13518fed18c5fa5a3 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Sun, 6 Dec 2009 19:28:47 +0000 Subject: orinoco: remove spare KERN_DEBUG A KERN_DEBUG didn't get removed when transitioning from printk to pr_debug Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco/hermes_dld.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c index a3eefe109df4..6d5660defd87 100644 --- a/drivers/net/wireless/orinoco/hermes_dld.c +++ b/drivers/net/wireless/orinoco/hermes_dld.c @@ -427,7 +427,7 @@ int hermesi_program_init(hermes_t *hw, u32 offset) if (err) return err; - pr_debug(KERN_DEBUG PFX "Enabling volatile, EP 0x%08x\n", offset); + pr_debug(PFX "Enabling volatile, EP 0x%08x\n", offset); err = hermes_doicmd_wait(hw, HERMES_PROGRAM_ENABLE_VOLATILE, offset & 0xFFFFu, -- cgit v1.2.3 From 6a213afd058436dbbd01098d7422c6a0073c39b5 Mon Sep 17 00:00:00 2001 From: Shahar Or Date: Mon, 7 Dec 2009 12:05:54 +0200 Subject: ath5k: add support for Dell Vostro A860 LED Adds support for the WiFi activity LED on the Dell Vostro A860 laptop. Signed-off-by: Shahar Or Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/led.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c index d495890355d9..60f547503d75 100644 --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -79,6 +79,8 @@ static const struct pci_device_id ath5k_led_devices[] = { { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) }, /* IBM-specific AR5212 (all others) */ { PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) }, + /* Dell Vostro A860 (shahar@shahar-or.co.il) */ + { ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0112), ATH_LED(3, 0) }, { } }; -- cgit v1.2.3 From 93a23f48df9c0111283302acd2a4ed2ef1d86453 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Tue, 8 Dec 2009 07:27:41 +0000 Subject: e1000e: minor correction to name of bit in CTRL_EXT register Bit 7 in the CTRL_REG register is actually the Software Definable Pin 3, not the Software Definable Pin 7. Signed-off-by: Bruce Allan Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/e1000e/defines.h | 2 +- drivers/net/e1000e/netdev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/e1000e/defines.h b/drivers/net/e1000e/defines.h index 86d2809763c3..e02e38221ed4 100644 --- a/drivers/net/e1000e/defines.h +++ b/drivers/net/e1000e/defines.h @@ -74,7 +74,7 @@ #define E1000_WUS_BC E1000_WUFC_BC /* Extended Device Control */ -#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Definable Pin 7 */ +#define E1000_CTRL_EXT_SDP3_DATA 0x00000080 /* Value of SW Definable Pin 3 */ #define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ #define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ #define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */ diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index c3105c5087e0..762b697ce731 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -4541,7 +4541,7 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) e1000_media_type_internal_serdes) { /* keep the laser running in D3 */ ctrl_ext = er32(CTRL_EXT); - ctrl_ext |= E1000_CTRL_EXT_SDP7_DATA; + ctrl_ext |= E1000_CTRL_EXT_SDP3_DATA; ew32(CTRL_EXT, ctrl_ext); } -- cgit v1.2.3 From 0781895067444db98050a1537bafbc7a0235ec9f Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Tue, 8 Dec 2009 07:28:01 +0000 Subject: e1000e: replace incorrect use of GG82563_REG macro The GG82563_REG() macro should not be used to determine the offset provided to the e1000e_[read|write]_kmrn_reg() functions since the first argument to the macro is already implied and gets masked off anyway in the functions. The resultant register reads/writes with this patch are functionally the same as before. Signed-off-by: Bruce Allan Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/e1000e/hw.h | 2 ++ drivers/net/e1000e/ich8lan.c | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h index a7d08dae79c4..5ed17889e64d 100644 --- a/drivers/net/e1000e/hw.h +++ b/drivers/net/e1000e/hw.h @@ -302,6 +302,8 @@ enum e1e_registers { #define E1000_KMRNCTRLSTA_OFFSET_SHIFT 16 #define E1000_KMRNCTRLSTA_REN 0x00200000 #define E1000_KMRNCTRLSTA_DIAG_OFFSET 0x3 /* Kumeran Diagnostic */ +#define E1000_KMRNCTRLSTA_TIMEOUTS 0x4 /* Kumeran Timeouts */ +#define E1000_KMRNCTRLSTA_INBAND_PARAM 0x9 /* Kumeran InBand Parameters */ #define E1000_KMRNCTRLSTA_DIAG_NELPBK 0x1000 /* Nearend Loopback mode */ #define E1000_KMRNCTRLSTA_K1_CONFIG 0x7 #define E1000_KMRNCTRLSTA_K1_ENABLE 0x140E diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c index 7b33be98a2ca..9b09246af064 100644 --- a/drivers/net/e1000e/ich8lan.c +++ b/drivers/net/e1000e/ich8lan.c @@ -2755,14 +2755,16 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw) * and increase the max iterations when polling the phy; * this fixes erroneous timeouts at 10Mbps. */ - ret_val = e1000e_write_kmrn_reg(hw, GG82563_REG(0x34, 4), 0xFFFF); + ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_TIMEOUTS, 0xFFFF); if (ret_val) return ret_val; - ret_val = e1000e_read_kmrn_reg(hw, GG82563_REG(0x34, 9), ®_data); + ret_val = e1000e_read_kmrn_reg(hw, E1000_KMRNCTRLSTA_INBAND_PARAM, + ®_data); if (ret_val) return ret_val; reg_data |= 0x3F; - ret_val = e1000e_write_kmrn_reg(hw, GG82563_REG(0x34, 9), reg_data); + ret_val = e1000e_write_kmrn_reg(hw, E1000_KMRNCTRLSTA_INBAND_PARAM, + reg_data); if (ret_val) return ret_val; -- cgit v1.2.3 From 3421eecdee750bafc78b12ac25b3e980195265eb Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Tue, 8 Dec 2009 07:28:20 +0000 Subject: e1000e: only perform ESB2 MDIC workaround on certain configurations A workaround added for all ESB2 devices (adds a delay for all MDIC accesses which resolves an issue with the MDIC ready bit being set prematurely) is applicable only to devices in which the MAC-PHY interconnect is not operating in a certain mode with in-band MDIO. Check the control register for the operating mode and enable the workaround accordingly. Signed-off-by: Bruce Allan Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/e1000e/es2lan.c | 97 ++++++++++++++++++++++++++++++--------------- drivers/net/e1000e/hw.h | 5 +++ 2 files changed, 69 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/net/e1000e/es2lan.c b/drivers/net/e1000e/es2lan.c index d2a104794609..3028f23da891 100644 --- a/drivers/net/e1000e/es2lan.c +++ b/drivers/net/e1000e/es2lan.c @@ -46,6 +46,9 @@ #define E1000_KMRNCTRLSTA_HD_CTRL_1000_DEFAULT 0x0000 #define E1000_KMRNCTRLSTA_OPMODE_E_IDLE 0x2000 +#define E1000_KMRNCTRLSTA_OPMODE_MASK 0x000C +#define E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO 0x0004 + #define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */ #define DEFAULT_TCTL_EXT_GCEX_80003ES2LAN 0x00010000 @@ -462,28 +465,36 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, return ret_val; } - /* - * The "ready" bit in the MDIC register may be incorrectly set - * before the device has completed the "Page Select" MDI - * transaction. So we wait 200us after each MDI command... - */ - udelay(200); + if (hw->dev_spec.e80003es2lan.mdic_wa_enable == true) { + /* + * The "ready" bit in the MDIC register may be incorrectly set + * before the device has completed the "Page Select" MDI + * transaction. So we wait 200us after each MDI command... + */ + udelay(200); - /* ...and verify the command was successful. */ - ret_val = e1000e_read_phy_reg_mdic(hw, page_select, &temp); + /* ...and verify the command was successful. */ + ret_val = e1000e_read_phy_reg_mdic(hw, page_select, &temp); - if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) { - ret_val = -E1000_ERR_PHY; - e1000_release_phy_80003es2lan(hw); - return ret_val; - } + if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) { + ret_val = -E1000_ERR_PHY; + e1000_release_phy_80003es2lan(hw); + return ret_val; + } - udelay(200); + udelay(200); - ret_val = e1000e_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, - data); + ret_val = e1000e_read_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + + udelay(200); + } else { + ret_val = e1000e_read_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + } - udelay(200); e1000_release_phy_80003es2lan(hw); return ret_val; @@ -526,28 +537,35 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, return ret_val; } + if (hw->dev_spec.e80003es2lan.mdic_wa_enable == true) { + /* + * The "ready" bit in the MDIC register may be incorrectly set + * before the device has completed the "Page Select" MDI + * transaction. So we wait 200us after each MDI command... + */ + udelay(200); - /* - * The "ready" bit in the MDIC register may be incorrectly set - * before the device has completed the "Page Select" MDI - * transaction. So we wait 200us after each MDI command... - */ - udelay(200); + /* ...and verify the command was successful. */ + ret_val = e1000e_read_phy_reg_mdic(hw, page_select, &temp); - /* ...and verify the command was successful. */ - ret_val = e1000e_read_phy_reg_mdic(hw, page_select, &temp); + if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) { + e1000_release_phy_80003es2lan(hw); + return -E1000_ERR_PHY; + } - if (((u16)offset >> GG82563_PAGE_SHIFT) != temp) { - e1000_release_phy_80003es2lan(hw); - return -E1000_ERR_PHY; - } + udelay(200); - udelay(200); + ret_val = e1000e_write_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); - ret_val = e1000e_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & offset, - data); + udelay(200); + } else { + ret_val = e1000e_write_phy_reg_mdic(hw, + MAX_PHY_REG_ADDRESS & offset, + data); + } - udelay(200); e1000_release_phy_80003es2lan(hw); return ret_val; @@ -866,6 +884,19 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw) reg_data &= ~0x00100000; E1000_WRITE_REG_ARRAY(hw, E1000_FFLT, 0x0001, reg_data); + /* default to true to enable the MDIC W/A */ + hw->dev_spec.e80003es2lan.mdic_wa_enable = true; + + ret_val = e1000_read_kmrn_reg_80003es2lan(hw, + E1000_KMRNCTRLSTA_OFFSET >> + E1000_KMRNCTRLSTA_OFFSET_SHIFT, + &i); + if (!ret_val) { + if ((i & E1000_KMRNCTRLSTA_OPMODE_MASK) == + E1000_KMRNCTRLSTA_OPMODE_INBAND_MDIO) + hw->dev_spec.e80003es2lan.mdic_wa_enable = false; + } + /* * Clear all of the statistics registers (clear on read). It is * important that we do this after we have tried to establish link diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h index 5ed17889e64d..2784cf44a6f3 100644 --- a/drivers/net/e1000e/hw.h +++ b/drivers/net/e1000e/hw.h @@ -900,6 +900,10 @@ struct e1000_dev_spec_82571 { u32 smb_counter; }; +struct e1000_dev_spec_80003es2lan { + bool mdic_wa_enable; +}; + struct e1000_shadow_ram { u16 value; bool modified; @@ -928,6 +932,7 @@ struct e1000_hw { union { struct e1000_dev_spec_82571 e82571; + struct e1000_dev_spec_80003es2lan e80003es2lan; struct e1000_dev_spec_ich8lan ich8lan; } dev_spec; }; -- cgit v1.2.3 From 60d51134281fbeb352013d782a440fb338d31f01 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 8 Dec 2009 07:22:03 +0000 Subject: ixgbe: Fix TX stats accounting Here is an updated version, because ixgbe_get_ethtool_stats() needs to call dev_get_stats() or "ethtool -S" wont give correct tx_bytes/tx_packets values. Several cpus can update netdev->stats.tx_bytes & netdev->stats.tx_packets in parallel. In this case, TX stats are under estimated and false sharing takes place. After a pktgen session sending exactly 200000000 packets : # ifconfig fiber0 | grep TX TX packets:198501982 errors:0 dropped:0 overruns:0 carrier:0 Multi queue devices should instead use txq->tx_bytes & txq->tx_packets in their xmit() method (appropriate txq lock already held by caller, no cache line miss), or use appropriate locking. After patch, same pktgen session gives : # ifconfig fiber0 | grep TX TX packets:200000000 errors:0 dropped:0 overruns:0 carrier:0 Signed-off-by: Eric Dumazet Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ixgbe/ixgbe_ethtool.c | 1 + drivers/net/ixgbe/ixgbe_main.c | 20 ++++---------------- 2 files changed, 5 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 06a9d18bbdbc..0bd49d3b9f65 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -990,6 +990,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, char *p = NULL; ixgbe_update_stats(adapter); + dev_get_stats(netdev); for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) { switch (ixgbe_gstrings_stats[i].type) { case NETDEV_STATS: diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 247ed2a24769..4374f441c426 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -435,8 +435,6 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, tx_ring->total_packets += total_packets; tx_ring->stats.packets += total_packets; tx_ring->stats.bytes += total_bytes; - netdev->stats.tx_bytes += total_bytes; - netdev->stats.tx_packets += total_packets; return (count < tx_ring->work_limit); } @@ -5327,6 +5325,7 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_ring *tx_ring; + struct netdev_queue *txq; unsigned int first; unsigned int tx_flags = 0; u8 hdr_len = 0; @@ -5424,6 +5423,9 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, tx_ring->atr_count = 0; } } + txq = netdev_get_tx_queue(netdev, tx_ring->queue_index); + txq->tx_bytes += skb->len; + txq->tx_packets++; ixgbe_tx_queue(adapter, tx_ring, tx_flags, count, skb->len, hdr_len); ixgbe_maybe_stop_tx(netdev, tx_ring, DESC_NEEDED); @@ -5437,19 +5439,6 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, return NETDEV_TX_OK; } -/** - * ixgbe_get_stats - Get System Network Statistics - * @netdev: network interface device structure - * - * Returns the address of the device statistics structure. - * The statistics are actually updated from the timer callback. - **/ -static struct net_device_stats *ixgbe_get_stats(struct net_device *netdev) -{ - /* only return the current stats */ - return &netdev->stats; -} - /** * ixgbe_set_mac - Change the Ethernet Address of the NIC * @netdev: network interface device structure @@ -5580,7 +5569,6 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_stop = ixgbe_close, .ndo_start_xmit = ixgbe_xmit_frame, .ndo_select_queue = ixgbe_select_queue, - .ndo_get_stats = ixgbe_get_stats, .ndo_set_rx_mode = ixgbe_set_rx_mode, .ndo_set_multicast_list = ixgbe_set_rx_mode, .ndo_validate_addr = eth_validate_addr, -- cgit v1.2.3 From 74757d49016a8b06ca028196886641d7aeb78de5 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Tue, 8 Dec 2009 07:22:23 +0000 Subject: ixgbe: add support for 82599 KR device 0x1517 Signed-off-by: Don Skidmore Acked-by: Peter P Waskiewicz Jr Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ixgbe/ixgbe_82599.c | 1 + drivers/net/ixgbe/ixgbe_main.c | 2 ++ drivers/net/ixgbe/ixgbe_type.h | 1 + 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ixgbe/ixgbe_82599.c b/drivers/net/ixgbe/ixgbe_82599.c index 72106898a5cb..538340527aa6 100644 --- a/drivers/net/ixgbe/ixgbe_82599.c +++ b/drivers/net/ixgbe/ixgbe_82599.c @@ -342,6 +342,7 @@ static enum ixgbe_media_type ixgbe_get_media_type_82599(struct ixgbe_hw *hw) case IXGBE_DEV_ID_82599_KX4: case IXGBE_DEV_ID_82599_KX4_MEZZ: case IXGBE_DEV_ID_82599_COMBO_BACKPLANE: + case IXGBE_DEV_ID_82599_KR: case IXGBE_DEV_ID_82599_XAUI_LOM: /* Default device ID is mezzanine card KX/KX4 */ media_type = ixgbe_media_type_backplane; diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 4374f441c426..35ea8c93fd80 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -96,6 +96,8 @@ static struct pci_device_id ixgbe_pci_tbl[] = { board_82599 }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_XAUI_LOM), board_82599 }, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_KR), + board_82599 }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP), board_82599 }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_82599_SFP_EM), diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ixgbe/ixgbe_type.h index 21b6633da578..f3e8d52610b7 100644 --- a/drivers/net/ixgbe/ixgbe_type.h +++ b/drivers/net/ixgbe/ixgbe_type.h @@ -50,6 +50,7 @@ #define IXGBE_DEV_ID_82598EB_XF_LR 0x10F4 #define IXGBE_DEV_ID_82599_KX4 0x10F7 #define IXGBE_DEV_ID_82599_KX4_MEZZ 0x1514 +#define IXGBE_DEV_ID_82599_KR 0x1517 #define IXGBE_DEV_ID_82599_CX4 0x10F9 #define IXGBE_DEV_ID_82599_SFP 0x10FB #define IXGBE_DEV_ID_82599_SFP_EM 0x1507 -- cgit v1.2.3 From 504bb3b58e7314e0fe026da280e8168c1314be3d Mon Sep 17 00:00:00 2001 From: chas williams - CONTRACTOR Date: Fri, 4 Dec 2009 05:22:17 +0000 Subject: atm: [he] adjust tests to account for sk_wmem_alloc changes due to reference counting sk_wmem_alloc now has a value of 1 when all the outstanding data has been sent. Signed-off-by: Chas Williams - CONTRACTOR Signed-off-by: David S. Miller --- drivers/atm/he.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/atm/he.c b/drivers/atm/he.c index e90665876c47..e8c6529dc366 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -2505,7 +2505,7 @@ he_close(struct atm_vcc *vcc) * TBRQ, the host issues the close command to the adapter. */ - while (((tx_inuse = atomic_read(&sk_atm(vcc)->sk_wmem_alloc)) > 0) && + while (((tx_inuse = atomic_read(&sk_atm(vcc)->sk_wmem_alloc)) > 1) && (retry < MAX_RETRY)) { msleep(sleep); if (sleep < 250) @@ -2514,7 +2514,7 @@ he_close(struct atm_vcc *vcc) ++retry; } - if (tx_inuse) + if (tx_inuse > 1) hprintk("close tx cid 0x%x tx_inuse = %d\n", cid, tx_inuse); /* 2.3.1.1 generic close operations with flush */ -- cgit v1.2.3 From 4b860abf636fdd963731ae4ccafdd39ebcd5f962 Mon Sep 17 00:00:00 2001 From: Brice Goglin Date: Tue, 8 Dec 2009 20:24:35 -0800 Subject: myri10ge: use src+dst for rss hashing Use a more effective rss hash by default (src + dst, rather than just src). Signed-off-by: Brice Goglin Signed-off-by: David S. Miller --- drivers/net/myri10ge/myri10ge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index d38921906bb7..3fcb1c356e0d 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -75,7 +75,7 @@ #include "myri10ge_mcp.h" #include "myri10ge_mcp_gen_header.h" -#define MYRI10GE_VERSION_STR "1.5.1-1.451" +#define MYRI10GE_VERSION_STR "1.5.1-1.453" MODULE_DESCRIPTION("Myricom 10G driver (10GbE)"); MODULE_AUTHOR("Maintainer: help@myri.com"); @@ -347,7 +347,7 @@ static int myri10ge_max_slices = 1; module_param(myri10ge_max_slices, int, S_IRUGO); MODULE_PARM_DESC(myri10ge_max_slices, "Max tx/rx queues"); -static int myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_PORT; +static int myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT; module_param(myri10ge_rss_hash, int, S_IRUGO); MODULE_PARM_DESC(myri10ge_rss_hash, "Type of RSS hashing to do"); -- cgit v1.2.3 From 1e4e0767ecb1cf53a43343518c0e09ad7ee5e23a Mon Sep 17 00:00:00 2001 From: Asier Llano Date: Tue, 8 Dec 2009 20:29:10 -0800 Subject: net/mpc5200: Fix locking on fec_mpc52xx driver Fix the locking scheme on the fec_mpc52xx driver. This device can receive IRQs from three sources; the FEC itself, the tx DMA, and the rx DMA. Mutual exclusion was handled by taking a spin_lock() in the critical regions, but because the handlers are run with IRQs enabled, spin_lock() is insufficient and the driver can end up interrupting a critical region anyway from another IRQ. Asier Llano discovered that this occurs when an error IRQ is raised in the middle of handling rx irqs which resulted in an sk_buff memory leak. In addition, locking is spotty at best in the driver and inspection revealed quite a few places with insufficient locking. This patch is based on Asier's initial work, but reworks a number of things so that locks are held for as short a time as possible, so that spin_lock_irqsave() is used everywhere, and so the locks are dropped when calling into the network stack (because the lock only protects the hardware interface; not the network stack). Boot tested on a lite5200 with an NFS root. Has not been performance tested. Signed-off-by: Asier Llano Signed-off-by: Grant Likely Signed-off-by: David S. Miller --- drivers/net/fec_mpc52xx.c | 121 ++++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c index 6407672b28e9..848e8407ea8f 100644 --- a/drivers/net/fec_mpc52xx.c +++ b/drivers/net/fec_mpc52xx.c @@ -85,11 +85,15 @@ MODULE_PARM_DESC(debug, "debugging messages level"); static void mpc52xx_fec_tx_timeout(struct net_device *dev) { + struct mpc52xx_fec_priv *priv = netdev_priv(dev); + unsigned long flags; + dev_warn(&dev->dev, "transmit timed out\n"); + spin_lock_irqsave(&priv->lock, flags); mpc52xx_fec_reset(dev); - dev->stats.tx_errors++; + spin_unlock_irqrestore(&priv->lock, flags); netif_wake_queue(dev); } @@ -135,28 +139,32 @@ static void mpc52xx_fec_free_rx_buffers(struct net_device *dev, struct bcom_task } } +static void +mpc52xx_fec_rx_submit(struct net_device *dev, struct sk_buff *rskb) +{ + struct mpc52xx_fec_priv *priv = netdev_priv(dev); + struct bcom_fec_bd *bd; + + bd = (struct bcom_fec_bd *) bcom_prepare_next_buffer(priv->rx_dmatsk); + bd->status = FEC_RX_BUFFER_SIZE; + bd->skb_pa = dma_map_single(dev->dev.parent, rskb->data, + FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE); + bcom_submit_next_buffer(priv->rx_dmatsk, rskb); +} + static int mpc52xx_fec_alloc_rx_buffers(struct net_device *dev, struct bcom_task *rxtsk) { - while (!bcom_queue_full(rxtsk)) { - struct sk_buff *skb; - struct bcom_fec_bd *bd; + struct sk_buff *skb; + while (!bcom_queue_full(rxtsk)) { skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); - if (skb == NULL) + if (!skb) return -EAGAIN; /* zero out the initial receive buffers to aid debugging */ memset(skb->data, 0, FEC_RX_BUFFER_SIZE); - - bd = (struct bcom_fec_bd *)bcom_prepare_next_buffer(rxtsk); - - bd->status = FEC_RX_BUFFER_SIZE; - bd->skb_pa = dma_map_single(dev->dev.parent, skb->data, - FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE); - - bcom_submit_next_buffer(rxtsk, skb); + mpc52xx_fec_rx_submit(dev, skb); } - return 0; } @@ -328,13 +336,12 @@ static int mpc52xx_fec_start_xmit(struct sk_buff *skb, struct net_device *dev) DMA_TO_DEVICE); bcom_submit_next_buffer(priv->tx_dmatsk, skb); + spin_unlock_irqrestore(&priv->lock, flags); if (bcom_queue_full(priv->tx_dmatsk)) { netif_stop_queue(dev); } - spin_unlock_irqrestore(&priv->lock, flags); - return NETDEV_TX_OK; } @@ -359,9 +366,9 @@ static irqreturn_t mpc52xx_fec_tx_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; struct mpc52xx_fec_priv *priv = netdev_priv(dev); + unsigned long flags; - spin_lock(&priv->lock); - + spin_lock_irqsave(&priv->lock, flags); while (bcom_buffer_done(priv->tx_dmatsk)) { struct sk_buff *skb; struct bcom_fec_bd *bd; @@ -372,11 +379,10 @@ static irqreturn_t mpc52xx_fec_tx_interrupt(int irq, void *dev_id) dev_kfree_skb_irq(skb); } + spin_unlock_irqrestore(&priv->lock, flags); netif_wake_queue(dev); - spin_unlock(&priv->lock); - return IRQ_HANDLED; } @@ -384,67 +390,60 @@ static irqreturn_t mpc52xx_fec_rx_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; struct mpc52xx_fec_priv *priv = netdev_priv(dev); + struct sk_buff *rskb; /* received sk_buff */ + struct sk_buff *skb; /* new sk_buff to enqueue in its place */ + struct bcom_fec_bd *bd; + u32 status, physaddr; + int length; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); while (bcom_buffer_done(priv->rx_dmatsk)) { - struct sk_buff *skb; - struct sk_buff *rskb; - struct bcom_fec_bd *bd; - u32 status; rskb = bcom_retrieve_buffer(priv->rx_dmatsk, &status, - (struct bcom_bd **)&bd); - dma_unmap_single(dev->dev.parent, bd->skb_pa, rskb->len, - DMA_FROM_DEVICE); + (struct bcom_bd **)&bd); + physaddr = bd->skb_pa; /* Test for errors in received frame */ if (status & BCOM_FEC_RX_BD_ERRORS) { /* Drop packet and reuse the buffer */ - bd = (struct bcom_fec_bd *) - bcom_prepare_next_buffer(priv->rx_dmatsk); - - bd->status = FEC_RX_BUFFER_SIZE; - bd->skb_pa = dma_map_single(dev->dev.parent, - rskb->data, - FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE); - - bcom_submit_next_buffer(priv->rx_dmatsk, rskb); - + mpc52xx_fec_rx_submit(dev, rskb); dev->stats.rx_dropped++; - continue; } /* skbs are allocated on open, so now we allocate a new one, * and remove the old (with the packet) */ skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE); - if (skb) { - /* Process the received skb */ - int length = status & BCOM_FEC_RX_BD_LEN_MASK; - - skb_put(rskb, length - 4); /* length without CRC32 */ - - rskb->dev = dev; - rskb->protocol = eth_type_trans(rskb, dev); - - netif_rx(rskb); - } else { + if (!skb) { /* Can't get a new one : reuse the same & drop pkt */ - dev_notice(&dev->dev, "Memory squeeze, dropping packet.\n"); + dev_notice(&dev->dev, "Low memory - dropped packet.\n"); + mpc52xx_fec_rx_submit(dev, rskb); dev->stats.rx_dropped++; - - skb = rskb; + continue; } - bd = (struct bcom_fec_bd *) - bcom_prepare_next_buffer(priv->rx_dmatsk); + /* Enqueue the new sk_buff back on the hardware */ + mpc52xx_fec_rx_submit(dev, skb); - bd->status = FEC_RX_BUFFER_SIZE; - bd->skb_pa = dma_map_single(dev->dev.parent, skb->data, - FEC_RX_BUFFER_SIZE, DMA_FROM_DEVICE); + /* Process the received skb - Drop the spin lock while + * calling into the network stack */ + spin_unlock_irqrestore(&priv->lock, flags); - bcom_submit_next_buffer(priv->rx_dmatsk, skb); + dma_unmap_single(dev->dev.parent, physaddr, rskb->len, + DMA_FROM_DEVICE); + length = status & BCOM_FEC_RX_BD_LEN_MASK; + skb_put(rskb, length - 4); /* length without CRC32 */ + rskb->dev = dev; + rskb->protocol = eth_type_trans(rskb, dev); + netif_rx(rskb); + + spin_lock_irqsave(&priv->lock, flags); } + spin_unlock_irqrestore(&priv->lock, flags); + return IRQ_HANDLED; } @@ -454,6 +453,7 @@ static irqreturn_t mpc52xx_fec_interrupt(int irq, void *dev_id) struct mpc52xx_fec_priv *priv = netdev_priv(dev); struct mpc52xx_fec __iomem *fec = priv->fec; u32 ievent; + unsigned long flags; ievent = in_be32(&fec->ievent); @@ -471,9 +471,10 @@ static irqreturn_t mpc52xx_fec_interrupt(int irq, void *dev_id) if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR)) dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n"); + spin_lock_irqsave(&priv->lock, flags); mpc52xx_fec_reset(dev); + spin_unlock_irqrestore(&priv->lock, flags); - netif_wake_queue(dev); return IRQ_HANDLED; } @@ -768,6 +769,8 @@ static void mpc52xx_fec_reset(struct net_device *dev) bcom_enable(priv->tx_dmatsk); mpc52xx_fec_start(dev); + + netif_wake_queue(dev); } -- cgit v1.2.3 From 073886ff23bfbe7b88c9133d7a556d6ec51f0b50 Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Sat, 5 Dec 2009 08:04:16 +0000 Subject: gigaset: don't enable any debugging output by default When built with debugging support, the Gigaset driver enabled some debugging messages by default. Change the default to "all off". Signed-off-by: Tilman Schmidt Signed-off-by: David S. Miller --- drivers/isdn/gigaset/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index 82ed1cd14ff5..664b0c519c3e 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -29,7 +29,7 @@ #endif /* Module parameters */ -int gigaset_debuglevel = DEBUG_DEFAULT; +int gigaset_debuglevel; EXPORT_SYMBOL_GPL(gigaset_debuglevel); module_param_named(debug, gigaset_debuglevel, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug, "debug level"); -- cgit v1.2.3 From a29ec08a1b649436248c140a3bc389bc11434c94 Mon Sep 17 00:00:00 2001 From: Denis Kirjanov Date: Tue, 8 Dec 2009 20:36:00 -0800 Subject: mv643xx_eth: check for valid hw address (resubmit) Check for valid hw address. Signed-off-by: Denis Kirjanov Signed-off-by: David S. Miller --- drivers/net/mv643xx_eth.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 796a493f95ab..1405a170bb43 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1827,6 +1827,9 @@ static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr) { struct sockaddr *sa = addr; + if (!is_valid_ether_addr(sa->sa_data)) + return -EINVAL; + memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); netif_addr_lock_bh(dev); -- cgit v1.2.3 From 6f7714196b4e01d7462df31d705c3ca53ecbdfc1 Mon Sep 17 00:00:00 2001 From: Amit Kumar Salecha Date: Sat, 5 Dec 2009 12:23:54 +0000 Subject: netxen: protect device reset by rtnl_lock o To prevent race conditions with other reset events. During suspend/resume and firmware recovery, acquire rtnl_lock, while changing interface state. Acked-by: Dhananjay Phadke Signed-off-by: Amit Kumar Salecha Signed-off-by: David S. Miller --- drivers/net/netxen/netxen_nic_main.c | 43 +++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index e5d187fce51b..8ffb30bc798f 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -946,8 +946,9 @@ netxen_nic_init_coalesce_defaults(struct netxen_adapter *adapter) NETXEN_DEFAULT_INTR_COALESCE_TX_PACKETS; } +/* with rtnl_lock */ static int -netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev) +__netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev) { int err; @@ -988,8 +989,24 @@ netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev) return 0; } +/* Usage: During resume and firmware recovery module.*/ + +static inline int +netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev) +{ + int err = 0; + + rtnl_lock(); + if (netif_running(netdev)) + err = __netxen_nic_up(adapter, netdev); + rtnl_unlock(); + + return err; +} + +/* with rtnl_lock */ static void -netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev) +__netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev) { if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) return; @@ -1014,6 +1031,17 @@ netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev) spin_unlock(&adapter->tx_clean_lock); } +/* Usage: During suspend and firmware recovery module */ + +static inline void +netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev) +{ + rtnl_lock(); + if (netif_running(netdev)) + __netxen_nic_down(adapter, netdev); + rtnl_unlock(); + +} static int netxen_nic_attach(struct netxen_adapter *adapter) @@ -1122,14 +1150,14 @@ netxen_nic_reset_context(struct netxen_adapter *adapter) netif_device_detach(netdev); if (netif_running(netdev)) - netxen_nic_down(adapter, netdev); + __netxen_nic_down(adapter, netdev); netxen_nic_detach(adapter); if (netif_running(netdev)) { err = netxen_nic_attach(adapter); if (!err) - err = netxen_nic_up(adapter, netdev); + err = __netxen_nic_up(adapter, netdev); if (err) goto done; @@ -1499,7 +1527,7 @@ static int netxen_nic_open(struct net_device *netdev) if (err) return err; - err = netxen_nic_up(adapter, netdev); + err = __netxen_nic_up(adapter, netdev); if (err) goto err_out; @@ -1519,7 +1547,7 @@ static int netxen_nic_close(struct net_device *netdev) { struct netxen_adapter *adapter = netdev_priv(netdev); - netxen_nic_down(adapter, netdev); + __netxen_nic_down(adapter, netdev); return 0; } @@ -2210,8 +2238,7 @@ netxen_detach_work(struct work_struct *work) netif_device_detach(netdev); - if (netif_running(netdev)) - netxen_nic_down(adapter, netdev); + netxen_nic_down(adapter, netdev); netxen_nic_detach(adapter); -- cgit v1.2.3 From f2251f668e9527b87c9ba7256e7497cb49abbc20 Mon Sep 17 00:00:00 2001 From: Amit Kumar Salecha Date: Sat, 5 Dec 2009 12:23:55 +0000 Subject: netxen:fix napi intr enable check o netif_running() check for enabling interrupt at end of napi poll is not enough to cover firmwar recovery. Instead test __NX_DEV_UP bit. o Avoid re-entry into to netxen_nic_down() with __NX_DEV_UP bit check. Acked-by: Dhananjay Phadke Signed-off-by: Amit Kumar Salecha Signed-off-by: David S. Miller --- drivers/net/netxen/netxen_nic_main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 8ffb30bc798f..f4996846a234 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -1011,8 +1011,10 @@ __netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev) if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) return; - clear_bit(__NX_DEV_UP, &adapter->state); + if (!test_and_clear_bit(__NX_DEV_UP, &adapter->state)) + return; + smp_mb(); spin_lock(&adapter->tx_clean_lock); netif_carrier_off(netdev); netif_tx_disable(netdev); @@ -2053,7 +2055,7 @@ static int netxen_nic_poll(struct napi_struct *napi, int budget) if ((work_done < budget) && tx_complete) { napi_complete(&sds_ring->napi); - if (netif_running(adapter->netdev)) + if (test_bit(__NX_DEV_UP, &adapter->state)) netxen_nic_enable_int(sds_ring); } -- cgit v1.2.3 From 634d7df89a93a52561f598bf9512f4cf72946954 Mon Sep 17 00:00:00 2001 From: Dhananjay Phadke Date: Sat, 5 Dec 2009 12:23:56 +0000 Subject: netxen: fix firmware type check Unified firmware image may not contain MN type of firmware. Driver should fall back to NOMN firmware type instead of going to flash. Signed-off-by: Dhananjay Phadke Signed-off-by: Amit Kumar Salecha Signed-off-by: David S. Miller --- drivers/net/netxen/netxen_nic_init.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index 80a667460514..02f8d4b4db63 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -619,17 +619,20 @@ nx_set_product_offs(struct netxen_adapter *adapter) uint32_t i; __le32 entries; + int mn_present = (NX_IS_REVISION_P2(adapter->ahw.revision_id)) ? + 1 : netxen_p3_has_mn(adapter); + ptab_descr = nx_get_table_desc(unirom, NX_UNI_DIR_SECT_PRODUCT_TBL); if (ptab_descr == NULL) return -1; entries = cpu_to_le32(ptab_descr->num_entries); +nomn: for (i = 0; i < entries; i++) { __le32 flags, file_chiprev, offs; u8 chiprev = adapter->ahw.revision_id; - int mn_present = netxen_p3_has_mn(adapter); uint32_t flagbit; offs = cpu_to_le32(ptab_descr->findex) + @@ -647,6 +650,11 @@ nx_set_product_offs(struct netxen_adapter *adapter) } } + if (mn_present && NX_IS_REVISION_P3(adapter->ahw.revision_id)) { + mn_present = 0; + goto nomn; + } + return -1; } @@ -1021,6 +1029,10 @@ netxen_p3_has_mn(struct netxen_adapter *adapter) u32 capability, flashed_ver; capability = 0; + /* NX2031 always had MN */ + if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) + return 1; + netxen_rom_fast_read(adapter, NX_FW_VERSION_OFFSET, (int *)&flashed_ver); flashed_ver = NETXEN_DECODE_VERSION(flashed_ver); -- cgit v1.2.3 From 4b45e3424e7210688f95039b8cdffb11d2e48934 Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Sun, 6 Dec 2009 22:56:59 +0000 Subject: atl1c:add pci map direction in atl1c_buffer flags add pci map direction in atl1c_buffer flags, it is used when call pci_unmap apis. Signed-off-by: Jie Yang Signed-off-by: David S. Miller --- drivers/net/atl1c/atl1c.h | 11 ++++++++--- drivers/net/atl1c/atl1c_main.c | 22 ++++++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h index a348a22551d9..7e09084f75a9 100644 --- a/drivers/net/atl1c/atl1c.h +++ b/drivers/net/atl1c/atl1c.h @@ -479,6 +479,9 @@ struct atl1c_buffer { #define ATL1C_PCIMAP_PAGE 0x0008 #define ATL1C_PCIMAP_TYPE_MASK 0x000C +#define ATL1C_PCIMAP_TODEVICE 0x0010 +#define ATL1C_PCIMAP_FROMDEVICE 0x0020 +#define ATL1C_PCIMAP_DIRECTION_MASK 0x0030 dma_addr_t dma; }; @@ -487,9 +490,11 @@ struct atl1c_buffer { ((buff)->flags) |= (state); \ } while (0) -#define ATL1C_SET_PCIMAP_TYPE(buff, type) do { \ - ((buff)->flags) &= ~ATL1C_PCIMAP_TYPE_MASK; \ - ((buff)->flags) |= (type); \ +#define ATL1C_SET_PCIMAP_TYPE(buff, type, direction) do { \ + ((buff)->flags) &= ~ATL1C_PCIMAP_TYPE_MASK; \ + ((buff)->flags) |= (type); \ + ((buff)->flags) &= ~ATL1C_PCIMAP_DIRECTION_MASK; \ + ((buff)->flags) |= (direction); \ } while (0) /* transimit packet descriptor (tpd) ring */ diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index 1e2f57d4c367..1098dad3d381 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -713,15 +713,21 @@ static int __devinit atl1c_sw_init(struct atl1c_adapter *adapter) static inline void atl1c_clean_buffer(struct pci_dev *pdev, struct atl1c_buffer *buffer_info, int in_irq) { + u16 pci_driection; if (buffer_info->flags & ATL1C_BUFFER_FREE) return; if (buffer_info->dma) { + if (buffer_info->flags & ATL1C_PCIMAP_FROMDEVICE) + pci_driection = PCI_DMA_FROMDEVICE; + else + pci_driection = PCI_DMA_TODEVICE; + if (buffer_info->flags & ATL1C_PCIMAP_SINGLE) pci_unmap_single(pdev, buffer_info->dma, - buffer_info->length, PCI_DMA_TODEVICE); + buffer_info->length, pci_driection); else if (buffer_info->flags & ATL1C_PCIMAP_PAGE) pci_unmap_page(pdev, buffer_info->dma, - buffer_info->length, PCI_DMA_TODEVICE); + buffer_info->length, pci_driection); } if (buffer_info->skb) { if (in_irq) @@ -1606,7 +1612,8 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter, const int ringid buffer_info->dma = pci_map_single(pdev, vir_addr, buffer_info->length, PCI_DMA_FROMDEVICE); - ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE); + ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE, + ATL1C_PCIMAP_FROMDEVICE); rfd_desc->buffer_addr = cpu_to_le64(buffer_info->dma); rfd_next_to_use = next_next; if (++next_next == rfd_ring->count) @@ -1967,7 +1974,8 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter, buffer_info->dma = pci_map_single(adapter->pdev, skb->data, hdr_len, PCI_DMA_TODEVICE); ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); - ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE); + ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE, + ATL1C_PCIMAP_TODEVICE); mapped_len += map_len; use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); use_tpd->buffer_len = cpu_to_le16(buffer_info->length); @@ -1988,7 +1996,8 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter, pci_map_single(adapter->pdev, skb->data + mapped_len, buffer_info->length, PCI_DMA_TODEVICE); ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); - ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE); + ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_SINGLE, + ATL1C_PCIMAP_TODEVICE); use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); use_tpd->buffer_len = cpu_to_le16(buffer_info->length); } @@ -2009,7 +2018,8 @@ static void atl1c_tx_map(struct atl1c_adapter *adapter, buffer_info->length, PCI_DMA_TODEVICE); ATL1C_SET_BUFFER_STATE(buffer_info, ATL1C_BUFFER_BUSY); - ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_PAGE); + ATL1C_SET_PCIMAP_TYPE(buffer_info, ATL1C_PCIMAP_PAGE, + ATL1C_PCIMAP_TODEVICE); use_tpd->buffer_addr = cpu_to_le64(buffer_info->dma); use_tpd->buffer_len = cpu_to_le16(buffer_info->length); } -- cgit v1.2.3 From cb19054697e92a793f336380fd72c588521178ff Mon Sep 17 00:00:00 2001 From: Jie Yang Date: Sun, 6 Dec 2009 23:16:58 +0000 Subject: atl1c:use common_task instead of reset_task and link_chg_task use common_task instead of reset_task and link_chg_task, so it fix "call cancel_work_sync from the work itself". Signed-off-by: Jie Yang Signed-off-by: David S. Miller --- drivers/net/atl1c/atl1c.h | 6 ++-- drivers/net/atl1c/atl1c_main.c | 72 +++++++++++++++++++----------------------- 2 files changed, 37 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h index 7e09084f75a9..efe5435bc3d3 100644 --- a/drivers/net/atl1c/atl1c.h +++ b/drivers/net/atl1c/atl1c.h @@ -555,6 +555,9 @@ struct atl1c_adapter { #define __AT_TESTING 0x0001 #define __AT_RESETTING 0x0002 #define __AT_DOWN 0x0003 + u8 work_event; +#define ATL1C_WORK_EVENT_RESET 0x01 +#define ATL1C_WORK_EVENT_LINK_CHANGE 0x02 u32 msg_enable; bool have_msi; @@ -566,8 +569,7 @@ struct atl1c_adapter { spinlock_t tx_lock; atomic_t irq_sem; - struct work_struct reset_task; - struct work_struct link_chg_task; + struct work_struct common_task; struct timer_list watchdog_timer; struct timer_list phy_config_timer; diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index 1098dad3d381..666261b5851e 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -198,27 +198,12 @@ static void atl1c_phy_config(unsigned long data) void atl1c_reinit_locked(struct atl1c_adapter *adapter) { - WARN_ON(in_interrupt()); atl1c_down(adapter); atl1c_up(adapter); clear_bit(__AT_RESETTING, &adapter->flags); } -static void atl1c_reset_task(struct work_struct *work) -{ - struct atl1c_adapter *adapter; - struct net_device *netdev; - - adapter = container_of(work, struct atl1c_adapter, reset_task); - netdev = adapter->netdev; - - netif_device_detach(netdev); - atl1c_down(adapter); - atl1c_up(adapter); - netif_device_attach(netdev); -} - static void atl1c_check_link_status(struct atl1c_adapter *adapter) { struct atl1c_hw *hw = &adapter->hw; @@ -275,18 +260,6 @@ static void atl1c_check_link_status(struct atl1c_adapter *adapter) } } -/* - * atl1c_link_chg_task - deal with link change event Out of interrupt context - * @netdev: network interface device structure - */ -static void atl1c_link_chg_task(struct work_struct *work) -{ - struct atl1c_adapter *adapter; - - adapter = container_of(work, struct atl1c_adapter, link_chg_task); - atl1c_check_link_status(adapter); -} - static void atl1c_link_chg_event(struct atl1c_adapter *adapter) { struct net_device *netdev = adapter->netdev; @@ -311,20 +284,40 @@ static void atl1c_link_chg_event(struct atl1c_adapter *adapter) adapter->link_speed = SPEED_0; } } - schedule_work(&adapter->link_chg_task); + + adapter->work_event |= ATL1C_WORK_EVENT_LINK_CHANGE; + schedule_work(&adapter->common_task); } -static void atl1c_del_timer(struct atl1c_adapter *adapter) +static void atl1c_common_task(struct work_struct *work) { - del_timer_sync(&adapter->phy_config_timer); + struct atl1c_adapter *adapter; + struct net_device *netdev; + + adapter = container_of(work, struct atl1c_adapter, common_task); + netdev = adapter->netdev; + + if (adapter->work_event & ATL1C_WORK_EVENT_RESET) { + netif_device_detach(netdev); + atl1c_down(adapter); + atl1c_up(adapter); + netif_device_attach(netdev); + return; + } + + if (adapter->work_event & ATL1C_WORK_EVENT_LINK_CHANGE) + atl1c_check_link_status(adapter); + + return; } -static void atl1c_cancel_work(struct atl1c_adapter *adapter) + +static void atl1c_del_timer(struct atl1c_adapter *adapter) { - cancel_work_sync(&adapter->reset_task); - cancel_work_sync(&adapter->link_chg_task); + del_timer_sync(&adapter->phy_config_timer); } + /* * atl1c_tx_timeout - Respond to a Tx Hang * @netdev: network interface device structure @@ -334,7 +327,8 @@ static void atl1c_tx_timeout(struct net_device *netdev) struct atl1c_adapter *adapter = netdev_priv(netdev); /* Do the reset outside of interrupt context */ - schedule_work(&adapter->reset_task); + adapter->work_event |= ATL1C_WORK_EVENT_RESET; + schedule_work(&adapter->common_task); } /* @@ -1539,7 +1533,8 @@ static irqreturn_t atl1c_intr(int irq, void *data) /* reset MAC */ hw->intr_mask &= ~ISR_ERROR; AT_WRITE_REG(hw, REG_IMR, hw->intr_mask); - schedule_work(&adapter->reset_task); + adapter->work_event |= ATL1C_WORK_EVENT_RESET; + schedule_work(&adapter->common_task); break; } @@ -2208,8 +2203,7 @@ void atl1c_down(struct atl1c_adapter *adapter) struct net_device *netdev = adapter->netdev; atl1c_del_timer(adapter); - atl1c_cancel_work(adapter); - + adapter->work_event = 0; /* clear all event */ /* signal that we're down so the interrupt handler does not * reschedule our watchdog timer */ set_bit(__AT_DOWN, &adapter->flags); @@ -2609,8 +2603,8 @@ static int __devinit atl1c_probe(struct pci_dev *pdev, adapter->hw.mac_addr[4], adapter->hw.mac_addr[5]); atl1c_hw_set_mac_addr(&adapter->hw); - INIT_WORK(&adapter->reset_task, atl1c_reset_task); - INIT_WORK(&adapter->link_chg_task, atl1c_link_chg_task); + INIT_WORK(&adapter->common_task, atl1c_common_task); + adapter->work_event = 0; err = register_netdev(netdev); if (err) { dev_err(&pdev->dev, "register netdevice failed\n"); -- cgit v1.2.3 From e0188829cb724e7d12a2d4e343b368ff1d6e1471 Mon Sep 17 00:00:00 2001 From: Stanislav Brabec Date: Tue, 8 Dec 2009 21:00:22 -0800 Subject: b44 WOL setup: one-bit-off stack corruption kernel panic fix About 50% of shutdowns of b44 Ethernet adapter ends by kernel panic with kernels compiled with stack-protector. Checking b44_magic_pattern() return values, one call of b44_magic_pattern() returns 127. It means, that set_bit(128, pmask) was called on line 1509. It means that bit 0 of 17th byte of pmask was overwritten. But pmask has only 16 bytes. Stack corruption happens. It seems that set_bit() on line 1509 always writes one bit off. The fix does not only solve the stack corruption, but also makes Wake On LAN working on my onboard B44 on Asus A7V-333X mainboard. It seems that this problem affects all kernel versions since commit 725ad800 ([PATCH] b44: add wol for old nic) on 2006-06-20. Signed-off-by: Stanislav Brabec Signed-off-by: David S. Miller --- drivers/net/b44.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/b44.c b/drivers/net/b44.c index 2a9132343b66..4869adb69586 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -1505,8 +1505,7 @@ static int b44_magic_pattern(u8 *macaddr, u8 *ppattern, u8 *pmask, int offset) for (k = 0; k< ethaddr_bytes; k++) { ppattern[offset + magicsync + (j * ETH_ALEN) + k] = macaddr[k]; - len++; - set_bit(len, (unsigned long *) pmask); + set_bit(len++, (unsigned long *) pmask); } } return len - 1; -- cgit v1.2.3 From 43de004b6c197b0ea408bdebf4f14afdead74b63 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Wed, 9 Dec 2009 02:52:19 -0800 Subject: gianfar: Fix build with CONFIG_NET_POLL_CONTROLLER=y commit 46ceb60ca80fa07703bc6eb8f4651f900dff5a82 ("gianfar: Add Multiple group Support") introduced the following build error with CONFIG_NET_POLL_CONTROLLER=y: CC ggianfar.o ggianfar.c: In function 'gfar_netpoll': ggianfar.c:2653: error: invalid storage class for function 'gfar_interrupt' ggianfar.c:2652: warning: ISO C90 forbids mixed declarations and code ggianfar.c:2681: error: invalid storage class for function 'adjust_link' ggianfar.c:2764: error: invalid storage class for function 'gfar_set_multi' ggianfar.c:2855: error: invalid storage class for function 'gfar_clear_exact_match' ggianfar.c:2877: error: invalid storage class for function 'gfar_set_hash_for_addr' ggianfar.c:2898: error: invalid storage class for function 'gfar_set_mac_for_addr' ggianfar.c:2922: error: invalid storage class for function 'gfar_error' ggianfar.c:3020: warning: ISO C90 forbids mixed declarations and code ggianfar.c:3032: error: invalid storage class for function 'gfar_init' ggianfar.c:3037: error: invalid storage class for function 'gfar_exit' ggianfar.c:3041: error: initializer element is not constant ggianfar.c:3042: error: initializer element is not constant ggianfar.c:3042: warning: ISO C90 forbids mixed declarations and code ggianfar.c:3042: error: expected declaration or statement at end of input make[1]: *** [ggianfar.o] Error 1 This patch fixes the issue. Reported-by: Benjamin Herrenschmidt Signed-off-by: Anton Vorontsov Signed-off-by: David S. Miller --- drivers/net/gianfar.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 16def131c390..6850dc0a7b91 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -2644,6 +2644,7 @@ static void gfar_netpoll(struct net_device *dev) gfar_interrupt(priv->gfargrp[i].interruptTransmit, &priv->gfargrp[i]); enable_irq(priv->gfargrp[i].interruptTransmit); + } } } #endif -- cgit v1.2.3 From 512414b0bed0d376ac4d5ec1dd6f0b1a3551febc Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 25 Nov 2009 17:23:26 -0500 Subject: ath5k: enable EEPROM checksum check Without this we have no gaurantee of the integrity of the EEPROM and are likely to encounter a lot of bogus bug reports due to actual issues on the EEPROM. With the EEPROM checksum check in place we can easily rule those issues out. If you run patch during a revert *you* have a card with a busted EEPROM and only older kernel will support that concoction. This patch is a trade off between not accepitng bogus EEPROMs and avoiding bogus bug reports allowing developers to focus instead on real concrete issues. If stable keeps bogus bug reports because of a possibly busted EEPROM feel free to apply this there too. Tested on an AR5414 Cc: stable@kernel.org Cc: jirislaby@gmail.com Cc: akpm@linux-foundation.org Cc: rjw@sisk.pl Cc: me@bobcopeland.com Cc: david.quan@atheros.com Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/eeprom.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c index 644962adda97..791885262602 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -97,6 +97,7 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah) struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; int ret; u16 val; + u32 cksum, offset; /* * Read values from EEPROM and store them in the capability structure @@ -111,7 +112,6 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah) if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0) return 0; -#ifdef notyet /* * Validate the checksum of the EEPROM date. There are some * devices with invalid EEPROMs. @@ -124,7 +124,6 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah) ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum); return -EIO; } -#endif AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version), ee_ant_gain); -- cgit v1.2.3 From 49d7590ce494a971311ca55d8aaa6ea9a87b108f Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Wed, 9 Dec 2009 14:56:13 -0200 Subject: rtl8187: add radio led and fix warnings on suspend Michael Buesch reports that his rtl8187 gives warnings on suspend ("queueing ieee80211 work while going to suspend" warnings), as rtl8187 can call ieee80211_queue_delayed_work after mac80211 is suspended. This change enhances rtl8187 led code so we can avoid queuing work after mac80211 is suspended: now we register a radio led and make additional checks to ensure led is off/on properly as mac80211 wants. Signed-off-by: Herton Ronaldo Krzesinski Tested-by: Larry Finger Cc: Stable Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8187.h | 1 + drivers/net/wireless/rtl818x/rtl8187_leds.c | 68 ++++++++++++++++++++--------- drivers/net/wireless/rtl818x/rtl8187_leds.h | 2 + 3 files changed, 50 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index b1a24de0b7b0..6af0f3f71f3a 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h @@ -108,6 +108,7 @@ struct rtl8187_priv { struct delayed_work work; struct ieee80211_hw *dev; #ifdef CONFIG_RTL8187_LEDS + struct rtl8187_led led_radio; struct rtl8187_led led_tx; struct rtl8187_led led_rx; struct delayed_work led_on; diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c index cf8a4a40fdf6..ded44c045eb2 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.c +++ b/drivers/net/wireless/rtl818x/rtl8187_leds.c @@ -105,19 +105,36 @@ static void rtl8187_led_brightness_set(struct led_classdev *led_dev, struct rtl8187_led *led = container_of(led_dev, struct rtl8187_led, led_dev); struct ieee80211_hw *hw = led->dev; - struct rtl8187_priv *priv = hw->priv; + struct rtl8187_priv *priv; + static bool radio_on; - if (brightness == LED_OFF) { - ieee80211_queue_delayed_work(hw, &priv->led_off, 0); - /* The LED is off for 1/20 sec so that it just blinks. */ - ieee80211_queue_delayed_work(hw, &priv->led_on, HZ / 20); - } else - ieee80211_queue_delayed_work(hw, &priv->led_on, 0); + if (!hw) + return; + priv = hw->priv; + if (led->is_radio) { + if (brightness == LED_FULL) { + ieee80211_queue_delayed_work(hw, &priv->led_on, 0); + radio_on = true; + } else if (radio_on) { + radio_on = false; + cancel_delayed_work_sync(&priv->led_on); + ieee80211_queue_delayed_work(hw, &priv->led_off, 0); + } + } else if (radio_on) { + if (brightness == LED_OFF) { + ieee80211_queue_delayed_work(hw, &priv->led_off, 0); + /* The LED is off for 1/20 sec - it just blinks. */ + ieee80211_queue_delayed_work(hw, &priv->led_on, + HZ / 20); + } else + ieee80211_queue_delayed_work(hw, &priv->led_on, 0); + } } static int rtl8187_register_led(struct ieee80211_hw *dev, struct rtl8187_led *led, const char *name, - const char *default_trigger, u8 ledpin) + const char *default_trigger, u8 ledpin, + bool is_radio) { int err; struct rtl8187_priv *priv = dev->priv; @@ -128,6 +145,7 @@ static int rtl8187_register_led(struct ieee80211_hw *dev, return -EINVAL; led->dev = dev; led->ledpin = ledpin; + led->is_radio = is_radio; strncpy(led->name, name, sizeof(led->name)); led->led_dev.name = led->name; @@ -145,7 +163,11 @@ static int rtl8187_register_led(struct ieee80211_hw *dev, static void rtl8187_unregister_led(struct rtl8187_led *led) { + struct ieee80211_hw *hw = led->dev; + struct rtl8187_priv *priv = hw->priv; + led_classdev_unregister(&led->led_dev); + flush_delayed_work(&priv->led_off); led->dev = NULL; } @@ -182,34 +204,38 @@ void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid) INIT_DELAYED_WORK(&priv->led_on, led_turn_on); INIT_DELAYED_WORK(&priv->led_off, led_turn_off); + snprintf(name, sizeof(name), + "rtl8187-%s::radio", wiphy_name(dev->wiphy)); + err = rtl8187_register_led(dev, &priv->led_radio, name, + ieee80211_get_radio_led_name(dev), ledpin, true); + if (err) + return; + snprintf(name, sizeof(name), "rtl8187-%s::tx", wiphy_name(dev->wiphy)); err = rtl8187_register_led(dev, &priv->led_tx, name, - ieee80211_get_tx_led_name(dev), ledpin); + ieee80211_get_tx_led_name(dev), ledpin, false); if (err) - goto error; + goto err_tx; + snprintf(name, sizeof(name), "rtl8187-%s::rx", wiphy_name(dev->wiphy)); err = rtl8187_register_led(dev, &priv->led_rx, name, - ieee80211_get_rx_led_name(dev), ledpin); - if (!err) { - ieee80211_queue_delayed_work(dev, &priv->led_on, 0); + ieee80211_get_rx_led_name(dev), ledpin, false); + if (!err) return; - } - /* registration of RX LED failed - unregister TX */ + + /* registration of RX LED failed - unregister */ rtl8187_unregister_led(&priv->led_tx); -error: - /* If registration of either failed, cancel delayed work */ - cancel_delayed_work_sync(&priv->led_off); - cancel_delayed_work_sync(&priv->led_on); +err_tx: + rtl8187_unregister_led(&priv->led_radio); } void rtl8187_leds_exit(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; - /* turn the LED off before exiting */ - ieee80211_queue_delayed_work(dev, &priv->led_off, 0); + rtl8187_unregister_led(&priv->led_radio); rtl8187_unregister_led(&priv->led_rx); rtl8187_unregister_led(&priv->led_tx); cancel_delayed_work_sync(&priv->led_off); diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.h b/drivers/net/wireless/rtl818x/rtl8187_leds.h index a0332027aead..efe8041bdda4 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.h +++ b/drivers/net/wireless/rtl818x/rtl8187_leds.h @@ -47,6 +47,8 @@ struct rtl8187_led { u8 ledpin; /* The unique name string for this LED device. */ char name[RTL8187_LED_MAX_NAME_LEN + 1]; + /* If the LED is radio or tx/rx */ + bool is_radio; }; void rtl8187_leds_init(struct ieee80211_hw *dev, u16 code); -- cgit v1.2.3 From 214ac9a4ead6cb254451c09d9c8234a76693feb1 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 9 Dec 2009 13:25:56 -0600 Subject: b43: Remove reset after fatal DMA error As shown in Kernel Bugzilla #14761, doing a controller restart after a fatal DMA error does not accomplish anything other than consume the CPU on an affected system. Accordingly, substitute a meaningful message for the restart. Signed-off-by: Larry Finger Cc: Stable [2.6.32] Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 077480c4916a..19b4eae47b59 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -1784,7 +1784,10 @@ static void b43_do_interrupt_thread(struct b43_wldev *dev) dma_reason[0], dma_reason[1], dma_reason[2], dma_reason[3], dma_reason[4], dma_reason[5]); - b43_controller_restart(dev, "DMA error"); + b43err(dev->wl, "This device does not support DMA " + "on your system. Please use PIO instead.\n"); + b43err(dev->wl, "CONFIG_B43_FORCE_PIO must be set in " + "your kernel configuration.\n"); return; } if (merged_dma_reason & B43_DMAIRQ_NONFATALMASK) { -- cgit v1.2.3 From c557c15d83ea16e6c1a043c8eccb0f60bb19cb96 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 10 Dec 2009 13:50:56 -0800 Subject: net: niu uses crc32, so select CRC32 From: Randy Dunlap niu drivers uses crc32 functions, so it needs to select CRC32. niu.c:(.text+0x18a7f8): undefined reference to `crc32_le' Signed-off-by: Randy Dunlap Signed-off-by: David S. Miller --- drivers/net/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 0bbd5ae49862..a5be9ac6405c 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2696,6 +2696,7 @@ config NETXEN_NIC config NIU tristate "Sun Neptune 10Gbit Ethernet support" depends on PCI + select CRC32 help This enables support for cards based upon Sun's Neptune chipset. -- cgit v1.2.3 From b338682dc5c20e8ff986e58407bdb6e3a3e3f0a3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Dec 2009 05:12:01 +0000 Subject: net: Fix Yukon-2 Optima TCP offload setup Fix the TCP offload setup for Yukon-2 Optima. It requires SKY2_HW_NE_LE flag unlike Ultra 2. Signed-off-by: Takashi Iwai Signed-off-by: David S. Miller --- drivers/net/sky2.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 3943d89afb2b..050e6b58d4f9 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2968,8 +2968,13 @@ static int __devinit sky2_init(struct sky2_hw *hw) break; case CHIP_ID_YUKON_UL_2: + hw->flags = SKY2_HW_GIGABIT + | SKY2_HW_ADV_POWER_CTL; + break; + case CHIP_ID_YUKON_OPT: hw->flags = SKY2_HW_GIGABIT + | SKY2_HW_NEW_LE | SKY2_HW_ADV_POWER_CTL; break; -- cgit v1.2.3 From d66f0b20b2f8eac365fadf5ca492efe4ba539446 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Dec 2009 05:12:02 +0000 Subject: net: Add missing TST_CFG_WRITE bits around sky2_pci_write Add missing TST_CFG_WRITE bits around sky2_pci_write*() in Optima setup routines. Without the cfg-write bits, the driver may spew endless link-up messages through qlink irq. Signed-off-by: Takashi Iwai Signed-off-by: David S. Miller --- drivers/net/sky2.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 050e6b58d4f9..013c9f52ceaa 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2152,7 +2152,9 @@ static void sky2_qlink_intr(struct sky2_hw *hw) /* reset PHY Link Detect */ phy = sky2_pci_read16(hw, PSM_CONFIG_REG4); + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); sky2_pci_write16(hw, PSM_CONFIG_REG4, phy | 1); + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); sky2_link_up(sky2); } @@ -3082,6 +3084,7 @@ static void sky2_reset(struct sky2_hw *hw) reg <<= PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE; /* reset PHY Link Detect */ + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); sky2_pci_write16(hw, PSM_CONFIG_REG4, reg | PSM_CONFIG_REG4_RST_PHY_LINK_DETECT); sky2_pci_write16(hw, PSM_CONFIG_REG4, reg); @@ -3099,6 +3102,7 @@ static void sky2_reset(struct sky2_hw *hw) /* restore the PCIe Link Control register */ sky2_pci_write16(hw, cap + PCI_EXP_LNKCTL, reg); } + sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */ sky2_write32(hw, Y2_PEX_PHY_DATA, PEX_DB_ACCESS | (0x08UL << 16)); -- cgit v1.2.3 From fd0775bfc9feb036e6efb669133d644ae29e12b8 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 9 Dec 2009 03:40:04 +0000 Subject: smc91x: fix unused flags warnings on UP systems Local flags variables will be declared whenever these functions get used, but obviously on UP systems the flags parameter won't be touched. So add some dummy ops that get optimized away anyways to satisfy gcc's warnings. Signed-off-by: Mike Frysinger Acked-by: Nicolas Pitre Signed-off-by: David S. Miller --- drivers/net/smc91x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index ae4983a5127d..b0c92b60c925 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -534,9 +534,9 @@ static inline void smc_rcv(struct net_device *dev) #define smc_special_lock(lock, flags) spin_lock_irqsave(lock, flags) #define smc_special_unlock(lock, flags) spin_unlock_irqrestore(lock, flags) #else -#define smc_special_trylock(lock, flags) (1) -#define smc_special_lock(lock, flags) do { } while (0) -#define smc_special_unlock(lock, flags) do { } while (0) +#define smc_special_trylock(lock, flags) (flags == flags) +#define smc_special_lock(lock, flags) do { flags = 0; } while (0) +#define smc_special_unlock(lock, flags) do { flags = 0; } while (0) #endif /* -- cgit v1.2.3 From 5fc3441349936ea6f1f95a1ef17492223c76a695 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 10 Dec 2009 20:42:27 +0000 Subject: net: smc91x: Fix up type mismatch in smc_drv_resume(). smc_drv_resume() takes a struct device, while smc_enable_device() takes a platform device. This fixes up the smc_enable_device() callsite with the proper pointer. It's not obvious when this change was introduced, as git history doesn't go back that far. Presumably the resume code has always been broken in this fashion. Signed-off-by: Paul Mundt Signed-off-by: David S. Miller --- drivers/net/smc91x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index b0c92b60c925..ea4fae79d6ec 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -2387,7 +2387,7 @@ static int smc_drv_resume(struct device *dev) if (ndev) { struct smc_local *lp = netdev_priv(ndev); - smc_enable_device(dev); + smc_enable_device(pdev); if (netif_running(ndev)) { smc_reset(ndev); smc_enable(ndev); -- cgit v1.2.3 From 1bcdc32cf4d94442eba79599ce8438ea0b8f78b5 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Thu, 10 Dec 2009 15:40:57 +0000 Subject: cnic: Send delete command when shutting down iSCSI ring. This step is necessary on the bnx2x devices when restarting the iSCSI ring. Without it, the firmware can assert and cause bnx2x to report errors. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/cnic.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index d4c6e7fcff53..a2c33289326a 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -4183,6 +4183,12 @@ static void cnic_shutdown_rings(struct cnic_dev *dev) cnic_submit_kwqe_16(dev, RAMROD_CMD_ID_ETH_HALT, BNX2X_ISCSI_L2_CID, ETH_CONNECTION_TYPE, &l5_data); msleep(10); + + memset(&l5_data, 0, sizeof(l5_data)); + cnic_submit_kwqe_16(dev, RAMROD_CMD_ID_ETH_CFC_DEL, + BNX2X_ISCSI_L2_CID, ETH_CONNECTION_TYPE | + (1 << SPE_HDR_COMMON_RAMROD_SHIFT), &l5_data); + msleep(10); } } -- cgit v1.2.3 From 4e9c4fd3e7e022c7a5b8bb7cd06bf914b202cfea Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Thu, 10 Dec 2009 15:40:58 +0000 Subject: cnic: Zero out status block and Event Queue indices. To prevent stale indices from causing spurious events when restarting the bnx2x devices. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/cnic.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index a2c33289326a..4332b3a2fafb 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -1104,6 +1104,8 @@ static int cnic_alloc_bnx2x_resc(struct cnic_dev *dev) cp->bnx2x_status_blk = cp->status_blk; cp->bnx2x_def_status_blk = cp->ethdev->irq_arr[1].status_blk; + memset(cp->bnx2x_status_blk, 0, sizeof(struct host_status_block)); + cp->l2_rx_ring_size = 15; ret = cnic_alloc_l2_rings(dev, 4); @@ -4295,6 +4297,9 @@ static void cnic_stop_bnx2x_hw(struct cnic_dev *dev) offsetof(struct cstorm_status_block_c, index_values[HC_INDEX_C_ISCSI_EQ_CONS]), 0); + CNIC_WR(dev, BAR_CSTRORM_INTMEM + + CSTORM_ISCSI_EQ_CONS_OFFSET(cp->func, 0), 0); + CNIC_WR16(dev, cp->kcq_io_addr, 0); cnic_free_resc(dev); } -- cgit v1.2.3 From ccdddf500f2b1b8e88ac8e3d4dfc15cce9f73886 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 10 Dec 2009 09:03:37 +0000 Subject: cxgb3: Fixing EEH handlers After commit 4b77b0a2ba27d64f58f16d8d4d48d8319dda36ff ("PCI: Clear saved_state after the state has been restored"), the EEH is not working proplery on cxgb3. This patch fixes it, always saving the PCI state after a recovery, in order to allow further reoveries. Signed-off-by: Breno Leitao Signed-off-by: David S. Miller --- drivers/net/cxgb3/cxgb3_main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index cef3f882e2b6..89bec9c3c141 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -2860,6 +2860,7 @@ static int t3_reenable_adapter(struct adapter *adapter) } pci_set_master(adapter->pdev); pci_restore_state(adapter->pdev); + pci_save_state(adapter->pdev); /* Free sge resources */ t3_free_sge_resources(adapter); -- cgit v1.2.3 From bbb84619c378414118fd4f1778125cd246c71e53 Mon Sep 17 00:00:00 2001 From: Barry Song <21cnbao@gmail.com> Date: Thu, 10 Dec 2009 23:46:28 +0000 Subject: can: add the driver for Analog Devices Blackfin on-chip CAN controllers Signed-off-by: Barry Song <21cnbao@gmail.com> Signed-off-by: H.J. Oertel Signed-off-by: Wolfgang Grandegger Signed-off-by: David S. Miller --- drivers/net/can/Kconfig | 9 + drivers/net/can/Makefile | 1 + drivers/net/can/bfin_can.c | 783 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 793 insertions(+) create mode 100644 drivers/net/can/bfin_can.c (limited to 'drivers') diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index bb803fa1e6a7..8c485aad1b94 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -54,6 +54,15 @@ config CAN_MCP251X ---help--- Driver for the Microchip MCP251x SPI CAN controllers. +config CAN_BFIN + depends on CAN_DEV && (BF534 || BF536 || BF537 || BF538 || BF539 || BF54x) + tristate "Analog Devices Blackfin on-chip CAN" + ---help--- + Driver for the Analog Devices Blackfin on-chip CAN controllers + + To compile this driver as a module, choose M here: the + module will be called bfin_can. + source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/sja1000/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 56899fef1c6a..7a702f28d01c 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_AT91) += at91_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o obj-$(CONFIG_CAN_MCP251X) += mcp251x.o +obj-$(CONFIG_CAN_BFIN) += bfin_can.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c new file mode 100644 index 000000000000..c7fc1de28173 --- /dev/null +++ b/drivers/net/can/bfin_can.c @@ -0,0 +1,783 @@ +/* + * Blackfin On-Chip CAN Driver + * + * Copyright 2004-2009 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define DRV_NAME "bfin_can" +#define BFIN_CAN_TIMEOUT 100 + +/* + * transmit and receive channels + */ +#define TRANSMIT_CHL 24 +#define RECEIVE_STD_CHL 0 +#define RECEIVE_EXT_CHL 4 +#define RECEIVE_RTR_CHL 8 +#define RECEIVE_EXT_RTR_CHL 12 +#define MAX_CHL_NUMBER 32 + +/* + * bfin can registers layout + */ +struct bfin_can_mask_regs { + u16 aml; + u16 dummy1; + u16 amh; + u16 dummy2; +}; + +struct bfin_can_channel_regs { + u16 data[8]; + u16 dlc; + u16 dummy1; + u16 tsv; + u16 dummy2; + u16 id0; + u16 dummy3; + u16 id1; + u16 dummy4; +}; + +struct bfin_can_regs { + /* + * global control and status registers + */ + u16 mc1; /* offset 0 */ + u16 dummy1; + u16 md1; /* offset 4 */ + u16 rsv1[13]; + u16 mbtif1; /* offset 0x20 */ + u16 dummy2; + u16 mbrif1; /* offset 0x24 */ + u16 dummy3; + u16 mbim1; /* offset 0x28 */ + u16 rsv2[11]; + u16 mc2; /* offset 0x40 */ + u16 dummy4; + u16 md2; /* offset 0x44 */ + u16 dummy5; + u16 trs2; /* offset 0x48 */ + u16 rsv3[11]; + u16 mbtif2; /* offset 0x60 */ + u16 dummy6; + u16 mbrif2; /* offset 0x64 */ + u16 dummy7; + u16 mbim2; /* offset 0x68 */ + u16 rsv4[11]; + u16 clk; /* offset 0x80 */ + u16 dummy8; + u16 timing; /* offset 0x84 */ + u16 rsv5[3]; + u16 status; /* offset 0x8c */ + u16 dummy9; + u16 cec; /* offset 0x90 */ + u16 dummy10; + u16 gis; /* offset 0x94 */ + u16 dummy11; + u16 gim; /* offset 0x98 */ + u16 rsv6[3]; + u16 ctrl; /* offset 0xa0 */ + u16 dummy12; + u16 intr; /* offset 0xa4 */ + u16 rsv7[7]; + u16 esr; /* offset 0xb4 */ + u16 rsv8[37]; + + /* + * channel(mailbox) mask and message registers + */ + struct bfin_can_mask_regs msk[MAX_CHL_NUMBER]; /* offset 0x100 */ + struct bfin_can_channel_regs chl[MAX_CHL_NUMBER]; /* offset 0x200 */ +}; + +/* + * bfin can private data + */ +struct bfin_can_priv { + struct can_priv can; /* must be the first member */ + struct net_device *dev; + void __iomem *membase; + int rx_irq; + int tx_irq; + int err_irq; + unsigned short *pin_list; +}; + +/* + * bfin can timing parameters + */ +static struct can_bittiming_const bfin_can_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + /* + * Although the BRP field can be set to any value, it is recommended + * that the value be greater than or equal to 4, as restrictions + * apply to the bit timing configuration when BRP is less than 4. + */ + .brp_min = 4, + .brp_max = 1024, + .brp_inc = 1, +}; + +static int bfin_can_set_bittiming(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + struct can_bittiming *bt = &priv->can.bittiming; + u16 clk, timing; + + clk = bt->brp - 1; + timing = ((bt->sjw - 1) << 8) | (bt->prop_seg + bt->phase_seg1 - 1) | + ((bt->phase_seg2 - 1) << 4); + + /* + * If the SAM bit is set, the input signal is oversampled three times + * at the SCLK rate. + */ + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + timing |= SAM; + + bfin_write16(®->clk, clk); + bfin_write16(®->timing, timing); + + dev_info(dev->dev.parent, "setting CLOCK=0x%04x TIMING=0x%04x\n", + clk, timing); + + return 0; +} + +static void bfin_can_set_reset_mode(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + int timeout = BFIN_CAN_TIMEOUT; + int i; + + /* disable interrupts */ + bfin_write16(®->mbim1, 0); + bfin_write16(®->mbim2, 0); + bfin_write16(®->gim, 0); + + /* reset can and enter configuration mode */ + bfin_write16(®->ctrl, SRS | CCR); + SSYNC(); + bfin_write16(®->ctrl, CCR); + SSYNC(); + while (!(bfin_read16(®->ctrl) & CCA)) { + udelay(10); + if (--timeout == 0) { + dev_err(dev->dev.parent, + "fail to enter configuration mode\n"); + BUG(); + } + } + + /* + * All mailbox configurations are marked as inactive + * by writing to CAN Mailbox Configuration Registers 1 and 2 + * For all bits: 0 - Mailbox disabled, 1 - Mailbox enabled + */ + bfin_write16(®->mc1, 0); + bfin_write16(®->mc2, 0); + + /* Set Mailbox Direction */ + bfin_write16(®->md1, 0xFFFF); /* mailbox 1-16 are RX */ + bfin_write16(®->md2, 0); /* mailbox 17-32 are TX */ + + /* RECEIVE_STD_CHL */ + for (i = 0; i < 2; i++) { + bfin_write16(®->chl[RECEIVE_STD_CHL + i].id0, 0); + bfin_write16(®->chl[RECEIVE_STD_CHL + i].id1, AME); + bfin_write16(®->chl[RECEIVE_STD_CHL + i].dlc, 0); + bfin_write16(®->msk[RECEIVE_STD_CHL + i].amh, 0x1FFF); + bfin_write16(®->msk[RECEIVE_STD_CHL + i].aml, 0xFFFF); + } + + /* RECEIVE_EXT_CHL */ + for (i = 0; i < 2; i++) { + bfin_write16(®->chl[RECEIVE_EXT_CHL + i].id0, 0); + bfin_write16(®->chl[RECEIVE_EXT_CHL + i].id1, AME | IDE); + bfin_write16(®->chl[RECEIVE_EXT_CHL + i].dlc, 0); + bfin_write16(®->msk[RECEIVE_EXT_CHL + i].amh, 0x1FFF); + bfin_write16(®->msk[RECEIVE_EXT_CHL + i].aml, 0xFFFF); + } + + bfin_write16(®->mc2, BIT(TRANSMIT_CHL - 16)); + bfin_write16(®->mc1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL)); + SSYNC(); + + priv->can.state = CAN_STATE_STOPPED; +} + +static void bfin_can_set_normal_mode(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + int timeout = BFIN_CAN_TIMEOUT; + + /* + * leave configuration mode + */ + bfin_write16(®->ctrl, bfin_read16(®->ctrl) & ~CCR); + + while (bfin_read16(®->status) & CCA) { + udelay(10); + if (--timeout == 0) { + dev_err(dev->dev.parent, + "fail to leave configuration mode\n"); + BUG(); + } + } + + /* + * clear _All_ tx and rx interrupts + */ + bfin_write16(®->mbtif1, 0xFFFF); + bfin_write16(®->mbtif2, 0xFFFF); + bfin_write16(®->mbrif1, 0xFFFF); + bfin_write16(®->mbrif2, 0xFFFF); + + /* + * clear global interrupt status register + */ + bfin_write16(®->gis, 0x7FF); /* overwrites with '1' */ + + /* + * Initialize Interrupts + * - set bits in the mailbox interrupt mask register + * - global interrupt mask + */ + bfin_write16(®->mbim1, BIT(RECEIVE_STD_CHL) + BIT(RECEIVE_EXT_CHL)); + bfin_write16(®->mbim2, BIT(TRANSMIT_CHL - 16)); + + bfin_write16(®->gim, EPIM | BOIM | RMLIM); + SSYNC(); +} + +static void bfin_can_start(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + + /* enter reset mode */ + if (priv->can.state != CAN_STATE_STOPPED) + bfin_can_set_reset_mode(dev); + + /* leave reset mode */ + bfin_can_set_normal_mode(dev); +} + +static int bfin_can_set_mode(struct net_device *dev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + bfin_can_start(dev); + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int bfin_can_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + struct can_frame *cf = (struct can_frame *)skb->data; + u8 dlc = cf->can_dlc; + canid_t id = cf->can_id; + u8 *data = cf->data; + u16 val; + int i; + + netif_stop_queue(dev); + + /* fill id */ + if (id & CAN_EFF_FLAG) { + bfin_write16(®->chl[TRANSMIT_CHL].id0, id); + if (id & CAN_RTR_FLAG) + writew(((id & 0x1FFF0000) >> 16) | IDE | AME | RTR, + ®->chl[TRANSMIT_CHL].id1); + else + writew(((id & 0x1FFF0000) >> 16) | IDE | AME, + ®->chl[TRANSMIT_CHL].id1); + + } else { + if (id & CAN_RTR_FLAG) + writew((id << 2) | AME | RTR, + ®->chl[TRANSMIT_CHL].id1); + else + bfin_write16(®->chl[TRANSMIT_CHL].id1, + (id << 2) | AME); + } + + /* fill payload */ + for (i = 0; i < 8; i += 2) { + val = ((7 - i) < dlc ? (data[7 - i]) : 0) + + ((6 - i) < dlc ? (data[6 - i] << 8) : 0); + bfin_write16(®->chl[TRANSMIT_CHL].data[i], val); + } + + /* fill data length code */ + bfin_write16(®->chl[TRANSMIT_CHL].dlc, dlc); + + dev->trans_start = jiffies; + + can_put_echo_skb(skb, dev, 0); + + /* set transmit request */ + bfin_write16(®->trs2, BIT(TRANSMIT_CHL - 16)); + + return 0; +} + +static void bfin_can_rx(struct net_device *dev, u16 isrc) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + struct bfin_can_regs __iomem *reg = priv->membase; + struct can_frame *cf; + struct sk_buff *skb; + int obj; + int i; + u16 val; + + skb = alloc_can_skb(dev, &cf); + if (skb == NULL) + return; + + /* get id */ + if (isrc & BIT(RECEIVE_EXT_CHL)) { + /* extended frame format (EFF) */ + cf->can_id = ((bfin_read16(®->chl[RECEIVE_EXT_CHL].id1) + & 0x1FFF) << 16) + + bfin_read16(®->chl[RECEIVE_EXT_CHL].id0); + cf->can_id |= CAN_EFF_FLAG; + obj = RECEIVE_EXT_CHL; + } else { + /* standard frame format (SFF) */ + cf->can_id = (bfin_read16(®->chl[RECEIVE_STD_CHL].id1) + & 0x1ffc) >> 2; + obj = RECEIVE_STD_CHL; + } + if (bfin_read16(®->chl[obj].id1) & RTR) + cf->can_id |= CAN_RTR_FLAG; + + /* get data length code */ + cf->can_dlc = bfin_read16(®->chl[obj].dlc); + + /* get payload */ + for (i = 0; i < 8; i += 2) { + val = bfin_read16(®->chl[obj].data[i]); + cf->data[7 - i] = (7 - i) < cf->can_dlc ? val : 0; + cf->data[6 - i] = (6 - i) < cf->can_dlc ? (val >> 8) : 0; + } + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + struct net_device_stats *stats = &dev->stats; + struct can_frame *cf; + struct sk_buff *skb; + enum can_state state = priv->can.state; + + skb = alloc_can_err_skb(dev, &cf); + if (skb == NULL) + return -ENOMEM; + + if (isrc & RMLIS) { + /* data overrun interrupt */ + dev_dbg(dev->dev.parent, "data overrun interrupt\n"); + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + stats->rx_over_errors++; + stats->rx_errors++; + } + + if (isrc & BOIS) { + dev_dbg(dev->dev.parent, "bus-off mode interrupt\n"); + state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + can_bus_off(dev); + } + + if (isrc & EPIS) { + /* error passive interrupt */ + dev_dbg(dev->dev.parent, "error passive interrupt\n"); + state = CAN_STATE_ERROR_PASSIVE; + } + + if ((isrc & EWTIS) || (isrc & EWRIS)) { + dev_dbg(dev->dev.parent, + "Error Warning Transmit/Receive Interrupt\n"); + state = CAN_STATE_ERROR_WARNING; + } + + if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING || + state == CAN_STATE_ERROR_PASSIVE)) { + u16 cec = bfin_read16(®->cec); + u8 rxerr = cec; + u8 txerr = cec >> 8; + + cf->can_id |= CAN_ERR_CRTL; + if (state == CAN_STATE_ERROR_WARNING) { + priv->can.can_stats.error_warning++; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + } else { + priv->can.can_stats.error_passive++; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + } + } + + if (status) { + priv->can.can_stats.bus_error++; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + if (status & BEF) + cf->data[2] |= CAN_ERR_PROT_BIT; + else if (status & FER) + cf->data[2] |= CAN_ERR_PROT_FORM; + else if (status & SER) + cf->data[2] |= CAN_ERR_PROT_STUFF; + else + cf->data[2] |= CAN_ERR_PROT_UNSPEC; + } + + priv->can.state = state; + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 0; +} + +irqreturn_t bfin_can_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + struct net_device_stats *stats = &dev->stats; + u16 status, isrc; + + if ((irq == priv->tx_irq) && bfin_read16(®->mbtif2)) { + /* transmission complete interrupt */ + bfin_write16(®->mbtif2, 0xFFFF); + stats->tx_packets++; + stats->tx_bytes += bfin_read16(®->chl[TRANSMIT_CHL].dlc); + can_get_echo_skb(dev, 0); + netif_wake_queue(dev); + } else if ((irq == priv->rx_irq) && bfin_read16(®->mbrif1)) { + /* receive interrupt */ + isrc = bfin_read16(®->mbrif1); + bfin_write16(®->mbrif1, 0xFFFF); + bfin_can_rx(dev, isrc); + } else if ((irq == priv->err_irq) && bfin_read16(®->gis)) { + /* error interrupt */ + isrc = bfin_read16(®->gis); + status = bfin_read16(®->esr); + bfin_write16(®->gis, 0x7FF); + bfin_can_err(dev, isrc, status); + } else { + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int bfin_can_open(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + int err; + + /* set chip into reset mode */ + bfin_can_set_reset_mode(dev); + + /* common open */ + err = open_candev(dev); + if (err) + goto exit_open; + + /* register interrupt handler */ + err = request_irq(priv->rx_irq, &bfin_can_interrupt, 0, + "bfin-can-rx", dev); + if (err) + goto exit_rx_irq; + err = request_irq(priv->tx_irq, &bfin_can_interrupt, 0, + "bfin-can-tx", dev); + if (err) + goto exit_tx_irq; + err = request_irq(priv->err_irq, &bfin_can_interrupt, 0, + "bfin-can-err", dev); + if (err) + goto exit_err_irq; + + bfin_can_start(dev); + + netif_start_queue(dev); + + return 0; + +exit_err_irq: + free_irq(priv->tx_irq, dev); +exit_tx_irq: + free_irq(priv->rx_irq, dev); +exit_rx_irq: + close_candev(dev); +exit_open: + return err; +} + +static int bfin_can_close(struct net_device *dev) +{ + struct bfin_can_priv *priv = netdev_priv(dev); + + netif_stop_queue(dev); + bfin_can_set_reset_mode(dev); + + close_candev(dev); + + free_irq(priv->rx_irq, dev); + free_irq(priv->tx_irq, dev); + free_irq(priv->err_irq, dev); + + return 0; +} + +struct net_device *alloc_bfin_candev(void) +{ + struct net_device *dev; + struct bfin_can_priv *priv; + + dev = alloc_candev(sizeof(*priv)); + if (!dev) + return NULL; + + priv = netdev_priv(dev); + + priv->dev = dev; + priv->can.bittiming_const = &bfin_can_bittiming_const; + priv->can.do_set_bittiming = bfin_can_set_bittiming; + priv->can.do_set_mode = bfin_can_set_mode; + + return dev; +} + +static const struct net_device_ops bfin_can_netdev_ops = { + .ndo_open = bfin_can_open, + .ndo_stop = bfin_can_close, + .ndo_start_xmit = bfin_can_start_xmit, +}; + +static int __devinit bfin_can_probe(struct platform_device *pdev) +{ + int err; + struct net_device *dev; + struct bfin_can_priv *priv; + struct resource *res_mem, *rx_irq, *tx_irq, *err_irq; + unsigned short *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data provided!\n"); + err = -EINVAL; + goto exit; + } + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rx_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + tx_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + err_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (!res_mem || !rx_irq || !tx_irq || !err_irq) { + err = -EINVAL; + goto exit; + } + + if (!request_mem_region(res_mem->start, resource_size(res_mem), + dev_name(&pdev->dev))) { + err = -EBUSY; + goto exit; + } + + /* request peripheral pins */ + err = peripheral_request_list(pdata, dev_name(&pdev->dev)); + if (err) + goto exit_mem_release; + + dev = alloc_bfin_candev(); + if (!dev) { + err = -ENOMEM; + goto exit_peri_pin_free; + } + + priv = netdev_priv(dev); + priv->membase = (void __iomem *)res_mem->start; + priv->rx_irq = rx_irq->start; + priv->tx_irq = tx_irq->start; + priv->err_irq = err_irq->start; + priv->pin_list = pdata; + priv->can.clock.freq = get_sclk(); + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + dev->flags |= IFF_ECHO; /* we support local echo */ + dev->netdev_ops = &bfin_can_netdev_ops; + + bfin_can_set_reset_mode(dev); + + err = register_candev(dev); + if (err) { + dev_err(&pdev->dev, "registering failed (err=%d)\n", err); + goto exit_candev_free; + } + + dev_info(&pdev->dev, + "%s device registered" + "(®_base=%p, rx_irq=%d, tx_irq=%d, err_irq=%d, sclk=%d)\n", + DRV_NAME, (void *)priv->membase, priv->rx_irq, + priv->tx_irq, priv->err_irq, priv->can.clock.freq); + return 0; + +exit_candev_free: + free_candev(dev); +exit_peri_pin_free: + peripheral_free_list(pdata); +exit_mem_release: + release_mem_region(res_mem->start, resource_size(res_mem)); +exit: + return err; +} + +static int __devexit bfin_can_remove(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct bfin_can_priv *priv = netdev_priv(dev); + struct resource *res; + + bfin_can_set_reset_mode(dev); + + unregister_candev(dev); + + dev_set_drvdata(&pdev->dev, NULL); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + peripheral_free_list(priv->pin_list); + + free_candev(dev); + return 0; +} + +#ifdef CONFIG_PM +static int bfin_can_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + int timeout = BFIN_CAN_TIMEOUT; + + if (netif_running(dev)) { + /* enter sleep mode */ + bfin_write16(®->ctrl, bfin_read16(®->ctrl) | SMR); + SSYNC(); + while (!(bfin_read16(®->intr) & SMACK)) { + udelay(10); + if (--timeout == 0) { + dev_err(dev->dev.parent, + "fail to enter sleep mode\n"); + BUG(); + } + } + } + + return 0; +} + +static int bfin_can_resume(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct bfin_can_priv *priv = netdev_priv(dev); + struct bfin_can_regs __iomem *reg = priv->membase; + + if (netif_running(dev)) { + /* leave sleep mode */ + bfin_write16(®->intr, 0); + SSYNC(); + } + + return 0; +} +#else +#define bfin_can_suspend NULL +#define bfin_can_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver bfin_can_driver = { + .probe = bfin_can_probe, + .remove = __devexit_p(bfin_can_remove), + .suspend = bfin_can_suspend, + .resume = bfin_can_resume, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init bfin_can_init(void) +{ + return platform_driver_register(&bfin_can_driver); +} +module_init(bfin_can_init); + +static void __exit bfin_can_exit(void) +{ + platform_driver_unregister(&bfin_can_driver); +} +module_exit(bfin_can_exit); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Blackfin on-chip CAN netdevice driver"); -- cgit v1.2.3