* Re: [PATCH 7/9] mac80211: add flags for STBC (Space-Time Block Coding)
From: Johannes Berg @ 2010-04-18 15:53 UTC (permalink / raw)
To: Felix Fietkau; +Cc: linux-wireless, linville, lrodriguez
In-Reply-To: <1271602602-8538-7-git-send-email-nbd@openwrt.org>
On Sun, 2010-04-18 at 16:56 +0200, Felix Fietkau wrote:
> Signed-off-by: Felix Fietkau <nbd@openwrt.org>
> ---
> include/linux/ieee80211.h | 1 +
> include/net/mac80211.h | 3 +++
> 2 files changed, 4 insertions(+), 0 deletions(-)
>
> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
> index 1252ba1..97b2eae 100644
> --- a/include/linux/ieee80211.h
> +++ b/include/linux/ieee80211.h
> @@ -876,6 +876,7 @@ struct ieee80211_ht_cap {
> #define IEEE80211_HT_CAP_SGI_40 0x0040
> #define IEEE80211_HT_CAP_TX_STBC 0x0080
> #define IEEE80211_HT_CAP_RX_STBC 0x0300
> +#define IEEE80211_HT_CAP_RX_STBC_SHIFT 8
> #define IEEE80211_HT_CAP_DELAY_BA 0x0400
> #define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
> #define IEEE80211_HT_CAP_DSSSCCK40 0x1000
> diff --git a/include/net/mac80211.h b/include/net/mac80211.h
> index 75056dd..eadf794 100644
> --- a/include/net/mac80211.h
> +++ b/include/net/mac80211.h
> @@ -275,6 +275,8 @@ struct ieee80211_bss_conf {
> * MLME command (internal to mac80211 to figure out whether to send TX
> * status to user space)
> * @IEEE80211_TX_CTL_LDPC: tells the driver to use LDPC for this frame
> + * @IEEE80211_TX_CTL_STBC: tells the driver to use Space-Time Block Coding
> + * (STBC) for this frame.
> */
> enum mac80211_tx_control_flags {
> IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0),
> @@ -299,6 +301,7 @@ enum mac80211_tx_control_flags {
> IEEE80211_TX_INTFL_HAS_RADIOTAP = BIT(20),
> IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21),
> IEEE80211_TX_CTL_LDPC = BIT(22),
> + IEEE80211_TX_CTL_STBC = BIT(23),
What if the # of streams is different? That doesn't look sufficient.
johannes
^ permalink raw reply
* [PATCH 2/9] ath9k: clean up tx buffer handling
From: Felix Fietkau @ 2010-04-18 14:56 UTC (permalink / raw)
To: linux-wireless; +Cc: linville, lrodriguez
In-Reply-To: <1271602602-8538-1-git-send-email-nbd@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
drivers/net/wireless/ath/ath9k/xmit.c | 67 ++++++++++++++++-----------------
1 files changed, 32 insertions(+), 35 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index cac178a..fcbb4a8 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -261,19 +261,40 @@ static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq,
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_RETRY);
}
-static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf)
+static struct ath_buf *ath_tx_get_buffer(struct ath_softc *sc)
{
- struct ath_buf *tbf;
+ struct ath_buf *bf = NULL;
spin_lock_bh(&sc->tx.txbuflock);
- if (WARN_ON(list_empty(&sc->tx.txbuf))) {
+
+ if (unlikely(list_empty(&sc->tx.txbuf))) {
spin_unlock_bh(&sc->tx.txbuflock);
return NULL;
}
- tbf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list);
- list_del(&tbf->list);
+
+ bf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list);
+ list_del(&bf->list);
+
spin_unlock_bh(&sc->tx.txbuflock);
+ return bf;
+}
+
+static void ath_tx_return_buffer(struct ath_softc *sc, struct ath_buf *bf)
+{
+ spin_lock_bh(&sc->tx.txbuflock);
+ list_add_tail(&bf->list, &sc->tx.txbuf);
+ spin_unlock_bh(&sc->tx.txbuflock);
+}
+
+static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf)
+{
+ struct ath_buf *tbf;
+
+ tbf = ath_tx_get_buffer(sc);
+ if (WARN_ON(!tbf))
+ return NULL;
+
ATH_TXBUF_RESET(tbf);
tbf->aphy = bf->aphy;
@@ -1081,9 +1102,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
list_del(&bf->list);
spin_unlock_bh(&txq->axq_lock);
- spin_lock_bh(&sc->tx.txbuflock);
- list_add_tail(&bf->list, &sc->tx.txbuf);
- spin_unlock_bh(&sc->tx.txbuflock);
+ ath_tx_return_buffer(sc, bf);
continue;
}
}
@@ -1325,25 +1344,6 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
txq->axq_depth++;
}
-static struct ath_buf *ath_tx_get_buffer(struct ath_softc *sc)
-{
- struct ath_buf *bf = NULL;
-
- spin_lock_bh(&sc->tx.txbuflock);
-
- if (unlikely(list_empty(&sc->tx.txbuf))) {
- spin_unlock_bh(&sc->tx.txbuflock);
- return NULL;
- }
-
- bf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list);
- list_del(&bf->list);
-
- spin_unlock_bh(&sc->tx.txbuflock);
-
- return bf;
-}
-
static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
struct list_head *bf_head,
struct ath_tx_control *txctl)
@@ -1825,9 +1825,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
}
spin_unlock_bh(&txq->axq_lock);
- spin_lock_bh(&sc->tx.txbuflock);
- list_add_tail(&bf->list, &sc->tx.txbuf);
- spin_unlock_bh(&sc->tx.txbuflock);
+ ath_tx_return_buffer(sc, bf);
return r;
}
@@ -2141,13 +2139,12 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
txq->axq_depth--;
txok = !(ts.ts_status & ATH9K_TXERR_MASK);
txq->axq_tx_inprogress = false;
+ if (bf_held)
+ list_del(&bf_held->list);
spin_unlock_bh(&txq->axq_lock);
- if (bf_held) {
- spin_lock_bh(&sc->tx.txbuflock);
- list_move_tail(&bf_held->list, &sc->tx.txbuf);
- spin_unlock_bh(&sc->tx.txbuflock);
- }
+ if (bf_held)
+ ath_tx_return_buffer(sc, bf_held);
if (!bf_isampdu(bf)) {
/*
--
1.6.4.2
^ permalink raw reply related
* [PATCH 8/9] ath9k: add support for Tx and Rx STBC
From: Felix Fietkau @ 2010-04-18 14:56 UTC (permalink / raw)
To: linux-wireless; +Cc: linville, lrodriguez
In-Reply-To: <1271602602-8538-7-git-send-email-nbd@openwrt.org>
Supported only for single stream rates by the hardware
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
drivers/net/wireless/ath/ath9k/init.c | 6 ++++++
drivers/net/wireless/ath/ath9k/mac.h | 8 +++++++-
drivers/net/wireless/ath/ath9k/xmit.c | 2 ++
3 files changed, 15 insertions(+), 1 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 2c0630e..8c79548 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -216,6 +216,12 @@ static void setup_ht_cap(struct ath_softc *sc,
else
max_streams = 2;
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ if (max_streams >= 2)
+ ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
+ ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+ }
+
/* set up supported mcs set */
memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
tx_streams = count_streams(common->tx_chainmask, max_streams);
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 66d0d5e..00f3e0c 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -37,6 +37,8 @@
AR_2040_##_index : 0) \
|((_series)[_index].RateFlags & ATH9K_RATESERIES_HALFGI ? \
AR_GI##_index : 0) \
+ |((_series)[_index].RateFlags & ATH9K_RATESERIES_STBC ? \
+ AR_STBC##_index : 0) \
|SM((_series)[_index].ChSel, AR_ChainSel##_index))
#define CCK_SIFS_TIME 10
@@ -434,7 +436,10 @@ struct ar5416_desc {
#define AR_ChainSel3_S 17
#define AR_RTSCTSRate 0x0ff00000
#define AR_RTSCTSRate_S 20
-#define AR_TxCtlRsvd70 0xf0000000
+#define AR_STBC0 0x10000000
+#define AR_STBC1 0x20000000
+#define AR_STBC2 0x40000000
+#define AR_STBC3 0x80000000
#define AR_TxRSSIAnt00 0x000000ff
#define AR_TxRSSIAnt00_S 0
@@ -647,6 +652,7 @@ enum ath9k_rx_filter {
#define ATH9K_RATESERIES_RTS_CTS 0x0001
#define ATH9K_RATESERIES_2040 0x0002
#define ATH9K_RATESERIES_HALFGI 0x0004
+#define ATH9K_RATESERIES_STBC 0x0008
struct ath9k_11n_rate_series {
u32 Tries;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 2237658..b0d345a 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1607,6 +1607,8 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
series[i].Rate = rix | 0x80;
series[i].PktDuration = ath_pkt_duration(sc, rix, bf,
is_40, is_sgi, is_sp);
+ if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
+ series[i].RateFlags |= ATH9K_RATESERIES_STBC;
continue;
}
--
1.6.4.2
^ permalink raw reply related
* [PATCH 5/9] ath9k: reduce the bits_per_symbol table size, support more streams
From: Felix Fietkau @ 2010-04-18 14:56 UTC (permalink / raw)
To: linux-wireless; +Cc: linville, lrodriguez
In-Reply-To: <1271602602-8538-4-git-send-email-nbd@openwrt.org>
Instead of increasing bits_per_symbol for supporting more streams, keep
it single-stream only and multiply the values by the numer of streams.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
---
drivers/net/wireless/ath/ath9k/xmit.c | 19 ++++++-------------
1 files changed, 6 insertions(+), 13 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 4078982..2237658 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -34,7 +34,7 @@
#define OFDM_SIFS_TIME 16
-static u32 bits_per_symbol[][2] = {
+static u16 bits_per_symbol[][2] = {
/* 20MHz 40MHz */
{ 26, 54 }, /* 0: BPSK */
{ 52, 108 }, /* 1: QPSK 1/2 */
@@ -44,14 +44,6 @@ static u32 bits_per_symbol[][2] = {
{ 208, 432 }, /* 5: 64-QAM 2/3 */
{ 234, 486 }, /* 6: 64-QAM 3/4 */
{ 260, 540 }, /* 7: 64-QAM 5/6 */
- { 52, 108 }, /* 8: BPSK */
- { 104, 216 }, /* 9: QPSK 1/2 */
- { 156, 324 }, /* 10: QPSK 3/4 */
- { 208, 432 }, /* 11: 16-QAM 1/2 */
- { 312, 648 }, /* 12: 16-QAM 3/4 */
- { 416, 864 }, /* 13: 64-QAM 2/3 */
- { 468, 972 }, /* 14: 64-QAM 3/4 */
- { 520, 1080 }, /* 15: 64-QAM 5/6 */
};
#define IS_HT_RATE(_rate) ((_rate) & 0x80)
@@ -601,7 +593,7 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
u32 nsymbits, nsymbols;
u16 minlen;
u8 flags, rix;
- int width, half_gi, ndelim, mindelim;
+ int width, streams, half_gi, ndelim, mindelim;
/* Select standard number of delimiters based on frame length alone */
ndelim = ATH_AGGR_GET_NDELIM(frmlen);
@@ -641,7 +633,8 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid,
if (nsymbols == 0)
nsymbols = 1;
- nsymbits = bits_per_symbol[rix][width];
+ streams = HT_RC_2_STREAMS(rix);
+ nsymbits = bits_per_symbol[rix % 8][width] * streams;
minlen = (nsymbols * nsymbits) / BITS_PER_BYTE;
if (frmlen < minlen) {
@@ -1533,8 +1526,9 @@ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf,
pktlen = bf_isaggr(bf) ? bf->bf_al : bf->bf_frmlen;
/* find number of symbols: PLCP + data */
+ streams = HT_RC_2_STREAMS(rix);
nbits = (pktlen << 3) + OFDM_PLCP_BITS;
- nsymbits = bits_per_symbol[rix][width];
+ nsymbits = bits_per_symbol[rix % 8][width] * streams;
nsymbols = (nbits + nsymbits - 1) / nsymbits;
if (!half_gi)
@@ -1543,7 +1537,6 @@ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf,
duration = SYMBOL_TIME_HALFGI(nsymbols);
/* addup duration for legacy/ht training and signal fields */
- streams = HT_RC_2_STREAMS(rix);
duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
return duration;
--
1.6.4.2
^ permalink raw reply related
* Re: [PATCH 7/9] mac80211: add flags for STBC (Space-Time Block Coding)
From: Felix Fietkau @ 2010-04-18 16:05 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, linville, lrodriguez
In-Reply-To: <1271606009.3873.0.camel@jlt3.sipsolutions.net>
On 2010-04-18 5:53 PM, Johannes Berg wrote:
> On Sun, 2010-04-18 at 16:56 +0200, Felix Fietkau wrote:
>> Signed-off-by: Felix Fietkau <nbd@openwrt.org>
>> ---
>> include/linux/ieee80211.h | 1 +
>> include/net/mac80211.h | 3 +++
>> 2 files changed, 4 insertions(+), 0 deletions(-)
>>
>> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
>> index 1252ba1..97b2eae 100644
>> --- a/include/linux/ieee80211.h
>> +++ b/include/linux/ieee80211.h
>> @@ -876,6 +876,7 @@ struct ieee80211_ht_cap {
>> #define IEEE80211_HT_CAP_SGI_40 0x0040
>> #define IEEE80211_HT_CAP_TX_STBC 0x0080
>> #define IEEE80211_HT_CAP_RX_STBC 0x0300
>> +#define IEEE80211_HT_CAP_RX_STBC_SHIFT 8
>> #define IEEE80211_HT_CAP_DELAY_BA 0x0400
>> #define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
>> #define IEEE80211_HT_CAP_DSSSCCK40 0x1000
>> diff --git a/include/net/mac80211.h b/include/net/mac80211.h
>> index 75056dd..eadf794 100644
>> --- a/include/net/mac80211.h
>> +++ b/include/net/mac80211.h
>> @@ -275,6 +275,8 @@ struct ieee80211_bss_conf {
>> * MLME command (internal to mac80211 to figure out whether to send TX
>> * status to user space)
>> * @IEEE80211_TX_CTL_LDPC: tells the driver to use LDPC for this frame
>> + * @IEEE80211_TX_CTL_STBC: tells the driver to use Space-Time Block Coding
>> + * (STBC) for this frame.
>> */
>> enum mac80211_tx_control_flags {
>> IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0),
>> @@ -299,6 +301,7 @@ enum mac80211_tx_control_flags {
>> IEEE80211_TX_INTFL_HAS_RADIOTAP = BIT(20),
>> IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21),
>> IEEE80211_TX_CTL_LDPC = BIT(22),
>> + IEEE80211_TX_CTL_STBC = BIT(23),
>
> What if the # of streams is different? That doesn't look sufficient.
Hm, you're right. I initially thought the combination of the MCS index
and the STBC flag would be enough, but there are still some corner cases.
Want me to use BIT(23)|BIT(24) for this and add a shift, as with the HT
capability?
- Felix
^ permalink raw reply
* [PATCH] mac80211: fix typo in comments
From: Daniel Halperin @ 2010-04-18 16:28 UTC (permalink / raw)
To: linux-wireless
The flag is called IEEE80211_TX_STAT_AMPDU rather than using the whole word
STATUS.
Signed-off-by: Daniel Halperin <dhalperi@cs.washington.edu>
---
include/net/mac80211.h | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index dcf3c5f..ecf2273 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -395,11 +395,11 @@ struct ieee80211_tx_rate {
* @status: union for status data
* @driver_data: array of driver_data pointers
* @ampdu_ack_len: number of acked aggregated frames.
- * relevant only if IEEE80211_TX_STATUS_AMPDU was set.
+ * relevant only if IEEE80211_TX_STAT_AMPDU was set.
* @ampdu_ack_map: block ack bit map for the aggregation.
- * relevant only if IEEE80211_TX_STATUS_AMPDU was set.
+ * relevant only if IEEE80211_TX_STAT_AMPDU was set.
* @ampdu_len: number of aggregated frames.
- * relevant only if IEEE80211_TX_STATUS_AMPDU was set.
+ * relevant only if IEEE80211_TX_STAT_AMPDU was set.
* @ack_signal: signal strength of the ACK frame
*/
struct ieee80211_tx_info {
--
1.5.4.3
^ permalink raw reply related
* 3dsp shared a USB Wifi Dongle Source Code
From: João Neto @ 2010-04-18 18:02 UTC (permalink / raw)
To: linux-wireless
Hello
Recently 3dsp has shared the source code of your Wifi and Bluetooth USB card:
ftp://3dsp_lpkt_usb:m4rt9s@3dsp.com.cn/
in the folder "Open Source"
--
Joao Neto - Web Developer | PHP Zend Certified Engineer
@joao_neto | http://www.joaoneto.blog.br
^ permalink raw reply
* Re: 3dsp shared a USB Wifi Dongle Source Code
From: Gábor Stefanik @ 2010-04-18 18:35 UTC (permalink / raw)
To: João Neto; +Cc: linux-wireless
In-Reply-To: <m2y58f5e62a1004181102gd58a214at3419e7a4b96c099c@mail.gmail.com>
There is no license provided in the file. In addition, files in the
driver_src/wlan directory explicitly forbid any form of distribution
or modification, in source, binary, or any other form. Even quoting
comments, and thus the license block in the files, is forbidden!
On Sun, Apr 18, 2010 at 8:02 PM, João Neto <joao.gsneto@gmail.com> wrote:
> Hello
>
> Recently 3dsp has shared the source code of your Wifi and Bluetooth USB card:
>
>
> ftp://3dsp_lpkt_usb:m4rt9s@3dsp.com.cn/
>
> in the folder "Open Source"
>
> --
> Joao Neto - Web Developer | PHP Zend Certified Engineer
> @joao_neto | http://www.joaoneto.blog.br
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Vista: [V]iruses, [I]ntruders, [S]pyware, [T]rojans and [A]dware. :-)
^ permalink raw reply
* Re: [PATCH 1/2] ath5k: Use high bitrates for ACK/CTS
From: Bruno Randolf @ 2010-04-19 0:24 UTC (permalink / raw)
To: Stanislaw Gruszka; +Cc: linville, ath5k-devel, linux-wireless
In-Reply-To: <20100416155907.6e33c722@dhcp-lab-109.englab.brq.redhat.com>
On Friday 16 April 2010 22:59:07 Stanislaw Gruszka wrote:
> On Mon, 12 Apr 2010 16:38:47 +0900
>
> Bruno Randolf <br1@einfach.org> wrote:
> > There was a confusion in the usage of the bits AR5K_STA_ID1_ACKCTS_6MB
> > and AR5K_STA_ID1_BASE_RATE_11B. If they are set (1), we will get lower
> > bitrates for ACK and CTS. Therefore ath5k_hw_set_ack_bitrate_high(ah,
> > false) actually resulted in high bitrates, which i think is what we want
> > anyways. Cleared the confusion and added some documentation.
>
> I thought ACK and other control frames have to be modulated at slow/robust
> bitrates, but can't remember where I read that ...
this has been discussed on ath5k-devel before. i'm copying it here. please let
me know if i missed something...
(https://lists.ath5k.org/pipermail/ath5k-devel/2010-March/003391.html)
control frames have to be sent at one of the lower bitrates - one of the
"basic rates". in G mode these are 1, 2, 5.5, 11, 6, 12, 24Mbps.
this is from the 802.11g spec:
"a STA responding to a received frame shall transmit its Control Response
(either CTS or ACK) frames at the highest rate in the BSSBasicRateSet that is
less than or equal to the rate of the immediately previous frame in the frame
exchange sequence (as defined in 9.7)"
basically we have to make a tradeoff here: using lower rates for ACK/CTS will
improve reliability, but lower performance. for example i could get only
10Mbps truput with ACK/CTS at the lowest bitrates and RTS/CTS enabled
(iwconfig wlan0 rts 250) and i can get more than 20Mbps with higher bitrates.
so i think it's worth to use high bitrates. actually also this should be
tuneable for the user, too...
bruno
^ permalink raw reply
* Re: [RFC PATCH] mac80211: Prevent running sta_cleanup timer unnecessarily
From: Juuso Oikarinen @ 2010-04-19 4:56 UTC (permalink / raw)
To: ext Johannes Berg; +Cc: linux-wireless@vger.kernel.org
In-Reply-To: <1271449060.4184.1.camel@jlt3.sipsolutions.net>
On Fri, 2010-04-16 at 22:17 +0200, ext Johannes Berg wrote:
> On Fri, 2010-04-16 at 10:35 +0300, Juuso Oikarinen wrote:
> > The sta_cleanup timer is used to periodically expire buffered frames from the
> > tx buf. The timer is executing periodically, regardless of the need for it.
> > This is wasting resources.
> >
> > Fix this simply by not restarting the sta_cleanup timer if the tx buffer was
> > empty. Restart the timer when there is some more tx-traffic.
> >
> > Cc: Janne Ylälehto <janne.ylalehto@nokia.com>
> > Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
> > ---
> > net/mac80211/sta_info.c | 13 ++++++++++---
> > net/mac80211/tx.c | 7 +++++++
> > 2 files changed, 17 insertions(+), 3 deletions(-)
> >
> > diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
> > index ff0eb94..3de7a22 100644
> > --- a/net/mac80211/sta_info.c
> > +++ b/net/mac80211/sta_info.c
> > @@ -575,7 +575,7 @@ static int sta_info_buffer_expired(struct sta_info *sta,
> > }
> >
> >
> > -static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
> > +static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
> > struct sta_info *sta)
> > {
> > unsigned long flags;
> > @@ -583,7 +583,7 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
> > struct ieee80211_sub_if_data *sdata;
> >
> > if (skb_queue_empty(&sta->ps_tx_buf))
> > - return;
> > + return false;
> >
> > for (;;) {
> > spin_lock_irqsave(&sta->ps_tx_buf.lock, flags);
> > @@ -608,6 +608,8 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
> > if (skb_queue_empty(&sta->ps_tx_buf))
> > sta_info_clear_tim_bit(sta);
> > }
> > +
> > + return true;
> > }
> >
> > static int __must_check __sta_info_destroy(struct sta_info *sta)
> > @@ -755,15 +757,20 @@ static void sta_info_cleanup(unsigned long data)
> > {
> > struct ieee80211_local *local = (struct ieee80211_local *) data;
> > struct sta_info *sta;
> > + bool timer_needed = false;
> >
> > rcu_read_lock();
> > list_for_each_entry_rcu(sta, &local->sta_list, list)
> > - sta_info_cleanup_expire_buffered(local, sta);
> > + if (sta_info_cleanup_expire_buffered(local, sta))
> > + timer_needed = true;
> > rcu_read_unlock();
> >
> > if (local->quiescing)
> > return;
> >
> > + if (!timer_needed)
> > + return;
> > +
> > local->sta_cleanup.expires =
> > round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
> > add_timer(&local->sta_cleanup);
> > diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> > index 2cb7726..e2aa972 100644
> > --- a/net/mac80211/tx.c
> > +++ b/net/mac80211/tx.c
> > @@ -429,6 +429,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
> > struct sta_info *sta = tx->sta;
> > struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
> > struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
> > + struct ieee80211_local *local = tx->local;
> > u32 staflags;
> >
> > if (unlikely(!sta ||
> > @@ -476,6 +477,12 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
> > info->control.vif = &tx->sdata->vif;
> > info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
> > skb_queue_tail(&sta->ps_tx_buf, tx->skb);
> > +
> > + if (!timer_pending(&local->sta_cleanup))
> > + mod_timer(&local->sta_cleanup,
> > + round_jiffies(jiffies +
> > + STA_INFO_CLEANUP_INTERVAL));
> > +
>
> Why bother to check !timer_pending?
>
Otherwise, if we have some periodic TX going on with an interval less
than STA_INFO_CLEANUP_INTERVAL, the sta_cleanup could be delayed
considerably.
-Juuso
> johannes
>
^ permalink raw reply
* Re: [RFC PATCH] mac80211: Prevent running sta_cleanup timer unnecessarily
From: Johannes Berg @ 2010-04-19 5:57 UTC (permalink / raw)
To: Juuso Oikarinen; +Cc: linux-wireless@vger.kernel.org
In-Reply-To: <1271652964.6205.4211.camel@wimaxnb.nmp.nokia.com>
On Mon, 2010-04-19 at 07:56 +0300, Juuso Oikarinen wrote:
> > > --- a/net/mac80211/tx.c
> > > +++ b/net/mac80211/tx.c
> > > @@ -429,6 +429,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
> > > struct sta_info *sta = tx->sta;
> > > struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
> > > struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
> > > + struct ieee80211_local *local = tx->local;
> > > u32 staflags;
> > >
> > > if (unlikely(!sta ||
> > > @@ -476,6 +477,12 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
> > > info->control.vif = &tx->sdata->vif;
> > > info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
> > > skb_queue_tail(&sta->ps_tx_buf, tx->skb);
> > > +
> > > + if (!timer_pending(&local->sta_cleanup))
> > > + mod_timer(&local->sta_cleanup,
> > > + round_jiffies(jiffies +
> > > + STA_INFO_CLEANUP_INTERVAL));
> > > +
> >
> > Why bother to check !timer_pending?
> >
>
> Otherwise, if we have some periodic TX going on with an interval less
> than STA_INFO_CLEANUP_INTERVAL, the sta_cleanup could be delayed
> considerably.
Ok, makes sense, I didn't check the context there.
johannes
^ permalink raw reply
* Re: [PATCH 7/9] mac80211: add flags for STBC (Space-Time Block Coding)
From: Johannes Berg @ 2010-04-19 5:57 UTC (permalink / raw)
To: Felix Fietkau; +Cc: linux-wireless, linville, lrodriguez
In-Reply-To: <4BCB2DB3.3020700@openwrt.org>
On Sun, 2010-04-18 at 18:05 +0200, Felix Fietkau wrote:
> >> + * @IEEE80211_TX_CTL_STBC: tells the driver to use Space-Time Block Coding
> >> + * (STBC) for this frame.
> >> */
> >> enum mac80211_tx_control_flags {
> >> IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0),
> >> @@ -299,6 +301,7 @@ enum mac80211_tx_control_flags {
> >> IEEE80211_TX_INTFL_HAS_RADIOTAP = BIT(20),
> >> IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21),
> >> IEEE80211_TX_CTL_LDPC = BIT(22),
> >> + IEEE80211_TX_CTL_STBC = BIT(23),
> >
> > What if the # of streams is different? That doesn't look sufficient.
> Hm, you're right. I initially thought the combination of the MCS index
> and the STBC flag would be enough, but there are still some corner cases.
Hm actually I guess that should be sufficient? What corner case are you
thinking of?
johannes
^ permalink raw reply
* Re: regression: soft lockup with ath9k on master-2010-04-14
From: Kalle Valo @ 2010-04-19 6:29 UTC (permalink / raw)
To: John W. Linville; +Cc: linux-wireless, johannes
In-Reply-To: <20100415195812.GC3020@tuxdriver.com>
"John W. Linville" <linville@tuxdriver.com> writes:
> On Thu, Apr 15, 2010 at 10:44:52PM +0300, Kalle Valo wrote:
>> Hello,
>>
>> I just updated my laptop to latest wireless-testing and it everytime
>> soft lockups few seconds after association.
>>
>> I haven't updated wireless-testing for few days, so I can't say when
>> this bug was introduced.
>
> It might be useful to do a bisect. If you choose to do that, you
> might want to use wireless-next-2.6 instead, since that doesn't have
> the occasional pulls from Linus that make bisecting wireless-testing
> more painful.
Thanks for the tip, it helped a lot. My new laptop is really slow to
compile kernels :/
I bisected it finally and found the culprit:
66b0470aeef10a3b0f9a6a1c60d908b5a06c62ae is the first bad commit
commit 66b0470aeef10a3b0f9a6a1c60d908b5a06c62ae
Author: Johannes Berg <johannes@sipsolutions.net>
Date: Tue Apr 6 11:18:45 2010 +0200
mac80211: remove ieee80211_sta_stop_rx_ba_session
All callers of ieee80211_sta_stop_rx_ba_session can
just call __ieee80211_stop_rx_ba_session instead
because they already have the station struct, so do
that and remove ieee80211_sta_stop_rx_ba_session.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
I verified that reverting these three patches make my laptop stable
again:
54297e4d60b74e602138594c131097347d128b5a mac80211: fix some RX aggregation...
098a607091426e79178b9a6c318d993fea131791 mac80211: clean up/fix aggregation..
66b0470aeef10a3b0f9a6a1c60d908b5a06c62ae mac80211: remove ieee80211_sta_...
(I had to revert all three because of conflicts.)
I took a quick peek of the patches but I wasn't able to immediately
say what was wrong. This just made me suspicious:
- ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->sta.addr,
- (u16)*ptid, WLAN_BACK_TIMER,
- WLAN_REASON_QSTA_TIMEOUT);
+ __ieee80211_stop_rx_ba_session(sta, *ptid,
- WLAN_BACK_RECIPIENT,
+ WLAN_REASON_QSTA_TIMEOUT);
WLAN_BACK_TIMER was changed to WLAN_BACK_RECIPIENT, but I don't know
if it was in purpose or not. Johannes, any ideas?
--
Kalle Valo
^ permalink raw reply
* Re: regression: soft lockup with ath9k on master-2010-04-14
From: Johannes Berg @ 2010-04-19 6:43 UTC (permalink / raw)
To: Kalle Valo; +Cc: John W. Linville, linux-wireless
In-Reply-To: <871veclztk.fsf@purkki.valot.fi>
On Mon, 2010-04-19 at 09:29 +0300, Kalle Valo wrote:
> > It might be useful to do a bisect. If you choose to do that, you
> > might want to use wireless-next-2.6 instead, since that doesn't have
> > the occasional pulls from Linus that make bisecting wireless-testing
> > more painful.
>
> Thanks for the tip, it helped a lot. My new laptop is really slow to
> compile kernels :/
>
> I bisected it finally and found the culprit:
>
> 66b0470aeef10a3b0f9a6a1c60d908b5a06c62ae is the first bad commit
> commit 66b0470aeef10a3b0f9a6a1c60d908b5a06c62ae
> Author: Johannes Berg <johannes@sipsolutions.net>
> Date: Tue Apr 6 11:18:45 2010 +0200
>
> mac80211: remove ieee80211_sta_stop_rx_ba_session
[...]
> I took a quick peek of the patches but I wasn't able to immediately
> say what was wrong. This just made me suspicious:
>
> - ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->sta.addr,
> - (u16)*ptid, WLAN_BACK_TIMER,
> - WLAN_REASON_QSTA_TIMEOUT);
> + __ieee80211_stop_rx_ba_session(sta, *ptid,
> - WLAN_BACK_RECIPIENT,
> + WLAN_REASON_QSTA_TIMEOUT);
>
> WLAN_BACK_TIMER was changed to WLAN_BACK_RECIPIENT, but I don't know
> if it was in purpose or not. Johannes, any ideas?
That was on purpose but belongs into
098a607091426e79178b9a6c318d993fea131791 not this patch ... :(
However that shouldn't be the problem. Or rather, that could be the
reason you're seeing the problem on this patch, rather than the 098a
one.
Try the patch below?
johannes
--- wireless-testing.orig/net/mac80211/agg-rx.c 2010-04-19 08:40:17.000000000 +0200
+++ wireless-testing/net/mac80211/agg-rx.c 2010-04-19 08:40:27.000000000 +0200
@@ -47,11 +47,6 @@ void __ieee80211_stop_rx_ba_session(stru
printk(KERN_DEBUG "HW problem - can not stop rx "
"aggregation for tid %d\n", tid);
- /* check if this is a self generated aggregation halt */
- if (initiator == WLAN_BACK_RECIPIENT)
- ieee80211_send_delba(sta->sdata, sta->sta.addr,
- tid, 0, reason);
-
/* free the reordering buffer */
for (i = 0; i < tid_rx->buf_size; i++) {
if (tid_rx->reorder_buf[i]) {
@@ -69,6 +64,11 @@ void __ieee80211_stop_rx_ba_session(stru
spin_unlock_bh(&sta->lock);
+ /* check if this is a self generated aggregation halt */
+ if (initiator == WLAN_BACK_RECIPIENT)
+ ieee80211_send_delba(sta->sdata, sta->sta.addr,
+ tid, 0, reason);
+
del_timer_sync(&tid_rx->session_timer);
kfree(tid_rx);
}
^ permalink raw reply
* [PATCH 1/3] orinoco: implement set_wiphy_params
From: David Kilroy @ 2010-04-19 7:16 UTC (permalink / raw)
To: linux-wireless; +Cc: linville, David Kilroy
... to set fragmentation and RTS thresholds. Also report RTS retry
settings during wiphy init.
Note that the existing semantics for enabling microwave robustness are
preserved on firmwares that have it.
Signed-off-by: David Kilroy <kilroyd@googlemail.com>
---
drivers/net/wireless/orinoco/cfg.c | 88 +++++++++++++++-
drivers/net/wireless/orinoco/hw.c | 26 +++++
drivers/net/wireless/orinoco/orinoco.h | 2 +
drivers/net/wireless/orinoco/wext.c | 183 +-------------------------------
4 files changed, 120 insertions(+), 179 deletions(-)
diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c
index 27f2d33..90dd4d0 100644
--- a/drivers/net/wireless/orinoco/cfg.c
+++ b/drivers/net/wireless/orinoco/cfg.c
@@ -88,7 +88,9 @@ int orinoco_wiphy_register(struct wiphy *wiphy)
wiphy->rts_threshold = priv->rts_thresh;
if (!priv->has_mwo)
- wiphy->frag_threshold = priv->frag_thresh;
+ wiphy->frag_threshold = priv->frag_thresh + 1;
+ wiphy->retry_short = priv->short_retry_limit;
+ wiphy->retry_long = priv->long_retry_limit;
return wiphy_register(wiphy);
}
@@ -196,8 +198,92 @@ static int orinoco_set_channel(struct wiphy *wiphy,
return err;
}
+static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ struct orinoco_private *priv = wiphy_priv(wiphy);
+ int frag_value = -1;
+ int rts_value = -1;
+ int err = 0;
+
+ if (changed & WIPHY_PARAM_RETRY_SHORT) {
+ /* Setting short retry not supported */
+ err = -EINVAL;
+ }
+
+ if (changed & WIPHY_PARAM_RETRY_LONG) {
+ /* Setting long retry not supported */
+ err = -EINVAL;
+ }
+
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+ /* Set fragmentation */
+ if (priv->has_mwo) {
+ if (wiphy->frag_threshold < 0)
+ frag_value = 0;
+ else {
+ printk(KERN_WARNING "%s: Fixed fragmentation "
+ "is not supported on this firmware. "
+ "Using MWO robust instead.\n",
+ priv->ndev->name);
+ frag_value = 1;
+ }
+ } else {
+ if (wiphy->frag_threshold < 0)
+ frag_value = 2346;
+ else if ((wiphy->frag_threshold < 257) ||
+ (wiphy->frag_threshold > 2347))
+ err = -EINVAL;
+ else
+ /* cfg80211 value is 257-2347 (odd only)
+ * orinoco rid has range 256-2346 (even only) */
+ frag_value = wiphy->frag_threshold & ~0x1;
+ }
+ }
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+ /* Set RTS.
+ *
+ * Prism documentation suggests default of 2432,
+ * and a range of 0-3000.
+ *
+ * Current implementation uses 2347 as the default and
+ * the upper limit.
+ */
+
+ if (wiphy->rts_threshold < 0)
+ rts_value = 2347;
+ else if (wiphy->rts_threshold > 2347)
+ err = -EINVAL;
+ else
+ rts_value = wiphy->rts_threshold;
+ }
+
+ if (!err) {
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if (frag_value >= 0) {
+ if (priv->has_mwo)
+ priv->mwo_robust = frag_value;
+ else
+ priv->frag_thresh = frag_value;
+ }
+ if (rts_value >= 0)
+ priv->rts_thresh = rts_value;
+
+ err = orinoco_commit(priv);
+
+ orinoco_unlock(priv, &flags);
+ }
+
+ return err;
+}
+
const struct cfg80211_ops orinoco_cfg_ops = {
.change_virtual_intf = orinoco_change_vif,
.set_channel = orinoco_set_channel,
.scan = orinoco_scan,
+ .set_wiphy_params = orinoco_set_wiphy_params,
};
diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c
index 883b8f8..24ea4b4 100644
--- a/drivers/net/wireless/orinoco/hw.c
+++ b/drivers/net/wireless/orinoco/hw.c
@@ -374,6 +374,32 @@ int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr)
err = hermes_read_wordrec(hw, USER_BAP,
HERMES_RID_CNFPREAMBLE_SYMBOL,
&priv->preamble);
+ if (err) {
+ dev_err(dev, "Failed to read preamble setup\n");
+ goto out;
+ }
+ }
+
+ /* Retry settings */
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
+ &priv->short_retry_limit);
+ if (err) {
+ dev_err(dev, "Failed to read short retry limit\n");
+ goto out;
+ }
+
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
+ &priv->long_retry_limit);
+ if (err) {
+ dev_err(dev, "Failed to read long retry limit\n");
+ goto out;
+ }
+
+ err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
+ &priv->retry_lifetime);
+ if (err) {
+ dev_err(dev, "Failed to read max retry lifetime\n");
+ goto out;
}
out:
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h
index 665ef56..ff6b7b1 100644
--- a/drivers/net/wireless/orinoco/orinoco.h
+++ b/drivers/net/wireless/orinoco/orinoco.h
@@ -131,6 +131,8 @@ struct orinoco_private {
u16 ap_density, rts_thresh;
u16 pm_on, pm_mcast, pm_period, pm_timeout;
u16 preamble;
+ u16 short_retry_limit, long_retry_limit;
+ u16 retry_lifetime;
#ifdef WIRELESS_SPY
struct iw_spy_data spy_data; /* iwspy support */
struct iw_public_data wireless_data;
diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c
index 57b850e..a1006bf 100644
--- a/drivers/net/wireless/orinoco/wext.c
+++ b/drivers/net/wireless/orinoco/wext.c
@@ -538,125 +538,6 @@ static int orinoco_ioctl_setsens(struct net_device *dev,
return -EINPROGRESS; /* Call commit handler */
}
-static int orinoco_ioctl_setrts(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *rrq,
- char *extra)
-{
- struct orinoco_private *priv = ndev_priv(dev);
- int val = rrq->value;
- unsigned long flags;
-
- if (rrq->disabled)
- val = 2347;
-
- if ((val < 0) || (val > 2347))
- return -EINVAL;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- priv->rts_thresh = val;
- orinoco_unlock(priv, &flags);
-
- return -EINPROGRESS; /* Call commit handler */
-}
-
-static int orinoco_ioctl_getrts(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *rrq,
- char *extra)
-{
- struct orinoco_private *priv = ndev_priv(dev);
-
- rrq->value = priv->rts_thresh;
- rrq->disabled = (rrq->value == 2347);
- rrq->fixed = 1;
-
- return 0;
-}
-
-static int orinoco_ioctl_setfrag(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *frq,
- char *extra)
-{
- struct orinoco_private *priv = ndev_priv(dev);
- int err = -EINPROGRESS; /* Call commit handler */
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if (priv->has_mwo) {
- if (frq->disabled)
- priv->mwo_robust = 0;
- else {
- if (frq->fixed)
- printk(KERN_WARNING "%s: Fixed fragmentation "
- "is not supported on this firmware. "
- "Using MWO robust instead.\n",
- dev->name);
- priv->mwo_robust = 1;
- }
- } else {
- if (frq->disabled)
- priv->frag_thresh = 2346;
- else {
- if ((frq->value < 256) || (frq->value > 2346))
- err = -EINVAL;
- else
- /* must be even */
- priv->frag_thresh = frq->value & ~0x1;
- }
- }
-
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
-static int orinoco_ioctl_getfrag(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *frq,
- char *extra)
-{
- struct orinoco_private *priv = ndev_priv(dev);
- hermes_t *hw = &priv->hw;
- int err;
- u16 val;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- if (priv->has_mwo) {
- err = hermes_read_wordrec(hw, USER_BAP,
- HERMES_RID_CNFMWOROBUST_AGERE,
- &val);
- if (err)
- val = 0;
-
- frq->value = val ? 2347 : 0;
- frq->disabled = !val;
- frq->fixed = 0;
- } else {
- err = hermes_read_wordrec(hw, USER_BAP,
- HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
- &val);
- if (err)
- val = 0;
-
- frq->value = val;
- frq->disabled = (val >= 2346);
- frq->fixed = 1;
- }
-
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
static int orinoco_ioctl_setrate(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rrq,
@@ -1201,60 +1082,6 @@ static int orinoco_ioctl_set_mlme(struct net_device *dev,
return ret;
}
-static int orinoco_ioctl_getretry(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_param *rrq,
- char *extra)
-{
- struct orinoco_private *priv = ndev_priv(dev);
- hermes_t *hw = &priv->hw;
- int err = 0;
- u16 short_limit, long_limit, lifetime;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return -EBUSY;
-
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
- &short_limit);
- if (err)
- goto out;
-
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
- &long_limit);
- if (err)
- goto out;
-
- err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
- &lifetime);
- if (err)
- goto out;
-
- rrq->disabled = 0; /* Can't be disabled */
-
- /* Note : by default, display the retry number */
- if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
- rrq->flags = IW_RETRY_LIFETIME;
- rrq->value = lifetime * 1000; /* ??? */
- } else {
- /* By default, display the min number */
- if ((rrq->flags & IW_RETRY_LONG)) {
- rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
- rrq->value = long_limit;
- } else {
- rrq->flags = IW_RETRY_LIMIT;
- rrq->value = short_limit;
- if (short_limit != long_limit)
- rrq->flags |= IW_RETRY_SHORT;
- }
- }
-
- out:
- orinoco_unlock(priv, &flags);
-
- return err;
-}
-
static int orinoco_ioctl_reset(struct net_device *dev,
struct iw_request_info *info,
void *wrqu,
@@ -1528,11 +1355,11 @@ static const iw_handler orinoco_handler[] = {
IW_HANDLER(SIOCGIWESSID, (iw_handler)orinoco_ioctl_getessid),
IW_HANDLER(SIOCSIWRATE, (iw_handler)orinoco_ioctl_setrate),
IW_HANDLER(SIOCGIWRATE, (iw_handler)orinoco_ioctl_getrate),
- IW_HANDLER(SIOCSIWRTS, (iw_handler)orinoco_ioctl_setrts),
- IW_HANDLER(SIOCGIWRTS, (iw_handler)orinoco_ioctl_getrts),
- IW_HANDLER(SIOCSIWFRAG, (iw_handler)orinoco_ioctl_setfrag),
- IW_HANDLER(SIOCGIWFRAG, (iw_handler)orinoco_ioctl_getfrag),
- IW_HANDLER(SIOCGIWRETRY, (iw_handler)orinoco_ioctl_getretry),
+ IW_HANDLER(SIOCSIWRTS, (iw_handler)cfg80211_wext_siwrts),
+ IW_HANDLER(SIOCGIWRTS, (iw_handler)cfg80211_wext_giwrts),
+ IW_HANDLER(SIOCSIWFRAG, (iw_handler)cfg80211_wext_siwfrag),
+ IW_HANDLER(SIOCGIWFRAG, (iw_handler)cfg80211_wext_giwfrag),
+ IW_HANDLER(SIOCGIWRETRY, (iw_handler)cfg80211_wext_giwretry),
IW_HANDLER(SIOCSIWENCODE, (iw_handler)orinoco_ioctl_setiwencode),
IW_HANDLER(SIOCGIWENCODE, (iw_handler)orinoco_ioctl_getiwencode),
IW_HANDLER(SIOCSIWPOWER, (iw_handler)orinoco_ioctl_setpower),
--
1.6.4.4
^ permalink raw reply related
* [PATCH 2/3] orinoco: use cfg80211_find_ie
From: David Kilroy @ 2010-04-19 7:16 UTC (permalink / raw)
To: linux-wireless; +Cc: linville, David Kilroy
In-Reply-To: <1271661383-27839-1-git-send-email-kilroyd@googlemail.com>
Instead of using a local function.
Signed-off-by: David Kilroy <kilroyd@googlemail.com>
---
drivers/net/wireless/orinoco/main.h | 12 ------------
drivers/net/wireless/orinoco/scan.c | 4 ++--
2 files changed, 2 insertions(+), 14 deletions(-)
diff --git a/drivers/net/wireless/orinoco/main.h b/drivers/net/wireless/orinoco/main.h
index 21ab36c..4dadf98 100644
--- a/drivers/net/wireless/orinoco/main.h
+++ b/drivers/net/wireless/orinoco/main.h
@@ -33,18 +33,6 @@ int orinoco_commit(struct orinoco_private *priv);
void orinoco_reset(struct work_struct *work);
/* Information element helpers - find a home for these... */
-static inline u8 *orinoco_get_ie(u8 *data, size_t len,
- enum ieee80211_eid eid)
-{
- u8 *p = data;
- while ((p + 2) < (data + len)) {
- if (p[0] == eid)
- return p;
- p += p[1] + 2;
- }
- return NULL;
-}
-
#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
#define WPA_SELECTOR_LEN 4
static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c
index 330d42d..4300d9d 100644
--- a/drivers/net/wireless/orinoco/scan.c
+++ b/drivers/net/wireless/orinoco/scan.c
@@ -127,7 +127,7 @@ void orinoco_add_extscan_result(struct orinoco_private *priv,
{
struct wiphy *wiphy = priv_to_wiphy(priv);
struct ieee80211_channel *channel;
- u8 *ie;
+ const u8 *ie;
u64 timestamp;
s32 signal;
u16 capability;
@@ -136,7 +136,7 @@ void orinoco_add_extscan_result(struct orinoco_private *priv,
int chan, freq;
ie_len = len - sizeof(*bss);
- ie = orinoco_get_ie(bss->data, ie_len, WLAN_EID_DS_PARAMS);
+ ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len);
chan = ie ? ie[2] : 0;
freq = ieee80211_dsss_chan_to_freq(chan);
channel = ieee80211_get_channel(wiphy, freq);
--
1.6.4.4
^ permalink raw reply related
* [PATCH 3/3] orinoco: have sparse check endian issues
From: David Kilroy @ 2010-04-19 7:16 UTC (permalink / raw)
To: linux-wireless; +Cc: linville, David Kilroy
In-Reply-To: <1271661383-27839-1-git-send-email-kilroyd@googlemail.com>
Orinoco should be endian clean, so enable the checking.
Signed-off-by: David Kilroy <kilroyd@googlemail.com>
---
drivers/net/wireless/orinoco/Makefile | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/orinoco/Makefile b/drivers/net/wireless/orinoco/Makefile
index 9abd632..e645269 100644
--- a/drivers/net/wireless/orinoco/Makefile
+++ b/drivers/net/wireless/orinoco/Makefile
@@ -11,3 +11,6 @@ obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o
obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o
obj-$(CONFIG_NORTEL_HERMES) += orinoco_nortel.o
obj-$(CONFIG_PCMCIA_SPECTRUM) += spectrum_cs.o
+
+# Orinoco should be endian clean.
+ccflags-y += -D__CHECK_ENDIAN__
--
1.6.4.4
^ permalink raw reply related
* [PATCH] mac80211: Prevent running sta_cleanup timer unnecessarily
From: Juuso Oikarinen @ 2010-04-19 7:12 UTC (permalink / raw)
To: linville; +Cc: janne.ylalehto, linux-wireless
The sta_cleanup timer is used to periodically expire buffered frames from the
tx buf. The timer is executing periodically, regardless of the need for it.
This is wasting resources.
Fix this simply by not restarting the sta_cleanup timer if the tx buffer was
empty. Restart the timer when there is some more tx-traffic.
Cc: Janne Ylälehto <janne.ylalehto@nokia.com>
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
---
net/mac80211/sta_info.c | 13 ++++++++++---
net/mac80211/tx.c | 7 +++++++
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index ff0eb94..3de7a22 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -575,7 +575,7 @@ static int sta_info_buffer_expired(struct sta_info *sta,
}
-static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
+static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
struct sta_info *sta)
{
unsigned long flags;
@@ -583,7 +583,7 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata;
if (skb_queue_empty(&sta->ps_tx_buf))
- return;
+ return false;
for (;;) {
spin_lock_irqsave(&sta->ps_tx_buf.lock, flags);
@@ -608,6 +608,8 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
if (skb_queue_empty(&sta->ps_tx_buf))
sta_info_clear_tim_bit(sta);
}
+
+ return true;
}
static int __must_check __sta_info_destroy(struct sta_info *sta)
@@ -755,15 +757,20 @@ static void sta_info_cleanup(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *) data;
struct sta_info *sta;
+ bool timer_needed = false;
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list)
- sta_info_cleanup_expire_buffered(local, sta);
+ if (sta_info_cleanup_expire_buffered(local, sta))
+ timer_needed = true;
rcu_read_unlock();
if (local->quiescing)
return;
+ if (!timer_needed)
+ return;
+
local->sta_cleanup.expires =
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
add_timer(&local->sta_cleanup);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2cb7726..e2aa972 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -429,6 +429,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
struct sta_info *sta = tx->sta;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+ struct ieee80211_local *local = tx->local;
u32 staflags;
if (unlikely(!sta ||
@@ -476,6 +477,12 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
info->control.vif = &tx->sdata->vif;
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
skb_queue_tail(&sta->ps_tx_buf, tx->skb);
+
+ if (!timer_pending(&local->sta_cleanup))
+ mod_timer(&local->sta_cleanup,
+ round_jiffies(jiffies +
+ STA_INFO_CLEANUP_INTERVAL));
+
return TX_QUEUED;
}
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
--
1.6.3.3
^ permalink raw reply related
* Re: regression: soft lockup with ath9k on master-2010-04-14
From: Kalle Valo @ 2010-04-19 7:19 UTC (permalink / raw)
To: Johannes Berg; +Cc: John W. Linville, linux-wireless
In-Reply-To: <1271659397.3873.9.camel@jlt3.sipsolutions.net>
Johannes Berg <johannes@sipsolutions.net> writes:
> However that shouldn't be the problem. Or rather, that could be the
> reason you're seeing the problem on this patch, rather than the 098a
> one.
>
> Try the patch below?
Sorry, no luck. Still my laptop freezes.
--
Kalle Valo
^ permalink raw reply
* [RFC 0/5] orinoco: Add support for Agere based USB cards
From: David Kilroy @ 2010-04-19 7:35 UTC (permalink / raw)
To: linux-wireless; +Cc: orinoco-devel, David Kilroy
This patch series adds the orinoco_usb driver, originally written by
Manuel Estrada Sainz.
I believe the main stumbling block the last time this driver was
submitted for inclusion was the way it handled the orinoco driver
locking. I've reworked this into something reasonably sane (patch 3).
The resulting driver has been basically tested with a Compaq WL215
(thanks to Mark Davis for supplying one). Managed mode with WEP works
on this card while a WPA-enabled PCMCIA orinoco card is running.
I'm not familiar with the USB subsystem, so there may be issues that
need to be resolved in that area. Also the original driver used
in_atomic in one location. I left this as-is in the main driver
commit (patch 4), and the subsequent patch (5) is my attempt to fix
this up. It works, but there may be a better way to do this.
Regards,
Dave.
---
David Kilroy (5):
orinoco: add hermes_ops
orinoco: allow driver to specify netdev_ops
orinoco: encapsulate driver locking
orinoco: add orinoco_usb driver
orinoco_usb: avoid in_atomic
drivers/net/wireless/orinoco/Kconfig | 7 +
drivers/net/wireless/orinoco/Makefile | 1 +
drivers/net/wireless/orinoco/airport.c | 4 +-
drivers/net/wireless/orinoco/cfg.c | 2 +-
drivers/net/wireless/orinoco/fw.c | 2 +-
drivers/net/wireless/orinoco/hermes.c | 76 +-
drivers/net/wireless/orinoco/hermes.h | 55 +-
drivers/net/wireless/orinoco/hermes_dld.c | 34 +-
drivers/net/wireless/orinoco/hw.c | 63 +-
drivers/net/wireless/orinoco/main.c | 129 ++-
drivers/net/wireless/orinoco/orinoco.h | 30 +-
drivers/net/wireless/orinoco/orinoco_cs.c | 2 +-
drivers/net/wireless/orinoco/orinoco_nortel.c | 2 +-
drivers/net/wireless/orinoco/orinoco_pci.c | 2 +-
drivers/net/wireless/orinoco/orinoco_plx.c | 2 +-
drivers/net/wireless/orinoco/orinoco_tmd.c | 2 +-
drivers/net/wireless/orinoco/orinoco_usb.c | 1678 +++++++++++++++++++++++++
drivers/net/wireless/orinoco/spectrum_cs.c | 2 +-
drivers/net/wireless/orinoco/wext.c | 6 +-
19 files changed, 1932 insertions(+), 167 deletions(-)
create mode 100644 drivers/net/wireless/orinoco/orinoco_usb.c
^ permalink raw reply
* [RFC 1/5] orinoco: add hermes_ops
From: David Kilroy @ 2010-04-19 7:35 UTC (permalink / raw)
To: linux-wireless; +Cc: orinoco-devel, David Kilroy
In-Reply-To: <1271662522-29119-1-git-send-email-kilroyd@googlemail.com>
Pave the way for introducing USB alternative functions.
Force callers to dereference ops instead of providing wrappers.
Signed-off-by: David Kilroy <kilroyd@googlemail.com>
---
drivers/net/wireless/orinoco/airport.c | 2 +-
drivers/net/wireless/orinoco/cfg.c | 2 +-
drivers/net/wireless/orinoco/fw.c | 2 +-
drivers/net/wireless/orinoco/hermes.c | 52 ++++++++++--------
drivers/net/wireless/orinoco/hermes.h | 50 ++++++++++--------
drivers/net/wireless/orinoco/hermes_dld.c | 34 ++++++------
drivers/net/wireless/orinoco/hw.c | 63 +++++++++++-----------
drivers/net/wireless/orinoco/main.c | 83 +++++++++++++++--------------
drivers/net/wireless/orinoco/wext.c | 6 +-
9 files changed, 154 insertions(+), 140 deletions(-)
diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/orinoco/airport.c
index c60df2c..ea23c4f 100644
--- a/drivers/net/wireless/orinoco/airport.c
+++ b/drivers/net/wireless/orinoco/airport.c
@@ -195,7 +195,7 @@ airport_attach(struct macio_dev *mdev, const struct of_device_id *match)
ssleep(1);
/* Reset it before we get the interrupt */
- hermes_init(hw);
+ hw->ops->init(hw);
if (request_irq(card->irq, orinoco_interrupt, 0, DRIVER_NAME, priv)) {
printk(KERN_ERR PFX "Couldn't get IRQ %d\n", card->irq);
diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c
index 90dd4d0..81d228d 100644
--- a/drivers/net/wireless/orinoco/cfg.c
+++ b/drivers/net/wireless/orinoco/cfg.c
@@ -189,7 +189,7 @@ static int orinoco_set_channel(struct wiphy *wiphy,
if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
/* Fast channel change - no commit if successful */
hermes_t *hw = &priv->hw;
- err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+ err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
HERMES_TEST_SET_CHANNEL,
channel, NULL);
}
diff --git a/drivers/net/wireless/orinoco/fw.c b/drivers/net/wireless/orinoco/fw.c
index 5ea0f7c..4c99240 100644
--- a/drivers/net/wireless/orinoco/fw.c
+++ b/drivers/net/wireless/orinoco/fw.c
@@ -260,7 +260,7 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
}
/* Reset hermes chip and make sure it responds */
- ret = hermes_init(hw);
+ ret = hw->ops->init(hw);
/* hermes_reset() should return 0 with the secondary firmware */
if (secondary && ret != 0)
diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c
index 1a2fca7..a7df524 100644
--- a/drivers/net/wireless/orinoco/hermes.c
+++ b/drivers/net/wireless/orinoco/hermes.c
@@ -70,6 +70,7 @@
#endif /* ! HERMES_DEBUG */
+static const struct hermes_ops hermes_ops_local;
/*
* Internal functions
@@ -111,9 +112,9 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0,
*/
/* For doing cmds that wipe the magic constant in SWSUPPORT0 */
-int hermes_doicmd_wait(hermes_t *hw, u16 cmd,
- u16 parm0, u16 parm1, u16 parm2,
- struct hermes_response *resp)
+static int hermes_doicmd_wait(hermes_t *hw, u16 cmd,
+ u16 parm0, u16 parm1, u16 parm2,
+ struct hermes_response *resp)
{
int err = 0;
int k;
@@ -163,17 +164,17 @@ int hermes_doicmd_wait(hermes_t *hw, u16 cmd,
out:
return err;
}
-EXPORT_SYMBOL(hermes_doicmd_wait);
void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing)
{
hw->iobase = address;
hw->reg_spacing = reg_spacing;
hw->inten = 0x0;
+ hw->ops = &hermes_ops_local;
}
EXPORT_SYMBOL(hermes_struct_init);
-int hermes_init(hermes_t *hw)
+static int hermes_init(hermes_t *hw)
{
u16 reg;
int err = 0;
@@ -217,7 +218,6 @@ int hermes_init(hermes_t *hw)
return err;
}
-EXPORT_SYMBOL(hermes_init);
/* Issue a command to the chip, and (busy!) wait for it to
* complete.
@@ -228,8 +228,8 @@ EXPORT_SYMBOL(hermes_init);
* > 0 on error returned by the firmware
*
* Callable from any context, but locking is your problem. */
-int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
- struct hermes_response *resp)
+static int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
+ struct hermes_response *resp)
{
int err;
int k;
@@ -291,9 +291,8 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
out:
return err;
}
-EXPORT_SYMBOL(hermes_docmd_wait);
-int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
+static int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
{
int err = 0;
int k;
@@ -333,7 +332,6 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
return 0;
}
-EXPORT_SYMBOL(hermes_allocate);
/* Set up a BAP to read a particular chunk of data from card's internal buffer.
*
@@ -403,8 +401,8 @@ static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset)
* 0 on success
* > 0 on error from firmware
*/
-int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
- u16 id, u16 offset)
+static int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
+ u16 id, u16 offset)
{
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0;
@@ -422,7 +420,6 @@ int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
out:
return err;
}
-EXPORT_SYMBOL(hermes_bap_pread);
/* Write a block of data to the chip's buffer, via the
* BAP. Synchronization/serialization is the caller's problem.
@@ -432,8 +429,8 @@ EXPORT_SYMBOL(hermes_bap_pread);
* 0 on success
* > 0 on error from firmware
*/
-int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
- u16 id, u16 offset)
+static int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
+ u16 id, u16 offset)
{
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0;
@@ -451,7 +448,6 @@ int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
out:
return err;
}
-EXPORT_SYMBOL(hermes_bap_pwrite);
/* Read a Length-Type-Value record from the card.
*
@@ -461,8 +457,8 @@ EXPORT_SYMBOL(hermes_bap_pwrite);
* practice.
*
* Callable from user or bh context. */
-int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize,
- u16 *length, void *buf)
+static int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize,
+ u16 *length, void *buf)
{
int err = 0;
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
@@ -505,10 +501,9 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize,
return 0;
}
-EXPORT_SYMBOL(hermes_read_ltv);
-int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
- u16 length, const void *value)
+static int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
+ u16 length, const void *value)
{
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0;
@@ -533,4 +528,15 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
return err;
}
-EXPORT_SYMBOL(hermes_write_ltv);
+
+/* Hermes operations for local buses */
+static const struct hermes_ops hermes_ops_local = {
+ .init = hermes_init,
+ .cmd_wait = hermes_docmd_wait,
+ .init_cmd_wait = hermes_doicmd_wait,
+ .allocate = hermes_allocate,
+ .read_ltv = hermes_read_ltv,
+ .write_ltv = hermes_write_ltv,
+ .bap_pread = hermes_bap_pread,
+ .bap_pwrite = hermes_bap_pwrite
+};
diff --git a/drivers/net/wireless/orinoco/hermes.h b/drivers/net/wireless/orinoco/hermes.h
index 2dddbb5..18b268c 100644
--- a/drivers/net/wireless/orinoco/hermes.h
+++ b/drivers/net/wireless/orinoco/hermes.h
@@ -374,6 +374,27 @@ struct hermes_multicast {
/* Timeouts */
#define HERMES_BAP_BUSY_TIMEOUT (10000) /* In iterations of ~1us */
+struct hermes;
+
+/* Functions to access hardware */
+struct hermes_ops {
+ int (*init)(struct hermes *hw);
+ int (*cmd_wait)(struct hermes *hw, u16 cmd, u16 parm0,
+ struct hermes_response *resp);
+ int (*init_cmd_wait)(struct hermes *hw, u16 cmd,
+ u16 parm0, u16 parm1, u16 parm2,
+ struct hermes_response *resp);
+ int (*allocate)(struct hermes *hw, u16 size, u16 *fid);
+ int (*read_ltv)(struct hermes *hw, int bap, u16 rid, unsigned buflen,
+ u16 *length, void *buf);
+ int (*write_ltv)(struct hermes *hw, int bap, u16 rid,
+ u16 length, const void *value);
+ int (*bap_pread)(struct hermes *hw, int bap, void *buf, int len,
+ u16 id, u16 offset);
+ int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf,
+ int len, u16 id, u16 offset);
+};
+
/* Basic control structure */
typedef struct hermes {
void __iomem *iobase;
@@ -381,6 +402,7 @@ typedef struct hermes {
#define HERMES_16BIT_REGSPACING 0
#define HERMES_32BIT_REGSPACING 1
u16 inten; /* Which interrupts should be enabled? */
+ const struct hermes_ops *ops;
} hermes_t;
/* Register access convenience macros */
@@ -394,22 +416,6 @@ typedef struct hermes {
/* Function prototypes */
void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing);
-int hermes_init(hermes_t *hw);
-int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
- struct hermes_response *resp);
-int hermes_doicmd_wait(hermes_t *hw, u16 cmd,
- u16 parm0, u16 parm1, u16 parm2,
- struct hermes_response *resp);
-int hermes_allocate(hermes_t *hw, u16 size, u16 *fid);
-
-int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
- u16 id, u16 offset);
-int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
- u16 id, u16 offset);
-int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned buflen,
- u16 *length, void *buf);
-int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
- u16 length, const void *value);
/* Inline functions */
@@ -426,13 +432,13 @@ static inline void hermes_set_irqmask(hermes_t *hw, u16 events)
static inline int hermes_enable_port(hermes_t *hw, int port)
{
- return hermes_docmd_wait(hw, HERMES_CMD_ENABLE | (port << 8),
+ return hw->ops->cmd_wait(hw, HERMES_CMD_ENABLE | (port << 8),
0, NULL);
}
static inline int hermes_disable_port(hermes_t *hw, int port)
{
- return hermes_docmd_wait(hw, HERMES_CMD_DISABLE | (port << 8),
+ return hw->ops->cmd_wait(hw, HERMES_CMD_DISABLE | (port << 8),
0, NULL);
}
@@ -440,7 +446,7 @@ static inline int hermes_disable_port(hermes_t *hw, int port)
* information frame in __orinoco_ev_info() */
static inline int hermes_inquire(hermes_t *hw, u16 rid)
{
- return hermes_docmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL);
+ return hw->ops->cmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL);
}
#define HERMES_BYTES_TO_RECLEN(n) ((((n)+1)/2) + 1)
@@ -475,10 +481,10 @@ static inline void hermes_clear_words(struct hermes *hw, int off,
}
#define HERMES_READ_RECORD(hw, bap, rid, buf) \
- (hermes_read_ltv((hw), (bap), (rid), sizeof(*buf), NULL, (buf)))
+ (hw->ops->read_ltv((hw), (bap), (rid), sizeof(*buf), NULL, (buf)))
#define HERMES_WRITE_RECORD(hw, bap, rid, buf) \
- (hermes_write_ltv((hw), (bap), (rid), \
- HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf)))
+ (hw->ops->write_ltv((hw), (bap), (rid), \
+ HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf)))
static inline int hermes_read_wordrec(hermes_t *hw, int bap, u16 rid, u16 *word)
{
diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c
index fb157eb..8f22e20 100644
--- a/drivers/net/wireless/orinoco/hermes_dld.c
+++ b/drivers/net/wireless/orinoco/hermes_dld.c
@@ -293,7 +293,7 @@ int hermes_read_pda(hermes_t *hw,
/* PDA of spectrum symbol is in eeprom */
/* Issue command to read EEPROM */
- ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
+ ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
if (ret)
return ret;
} else {
@@ -408,16 +408,16 @@ int hermesi_program_init(hermes_t *hw, u32 offset)
/* Acknowledge any outstanding command */
hermes_write_regn(hw, EVACK, 0xFFFF);
- /* Using doicmd_wait rather than docmd_wait */
- err = hermes_doicmd_wait(hw,
- 0x0100 | HERMES_CMD_INIT,
- 0, 0, 0, NULL);
+ /* Using init_cmd_wait rather than cmd_wait */
+ err = hw->ops->init_cmd_wait(hw,
+ 0x0100 | HERMES_CMD_INIT,
+ 0, 0, 0, NULL);
if (err)
return err;
- err = hermes_doicmd_wait(hw,
- 0x0000 | HERMES_CMD_INIT,
- 0, 0, 0, NULL);
+ err = hw->ops->init_cmd_wait(hw,
+ 0x0000 | HERMES_CMD_INIT,
+ 0, 0, 0, NULL);
if (err)
return err;
@@ -428,12 +428,12 @@ int hermesi_program_init(hermes_t *hw, u32 offset)
return err;
pr_debug(PFX "Enabling volatile, EP 0x%08x\n", offset);
- err = hermes_doicmd_wait(hw,
- HERMES_PROGRAM_ENABLE_VOLATILE,
- offset & 0xFFFFu,
- offset >> 16,
- 0,
- NULL);
+ err = hw->ops->init_cmd_wait(hw,
+ HERMES_PROGRAM_ENABLE_VOLATILE,
+ offset & 0xFFFFu,
+ offset >> 16,
+ 0,
+ NULL);
pr_debug(PFX "PROGRAM_ENABLE returned %d\n", err);
return err;
@@ -451,7 +451,7 @@ int hermesi_program_end(hermes_t *hw)
int rc = 0;
int err;
- rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
+ rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
pr_debug(PFX "PROGRAM_DISABLE returned %d, "
"r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
@@ -468,8 +468,8 @@ int hermesi_program_end(hermes_t *hw)
hermes_write_regn(hw, EVACK, 0xFFFF);
/* Reinitialise, ignoring return */
- (void) hermes_doicmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
- 0, 0, 0, NULL);
+ (void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
+ 0, 0, 0, NULL);
return rc ? rc : err;
}
diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c
index 24ea4b4..9c86acc 100644
--- a/drivers/net/wireless/orinoco/hw.c
+++ b/drivers/net/wireless/orinoco/hw.c
@@ -177,9 +177,9 @@ int determine_fw_capabilities(struct orinoco_private *priv,
/* 3Com MAC : 00:50:DA:* */
memset(tmp, 0, sizeof(tmp));
/* Get the Symbol firmware version */
- err = hermes_read_ltv(hw, USER_BAP,
- HERMES_RID_SECONDARYVERSION_SYMBOL,
- SYMBOL_MAX_VER_LEN, NULL, &tmp);
+ err = hw->ops->read_ltv(hw, USER_BAP,
+ HERMES_RID_SECONDARYVERSION_SYMBOL,
+ SYMBOL_MAX_VER_LEN, NULL, &tmp);
if (err) {
dev_warn(dev, "Error %d reading Symbol firmware info. "
"Wildly guessing capabilities...\n", err);
@@ -286,8 +286,8 @@ int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr)
u16 reclen;
/* Get the MAC address */
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
- ETH_ALEN, NULL, dev_addr);
+ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
+ ETH_ALEN, NULL, dev_addr);
if (err) {
dev_warn(dev, "Failed to read MAC address!\n");
goto out;
@@ -296,8 +296,8 @@ int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr)
dev_dbg(dev, "MAC address %pM\n", dev_addr);
/* Get the station name */
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
- sizeof(nickbuf), &reclen, &nickbuf);
+ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
+ sizeof(nickbuf), &reclen, &nickbuf);
if (err) {
dev_err(dev, "failed to read station name\n");
goto out;
@@ -413,11 +413,11 @@ int orinoco_hw_allocate_fid(struct orinoco_private *priv)
struct hermes *hw = &priv->hw;
int err;
- err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
+ err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) {
/* Try workaround for old Symbol firmware bug */
priv->nicbuf_size = TX_NICBUF_SIZE_BUG;
- err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
+ err = hw->ops->allocate(hw, priv->nicbuf_size, &priv->txfid);
dev_warn(dev, "Firmware ALLOC bug detected "
"(old Symbol firmware?). Work around %s\n",
@@ -463,8 +463,9 @@ int orinoco_hw_program_rids(struct orinoco_private *priv)
struct hermes_idstring idbuf;
/* Set the MAC address */
- err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
- HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr);
+ err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
+ HERMES_BYTES_TO_RECLEN(ETH_ALEN),
+ dev->dev_addr);
if (err) {
printk(KERN_ERR "%s: Error %d setting MAC address\n",
dev->name, err);
@@ -527,7 +528,7 @@ int orinoco_hw_program_rids(struct orinoco_private *priv)
idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
/* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */
- err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
+ err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID,
HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
&idbuf);
if (err) {
@@ -535,7 +536,7 @@ int orinoco_hw_program_rids(struct orinoco_private *priv)
dev->name, err);
return err;
}
- err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
+ err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID,
HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
&idbuf);
if (err) {
@@ -547,9 +548,9 @@ int orinoco_hw_program_rids(struct orinoco_private *priv)
/* Set the station name */
idbuf.len = cpu_to_le16(strlen(priv->nick));
memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
- err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
- HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
- &idbuf);
+ err = hw->ops->write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
+ HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
+ &idbuf);
if (err) {
printk(KERN_ERR "%s: Error %d setting nickname\n",
dev->name, err);
@@ -664,12 +665,12 @@ int orinoco_hw_program_rids(struct orinoco_private *priv)
if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
/* Enable monitor mode */
dev->type = ARPHRD_IEEE80211;
- err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+ err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
HERMES_TEST_MONITOR, 0, NULL);
} else {
/* Disable monitor mode */
dev->type = ARPHRD_ETHER;
- err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+ err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
HERMES_TEST_STOP, 0, NULL);
}
if (err)
@@ -695,8 +696,8 @@ int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc)
if ((key < 0) || (key >= 4))
return -EINVAL;
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
- sizeof(tsc_arr), NULL, &tsc_arr);
+ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
+ sizeof(tsc_arr), NULL, &tsc_arr);
if (!err)
memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
@@ -875,7 +876,7 @@ int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
memcpy(key, priv->keys[i].key,
priv->keys[i].key_len);
- err = hermes_write_ltv(hw, USER_BAP,
+ err = hw->ops->write_ltv(hw, USER_BAP,
HERMES_RID_CNFDEFAULTKEY0 + i,
HERMES_BYTES_TO_RECLEN(keylen),
key);
@@ -1092,7 +1093,7 @@ int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
memcpy(mclist.addr[i++], p->dmi_addr, ETH_ALEN);
}
- err = hermes_write_ltv(hw, USER_BAP,
+ err = hw->ops->write_ltv(hw, USER_BAP,
HERMES_RID_CNFGROUPADDRESSES,
HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN),
&mclist);
@@ -1134,15 +1135,15 @@ int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
HERMES_RID_CNFDESIREDSSID;
- err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
- NULL, &essidbuf);
+ err = hw->ops->read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
+ NULL, &essidbuf);
if (err)
goto fail_unlock;
} else {
*active = 0;
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
- sizeof(essidbuf), NULL, &essidbuf);
+ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
+ sizeof(essidbuf), NULL, &essidbuf);
if (err)
goto fail_unlock;
}
@@ -1213,8 +1214,8 @@ int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
if (orinoco_lock(priv, &flags) != 0)
return -EBUSY;
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
- sizeof(list), NULL, &list);
+ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
+ sizeof(list), NULL, &list);
orinoco_unlock(priv, &flags);
if (err)
@@ -1281,7 +1282,7 @@ int orinoco_hw_trigger_scan(struct orinoco_private *priv,
idbuf.len = cpu_to_le16(len);
memcpy(idbuf.val, ssid->ssid, len);
- err = hermes_write_ltv(hw, USER_BAP,
+ err = hw->ops->write_ltv(hw, USER_BAP,
HERMES_RID_CNFSCANSSID_AGERE,
HERMES_BYTES_TO_RECLEN(len + 2),
&idbuf);
@@ -1345,8 +1346,8 @@ int orinoco_hw_get_current_bssid(struct orinoco_private *priv,
hermes_t *hw = &priv->hw;
int err;
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
- ETH_ALEN, NULL, addr);
+ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
+ ETH_ALEN, NULL, addr);
return err;
}
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c
index 413e9ab..8c8009c 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/orinoco/main.c
@@ -400,8 +400,8 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
memset(&desc, 0, sizeof(desc));
*txcntl = cpu_to_le16(tx_control);
- err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
- txfid, 0);
+ err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
+ txfid, 0);
if (err) {
if (net_ratelimit())
printk(KERN_ERR "%s: Error %d writing Tx "
@@ -414,8 +414,8 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
memset(&desc, 0, sizeof(desc));
desc.tx_control = cpu_to_le16(tx_control);
- err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
- txfid, 0);
+ err = hw->ops->bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
+ txfid, 0);
if (err) {
if (net_ratelimit())
printk(KERN_ERR "%s: Error %d writing Tx "
@@ -458,8 +458,8 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
memcpy(eh, &hdr, sizeof(hdr));
}
- err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
- txfid, HERMES_802_3_OFFSET);
+ err = hw->ops->bap_pwrite(hw, USER_BAP, skb->data, skb->len,
+ txfid, HERMES_802_3_OFFSET);
if (err) {
printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
dev->name, err);
@@ -490,8 +490,8 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic);
/* Write the MIC */
- err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
- txfid, HERMES_802_3_OFFSET + offset);
+ err = hw->ops->bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
+ txfid, HERMES_802_3_OFFSET + offset);
if (err) {
printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
dev->name, err);
@@ -502,7 +502,7 @@ static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
/* Finally, we actually initiate the send */
netif_stop_queue(dev);
- err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL,
+ err = hw->ops->cmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL,
txfid, NULL);
if (err) {
netif_start_queue(dev);
@@ -572,9 +572,9 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
return; /* Nothing's really happened */
/* Read part of the frame header - we need status and addr1 */
- err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
- sizeof(struct hermes_txexc_data),
- fid, 0);
+ err = hw->ops->bap_pread(hw, IRQ_BAP, &hdr,
+ sizeof(struct hermes_txexc_data),
+ fid, 0);
hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
stats->tx_errors++;
@@ -764,9 +764,9 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
/* If any, copy the data from the card to the skb */
if (datalen > 0) {
- err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
- ALIGN(datalen, 2), rxfid,
- HERMES_802_2_OFFSET);
+ err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
+ ALIGN(datalen, 2), rxfid,
+ HERMES_802_2_OFFSET);
if (err) {
printk(KERN_ERR "%s: error %d reading monitor frame\n",
dev->name, err);
@@ -814,8 +814,8 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
rxfid = hermes_read_regn(hw, RXFID);
- err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
- rxfid, 0);
+ err = hw->ops->bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
+ rxfid, 0);
if (err) {
printk(KERN_ERR "%s: error %d reading Rx descriptor. "
"Frame dropped.\n", dev->name, err);
@@ -882,9 +882,9 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
nothing is removed. 2 is for aligning the IP header. */
skb_reserve(skb, ETH_HLEN + 2);
- err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length),
- ALIGN(length, 2), rxfid,
- HERMES_802_2_OFFSET);
+ err = hw->ops->bap_pread(hw, IRQ_BAP, skb_put(skb, length),
+ ALIGN(length, 2), rxfid,
+ HERMES_802_2_OFFSET);
if (err) {
printk(KERN_ERR "%s: error %d reading frame. "
"Frame dropped.\n", dev->name, err);
@@ -1145,9 +1145,9 @@ static void orinoco_join_ap(struct work_struct *work)
goto out;
/* Read scan results from the firmware */
- err = hermes_read_ltv(hw, USER_BAP,
- HERMES_RID_SCANRESULTSTABLE,
- MAX_SCAN_LEN, &len, buf);
+ err = hw->ops->read_ltv(hw, USER_BAP,
+ HERMES_RID_SCANRESULTSTABLE,
+ MAX_SCAN_LEN, &len, buf);
if (err) {
printk(KERN_ERR "%s: Cannot read scan results\n",
dev->name);
@@ -1194,8 +1194,8 @@ static void orinoco_send_bssid_wevent(struct orinoco_private *priv)
union iwreq_data wrqu;
int err;
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
- ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
+ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
+ ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
if (err != 0)
return;
@@ -1217,8 +1217,8 @@ static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv)
if (!priv->has_wpa)
return;
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
- sizeof(buf), NULL, &buf);
+ err = hw->ops->read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
+ sizeof(buf), NULL, &buf);
if (err != 0)
return;
@@ -1247,8 +1247,9 @@ static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv)
if (!priv->has_wpa)
return;
- err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
- sizeof(buf), NULL, &buf);
+ err = hw->ops->read_ltv(hw, USER_BAP,
+ HERMES_RID_CURRENT_ASSOC_RESP_INFO,
+ sizeof(buf), NULL, &buf);
if (err != 0)
return;
@@ -1371,8 +1372,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
infofid = hermes_read_regn(hw, INFOFID);
/* Read the info frame header - don't try too hard */
- err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
- infofid, 0);
+ err = hw->ops->bap_pread(hw, IRQ_BAP, &info, sizeof(info),
+ infofid, 0);
if (err) {
printk(KERN_ERR "%s: error %d reading info frame. "
"Frame dropped.\n", dev->name, err);
@@ -1393,8 +1394,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
len = sizeof(tallies);
}
- err = hermes_bap_pread(hw, IRQ_BAP, &tallies, len,
- infofid, sizeof(info));
+ err = hw->ops->bap_pread(hw, IRQ_BAP, &tallies, len,
+ infofid, sizeof(info));
if (err)
break;
@@ -1429,8 +1430,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
break;
}
- err = hermes_bap_pread(hw, IRQ_BAP, &linkstatus, len,
- infofid, sizeof(info));
+ err = hw->ops->bap_pread(hw, IRQ_BAP, &linkstatus, len,
+ infofid, sizeof(info));
if (err)
break;
newstatus = le16_to_cpu(linkstatus.linkstatus);
@@ -1494,8 +1495,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
}
/* Read scan data */
- err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
- infofid, sizeof(info));
+ err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) buf, len,
+ infofid, sizeof(info));
if (err) {
kfree(buf);
qabort_scan(priv);
@@ -1547,8 +1548,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
break;
/* Read scan data */
- err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
- infofid, sizeof(info));
+ err = hw->ops->bap_pread(hw, IRQ_BAP, (void *) bss, len,
+ infofid, sizeof(info));
if (err)
kfree(bss);
else
@@ -1647,7 +1648,7 @@ static int orinoco_reinit_firmware(struct orinoco_private *priv)
struct hermes *hw = &priv->hw;
int err;
- err = hermes_init(hw);
+ err = hw->ops->init(hw);
if (priv->do_fw_download && !err) {
err = orinoco_download(priv);
if (err)
@@ -1984,7 +1985,7 @@ int orinoco_init(struct orinoco_private *priv)
priv->nicbuf_size = IEEE80211_MAX_FRAME_LEN + ETH_HLEN;
/* Initialize the firmware */
- err = hermes_init(hw);
+ err = hw->ops->init(hw);
if (err != 0) {
dev_err(dev, "Failed to initialize firmware (err = %d)\n",
err);
diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c
index a1006bf..5775124 100644
--- a/drivers/net/wireless/orinoco/wext.c
+++ b/drivers/net/wireless/orinoco/wext.c
@@ -458,7 +458,7 @@ static int orinoco_ioctl_setfreq(struct net_device *dev,
if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
/* Fast channel change - no commit if successful */
hermes_t *hw = &priv->hw;
- err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+ err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
HERMES_TEST_SET_CHANNEL,
chan, NULL);
}
@@ -1273,8 +1273,8 @@ static int orinoco_ioctl_getrid(struct net_device *dev,
if (orinoco_lock(priv, &flags) != 0)
return -EBUSY;
- err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
- extra);
+ err = hw->ops->read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
+ extra);
if (err)
goto out;
--
1.6.4.4
^ permalink raw reply related
* [RFC 3/5] orinoco: encapsulate driver locking
From: David Kilroy @ 2010-04-19 7:35 UTC (permalink / raw)
To: linux-wireless; +Cc: orinoco-devel, David Kilroy
In-Reply-To: <1271662522-29119-1-git-send-email-kilroyd@googlemail.com>
Local bus and USB drivers will need to do locking differently.
The original orinoco_usb patches had a boolean variable controlling
whether spin_lock_bh was used, or irq based locking. This version
provides wrappers for the lock functions and the drivers specify the
functions pointers needed.
This will introduce a performance penalty, but I'm not expecting it to
be noticable.
Signed-off-by: David Kilroy <kilroyd@googlemail.com>
---
drivers/net/wireless/orinoco/hermes.c | 26 +++++++++++++++++++++++++-
drivers/net/wireless/orinoco/hermes.h | 4 ++++
drivers/net/wireless/orinoco/main.c | 12 ++++++------
drivers/net/wireless/orinoco/orinoco.h | 16 +++++++++++++---
4 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c
index a7df524..6448366 100644
--- a/drivers/net/wireless/orinoco/hermes.c
+++ b/drivers/net/wireless/orinoco/hermes.c
@@ -529,6 +529,26 @@ static int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
return err;
}
+void hermes_lock_irqsave(spinlock_t *lock, unsigned long *flags)
+{
+ spin_lock_irqsave(lock, *flags);
+}
+
+void hermes_unlock_irqrestore(spinlock_t *lock, unsigned long *flags)
+{
+ spin_unlock_irqrestore(lock, *flags);
+}
+
+void hermes_lock_irq(spinlock_t *lock)
+{
+ spin_lock_irq(lock);
+}
+
+void hermes_unlock_irq(spinlock_t *lock)
+{
+ spin_unlock_irq(lock);
+}
+
/* Hermes operations for local buses */
static const struct hermes_ops hermes_ops_local = {
.init = hermes_init,
@@ -538,5 +558,9 @@ static const struct hermes_ops hermes_ops_local = {
.read_ltv = hermes_read_ltv,
.write_ltv = hermes_write_ltv,
.bap_pread = hermes_bap_pread,
- .bap_pwrite = hermes_bap_pwrite
+ .bap_pwrite = hermes_bap_pwrite,
+ .lock_irqsave = hermes_lock_irqsave,
+ .unlock_irqrestore = hermes_unlock_irqrestore,
+ .lock_irq = hermes_lock_irq,
+ .unlock_irq = hermes_unlock_irq,
};
diff --git a/drivers/net/wireless/orinoco/hermes.h b/drivers/net/wireless/orinoco/hermes.h
index 18b268c..9e21ecd 100644
--- a/drivers/net/wireless/orinoco/hermes.h
+++ b/drivers/net/wireless/orinoco/hermes.h
@@ -393,6 +393,10 @@ struct hermes_ops {
u16 id, u16 offset);
int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf,
int len, u16 id, u16 offset);
+ void (*lock_irqsave)(spinlock_t *lock, unsigned long *flags);
+ void (*unlock_irqrestore)(spinlock_t *lock, unsigned long *flags);
+ void (*lock_irq)(spinlock_t *lock);
+ void (*unlock_irq)(spinlock_t *lock);
};
/* Basic control structure */
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c
index 9dc458d..9485da2 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/orinoco/main.c
@@ -282,13 +282,13 @@ int orinoco_stop(struct net_device *dev)
/* We mustn't use orinoco_lock() here, because we need to be
able to close the interface even if hw_unavailable is set
(e.g. as we're released after a PC Card removal) */
- spin_lock_irq(&priv->lock);
+ orinoco_lock_irq(priv);
priv->open = 0;
err = __orinoco_down(priv);
- spin_unlock_irq(&priv->lock);
+ orinoco_unlock_irq(priv);
return err;
}
@@ -1742,7 +1742,7 @@ void orinoco_reset(struct work_struct *work)
}
/* This has to be called from user context */
- spin_lock_irq(&priv->lock);
+ orinoco_lock_irq(priv);
priv->hw_unavailable--;
@@ -1757,7 +1757,7 @@ void orinoco_reset(struct work_struct *work)
dev->trans_start = jiffies;
}
- spin_unlock_irq(&priv->lock);
+ orinoco_unlock_irq(priv);
return;
disable:
@@ -2074,9 +2074,9 @@ int orinoco_init(struct orinoco_private *priv)
/* Make the hardware available, as long as it hasn't been
* removed elsewhere (e.g. by PCMCIA hot unplug) */
- spin_lock_irq(&priv->lock);
+ orinoco_lock_irq(priv);
priv->hw_unavailable--;
- spin_unlock_irq(&priv->lock);
+ orinoco_unlock_irq(priv);
dev_dbg(dev, "Ready\n");
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h
index f1901d6..80a1386 100644
--- a/drivers/net/wireless/orinoco/orinoco.h
+++ b/drivers/net/wireless/orinoco/orinoco.h
@@ -212,11 +212,11 @@ void orinoco_tx_timeout(struct net_device *dev);
static inline int orinoco_lock(struct orinoco_private *priv,
unsigned long *flags)
{
- spin_lock_irqsave(&priv->lock, *flags);
+ priv->hw.ops->lock_irqsave(&priv->lock, flags);
if (priv->hw_unavailable) {
DEBUG(1, "orinoco_lock() called with hw_unavailable (dev=%p)\n",
priv->ndev);
- spin_unlock_irqrestore(&priv->lock, *flags);
+ priv->hw.ops->unlock_irqrestore(&priv->lock, flags);
return -EBUSY;
}
return 0;
@@ -225,7 +225,17 @@ static inline int orinoco_lock(struct orinoco_private *priv,
static inline void orinoco_unlock(struct orinoco_private *priv,
unsigned long *flags)
{
- spin_unlock_irqrestore(&priv->lock, *flags);
+ priv->hw.ops->unlock_irqrestore(&priv->lock, flags);
+}
+
+static inline void orinoco_lock_irq(struct orinoco_private *priv)
+{
+ priv->hw.ops->lock_irq(&priv->lock);
+}
+
+static inline void orinoco_unlock_irq(struct orinoco_private *priv)
+{
+ priv->hw.ops->unlock_irq(&priv->lock);
}
/*** Navigate from net_device to orinoco_private ***/
--
1.6.4.4
^ permalink raw reply related
* [RFC 4/5] orinoco: add orinoco_usb driver
From: David Kilroy @ 2010-04-19 7:35 UTC (permalink / raw)
To: linux-wireless; +Cc: orinoco-devel, David Kilroy
In-Reply-To: <1271662522-29119-1-git-send-email-kilroyd@googlemail.com>
This driver uses the core orinoco modules for the bulk of
the functionality. The low level hermes routines (for local bus
cards) are replaced, the driver supplies its own ndo_xmit_start
function, and locking is done with the _bh variant.
Some recent functionality is not available to the USB cards yet
(firmware loading and WPA).
Out-of-tree driver originally written by Manuel Estrada Sainz.
Thanks to Mark Davis for supplying hardware to test the updates.
Signed-off-by: David Kilroy <kilroyd@googlemail.com>
---
drivers/net/wireless/orinoco/Kconfig | 7 +
drivers/net/wireless/orinoco/Makefile | 1 +
drivers/net/wireless/orinoco/hermes.h | 1 +
drivers/net/wireless/orinoco/main.c | 6 +-
drivers/net/wireless/orinoco/orinoco.h | 3 +
drivers/net/wireless/orinoco/orinoco_usb.c | 1678 ++++++++++++++++++++++++++++
6 files changed, 1694 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/wireless/orinoco/orinoco_usb.c
diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig
index 6116b54..60819bc 100644
--- a/drivers/net/wireless/orinoco/Kconfig
+++ b/drivers/net/wireless/orinoco/Kconfig
@@ -132,3 +132,10 @@ config PCMCIA_SPECTRUM
This driver requires firmware download on startup. Utilities
for downloading Symbol firmware are available at
<http://sourceforge.net/projects/orinoco/>
+
+config ORINOCO_USB
+ tristate "Agere Orinoco USB support"
+ depends on USB && HERMES
+ select FW_LOADER
+ ---help---
+ This driver is for USB versions of the Agere Orinoco card.
diff --git a/drivers/net/wireless/orinoco/Makefile b/drivers/net/wireless/orinoco/Makefile
index e645269..bfdefb8 100644
--- a/drivers/net/wireless/orinoco/Makefile
+++ b/drivers/net/wireless/orinoco/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o
obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o
obj-$(CONFIG_NORTEL_HERMES) += orinoco_nortel.o
obj-$(CONFIG_PCMCIA_SPECTRUM) += spectrum_cs.o
+obj-$(CONFIG_ORINOCO_USB) += orinoco_usb.o
# Orinoco should be endian clean.
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/orinoco/hermes.h b/drivers/net/wireless/orinoco/hermes.h
index 9e21ecd..aed14ff 100644
--- a/drivers/net/wireless/orinoco/hermes.h
+++ b/drivers/net/wireless/orinoco/hermes.h
@@ -407,6 +407,7 @@ typedef struct hermes {
#define HERMES_32BIT_REGSPACING 1
u16 inten; /* Which interrupts should be enabled? */
const struct hermes_ops *ops;
+ void *priv;
} hermes_t;
/* Register access convenience macros */
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c
index 9485da2..e48d6a0 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/orinoco/main.c
@@ -798,7 +798,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
stats->rx_dropped++;
}
-static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
+void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
{
struct orinoco_private *priv = ndev_priv(dev);
struct net_device_stats *stats = &priv->stats;
@@ -919,6 +919,7 @@ update_stats:
out:
kfree(desc);
}
+EXPORT_SYMBOL(__orinoco_ev_rx);
static void orinoco_rx(struct net_device *dev,
struct hermes_rx_descriptor *desc,
@@ -1360,7 +1361,7 @@ static void orinoco_process_scan_results(struct work_struct *work)
spin_unlock_irqrestore(&priv->scan_lock, flags);
}
-static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
+void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
{
struct orinoco_private *priv = ndev_priv(dev);
u16 infofid;
@@ -1578,6 +1579,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
return;
}
+EXPORT_SYMBOL(__orinoco_ev_info);
static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
{
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h
index 80a1386..e9f415a 100644
--- a/drivers/net/wireless/orinoco/orinoco.h
+++ b/drivers/net/wireless/orinoco/orinoco.h
@@ -197,6 +197,9 @@ extern int orinoco_up(struct orinoco_private *priv);
extern void orinoco_down(struct orinoco_private *priv);
extern irqreturn_t orinoco_interrupt(int irq, void *dev_id);
+extern void __orinoco_ev_info(struct net_device *dev, hermes_t *hw);
+extern void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw);
+
/* Common ndo functions exported for reuse by orinoco_usb */
int orinoco_open(struct net_device *dev);
int orinoco_stop(struct net_device *dev);
diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c
new file mode 100644
index 0000000..eaec04e
--- /dev/null
+++ b/drivers/net/wireless/orinoco/orinoco_usb.c
@@ -0,0 +1,1678 @@
+/*
+ * USB Orinoco driver
+ *
+ * Copyright (c) 2003 Manuel Estrada Sainz
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License
+ * at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the MPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the MPL or the GPL.
+ *
+ * Queueing code based on linux-wlan-ng 0.2.1-pre5
+ *
+ * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+ *
+ * The license is the same as above.
+ *
+ * Initialy based on USB Skeleton driver - 0.7
+ *
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * NOTE: The original USB Skeleton driver is GPL, but all that code is
+ * gone so MPL/GPL applies.
+ */
+
+#define DRIVER_NAME "orinoco_usb"
+#define PFX DRIVER_NAME ": "
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/timer.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <linux/firmware.h>
+
+#include "orinoco.h"
+
+#ifndef URB_ASYNC_UNLINK
+#define URB_ASYNC_UNLINK 0
+#endif
+
+/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
+static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
+#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2)
+
+struct header_struct {
+ /* 802.3 */
+ u8 dest[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ __be16 len;
+ /* 802.2 */
+ u8 dsap;
+ u8 ssap;
+ u8 ctrl;
+ /* SNAP */
+ u8 oui[3];
+ __be16 ethertype;
+} __attribute__ ((packed));
+
+struct ez_usb_fw {
+ u16 size;
+ const u8 *code;
+};
+
+static struct ez_usb_fw firmware = {
+ .size = 0,
+ .code = NULL,
+};
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+/* Debugging macros */
+#undef dbg
+#define dbg(format, arg...) \
+ do { if (debug) printk(KERN_DEBUG PFX "%s: " format "\n", \
+ __func__ , ## arg); } while (0)
+#undef err
+#define err(format, arg...) \
+ do { printk(KERN_ERR PFX format "\n", ## arg); } while (0)
+
+/* Module paramaters */
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+MODULE_FIRMWARE("orinoco_ezusb_fw");
+
+/*
+ * Under some conditions, the card gets stuck and stops paying attention
+ * to the world (i.e. data communication stalls) until we do something to
+ * it. Sending an INQ_TALLIES command seems to be enough and should be
+ * harmless otherwise. This behaviour has been observed when using the
+ * driver on a systemimager client during installation. In the past a
+ * timer was used to send INQ_TALLIES commands when there was no other
+ * activity, but it was troublesome and was removed.
+ */
+
+#define USB_COMPAQ_VENDOR_ID 0x049f /* Compaq Computer Corp. */
+#define USB_COMPAQ_WL215_ID 0x001f /* Compaq WL215 USB Adapter */
+#define USB_COMPAQ_W200_ID 0x0076 /* Compaq W200 USB Adapter */
+#define USB_HP_WL215_ID 0x0082 /* Compaq WL215 USB Adapter */
+
+#define USB_MELCO_VENDOR_ID 0x0411
+#define USB_BUFFALO_L11_ID 0x0006 /* BUFFALO WLI-USB-L11 */
+#define USB_BUFFALO_L11G_WR_ID 0x000B /* BUFFALO WLI-USB-L11G-WR */
+#define USB_BUFFALO_L11G_ID 0x000D /* BUFFALO WLI-USB-L11G */
+
+#define USB_LUCENT_VENDOR_ID 0x047E /* Lucent Technologies */
+#define USB_LUCENT_ORINOCO_ID 0x0300 /* Lucent/Agere Orinoco USB Client */
+
+#define USB_AVAYA8_VENDOR_ID 0x0D98
+#define USB_AVAYAE_VENDOR_ID 0x0D9E
+#define USB_AVAYA_WIRELESS_ID 0x0300 /* Avaya Wireless USB Card */
+
+#define USB_AGERE_VENDOR_ID 0x0D4E /* Agere Systems */
+#define USB_AGERE_MODEL0801_ID 0x1000 /* Wireless USB Card Model 0801 */
+#define USB_AGERE_MODEL0802_ID 0x1001 /* Wireless USB Card Model 0802 */
+#define USB_AGERE_REBRANDED_ID 0x047A /* WLAN USB Card */
+
+#define USB_ELSA_VENDOR_ID 0x05CC
+#define USB_ELSA_AIRLANCER_ID 0x3100 /* ELSA AirLancer USB-11 */
+
+#define USB_LEGEND_VENDOR_ID 0x0E7C
+#define USB_LEGEND_JOYNET_ID 0x0300 /* Joynet WLAN USB Card */
+
+#define USB_SAMSUNG_VENDOR_ID 0x04E8
+#define USB_SAMSUNG_SEW2001U1_ID 0x5002 /* Samsung SEW-2001u Card */
+#define USB_SAMSUNG_SEW2001U2_ID 0x5B11 /* Samsung SEW-2001u Card */
+#define USB_SAMSUNG_SEW2003U_ID 0x7011 /* Samsung SEW-2003U Card */
+
+#define USB_IGATE_VENDOR_ID 0x0681
+#define USB_IGATE_IGATE_11M_ID 0x0012 /* I-GATE 11M USB Card */
+
+#define USB_FUJITSU_VENDOR_ID 0x0BF8
+#define USB_FUJITSU_E1100_ID 0x1002 /* connect2AIR WLAN E-1100 USB */
+
+#define USB_2WIRE_VENDOR_ID 0x1630
+#define USB_2WIRE_WIRELESS_ID 0xff81 /* 2Wire Wireless USB adapter */
+
+
+#define EZUSB_REQUEST_FW_TRANS 0xA0
+#define EZUSB_REQUEST_TRIGER 0xAA
+#define EZUSB_REQUEST_TRIG_AC 0xAC
+#define EZUSB_CPUCS_REG 0x7F92
+
+#define EZUSB_RID_TX 0x0700
+#define EZUSB_RID_RX 0x0701
+#define EZUSB_RID_INIT1 0x0702
+#define EZUSB_RID_ACK 0x0710
+#define EZUSB_RID_DOCMD 0x0860
+
+/* Recognize info frames */
+#define EZUSB_IS_INFO(id) ((id >= 0xF000) && (id <= 0xF2FF))
+
+#define EZUSB_MAGIC 0x0210
+
+#define EZUSB_FRAME_DATA 1
+#define EZUSB_FRAME_CONTROL 2
+
+#define DEF_TIMEOUT (3*HZ)
+
+#define BULK_BUF_SIZE 2048
+
+#define FW_BUF_SIZE 64
+#define FW_VAR_OFFSET_PTR 0x359
+#define FW_VAR_VALUE 0
+#define FW_HOLE_START 0x100
+#define FW_HOLE_END 0x300
+
+struct ezusb_packet {
+ __le16 magic; /* 0x0210 */
+ u8 req_reply_count;
+ u8 ans_reply_count;
+ __le16 frame_type; /* 0x01 for data frames, 0x02 otherwise */
+ __le16 size; /* transport size */
+ __le16 crc; /* CRC up to here */
+ __le16 hermes_len;
+ __le16 hermes_rid;
+ u8 data[0];
+} __attribute__ ((packed));
+
+/* Table of devices that work or may work with this driver */
+static struct usb_device_id ezusb_table[] = {
+ {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_WL215_ID)},
+ {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_HP_WL215_ID)},
+ {USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_W200_ID)},
+ {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11_ID)},
+ {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_WR_ID)},
+ {USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_ID)},
+ {USB_DEVICE(USB_LUCENT_VENDOR_ID, USB_LUCENT_ORINOCO_ID)},
+ {USB_DEVICE(USB_AVAYA8_VENDOR_ID, USB_AVAYA_WIRELESS_ID)},
+ {USB_DEVICE(USB_AVAYAE_VENDOR_ID, USB_AVAYA_WIRELESS_ID)},
+ {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0801_ID)},
+ {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0802_ID)},
+ {USB_DEVICE(USB_ELSA_VENDOR_ID, USB_ELSA_AIRLANCER_ID)},
+ {USB_DEVICE(USB_LEGEND_VENDOR_ID, USB_LEGEND_JOYNET_ID)},
+ {USB_DEVICE_VER(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U1_ID,
+ 0, 0)},
+ {USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U2_ID)},
+ {USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2003U_ID)},
+ {USB_DEVICE(USB_IGATE_VENDOR_ID, USB_IGATE_IGATE_11M_ID)},
+ {USB_DEVICE(USB_FUJITSU_VENDOR_ID, USB_FUJITSU_E1100_ID)},
+ {USB_DEVICE(USB_2WIRE_VENDOR_ID, USB_2WIRE_WIRELESS_ID)},
+ {USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_REBRANDED_ID)},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ezusb_table);
+
+/* Structure to hold all of our device specific stuff */
+struct ezusb_priv {
+ struct usb_device *udev;
+ struct net_device *dev;
+ struct mutex mtx;
+ spinlock_t req_lock;
+ struct list_head req_pending;
+ struct list_head req_active;
+ spinlock_t reply_count_lock;
+ u16 hermes_reg_fake[0x40];
+ u8 *bap_buf;
+ struct urb *read_urb;
+ int read_pipe;
+ int write_pipe;
+ u8 reply_count;
+};
+
+enum ezusb_state {
+ EZUSB_CTX_START,
+ EZUSB_CTX_QUEUED,
+ EZUSB_CTX_REQ_SUBMITTED,
+ EZUSB_CTX_REQ_COMPLETE,
+ EZUSB_CTX_RESP_RECEIVED,
+ EZUSB_CTX_REQ_TIMEOUT,
+ EZUSB_CTX_REQ_FAILED,
+ EZUSB_CTX_RESP_TIMEOUT,
+ EZUSB_CTX_REQSUBMIT_FAIL,
+ EZUSB_CTX_COMPLETE,
+};
+
+struct request_context {
+ struct list_head list;
+ atomic_t refcount;
+ struct completion done; /* Signals that CTX is dead */
+ int killed;
+ struct urb *outurb; /* OUT for req pkt */
+ struct ezusb_priv *upriv;
+ struct ezusb_packet *buf;
+ int buf_length;
+ struct timer_list timer; /* Timeout handling */
+ enum ezusb_state state; /* Current state */
+ /* the RID that we will wait for */
+ u16 out_rid;
+ u16 in_rid;
+};
+
+
+/* Forward declarations */
+static void ezusb_ctx_complete(struct request_context *ctx);
+static void ezusb_req_queue_run(struct ezusb_priv *upriv);
+static void ezusb_bulk_in_callback(struct urb *urb);
+
+static inline u8 ezusb_reply_inc(u8 count)
+{
+ if (count < 0x7F)
+ return count + 1;
+ else
+ return 1;
+}
+
+static void ezusb_request_context_put(struct request_context *ctx)
+{
+ if (!atomic_dec_and_test(&ctx->refcount))
+ return;
+
+ WARN_ON(!ctx->done.done);
+ BUG_ON(ctx->outurb->status == -EINPROGRESS);
+ BUG_ON(timer_pending(&ctx->timer));
+ usb_free_urb(ctx->outurb);
+ kfree(ctx->buf);
+ kfree(ctx);
+}
+
+static inline void ezusb_mod_timer(struct ezusb_priv *upriv,
+ struct timer_list *timer,
+ unsigned long expire)
+{
+ if (!upriv->udev)
+ return;
+ mod_timer(timer, expire);
+}
+
+static void ezusb_request_timerfn(u_long _ctx)
+{
+ struct request_context *ctx = (void *) _ctx;
+
+ ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK;
+ if (usb_unlink_urb(ctx->outurb) == -EINPROGRESS) {
+ ctx->state = EZUSB_CTX_REQ_TIMEOUT;
+ } else {
+ ctx->state = EZUSB_CTX_RESP_TIMEOUT;
+ dbg("couldn't unlink");
+ atomic_inc(&ctx->refcount);
+ ctx->killed = 1;
+ ezusb_ctx_complete(ctx);
+ ezusb_request_context_put(ctx);
+ }
+};
+
+static struct request_context *ezusb_alloc_ctx(struct ezusb_priv *upriv,
+ u16 out_rid, u16 in_rid)
+{
+ struct request_context *ctx;
+
+ ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC);
+ if (!ctx)
+ return NULL;
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->buf = kmalloc(BULK_BUF_SIZE, GFP_ATOMIC);
+ if (!ctx->buf) {
+ kfree(ctx);
+ return NULL;
+ }
+ ctx->outurb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!ctx->outurb) {
+ kfree(ctx->buf);
+ kfree(ctx);
+ return NULL;
+ }
+
+ ctx->upriv = upriv;
+ ctx->state = EZUSB_CTX_START;
+ ctx->out_rid = out_rid;
+ ctx->in_rid = in_rid;
+
+ atomic_set(&ctx->refcount, 1);
+ init_completion(&ctx->done);
+
+ init_timer(&ctx->timer);
+ ctx->timer.function = ezusb_request_timerfn;
+ ctx->timer.data = (u_long) ctx;
+ return ctx;
+}
+
+
+/* Hopefully the real complete_all will soon be exported, in the mean
+ * while this should work. */
+static inline void ezusb_complete_all(struct completion *comp)
+{
+ complete(comp);
+ complete(comp);
+ complete(comp);
+ complete(comp);
+}
+
+static void ezusb_ctx_complete(struct request_context *ctx)
+{
+ struct ezusb_priv *upriv = ctx->upriv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&upriv->req_lock, flags);
+
+ list_del_init(&ctx->list);
+ if (upriv->udev) {
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+ ezusb_req_queue_run(upriv);
+ spin_lock_irqsave(&upriv->req_lock, flags);
+ }
+
+ switch (ctx->state) {
+ case EZUSB_CTX_COMPLETE:
+ case EZUSB_CTX_REQSUBMIT_FAIL:
+ case EZUSB_CTX_REQ_FAILED:
+ case EZUSB_CTX_REQ_TIMEOUT:
+ case EZUSB_CTX_RESP_TIMEOUT:
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ if ((ctx->out_rid == EZUSB_RID_TX) && upriv->dev) {
+ struct net_device *dev = upriv->dev;
+ struct orinoco_private *priv = ndev_priv(dev);
+ struct net_device_stats *stats = &priv->stats;
+
+ if (ctx->state != EZUSB_CTX_COMPLETE)
+ stats->tx_errors++;
+ else
+ stats->tx_packets++;
+
+ netif_wake_queue(dev);
+ }
+ ezusb_complete_all(&ctx->done);
+ ezusb_request_context_put(ctx);
+ break;
+
+ default:
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+ if (!upriv->udev) {
+ /* This is normal, as all request contexts get flushed
+ * when the device is disconnected */
+ err("Called, CTX not terminating, but device gone");
+ ezusb_complete_all(&ctx->done);
+ ezusb_request_context_put(ctx);
+ break;
+ }
+
+ err("Called, CTX not in terminating state.");
+ /* Things are really bad if this happens. Just leak
+ * the CTX because it may still be linked to the
+ * queue or the OUT urb may still be active.
+ * Just leaking at least prevents an Oops or Panic.
+ */
+ break;
+ }
+}
+
+/**
+ * ezusb_req_queue_run:
+ * Description:
+ * Note: Only one active CTX at any one time, because there's no
+ * other (reliable) way to match the response URB to the correct
+ * CTX.
+ **/
+static void ezusb_req_queue_run(struct ezusb_priv *upriv)
+{
+ unsigned long flags;
+ struct request_context *ctx;
+ int result;
+
+ spin_lock_irqsave(&upriv->req_lock, flags);
+
+ if (!list_empty(&upriv->req_active))
+ goto unlock;
+
+ if (list_empty(&upriv->req_pending))
+ goto unlock;
+
+ ctx =
+ list_entry(upriv->req_pending.next, struct request_context,
+ list);
+
+ if (!ctx->upriv->udev)
+ goto unlock;
+
+ /* We need to split this off to avoid a race condition */
+ list_move_tail(&ctx->list, &upriv->req_active);
+
+ if (ctx->state == EZUSB_CTX_QUEUED) {
+ atomic_inc(&ctx->refcount);
+ result = usb_submit_urb(ctx->outurb, GFP_ATOMIC);
+ if (result) {
+ ctx->state = EZUSB_CTX_REQSUBMIT_FAIL;
+
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ err("Fatal, failed to submit command urb."
+ " error=%d\n", result);
+
+ ezusb_ctx_complete(ctx);
+ ezusb_request_context_put(ctx);
+ goto done;
+ }
+
+ ctx->state = EZUSB_CTX_REQ_SUBMITTED;
+ ezusb_mod_timer(ctx->upriv, &ctx->timer,
+ jiffies + DEF_TIMEOUT);
+ }
+
+ unlock:
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ done:
+ return;
+}
+
+static void ezusb_req_enqueue_run(struct ezusb_priv *upriv,
+ struct request_context *ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&upriv->req_lock, flags);
+
+ if (!ctx->upriv->udev) {
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+ goto done;
+ }
+ atomic_inc(&ctx->refcount);
+ list_add_tail(&ctx->list, &upriv->req_pending);
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ ctx->state = EZUSB_CTX_QUEUED;
+ ezusb_req_queue_run(upriv);
+
+ done:
+ return;
+}
+
+static void ezusb_request_out_callback(struct urb *urb)
+{
+ unsigned long flags;
+ enum ezusb_state state;
+ struct request_context *ctx = urb->context;
+ struct ezusb_priv *upriv = ctx->upriv;
+
+ spin_lock_irqsave(&upriv->req_lock, flags);
+
+ del_timer(&ctx->timer);
+
+ if (ctx->killed) {
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+ pr_warning("interrupt called with dead ctx");
+ goto out;
+ }
+
+ state = ctx->state;
+
+ if (urb->status == 0) {
+ switch (state) {
+ case EZUSB_CTX_REQ_SUBMITTED:
+ if (ctx->in_rid) {
+ ctx->state = EZUSB_CTX_REQ_COMPLETE;
+ /* reply URB still pending */
+ ezusb_mod_timer(upriv, &ctx->timer,
+ jiffies + DEF_TIMEOUT);
+ spin_unlock_irqrestore(&upriv->req_lock,
+ flags);
+ break;
+ }
+ /* fall through */
+ case EZUSB_CTX_RESP_RECEIVED:
+ /* IN already received before this OUT-ACK */
+ ctx->state = EZUSB_CTX_COMPLETE;
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+ ezusb_ctx_complete(ctx);
+ break;
+
+ default:
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+ err("Unexpected state(0x%x, %d) in OUT URB",
+ state, urb->status);
+ break;
+ }
+ } else {
+ /* If someone cancels the OUT URB then its status
+ * should be either -ECONNRESET or -ENOENT.
+ */
+ switch (state) {
+ case EZUSB_CTX_REQ_SUBMITTED:
+ case EZUSB_CTX_RESP_RECEIVED:
+ ctx->state = EZUSB_CTX_REQ_FAILED;
+ /* fall through */
+
+ case EZUSB_CTX_REQ_FAILED:
+ case EZUSB_CTX_REQ_TIMEOUT:
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ ezusb_ctx_complete(ctx);
+ break;
+
+ default:
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ err("Unexpected state(0x%x, %d) in OUT URB",
+ state, urb->status);
+ break;
+ }
+ }
+ out:
+ ezusb_request_context_put(ctx);
+}
+
+static void ezusb_request_in_callback(struct ezusb_priv *upriv,
+ struct urb *urb)
+{
+ struct ezusb_packet *ans = urb->transfer_buffer;
+ struct request_context *ctx = NULL;
+ enum ezusb_state state;
+ unsigned long flags;
+
+ /* Find the CTX on the active queue that requested this URB */
+ spin_lock_irqsave(&upriv->req_lock, flags);
+ if (upriv->udev) {
+ struct list_head *item;
+
+ list_for_each(item, &upriv->req_active) {
+ struct request_context *c;
+ int reply_count;
+
+ c = list_entry(item, struct request_context, list);
+ reply_count =
+ ezusb_reply_inc(c->buf->req_reply_count);
+ if ((ans->ans_reply_count == reply_count)
+ && (le16_to_cpu(ans->hermes_rid) == c->in_rid)) {
+ ctx = c;
+ break;
+ }
+ dbg("Skipped (0x%x/0x%x) (%d/%d)",
+ le16_to_cpu(ans->hermes_rid),
+ c->in_rid, ans->ans_reply_count, reply_count);
+ }
+ }
+
+ if (ctx == NULL) {
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+ err("%s: got unexpected RID: 0x%04X", __func__,
+ le16_to_cpu(ans->hermes_rid));
+ ezusb_req_queue_run(upriv);
+ return;
+ }
+
+ /* The data we want is in the in buffer, exchange */
+ urb->transfer_buffer = ctx->buf;
+ ctx->buf = (void *) ans;
+ ctx->buf_length = urb->actual_length;
+
+ state = ctx->state;
+ switch (state) {
+ case EZUSB_CTX_REQ_SUBMITTED:
+ /* We have received our response URB before
+ * our request has been acknowledged. Do NOT
+ * destroy our CTX yet, because our OUT URB
+ * is still alive ...
+ */
+ ctx->state = EZUSB_CTX_RESP_RECEIVED;
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ /* Let the machine continue running. */
+ break;
+
+ case EZUSB_CTX_REQ_COMPLETE:
+ /* This is the usual path: our request
+ * has already been acknowledged, and
+ * we have now received the reply.
+ */
+ ctx->state = EZUSB_CTX_COMPLETE;
+
+ /* Stop the intimer */
+ del_timer(&ctx->timer);
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ /* Call the completion handler */
+ ezusb_ctx_complete(ctx);
+ break;
+
+ default:
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ pr_warning("Matched IN URB, unexpected context state(0x%x)",
+ state);
+ /* Throw this CTX away and try submitting another */
+ del_timer(&ctx->timer);
+ ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK;
+ usb_unlink_urb(ctx->outurb);
+ ezusb_req_queue_run(upriv);
+ break;
+ } /* switch */
+}
+
+
+static void ezusb_req_ctx_wait(struct ezusb_priv *upriv,
+ struct request_context *ctx)
+{
+ switch (ctx->state) {
+ case EZUSB_CTX_QUEUED:
+ case EZUSB_CTX_REQ_SUBMITTED:
+ case EZUSB_CTX_REQ_COMPLETE:
+ case EZUSB_CTX_RESP_RECEIVED:
+ if (in_atomic()) {
+ /* If we get called from a timer, timeout timers don't
+ * get the chance to run themselves. So we make sure
+ * that we don't sleep for ever */
+ int msecs = DEF_TIMEOUT * (1000 / HZ);
+ while (!ctx->done.done && msecs--)
+ udelay(1000);
+ } else {
+ wait_event_interruptible(ctx->done.wait,
+ ctx->done.done);
+ }
+ break;
+ default:
+ /* Done or failed - nothing to wait for */
+ break;
+ }
+}
+
+static inline u16 build_crc(struct ezusb_packet *data)
+{
+ u16 crc = 0;
+ u8 *bytes = (u8 *)data;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ crc = (crc << 1) + bytes[i];
+
+ return crc;
+}
+
+/**
+ * ezusb_fill_req:
+ *
+ * if data == NULL and length > 0 the data is assumed to be already in
+ * the target buffer and only the header is filled.
+ *
+ */
+static int ezusb_fill_req(struct ezusb_packet *req, u16 length, u16 rid,
+ const void *data, u16 frame_type, u8 reply_count)
+{
+ int total_size = sizeof(*req) + length;
+
+ BUG_ON(total_size > BULK_BUF_SIZE);
+
+ req->magic = cpu_to_le16(EZUSB_MAGIC);
+ req->req_reply_count = reply_count;
+ req->ans_reply_count = 0;
+ req->frame_type = cpu_to_le16(frame_type);
+ req->size = cpu_to_le16(length + 4);
+ req->crc = cpu_to_le16(build_crc(req));
+ req->hermes_len = cpu_to_le16(HERMES_BYTES_TO_RECLEN(length));
+ req->hermes_rid = cpu_to_le16(rid);
+ if (data)
+ memcpy(req->data, data, length);
+ return total_size;
+}
+
+static int ezusb_submit_in_urb(struct ezusb_priv *upriv)
+{
+ int retval = 0;
+ void *cur_buf = upriv->read_urb->transfer_buffer;
+
+ if (upriv->read_urb->status == -EINPROGRESS) {
+ dbg("urb busy, not resubmiting");
+ retval = -EBUSY;
+ goto exit;
+ }
+ usb_fill_bulk_urb(upriv->read_urb, upriv->udev, upriv->read_pipe,
+ cur_buf, BULK_BUF_SIZE,
+ ezusb_bulk_in_callback, upriv);
+ upriv->read_urb->transfer_flags = 0;
+ retval = usb_submit_urb(upriv->read_urb, GFP_ATOMIC);
+ if (retval)
+ err("%s submit failed %d", __func__, retval);
+
+ exit:
+ return retval;
+}
+
+static inline int ezusb_8051_cpucs(struct ezusb_priv *upriv, int reset)
+{
+ u8 res_val = reset; /* avoid argument promotion */
+
+ if (!upriv->udev) {
+ err("%s: !upriv->udev", __func__);
+ return -EFAULT;
+ }
+ return usb_control_msg(upriv->udev,
+ usb_sndctrlpipe(upriv->udev, 0),
+ EZUSB_REQUEST_FW_TRANS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+ USB_DIR_OUT, EZUSB_CPUCS_REG, 0, &res_val,
+ sizeof(res_val), DEF_TIMEOUT);
+}
+
+static int ezusb_firmware_download(struct ezusb_priv *upriv,
+ struct ez_usb_fw *fw)
+{
+ u8 fw_buffer[FW_BUF_SIZE];
+ int retval, addr;
+ int variant_offset;
+
+ /*
+ * This byte is 1 and should be replaced with 0. The offset is
+ * 0x10AD in version 0.0.6. The byte in question should follow
+ * the end of the code pointed to by the jump in the beginning
+ * of the firmware. Also, it is read by code located at 0x358.
+ */
+ variant_offset = be16_to_cpup((__be16 *) &fw->code[FW_VAR_OFFSET_PTR]);
+ if (variant_offset >= fw->size) {
+ printk(KERN_ERR PFX "Invalid firmware variant offset: "
+ "0x%04x\n", variant_offset);
+ retval = -EINVAL;
+ goto fail;
+ }
+
+ retval = ezusb_8051_cpucs(upriv, 1);
+ if (retval < 0)
+ goto fail;
+ for (addr = 0; addr < fw->size; addr += FW_BUF_SIZE) {
+ /* 0x100-0x300 should be left alone, it contains card
+ * specific data, like USB enumeration information */
+ if ((addr >= FW_HOLE_START) && (addr < FW_HOLE_END))
+ continue;
+
+ memcpy(fw_buffer, &fw->code[addr], FW_BUF_SIZE);
+ if (variant_offset >= addr &&
+ variant_offset < addr + FW_BUF_SIZE) {
+ dbg("Patching card_variant byte at 0x%04X",
+ variant_offset);
+ fw_buffer[variant_offset - addr] = FW_VAR_VALUE;
+ }
+ retval = usb_control_msg(upriv->udev,
+ usb_sndctrlpipe(upriv->udev, 0),
+ EZUSB_REQUEST_FW_TRANS,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE
+ | USB_DIR_OUT,
+ addr, 0x0,
+ fw_buffer, FW_BUF_SIZE,
+ DEF_TIMEOUT);
+
+ if (retval < 0)
+ goto fail;
+ }
+ retval = ezusb_8051_cpucs(upriv, 0);
+ if (retval < 0)
+ goto fail;
+
+ goto exit;
+ fail:
+ printk(KERN_ERR PFX "Firmware download failed, error %d\n",
+ retval);
+ exit:
+ return retval;
+}
+
+static int ezusb_access_ltv(struct ezusb_priv *upriv,
+ struct request_context *ctx,
+ u16 length, const void *data, u16 frame_type,
+ void *ans_buff, int ans_size, u16 *ans_length)
+{
+ int req_size;
+ int retval = 0;
+ enum ezusb_state state;
+
+ BUG_ON(in_irq());
+
+ if (!upriv->udev) {
+ dbg("Device disconnected");
+ return -ENODEV;
+ }
+
+ if (upriv->read_urb->status != -EINPROGRESS)
+ err("%s: in urb not pending", __func__);
+
+ /* protect upriv->reply_count, guarantee sequential numbers */
+ spin_lock_bh(&upriv->reply_count_lock);
+ req_size = ezusb_fill_req(ctx->buf, length, ctx->out_rid, data,
+ frame_type, upriv->reply_count);
+ usb_fill_bulk_urb(ctx->outurb, upriv->udev, upriv->write_pipe,
+ ctx->buf, req_size,
+ ezusb_request_out_callback, ctx);
+
+ if (ctx->in_rid)
+ upriv->reply_count = ezusb_reply_inc(upriv->reply_count);
+
+ ezusb_req_enqueue_run(upriv, ctx);
+
+ spin_unlock_bh(&upriv->reply_count_lock);
+
+ if (ctx->in_rid)
+ ezusb_req_ctx_wait(upriv, ctx);
+
+ state = ctx->state;
+ switch (state) {
+ case EZUSB_CTX_COMPLETE:
+ retval = ctx->outurb->status;
+ break;
+
+ case EZUSB_CTX_QUEUED:
+ case EZUSB_CTX_REQ_SUBMITTED:
+ if (!ctx->in_rid)
+ break;
+ default:
+ err("%s: Unexpected context state %d", __func__,
+ state);
+ /* fall though */
+ case EZUSB_CTX_REQ_TIMEOUT:
+ case EZUSB_CTX_REQ_FAILED:
+ case EZUSB_CTX_RESP_TIMEOUT:
+ case EZUSB_CTX_REQSUBMIT_FAIL:
+ printk(KERN_ERR PFX "Access failed, resetting (state %d,"
+ " reply_count %d)\n", state, upriv->reply_count);
+ upriv->reply_count = 0;
+ if (state == EZUSB_CTX_REQ_TIMEOUT
+ || state == EZUSB_CTX_RESP_TIMEOUT) {
+ printk(KERN_ERR PFX "ctx timed out\n");
+ retval = -ETIMEDOUT;
+ } else {
+ printk(KERN_ERR PFX "ctx failed\n");
+ retval = -EFAULT;
+ }
+ goto exit;
+ break;
+ }
+ if (ctx->in_rid) {
+ struct ezusb_packet *ans = ctx->buf;
+ int exp_len;
+
+ if (ans->hermes_len != 0)
+ exp_len = le16_to_cpu(ans->hermes_len) * 2 + 12;
+ else
+ exp_len = 14;
+
+ if (exp_len != ctx->buf_length) {
+ err("%s: lenght mismatch for RID 0x%04x: "
+ "expected %d, got %d", __func__,
+ ctx->in_rid, exp_len, ctx->buf_length);
+ retval = -EIO;
+ goto exit;
+ }
+
+ if (ans_buff)
+ memcpy(ans_buff, ans->data,
+ min_t(int, exp_len, ans_size));
+ if (ans_length)
+ *ans_length = le16_to_cpu(ans->hermes_len);
+ }
+ exit:
+ ezusb_request_context_put(ctx);
+ return retval;
+}
+
+static int ezusb_write_ltv(hermes_t *hw, int bap, u16 rid,
+ u16 length, const void *data)
+{
+ struct ezusb_priv *upriv = hw->priv;
+ u16 frame_type;
+ struct request_context *ctx;
+
+ if (length == 0)
+ return -EINVAL;
+
+ length = HERMES_RECLEN_TO_BYTES(length);
+
+ /* On memory mapped devices HERMES_RID_CNFGROUPADDRESSES can be
+ * set to be empty, but the USB bridge doesn't like it */
+ if (length == 0)
+ return 0;
+
+ ctx = ezusb_alloc_ctx(upriv, rid, EZUSB_RID_ACK);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (rid == EZUSB_RID_TX)
+ frame_type = EZUSB_FRAME_DATA;
+ else
+ frame_type = EZUSB_FRAME_CONTROL;
+
+ return ezusb_access_ltv(upriv, ctx, length, data, frame_type,
+ NULL, 0, NULL);
+}
+
+static int ezusb_read_ltv(hermes_t *hw, int bap, u16 rid,
+ unsigned bufsize, u16 *length, void *buf)
+{
+ struct ezusb_priv *upriv = hw->priv;
+ struct request_context *ctx;
+
+ if ((bufsize < 0) || (bufsize % 2))
+ return -EINVAL;
+
+ ctx = ezusb_alloc_ctx(upriv, rid, rid);
+ if (!ctx)
+ return -ENOMEM;
+
+ return ezusb_access_ltv(upriv, ctx, 0, NULL, EZUSB_FRAME_CONTROL,
+ buf, bufsize, length);
+}
+
+static int ezusb_doicmd_wait(hermes_t *hw, u16 cmd, u16 parm0, u16 parm1,
+ u16 parm2, struct hermes_response *resp)
+{
+ struct ezusb_priv *upriv = hw->priv;
+ struct request_context *ctx;
+
+ __le16 data[4] = {
+ cpu_to_le16(cmd),
+ cpu_to_le16(parm0),
+ cpu_to_le16(parm1),
+ cpu_to_le16(parm2),
+ };
+ dbg("0x%04X, parm0 0x%04X, parm1 0x%04X, parm2 0x%04X",
+ cmd, parm0, parm1, parm2);
+ ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK);
+ if (!ctx)
+ return -ENOMEM;
+
+ return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
+ EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+}
+
+static int ezusb_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
+ struct hermes_response *resp)
+{
+ struct ezusb_priv *upriv = hw->priv;
+ struct request_context *ctx;
+
+ __le16 data[4] = {
+ cpu_to_le16(cmd),
+ cpu_to_le16(parm0),
+ 0,
+ 0,
+ };
+ dbg("0x%04X, parm0 0x%04X", cmd, parm0);
+ ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK);
+ if (!ctx)
+ return -ENOMEM;
+
+ return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
+ EZUSB_FRAME_CONTROL, NULL, 0, NULL);
+}
+
+static int ezusb_bap_pread(struct hermes *hw, int bap,
+ void *buf, int len, u16 id, u16 offset)
+{
+ struct ezusb_priv *upriv = hw->priv;
+ struct ezusb_packet *ans = (void *) upriv->read_urb->transfer_buffer;
+ int actual_length = upriv->read_urb->actual_length;
+
+ if (id == EZUSB_RID_RX) {
+ if ((sizeof(*ans) + offset + len) > actual_length) {
+ printk(KERN_ERR PFX "BAP read beyond buffer end "
+ "in rx frame\n");
+ return -EINVAL;
+ }
+ memcpy(buf, ans->data + offset, len);
+ return 0;
+ }
+
+ if (EZUSB_IS_INFO(id)) {
+ /* Include 4 bytes for length/type */
+ if ((sizeof(*ans) + offset + len - 4) > actual_length) {
+ printk(KERN_ERR PFX "BAP read beyond buffer end "
+ "in info frame\n");
+ return -EFAULT;
+ }
+ memcpy(buf, ans->data + offset - 4, len);
+ } else {
+ printk(KERN_ERR PFX "Unexpected fid 0x%04x\n", id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct orinoco_private *priv = ndev_priv(dev);
+ struct net_device_stats *stats = &priv->stats;
+ struct ezusb_priv *upriv = priv->card;
+ int err = 0;
+ char *p;
+ struct ethhdr *eh;
+ int len, data_len, data_off;
+ __le16 tx_control;
+ unsigned long flags;
+ struct request_context *ctx;
+ u8 *buf;
+ int tx_size;
+
+ if (!netif_running(dev)) {
+ printk(KERN_ERR "%s: Tx on stopped device!\n",
+ dev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (netif_queue_stopped(dev)) {
+ printk(KERN_DEBUG "%s: Tx while transmitter busy!\n",
+ dev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (orinoco_lock(priv, &flags) != 0) {
+ printk(KERN_ERR
+ "%s: orinoco_xmit() called while hw_unavailable\n",
+ dev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (!netif_carrier_ok(dev) ||
+ (priv->iw_mode == IW_MODE_MONITOR)) {
+ /* Oops, the firmware hasn't established a connection,
+ silently drop the packet (this seems to be the
+ safest approach). */
+ stats->tx_errors++;
+ orinoco_unlock(priv, &flags);
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_TX, 0);
+ if (!ctx)
+ goto fail;
+
+ memset(ctx->buf, 0, BULK_BUF_SIZE);
+ buf = ctx->buf->data;
+
+ /* Length of the packet body */
+ /* FIXME: what if the skb is smaller than this? */
+ len = max_t(int, skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN);
+
+ eh = (struct ethhdr *) skb->data;
+
+ tx_control = cpu_to_le16(0);
+ memcpy(buf, &tx_control, sizeof(tx_control));
+ buf += sizeof(tx_control);
+ /* Encapsulate Ethernet-II frames */
+ if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
+ struct header_struct *hdr = (void *) buf;
+ buf += sizeof(*hdr);
+ data_len = len;
+ data_off = sizeof(tx_control) + sizeof(*hdr);
+ p = skb->data + ETH_HLEN;
+
+ /* 802.3 header */
+ memcpy(hdr->dest, eh->h_dest, ETH_ALEN);
+ memcpy(hdr->src, eh->h_source, ETH_ALEN);
+ hdr->len = htons(data_len + ENCAPS_OVERHEAD);
+
+ /* 802.2 header */
+ memcpy(&hdr->dsap, &encaps_hdr, sizeof(encaps_hdr));
+
+ hdr->ethertype = eh->h_proto;
+ } else { /* IEEE 802.3 frame */
+ data_len = len + ETH_HLEN;
+ data_off = sizeof(tx_control);
+ p = skb->data;
+ }
+
+ memcpy(buf, p, data_len);
+ buf += data_len;
+
+ /* Finally, we actually initiate the send */
+ netif_stop_queue(dev);
+
+ /* The card may behave better if we send evenly sized usb transfers */
+ tx_size = ALIGN(buf - ctx->buf->data, 2);
+
+ err = ezusb_access_ltv(upriv, ctx, tx_size, NULL,
+ EZUSB_FRAME_DATA, NULL, 0, NULL);
+
+ if (err) {
+ netif_start_queue(dev);
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: Error %d transmitting packet\n",
+ dev->name, err);
+ stats->tx_errors++;
+ goto fail;
+ }
+
+ dev->trans_start = jiffies;
+ stats->tx_bytes += data_off + data_len;
+
+ orinoco_unlock(priv, &flags);
+
+ dev_kfree_skb(skb);
+
+ return NETDEV_TX_OK;
+
+ fail:
+ orinoco_unlock(priv, &flags);
+ return NETDEV_TX_BUSY;
+}
+
+static int ezusb_allocate(struct hermes *hw, u16 size, u16 *fid)
+{
+ *fid = EZUSB_RID_TX;
+ return 0;
+}
+
+
+static int ezusb_hard_reset(struct orinoco_private *priv)
+{
+ struct ezusb_priv *upriv = priv->card;
+ int retval = ezusb_8051_cpucs(upriv, 1);
+
+ if (retval < 0) {
+ err("Failed to reset");
+ return retval;
+ }
+
+ retval = ezusb_8051_cpucs(upriv, 0);
+ if (retval < 0) {
+ err("Failed to unreset");
+ return retval;
+ }
+
+ dbg("sending control message");
+ retval = usb_control_msg(upriv->udev,
+ usb_sndctrlpipe(upriv->udev, 0),
+ EZUSB_REQUEST_TRIGER,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+ USB_DIR_OUT, 0x0, 0x0, NULL, 0,
+ DEF_TIMEOUT);
+ if (retval < 0) {
+ err("EZUSB_REQUEST_TRIGER failed retval %d", retval);
+ return retval;
+ }
+#if 0
+ dbg("Sending EZUSB_REQUEST_TRIG_AC");
+ retval = usb_control_msg(upriv->udev,
+ usb_sndctrlpipe(upriv->udev, 0),
+ EZUSB_REQUEST_TRIG_AC,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+ USB_DIR_OUT, 0x00FA, 0x0, NULL, 0,
+ DEF_TIMEOUT);
+ if (retval < 0) {
+ err("EZUSB_REQUEST_TRIG_AC failed retval %d", retval);
+ return retval;
+ }
+#endif
+
+ return 0;
+}
+
+
+static int ezusb_init(hermes_t *hw)
+{
+ struct ezusb_priv *upriv = hw->priv;
+ int retval;
+
+ BUG_ON(in_interrupt());
+ BUG_ON(!upriv);
+
+ upriv->reply_count = 0;
+ /* Write the MAGIC number on the simulated registers to keep
+ * orinoco.c happy */
+ hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC);
+ hermes_write_regn(hw, RXFID, EZUSB_RID_RX);
+
+ usb_kill_urb(upriv->read_urb);
+ ezusb_submit_in_urb(upriv);
+
+ retval = ezusb_write_ltv(hw, 0, EZUSB_RID_INIT1,
+ HERMES_BYTES_TO_RECLEN(2), "\x10\x00");
+ if (retval < 0) {
+ printk(KERN_ERR PFX "EZUSB_RID_INIT1 error %d\n", retval);
+ return retval;
+ }
+
+ retval = ezusb_docmd_wait(hw, HERMES_CMD_INIT, 0, NULL);
+ if (retval < 0) {
+ printk(KERN_ERR PFX "HERMES_CMD_INIT error %d\n", retval);
+ return retval;
+ }
+
+ return 0;
+}
+
+static void ezusb_bulk_in_callback(struct urb *urb)
+{
+ struct ezusb_priv *upriv = (struct ezusb_priv *) urb->context;
+ struct ezusb_packet *ans = urb->transfer_buffer;
+ u16 crc;
+ u16 hermes_rid;
+
+ if (upriv->udev == NULL) {
+ dbg("disconnected");
+ return;
+ }
+
+ if (urb->status == -ETIMEDOUT) {
+ /* When a device gets unplugged we get this every time
+ * we resubmit, flooding the logs. Since we don't use
+ * USB timeouts, it shouldn't happen any other time*/
+ pr_warning("%s: urb timed out, not resubmiting", __func__);
+ return;
+ }
+ if (urb->status == -ECONNABORTED) {
+ pr_warning("%s: connection abort, resubmiting urb",
+ __func__);
+ goto resubmit;
+ }
+ if ((urb->status == -EILSEQ)
+ || (urb->status == -ENOENT)
+ || (urb->status == -ECONNRESET)) {
+ dbg("status %d, not resubmiting", urb->status);
+ return;
+ }
+ if (urb->status)
+ dbg("status: %d length: %d",
+ urb->status, urb->actual_length);
+ if (urb->actual_length < sizeof(*ans)) {
+ err("%s: short read, ignoring", __func__);
+ goto resubmit;
+ }
+ crc = build_crc(ans);
+ if (le16_to_cpu(ans->crc) != crc) {
+ err("CRC error, ignoring packet");
+ goto resubmit;
+ }
+
+ hermes_rid = le16_to_cpu(ans->hermes_rid);
+ if ((hermes_rid != EZUSB_RID_RX) && !EZUSB_IS_INFO(hermes_rid)) {
+ ezusb_request_in_callback(upriv, urb);
+ } else if (upriv->dev) {
+ struct net_device *dev = upriv->dev;
+ struct orinoco_private *priv = ndev_priv(dev);
+ hermes_t *hw = &priv->hw;
+
+ if (hermes_rid == EZUSB_RID_RX) {
+ __orinoco_ev_rx(dev, hw);
+ } else {
+ hermes_write_regn(hw, INFOFID,
+ le16_to_cpu(ans->hermes_rid));
+ __orinoco_ev_info(dev, hw);
+ }
+ }
+
+ resubmit:
+ if (upriv->udev)
+ ezusb_submit_in_urb(upriv);
+}
+
+static inline void ezusb_delete(struct ezusb_priv *upriv)
+{
+ struct net_device *dev;
+ struct list_head *item;
+ struct list_head *tmp_item;
+ unsigned long flags;
+
+ BUG_ON(in_interrupt());
+ BUG_ON(!upriv);
+
+ dev = upriv->dev;
+ mutex_lock(&upriv->mtx);
+
+ upriv->udev = NULL; /* No timer will be rearmed from here */
+
+ usb_kill_urb(upriv->read_urb);
+
+ spin_lock_irqsave(&upriv->req_lock, flags);
+ list_for_each_safe(item, tmp_item, &upriv->req_active) {
+ struct request_context *ctx;
+ int err;
+
+ ctx = list_entry(item, struct request_context, list);
+ atomic_inc(&ctx->refcount);
+
+ ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK;
+ err = usb_unlink_urb(ctx->outurb);
+
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+ if (err == -EINPROGRESS)
+ wait_for_completion(&ctx->done);
+
+ del_timer_sync(&ctx->timer);
+ /* FIXME: there is an slight chance for the irq handler to
+ * be running */
+ if (!list_empty(&ctx->list))
+ ezusb_ctx_complete(ctx);
+
+ ezusb_request_context_put(ctx);
+ spin_lock_irqsave(&upriv->req_lock, flags);
+ }
+ spin_unlock_irqrestore(&upriv->req_lock, flags);
+
+ list_for_each_safe(item, tmp_item, &upriv->req_pending)
+ ezusb_ctx_complete(list_entry(item,
+ struct request_context, list));
+
+ if (upriv->read_urb->status == -EINPROGRESS)
+ printk(KERN_ERR PFX "Some URB in progress\n");
+
+ mutex_unlock(&upriv->mtx);
+
+ kfree(upriv->read_urb->transfer_buffer);
+ if (upriv->bap_buf != NULL)
+ kfree(upriv->bap_buf);
+ if (upriv->read_urb != NULL)
+ usb_free_urb(upriv->read_urb);
+ if (upriv->dev) {
+ struct orinoco_private *priv = ndev_priv(upriv->dev);
+ orinoco_if_del(priv);
+ free_orinocodev(priv);
+ }
+}
+
+void ezusb_lock_irqsave(spinlock_t *lock, unsigned long *flags)
+{
+ spin_lock_bh(lock);
+}
+
+void ezusb_unlock_irqrestore(spinlock_t *lock, unsigned long *flags)
+{
+ spin_unlock_bh(lock);
+}
+
+void ezusb_lock_irq(spinlock_t *lock)
+{
+ spin_lock_bh(lock);
+}
+
+void ezusb_unlock_irq(spinlock_t *lock)
+{
+ spin_unlock_bh(lock);
+}
+
+static const struct hermes_ops ezusb_ops = {
+ .init = ezusb_init,
+ .cmd_wait = ezusb_docmd_wait,
+ .init_cmd_wait = ezusb_doicmd_wait,
+ .allocate = ezusb_allocate,
+ .read_ltv = ezusb_read_ltv,
+ .write_ltv = ezusb_write_ltv,
+ .bap_pread = ezusb_bap_pread,
+ .lock_irqsave = ezusb_lock_irqsave,
+ .unlock_irqrestore = ezusb_unlock_irqrestore,
+ .lock_irq = ezusb_lock_irq,
+ .unlock_irq = ezusb_unlock_irq,
+};
+
+static const struct net_device_ops ezusb_netdev_ops = {
+ .ndo_open = orinoco_open,
+ .ndo_stop = orinoco_stop,
+ .ndo_start_xmit = ezusb_xmit,
+ .ndo_set_multicast_list = orinoco_set_multicast_list,
+ .ndo_change_mtu = orinoco_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_tx_timeout = orinoco_tx_timeout,
+ .ndo_get_stats = orinoco_get_stats,
+};
+
+static int ezusb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct orinoco_private *priv;
+ hermes_t *hw;
+ struct ezusb_priv *upriv = NULL;
+ struct usb_interface_descriptor *iface_desc;
+ struct usb_endpoint_descriptor *ep;
+ const struct firmware *fw_entry;
+ int retval = 0;
+ int i;
+
+ priv = alloc_orinocodev(sizeof(*upriv), &udev->dev,
+ ezusb_hard_reset, NULL);
+ if (!priv) {
+ err("Couldn't allocate orinocodev");
+ goto exit;
+ }
+
+ hw = &priv->hw;
+
+ upriv = priv->card;
+
+ mutex_init(&upriv->mtx);
+ spin_lock_init(&upriv->reply_count_lock);
+
+ spin_lock_init(&upriv->req_lock);
+ INIT_LIST_HEAD(&upriv->req_pending);
+ INIT_LIST_HEAD(&upriv->req_active);
+
+ upriv->udev = udev;
+
+ hw->iobase = (void __force __iomem *) &upriv->hermes_reg_fake;
+ hw->reg_spacing = HERMES_16BIT_REGSPACING;
+ hw->priv = upriv;
+ hw->ops = &ezusb_ops;
+
+ /* set up the endpoint information */
+ /* check out the endpoints */
+
+ iface_desc = &interface->altsetting[0].desc;
+ for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
+ ep = &interface->altsetting[0].endpoint[i].desc;
+
+ if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) &&
+ ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_BULK)) {
+ /* we found a bulk in endpoint */
+ if (upriv->read_urb != NULL) {
+ pr_warning("Found a second bulk in ep, ignored");
+ continue;
+ }
+
+ upriv->read_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!upriv->read_urb) {
+ err("No free urbs available");
+ goto error;
+ }
+ if (le16_to_cpu(ep->wMaxPacketSize) != 64)
+ pr_warning("bulk in: wMaxPacketSize!= 64");
+ if (ep->bEndpointAddress != (2 | USB_DIR_IN))
+ pr_warning("bulk in: bEndpointAddress: %d",
+ ep->bEndpointAddress);
+ upriv->read_pipe = usb_rcvbulkpipe(udev,
+ ep->
+ bEndpointAddress);
+ upriv->read_urb->transfer_buffer =
+ kmalloc(BULK_BUF_SIZE, GFP_KERNEL);
+ if (!upriv->read_urb->transfer_buffer) {
+ err("Couldn't allocate IN buffer");
+ goto error;
+ }
+ }
+
+ if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_OUT) &&
+ ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_BULK)) {
+ /* we found a bulk out endpoint */
+ if (upriv->bap_buf != NULL) {
+ pr_warning("Found a second bulk out ep, ignored");
+ continue;
+ }
+
+ if (le16_to_cpu(ep->wMaxPacketSize) != 64)
+ pr_warning("bulk out: wMaxPacketSize != 64");
+ if (ep->bEndpointAddress != 2)
+ pr_warning("bulk out: bEndpointAddress: %d",
+ ep->bEndpointAddress);
+ upriv->write_pipe = usb_sndbulkpipe(udev,
+ ep->
+ bEndpointAddress);
+ upriv->bap_buf = kmalloc(BULK_BUF_SIZE, GFP_KERNEL);
+ if (!upriv->bap_buf) {
+ err("Couldn't allocate bulk_out_buffer");
+ goto error;
+ }
+ }
+ }
+ if (!upriv->bap_buf || !upriv->read_urb) {
+ err("Didn't find the required bulk endpoints");
+ goto error;
+ }
+
+ if (request_firmware(&fw_entry, "orinoco_ezusb_fw",
+ &interface->dev) == 0) {
+ firmware.size = fw_entry->size;
+ firmware.code = fw_entry->data;
+ }
+ if (firmware.size && firmware.code) {
+ ezusb_firmware_download(upriv, &firmware);
+ } else {
+ err("No firmware to download");
+ goto error;
+ }
+
+ if (ezusb_hard_reset(priv) < 0) {
+ err("Cannot reset the device");
+ goto error;
+ }
+
+ /* If the firmware is already downloaded orinoco.c will call
+ * ezusb_init but if the firmware is not already there, that will make
+ * the kernel very unstable, so we try initializing here and quit in
+ * case of error */
+ if (ezusb_init(hw) < 0) {
+ err("Couldn't initialize the device");
+ err("Firmware may not be downloaded or may be wrong.");
+ goto error;
+ }
+
+ /* Initialise the main driver */
+ if (orinoco_init(priv) != 0) {
+ err("orinoco_init() failed\n");
+ goto error;
+ }
+
+ if (orinoco_if_add(priv, 0, 0, &ezusb_netdev_ops) != 0) {
+ upriv->dev = NULL;
+ err("%s: orinoco_if_add() failed", __func__);
+ goto error;
+ }
+ upriv->dev = priv->ndev;
+
+ goto exit;
+
+ error:
+ ezusb_delete(upriv);
+ if (upriv->dev) {
+ /* upriv->dev was 0, so ezusb_delete() didn't free it */
+ free_orinocodev(priv);
+ }
+ upriv = NULL;
+ retval = -EFAULT;
+ exit:
+ if (fw_entry) {
+ firmware.code = NULL;
+ firmware.size = 0;
+ release_firmware(fw_entry);
+ }
+ usb_set_intfdata(interface, upriv);
+ return retval;
+}
+
+
+static void ezusb_disconnect(struct usb_interface *intf)
+{
+ struct ezusb_priv *upriv = usb_get_intfdata(intf);
+ usb_set_intfdata(intf, NULL);
+ ezusb_delete(upriv);
+ printk(KERN_INFO PFX "Disconnected\n");
+}
+
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver orinoco_driver = {
+ .name = DRIVER_NAME,
+ .probe = ezusb_probe,
+ .disconnect = ezusb_disconnect,
+ .id_table = ezusb_table,
+};
+
+/* Can't be declared "const" or the whole __initdata section will
+ * become const */
+static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
+ " (Manuel Estrada Sainz)";
+
+static int __init ezusb_module_init(void)
+{
+ int err;
+
+ printk(KERN_DEBUG "%s\n", version);
+
+ /* register this driver with the USB subsystem */
+ err = usb_register(&orinoco_driver);
+ if (err < 0) {
+ printk(KERN_ERR PFX "usb_register failed, error %d\n",
+ err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit ezusb_module_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&orinoco_driver);
+}
+
+
+module_init(ezusb_module_init);
+module_exit(ezusb_module_exit);
+
+MODULE_AUTHOR("Manuel Estrada Sainz");
+MODULE_DESCRIPTION
+ ("Driver for Orinoco wireless LAN cards using EZUSB bridge");
+MODULE_LICENSE("Dual MPL/GPL");
--
1.6.4.4
^ permalink raw reply related
* [RFC 5/5] orinoco_usb: avoid in_atomic
From: David Kilroy @ 2010-04-19 7:35 UTC (permalink / raw)
To: linux-wireless; +Cc: orinoco-devel, David Kilroy
In-Reply-To: <1271662522-29119-1-git-send-email-kilroyd@googlemail.com>
We expect to be either in process contect or soft interrupt context. So
use in_softirq instead.
Signed-off-by: David Kilroy <kilroyd@googlemail.com>
---
drivers/net/wireless/orinoco/orinoco_usb.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c
index eaec04e..1abc303 100644
--- a/drivers/net/wireless/orinoco/orinoco_usb.c
+++ b/drivers/net/wireless/orinoco/orinoco_usb.c
@@ -701,7 +701,7 @@ static void ezusb_req_ctx_wait(struct ezusb_priv *upriv,
case EZUSB_CTX_REQ_SUBMITTED:
case EZUSB_CTX_REQ_COMPLETE:
case EZUSB_CTX_RESP_RECEIVED:
- if (in_atomic()) {
+ if (in_softirq()) {
/* If we get called from a timer, timeout timers don't
* get the chance to run themselves. So we make sure
* that we don't sleep for ever */
--
1.6.4.4
^ permalink raw reply related
* [RFC 2/5] orinoco: allow driver to specify netdev_ops
From: David Kilroy @ 2010-04-19 7:35 UTC (permalink / raw)
To: linux-wireless; +Cc: orinoco-devel, David Kilroy
In-Reply-To: <1271662522-29119-1-git-send-email-kilroyd@googlemail.com>
Allow the main drivers to specify a custom version of the net_device_ops
structure. This is required by orinoco_usb to supply a separate transmit
function.
Export existing net_device_ops callbacks so that the drivers can reuse
some of the existing code.
Signed-off-by: David Kilroy <kilroyd@googlemail.com>
---
drivers/net/wireless/orinoco/airport.c | 2 +-
drivers/net/wireless/orinoco/main.c | 28 +++++++++++++++++-------
drivers/net/wireless/orinoco/orinoco.h | 11 +++++++++-
drivers/net/wireless/orinoco/orinoco_cs.c | 2 +-
drivers/net/wireless/orinoco/orinoco_nortel.c | 2 +-
drivers/net/wireless/orinoco/orinoco_pci.c | 2 +-
drivers/net/wireless/orinoco/orinoco_plx.c | 2 +-
drivers/net/wireless/orinoco/orinoco_tmd.c | 2 +-
drivers/net/wireless/orinoco/spectrum_cs.c | 2 +-
9 files changed, 37 insertions(+), 16 deletions(-)
diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/orinoco/airport.c
index ea23c4f..7dac5ad 100644
--- a/drivers/net/wireless/orinoco/airport.c
+++ b/drivers/net/wireless/orinoco/airport.c
@@ -210,7 +210,7 @@ airport_attach(struct macio_dev *mdev, const struct of_device_id *match)
}
/* Register an interface with the stack */
- if (orinoco_if_add(priv, phys_addr, card->irq) != 0) {
+ if (orinoco_if_add(priv, phys_addr, card->irq, NULL) != 0) {
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
goto failed;
}
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c
index 8c8009c..9dc458d 100644
--- a/drivers/net/wireless/orinoco/main.c
+++ b/drivers/net/wireless/orinoco/main.c
@@ -254,7 +254,7 @@ void set_port_type(struct orinoco_private *priv)
/* Device methods */
/********************************************************************/
-static int orinoco_open(struct net_device *dev)
+int orinoco_open(struct net_device *dev)
{
struct orinoco_private *priv = ndev_priv(dev);
unsigned long flags;
@@ -272,8 +272,9 @@ static int orinoco_open(struct net_device *dev)
return err;
}
+EXPORT_SYMBOL(orinoco_open);
-static int orinoco_stop(struct net_device *dev)
+int orinoco_stop(struct net_device *dev)
{
struct orinoco_private *priv = ndev_priv(dev);
int err = 0;
@@ -291,15 +292,17 @@ static int orinoco_stop(struct net_device *dev)
return err;
}
+EXPORT_SYMBOL(orinoco_stop);
-static struct net_device_stats *orinoco_get_stats(struct net_device *dev)
+struct net_device_stats *orinoco_get_stats(struct net_device *dev)
{
struct orinoco_private *priv = ndev_priv(dev);
return &priv->stats;
}
+EXPORT_SYMBOL(orinoco_get_stats);
-static void orinoco_set_multicast_list(struct net_device *dev)
+void orinoco_set_multicast_list(struct net_device *dev)
{
struct orinoco_private *priv = ndev_priv(dev);
unsigned long flags;
@@ -313,8 +316,9 @@ static void orinoco_set_multicast_list(struct net_device *dev)
__orinoco_set_multicast_list(dev);
orinoco_unlock(priv, &flags);
}
+EXPORT_SYMBOL(orinoco_set_multicast_list);
-static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
+int orinoco_change_mtu(struct net_device *dev, int new_mtu)
{
struct orinoco_private *priv = ndev_priv(dev);
@@ -330,6 +334,7 @@ static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+EXPORT_SYMBOL(orinoco_change_mtu);
/********************************************************************/
/* Tx path */
@@ -615,7 +620,7 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
netif_wake_queue(dev);
}
-static void orinoco_tx_timeout(struct net_device *dev)
+void orinoco_tx_timeout(struct net_device *dev)
{
struct orinoco_private *priv = ndev_priv(dev);
struct net_device_stats *stats = &priv->stats;
@@ -630,6 +635,7 @@ static void orinoco_tx_timeout(struct net_device *dev)
schedule_work(&priv->reset_work);
}
+EXPORT_SYMBOL(orinoco_tx_timeout);
/********************************************************************/
/* Rx path (data frames) */
@@ -2193,7 +2199,8 @@ EXPORT_SYMBOL(alloc_orinocodev);
*/
int orinoco_if_add(struct orinoco_private *priv,
unsigned long base_addr,
- unsigned int irq)
+ unsigned int irq,
+ const struct net_device_ops *ops)
{
struct wiphy *wiphy = priv_to_wiphy(priv);
struct wireless_dev *wdev;
@@ -2212,12 +2219,17 @@ int orinoco_if_add(struct orinoco_private *priv,
/* Setup / override net_device fields */
dev->ieee80211_ptr = wdev;
- dev->netdev_ops = &orinoco_netdev_ops;
dev->watchdog_timeo = HZ; /* 1 second timeout */
dev->wireless_handlers = &orinoco_handler_def;
#ifdef WIRELESS_SPY
dev->wireless_data = &priv->wireless_data;
#endif
+ /* Default to standard ops if not set */
+ if (ops)
+ dev->netdev_ops = ops;
+ else
+ dev->netdev_ops = &orinoco_netdev_ops;
+
/* we use the default eth_mac_addr for setting the MAC addr */
/* Reserve space in skb for the SNAP header */
diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h
index ff6b7b1..f1901d6 100644
--- a/drivers/net/wireless/orinoco/orinoco.h
+++ b/drivers/net/wireless/orinoco/orinoco.h
@@ -190,12 +190,21 @@ extern void free_orinocodev(struct orinoco_private *priv);
extern int orinoco_init(struct orinoco_private *priv);
extern int orinoco_if_add(struct orinoco_private *priv,
unsigned long base_addr,
- unsigned int irq);
+ unsigned int irq,
+ const struct net_device_ops *ops);
extern void orinoco_if_del(struct orinoco_private *priv);
extern int orinoco_up(struct orinoco_private *priv);
extern void orinoco_down(struct orinoco_private *priv);
extern irqreturn_t orinoco_interrupt(int irq, void *dev_id);
+/* Common ndo functions exported for reuse by orinoco_usb */
+int orinoco_open(struct net_device *dev);
+int orinoco_stop(struct net_device *dev);
+struct net_device_stats *orinoco_get_stats(struct net_device *dev);
+void orinoco_set_multicast_list(struct net_device *dev);
+int orinoco_change_mtu(struct net_device *dev, int new_mtu);
+void orinoco_tx_timeout(struct net_device *dev);
+
/********************************************************************/
/* Locking and synchronization functions */
/********************************************************************/
diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c
index fdc9613..525f74e 100644
--- a/drivers/net/wireless/orinoco/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco/orinoco_cs.c
@@ -296,7 +296,7 @@ orinoco_cs_config(struct pcmcia_device *link)
/* Register an interface with the stack */
if (orinoco_if_add(priv, link->io.BasePort1,
- link->irq.AssignedIRQ) != 0) {
+ link->irq.AssignedIRQ, NULL) != 0) {
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
goto failed;
}
diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/orinoco/orinoco_nortel.c
index 075f446..bc3ea0b 100644
--- a/drivers/net/wireless/orinoco/orinoco_nortel.c
+++ b/drivers/net/wireless/orinoco/orinoco_nortel.c
@@ -220,7 +220,7 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev,
goto fail;
}
- err = orinoco_if_add(priv, 0, 0);
+ err = orinoco_if_add(priv, 0, 0, NULL);
if (err) {
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
goto fail;
diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c
index bda5317..468197f 100644
--- a/drivers/net/wireless/orinoco/orinoco_pci.c
+++ b/drivers/net/wireless/orinoco/orinoco_pci.c
@@ -170,7 +170,7 @@ static int orinoco_pci_init_one(struct pci_dev *pdev,
goto fail;
}
- err = orinoco_if_add(priv, 0, 0);
+ err = orinoco_if_add(priv, 0, 0, NULL);
if (err) {
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
goto fail;
diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c
index e0d5874..9358f4d 100644
--- a/drivers/net/wireless/orinoco/orinoco_plx.c
+++ b/drivers/net/wireless/orinoco/orinoco_plx.c
@@ -259,7 +259,7 @@ static int orinoco_plx_init_one(struct pci_dev *pdev,
goto fail;
}
- err = orinoco_if_add(priv, 0, 0);
+ err = orinoco_if_add(priv, 0, 0, NULL);
if (err) {
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
goto fail;
diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c
index 88cbc79..784605f 100644
--- a/drivers/net/wireless/orinoco/orinoco_tmd.c
+++ b/drivers/net/wireless/orinoco/orinoco_tmd.c
@@ -156,7 +156,7 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev,
goto fail;
}
- err = orinoco_if_add(priv, 0, 0);
+ err = orinoco_if_add(priv, 0, 0, NULL);
if (err) {
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
goto fail;
diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c
index 59bda24..77b5871 100644
--- a/drivers/net/wireless/orinoco/spectrum_cs.c
+++ b/drivers/net/wireless/orinoco/spectrum_cs.c
@@ -374,7 +374,7 @@ spectrum_cs_config(struct pcmcia_device *link)
/* Register an interface with the stack */
if (orinoco_if_add(priv, link->io.BasePort1,
- link->irq.AssignedIRQ) != 0) {
+ link->irq.AssignedIRQ, NULL) != 0) {
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
goto failed;
}
--
1.6.4.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox