3 Broadcom BCM43xx wireless driver
5 Transmission (TX/RX) related functions.
7 Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
8 Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
9 Copyright (C) 2005, 2006 Michael Buesch <mb@bu3sch.de>
10 Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
11 Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; see the file COPYING. If not, write to
25 the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
26 Boston, MA 02110-1301, USA.
30 #include "bcm43xx_xmit.h"
31 #include "bcm43xx_phy.h"
32 #include "bcm43xx_dma.h"
33 #include "bcm43xx_pio.h"
36 /* Extract the bitrate out of a CCK PLCP header. */
37 static u8
bcm43xx_plcp_get_bitrate_cck(struct bcm43xx_plcp_hdr6
*plcp
)
39 switch (plcp
->raw
[0]) {
41 return BCM43xx_CCK_RATE_1MB
;
43 return BCM43xx_CCK_RATE_2MB
;
45 return BCM43xx_CCK_RATE_5MB
;
47 return BCM43xx_CCK_RATE_11MB
;
53 /* Extract the bitrate out of an OFDM PLCP header. */
54 static u8
bcm43xx_plcp_get_bitrate_ofdm(struct bcm43xx_plcp_hdr6
*plcp
)
56 switch (plcp
->raw
[0] & 0xF) {
58 return BCM43xx_OFDM_RATE_6MB
;
60 return BCM43xx_OFDM_RATE_9MB
;
62 return BCM43xx_OFDM_RATE_12MB
;
64 return BCM43xx_OFDM_RATE_18MB
;
66 return BCM43xx_OFDM_RATE_24MB
;
68 return BCM43xx_OFDM_RATE_36MB
;
70 return BCM43xx_OFDM_RATE_48MB
;
72 return BCM43xx_OFDM_RATE_54MB
;
78 u8
bcm43xx_plcp_get_ratecode_cck(const u8 bitrate
)
81 case BCM43xx_CCK_RATE_1MB
:
83 case BCM43xx_CCK_RATE_2MB
:
85 case BCM43xx_CCK_RATE_5MB
:
87 case BCM43xx_CCK_RATE_11MB
:
94 u8
bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate
)
97 case BCM43xx_OFDM_RATE_6MB
:
99 case BCM43xx_OFDM_RATE_9MB
:
101 case BCM43xx_OFDM_RATE_12MB
:
103 case BCM43xx_OFDM_RATE_18MB
:
105 case BCM43xx_OFDM_RATE_24MB
:
107 case BCM43xx_OFDM_RATE_36MB
:
109 case BCM43xx_OFDM_RATE_48MB
:
111 case BCM43xx_OFDM_RATE_54MB
:
118 void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4
*plcp
,
119 const u16 octets
, const u8 bitrate
)
121 __le32
*data
= &(plcp
->data
);
122 __u8
*raw
= plcp
->raw
;
124 if (bcm43xx_is_ofdm_rate(bitrate
)) {
125 *data
= bcm43xx_plcp_get_ratecode_ofdm(bitrate
);
126 assert(!(octets
& 0xF000));
127 *data
|= (octets
<< 5);
128 *data
= cpu_to_le32(*data
);
132 plen
= octets
* 16 / bitrate
;
133 if ((octets
* 16 % bitrate
) > 0) {
135 if ((bitrate
== BCM43xx_CCK_RATE_11MB
)
136 && ((octets
* 8 % 11) < 4)) {
142 *data
|= cpu_to_le32(plen
<< 16);
143 raw
[0] = bcm43xx_plcp_get_ratecode_cck(bitrate
);
147 static u8
bcm43xx_calc_fallback_rate(u8 bitrate
)
150 case BCM43xx_CCK_RATE_1MB
:
151 return BCM43xx_CCK_RATE_1MB
;
152 case BCM43xx_CCK_RATE_2MB
:
153 return BCM43xx_CCK_RATE_1MB
;
154 case BCM43xx_CCK_RATE_5MB
:
155 return BCM43xx_CCK_RATE_2MB
;
156 case BCM43xx_CCK_RATE_11MB
:
157 return BCM43xx_CCK_RATE_5MB
;
158 case BCM43xx_OFDM_RATE_6MB
:
159 return BCM43xx_CCK_RATE_5MB
;
160 case BCM43xx_OFDM_RATE_9MB
:
161 return BCM43xx_OFDM_RATE_6MB
;
162 case BCM43xx_OFDM_RATE_12MB
:
163 return BCM43xx_OFDM_RATE_9MB
;
164 case BCM43xx_OFDM_RATE_18MB
:
165 return BCM43xx_OFDM_RATE_12MB
;
166 case BCM43xx_OFDM_RATE_24MB
:
167 return BCM43xx_OFDM_RATE_18MB
;
168 case BCM43xx_OFDM_RATE_36MB
:
169 return BCM43xx_OFDM_RATE_24MB
;
170 case BCM43xx_OFDM_RATE_48MB
:
171 return BCM43xx_OFDM_RATE_36MB
;
172 case BCM43xx_OFDM_RATE_54MB
:
173 return BCM43xx_OFDM_RATE_48MB
;
179 static void generate_txhdr_fw4(struct bcm43xx_wldev
*dev
,
180 struct bcm43xx_txhdr_fw4
*txhdr
,
181 const unsigned char *fragment_data
,
182 unsigned int fragment_len
,
183 const struct ieee80211_tx_control
*txctl
,
186 const struct bcm43xx_phy
*phy
= &dev
->phy
;
187 const struct ieee80211_hdr
*wlhdr
= (const struct ieee80211_hdr
*)fragment_data
;
188 int use_encryption
= ((!(txctl
->flags
& IEEE80211_TXCTL_DO_NOT_ENCRYPT
)) &&
189 (txctl
->key_idx
>= 0));
190 u16 fctl
= le16_to_cpu(wlhdr
->frame_control
);
192 int rate_ofdm
, rate_fb_ofdm
;
193 unsigned int plcp_fragment_len
;
198 memset(txhdr
, 0, sizeof(*txhdr
));
200 rate
= txctl
->tx_rate
;
201 rate_ofdm
= bcm43xx_is_ofdm_rate(rate
);
202 rate_fb
= (txctl
->alt_retry_rate
== -1) ? rate
: txctl
->alt_retry_rate
;
203 rate_fb_ofdm
= bcm43xx_is_ofdm_rate(rate_fb
);
206 txhdr
->phy_rate
= bcm43xx_plcp_get_ratecode_ofdm(rate
);
208 txhdr
->phy_rate
= bcm43xx_plcp_get_ratecode_cck(rate
);
209 txhdr
->mac_frame_ctl
= wlhdr
->frame_control
;
210 memcpy(txhdr
->tx_receiver
, wlhdr
->addr1
, 6);
212 /* Calculate duration for fallback rate */
213 if ((rate_fb
== rate
) ||
214 (wlhdr
->duration_id
& cpu_to_le16(0x8000)) ||
215 (wlhdr
->duration_id
== cpu_to_le16(0))) {
216 /* If the fallback rate equals the normal rate or the
217 * dur_id field contains an AID, CFP magic or 0,
218 * use the original dur_id field. */
219 txhdr
->dur_fb
= wlhdr
->duration_id
;
221 int fbrate_base100kbps
= BCM43xx_RATE_TO_BASE100KBPS(rate_fb
);
222 txhdr
->dur_fb
= ieee80211_generic_frame_duration(dev
->wl
->hw
,
227 plcp_fragment_len
= fragment_len
+ FCS_LEN
;
228 if (use_encryption
) {
229 u8 key_idx
= (u16
)(txctl
->key_idx
);
230 struct bcm43xx_key
*key
;
234 assert(key_idx
< dev
->max_nr_keys
);
235 key
= &(dev
->key
[key_idx
]);
238 /* Hardware appends ICV. */
239 plcp_fragment_len
+= txctl
->icv_len
;
241 key_idx
= bcm43xx_kidx_to_fw(dev
, key_idx
);
242 mac_ctl
|= (key_idx
<< BCM43xx_TX4_MAC_KEYIDX_SHIFT
) &
243 BCM43xx_TX4_MAC_KEYIDX
;
244 mac_ctl
|= (key
->algorithm
<< BCM43xx_TX4_MAC_KEYALG_SHIFT
) &
245 BCM43xx_TX4_MAC_KEYALG
;
246 wlhdr_len
= ieee80211_get_hdrlen(fctl
);
247 iv_len
= min((size_t)txctl
->iv_len
,
248 ARRAY_SIZE(txhdr
->iv
));
249 memcpy(txhdr
->iv
, ((u8
*)wlhdr
) + wlhdr_len
, iv_len
);
252 bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4
*)(&txhdr
->plcp
),
253 plcp_fragment_len
, rate
);
254 bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4
*)(&txhdr
->plcp_fb
),
255 plcp_fragment_len
, rate_fb
);
257 /* Extra Frame Types */
259 extra_ft
|= BCM43xx_TX4_EFT_FBOFDM
;
261 /* Set channel radio code. Note that the micrcode ORs 0x100 to
262 * this value before comparing it to the value in SHM, if this
265 txhdr
->chan_radio_code
= phy
->channel
;
267 /* PHY TX Control word */
269 phy_ctl
|= BCM43xx_TX4_PHY_OFDM
;
270 if (dev
->short_preamble
)
271 phy_ctl
|= BCM43xx_TX4_PHY_SHORTPRMBL
;
272 switch (txctl
->antenna_sel_tx
) {
274 phy_ctl
|= BCM43xx_TX4_PHY_ANTLAST
;
277 phy_ctl
|= BCM43xx_TX4_PHY_ANT0
;
280 phy_ctl
|= BCM43xx_TX4_PHY_ANT1
;
287 if (!(txctl
->flags
& IEEE80211_TXCTL_NO_ACK
))
288 mac_ctl
|= BCM43xx_TX4_MAC_ACK
;
289 if (!(((fctl
& IEEE80211_FCTL_FTYPE
) == IEEE80211_FTYPE_CTL
) &&
290 ((fctl
& IEEE80211_FCTL_STYPE
) == IEEE80211_STYPE_PSPOLL
)))
291 mac_ctl
|= BCM43xx_TX4_MAC_HWSEQ
;
292 if (txctl
->flags
& IEEE80211_TXCTL_FIRST_FRAGMENT
)
293 mac_ctl
|= BCM43xx_TX4_MAC_STMSDU
;
294 if (phy
->type
== BCM43xx_PHYTYPE_A
)
295 mac_ctl
|= BCM43xx_TX4_MAC_5GHZ
;
297 /* Generate the RTS or CTS-to-self frame */
298 if ((txctl
->flags
& IEEE80211_TXCTL_USE_RTS_CTS
) ||
299 (txctl
->flags
& IEEE80211_TXCTL_USE_CTS_PROTECT
)) {
301 struct ieee80211_hdr
*hdr
;
302 int rts_rate
, rts_rate_fb
;
303 int rts_rate_ofdm
, rts_rate_fb_ofdm
;
305 rts_rate
= txctl
->rts_cts_rate
;
306 rts_rate_ofdm
= bcm43xx_is_ofdm_rate(rts_rate
);
307 rts_rate_fb
= bcm43xx_calc_fallback_rate(rts_rate
);
308 rts_rate_fb_ofdm
= bcm43xx_is_ofdm_rate(rts_rate_fb
);
310 if (txctl
->flags
& IEEE80211_TXCTL_USE_CTS_PROTECT
) {
311 ieee80211_ctstoself_get(dev
->wl
->hw
,
312 fragment_data
, fragment_len
, txctl
,
313 (struct ieee80211_cts
*)(txhdr
->rts_frame
));
314 mac_ctl
|= BCM43xx_TX4_MAC_SENDCTS
;
315 len
= sizeof(struct ieee80211_cts
);
317 ieee80211_rts_get(dev
->wl
->hw
,
318 fragment_data
, fragment_len
, txctl
,
319 (struct ieee80211_rts
*)(txhdr
->rts_frame
));
320 mac_ctl
|= BCM43xx_TX4_MAC_SENDRTS
;
321 len
= sizeof(struct ieee80211_rts
);
324 bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4
*)(&txhdr
->rts_plcp
),
326 bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4
*)(&txhdr
->rts_plcp_fb
),
328 hdr
= (struct ieee80211_hdr
*)(&txhdr
->rts_frame
);
329 txhdr
->rts_dur_fb
= hdr
->duration_id
;
331 extra_ft
|= BCM43xx_TX4_EFT_RTSOFDM
;
332 txhdr
->phy_rate_rts
= bcm43xx_plcp_get_ratecode_ofdm(rts_rate
);
334 txhdr
->phy_rate_rts
= bcm43xx_plcp_get_ratecode_cck(rts_rate
);
335 if (rts_rate_fb_ofdm
)
336 extra_ft
|= BCM43xx_TX4_EFT_RTSFBOFDM
;
337 mac_ctl
|= BCM43xx_TX4_MAC_LONGFRAME
;
341 txhdr
->cookie
= cpu_to_le16(cookie
);
343 /* Apply the bitfields */
344 txhdr
->mac_ctl
= cpu_to_le32(mac_ctl
);
345 txhdr
->phy_ctl
= cpu_to_le16(phy_ctl
);
346 txhdr
->extra_ft
= extra_ft
;
349 void bcm43xx_generate_txhdr(struct bcm43xx_wldev
*dev
,
351 const unsigned char *fragment_data
,
352 unsigned int fragment_len
,
353 const struct ieee80211_tx_control
*txctl
,
356 generate_txhdr_fw4(dev
, (struct bcm43xx_txhdr_fw4
*)txhdr
,
357 fragment_data
, fragment_len
,
361 static s8
bcm43xx_rssi_postprocess(struct bcm43xx_wldev
*dev
,
362 u8 in_rssi
, int ofdm
,
363 int adjust_2053
, int adjust_2050
)
365 struct bcm43xx_phy
*phy
= &dev
->phy
;
368 switch (phy
->radio_ver
) {
381 if (dev
->dev
->bus
->sprom
.r1
.boardflags_lo
& BCM43xx_BFL_RSSI
) {
384 tmp
= phy
->nrssi_lt
[in_rssi
];
396 if (phy
->type
== BCM43xx_PHYTYPE_G
&&
423 static s8
bcm43xx_rssinoise_postprocess(struct bcm43xx_wldev
*dev
,
426 struct bcm43xx_phy
*phy
= &dev
->phy
;
429 if (phy
->type
== BCM43xx_PHYTYPE_A
) {
430 //TODO: Incomplete specs.
433 ret
= bcm43xx_rssi_postprocess(dev
, in_rssi
, 0, 1, 1);
439 void bcm43xx_rx(struct bcm43xx_wldev
*dev
,
443 struct ieee80211_rx_status status
;
444 struct bcm43xx_plcp_hdr6
*plcp
;
445 struct ieee80211_hdr
*wlhdr
;
446 const struct bcm43xx_rxhdr_fw4
*rxhdr
= _rxhdr
;
448 u16 phystat0
, phystat3
, chanstat
, mactime
;
454 memset(&status
, 0, sizeof(status
));
456 /* Get metadata about the frame from the header. */
457 phystat0
= le16_to_cpu(rxhdr
->phy_status0
);
458 phystat3
= le16_to_cpu(rxhdr
->phy_status3
);
460 macstat
= le32_to_cpu(rxhdr
->mac_status
);
461 mactime
= le16_to_cpu(rxhdr
->mac_time
);
462 chanstat
= le16_to_cpu(rxhdr
->channel
);
464 if (macstat
& BCM43xx_RX_MAC_FCSERR
)
465 dev
->wl
->ieee_stats
.dot11FCSErrorCount
++;
467 /* Skip PLCP and padding */
468 padding
= (macstat
& BCM43xx_RX_MAC_PADDING
) ? 2 : 0;
469 if (unlikely(skb
->len
< (sizeof(struct bcm43xx_plcp_hdr6
) + padding
))) {
470 dprintkl(KERN_DEBUG PFX
"RX: Packet size underrun (1)\n");
473 plcp
= (struct bcm43xx_plcp_hdr6
*)(skb
->data
+ padding
);
474 skb_pull(skb
, sizeof(struct bcm43xx_plcp_hdr6
) + padding
);
475 /* The skb contains the Wireless Header + payload data now */
476 if (unlikely(skb
->len
< (2+2+6/*minimum hdr*/ + FCS_LEN
))) {
477 dprintkl(KERN_DEBUG PFX
"RX: Packet size underrun (2)\n");
480 wlhdr
= (struct ieee80211_hdr
*)(skb
->data
);
481 fctl
= le16_to_cpu(wlhdr
->frame_control
);
482 skb_trim(skb
, skb
->len
- FCS_LEN
);
484 if ((macstat
& BCM43xx_RX_MAC_DEC
) &&
485 !(macstat
& BCM43xx_RX_MAC_DECERR
)) {
491 keyidx
= ((macstat
& BCM43xx_RX_MAC_KEYIDX
)
492 >> BCM43xx_RX_MAC_KEYIDX_SHIFT
);
493 /* We must adjust the key index here. We want the "physical"
494 * key index, but the ucode passed it slightly different.
496 keyidx
= bcm43xx_kidx_to_raw(dev
, keyidx
);
497 assert(keyidx
< dev
->max_nr_keys
);
499 if (dev
->key
[keyidx
].algorithm
!= BCM43xx_SEC_ALGO_NONE
) {
500 /* Remove PROTECTED flag to mark it as decrypted. */
501 assert(fctl
& IEEE80211_FCTL_PROTECTED
);
502 fctl
&= ~IEEE80211_FCTL_PROTECTED
;
503 wlhdr
->frame_control
= cpu_to_le16(fctl
);
505 wlhdr_len
= ieee80211_get_hdrlen(fctl
);
506 if (unlikely(skb
->len
< (wlhdr_len
+ 3))) {
507 dprintkl(KERN_DEBUG PFX
508 "RX: Packet size underrun (3)\n");
511 if (skb
->data
[wlhdr_len
+ 3] & (1 << 5)) {
512 /* The Ext-IV Bit is set in the "KeyID"
521 if (unlikely(skb
->len
< (wlhdr_len
+ iv_len
+ icv_len
))) {
522 dprintkl(KERN_DEBUG PFX
523 "RX: Packet size underrun (4)\n");
527 memmove(skb
->data
+ iv_len
, skb
->data
, wlhdr_len
);
528 skb_pull(skb
, iv_len
);
530 skb_trim(skb
, skb
->len
- icv_len
);
532 status
.flag
|= RX_FLAG_DECRYPTED
;
536 status
.ssi
= bcm43xx_rssi_postprocess(dev
, jssi
,
537 (phystat0
& BCM43xx_RX_PHYST0_OFDM
),
538 (phystat0
& BCM43xx_RX_PHYST0_GAINCTL
),
539 (phystat3
& BCM43xx_RX_PHYST3_TRSTATE
));
540 status
.noise
= dev
->stats
.link_noise
;
541 status
.signal
= jssi
; /* this looks wrong, but is what mac80211 wants */
542 if (phystat0
& BCM43xx_RX_PHYST0_OFDM
)
543 status
.rate
= bcm43xx_plcp_get_bitrate_ofdm(plcp
);
545 status
.rate
= bcm43xx_plcp_get_bitrate_cck(plcp
);
546 status
.antenna
= !!(phystat0
& BCM43xx_RX_PHYST0_ANT
);
547 status
.mactime
= mactime
;
549 chanid
= (chanstat
& BCM43xx_RX_CHAN_ID
) >> BCM43xx_RX_CHAN_ID_SHIFT
;
550 switch (chanstat
& BCM43xx_RX_CHAN_PHYTYPE
) {
551 case BCM43xx_PHYTYPE_A
:
552 status
.phymode
= MODE_IEEE80211A
;
553 status
.freq
= chanid
;
554 status
.channel
= bcm43xx_freq_to_channel_a(chanid
);
556 case BCM43xx_PHYTYPE_B
:
557 status
.phymode
= MODE_IEEE80211B
;
558 status
.freq
= chanid
+ 2400;
559 status
.channel
= bcm43xx_freq_to_channel_bg(chanid
+ 2400);
561 case BCM43xx_PHYTYPE_G
:
562 status
.phymode
= MODE_IEEE80211G
;
563 status
.freq
= chanid
+ 2400;
564 status
.channel
= bcm43xx_freq_to_channel_bg(chanid
+ 2400);
570 dev
->stats
.last_rx
= jiffies
;
571 ieee80211_rx_irqsafe(dev
->wl
->hw
, skb
, &status
);
575 dprintkl(KERN_DEBUG PFX
"RX: Packet dropped\n");
576 dev_kfree_skb_any(skb
);
579 void bcm43xx_handle_txstatus(struct bcm43xx_wldev
*dev
,
580 const struct bcm43xx_txstatus
*status
)
582 bcm43xx_debugfs_log_txstat(dev
, status
);
584 if (status
->intermediate
)
586 if (status
->for_ampdu
)
589 dev
->wl
->ieee_stats
.dot11ACKFailureCount
++;
590 if (status
->rts_count
) {
591 if (status
->rts_count
== 0xF) //FIXME
592 dev
->wl
->ieee_stats
.dot11RTSFailureCount
++;
594 dev
->wl
->ieee_stats
.dot11RTSSuccessCount
++;
597 if (bcm43xx_using_pio(dev
))
598 bcm43xx_pio_handle_txstatus(dev
, status
);
600 bcm43xx_dma_handle_txstatus(dev
, status
);
603 /* Handle TX status report as received through DMA/PIO queues */
604 void bcm43xx_handle_hwtxstatus(struct bcm43xx_wldev
*dev
,
605 const struct bcm43xx_hwtxstatus
*hw
)
607 struct bcm43xx_txstatus status
;
610 status
.cookie
= le16_to_cpu(hw
->cookie
);
611 status
.seq
= le16_to_cpu(hw
->seq
);
612 status
.phy_stat
= hw
->phy_stat
;
614 status
.frame_count
= (tmp
>> 4);
615 status
.rts_count
= (tmp
& 0x0F);
617 status
.supp_reason
= ((tmp
& 0x1C) >> 2);
618 status
.pm_indicated
= !!(tmp
& 0x80);
619 status
.intermediate
= !!(tmp
& 0x40);
620 status
.for_ampdu
= !!(tmp
& 0x20);
621 status
.acked
= !!(tmp
& 0x02);
623 bcm43xx_handle_txstatus(dev
, &status
);
626 /* Stop any TX operation on the device (suspend the hardware queues) */
627 void bcm43xx_tx_suspend(struct bcm43xx_wldev
*dev
)
629 if (bcm43xx_using_pio(dev
))
630 bcm43xx_pio_freeze_txqueues(dev
);
632 bcm43xx_dma_tx_suspend(dev
);
635 /* Resume any TX operation on the device (resume the hardware queues) */
636 void bcm43xx_tx_resume(struct bcm43xx_wldev
*dev
)
638 if (bcm43xx_using_pio(dev
))
639 bcm43xx_pio_thaw_txqueues(dev
);
641 bcm43xx_dma_tx_resume(dev
);
645 static void upload_qos_parms(struct bcm43xx_wldev
*dev
,
651 for (i
= 0; i
< BCM43xx_NR_QOSPARMS
; i
++) {
652 bcm43xx_shm_write16(dev
, BCM43xx_SHM_SHARED
,
653 offset
+ (i
* 2), parms
[i
]);
658 /* Initialize the QoS parameters */
659 void bcm43xx_qos_init(struct bcm43xx_wldev
*dev
)
661 /* FIXME: This function must probably be called from the mac80211
662 * config callback. */
665 bcm43xx_hf_write(dev
, bcm43xx_hf_read(dev
) | BCM43xx_HF_EDCF
);
667 bcm43xx_write16(dev
, 0x688,
668 bcm43xx_read16(dev
, 0x688) | 0x4);
671 /*TODO: We might need some stack support here to get the values. */