diff options
Diffstat (limited to 'drivers/net/wireless/b43')
-rw-r--r-- | drivers/net/wireless/b43/Kconfig | 21 | ||||
-rw-r--r-- | drivers/net/wireless/b43/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/b43/b43.h | 185 | ||||
-rw-r--r-- | drivers/net/wireless/b43/debugfs.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/b43/debugfs.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/b43/dma.c | 19 | ||||
-rw-r--r-- | drivers/net/wireless/b43/leds.c | 266 | ||||
-rw-r--r-- | drivers/net/wireless/b43/leds.h | 34 | ||||
-rw-r--r-- | drivers/net/wireless/b43/main.c | 231 | ||||
-rw-r--r-- | drivers/net/wireless/b43/phy_lp.c | 12 | ||||
-rw-r--r-- | drivers/net/wireless/b43/pio.c | 95 | ||||
-rw-r--r-- | drivers/net/wireless/b43/rfkill.c | 5 | ||||
-rw-r--r-- | drivers/net/wireless/b43/sdio.c | 202 | ||||
-rw-r--r-- | drivers/net/wireless/b43/sdio.h | 45 | ||||
-rw-r--r-- | drivers/net/wireless/b43/xmit.c | 10 |
15 files changed, 813 insertions, 315 deletions
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 83e38134acc..54ea61c15d8 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -61,11 +61,28 @@ config B43_PCMCIA If unsure, say N. +config B43_SDIO + bool "Broadcom 43xx SDIO device support (EXPERIMENTAL)" + depends on B43 && SSB_SDIOHOST_POSSIBLE && EXPERIMENTAL + select SSB_SDIOHOST + ---help--- + Broadcom 43xx device support for Soft-MAC SDIO devices. + + With this config option you can drive Soft-MAC b43 cards with a + Secure Digital I/O interface. + This includes the WLAN daughter card found on the Nintendo Wii + video game console. + Note that this does not support Broadcom 43xx Full-MAC devices. + + It's safe to select Y here, even if you don't have a B43 SDIO device. + + If unsure, say N. + # Data transfers to the device via PIO -# This is only needed on PCMCIA devices. All others can do DMA properly. +# This is only needed on PCMCIA and SDIO devices. All others can do DMA properly. config B43_PIO bool - depends on B43 && (B43_PCMCIA || B43_FORCE_PIO) + depends on B43 && (B43_SDIO || B43_PCMCIA || B43_FORCE_PIO) select SSB_BLOCKIO default y diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile index da379f4b0c3..84772a2542d 100644 --- a/drivers/net/wireless/b43/Makefile +++ b/drivers/net/wireless/b43/Makefile @@ -16,6 +16,7 @@ b43-$(CONFIG_B43_PIO) += pio.o b43-y += rfkill.o b43-$(CONFIG_B43_LEDS) += leds.o b43-$(CONFIG_B43_PCMCIA) += pcmcia.o +b43-$(CONFIG_B43_SDIO) += sdio.o b43-$(CONFIG_B43_DEBUG) += debugfs.o obj-$(CONFIG_B43) += b43.o diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 09cfe68537b..660716214d4 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -607,86 +607,7 @@ struct b43_qos_params { struct ieee80211_tx_queue_params p; }; -struct b43_wldev; - -/* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */ -struct b43_wl { - /* Pointer to the active wireless device on this chip */ - struct b43_wldev *current_dev; - /* Pointer to the ieee80211 hardware data structure */ - struct ieee80211_hw *hw; - - /* Global driver mutex. Every operation must run with this mutex locked. */ - struct mutex mutex; - /* Hard-IRQ spinlock. This lock protects things used in the hard-IRQ - * handler, only. This basically is just the IRQ mask register. */ - spinlock_t hardirq_lock; - - /* The number of queues that were registered with the mac80211 subsystem - * initially. This is a backup copy of hw->queues in case hw->queues has - * to be dynamically lowered at runtime (Firmware does not support QoS). - * hw->queues has to be restored to the original value before unregistering - * from the mac80211 subsystem. */ - u16 mac80211_initially_registered_queues; - - /* R/W lock for data transmission. - * Transmissions on 2+ queues can run concurrently, but somebody else - * might sync with TX by write_lock_irqsave()'ing. */ - rwlock_t tx_lock; - /* Lock for LEDs access. */ - spinlock_t leds_lock; - - /* We can only have one operating interface (802.11 core) - * at a time. General information about this interface follows. - */ - - struct ieee80211_vif *vif; - /* The MAC address of the operating interface. */ - u8 mac_addr[ETH_ALEN]; - /* Current BSSID */ - u8 bssid[ETH_ALEN]; - /* Interface type. (NL80211_IFTYPE_XXX) */ - int if_type; - /* Is the card operating in AP, STA or IBSS mode? */ - bool operating; - /* filter flags */ - unsigned int filter_flags; - /* Stats about the wireless interface */ - struct ieee80211_low_level_stats ieee_stats; - -#ifdef CONFIG_B43_HWRNG - struct hwrng rng; - bool rng_initialized; - char rng_name[30 + 1]; -#endif /* CONFIG_B43_HWRNG */ - - /* List of all wireless devices on this chip */ - struct list_head devlist; - u8 nr_devs; - - bool radiotap_enabled; - bool radio_enabled; - - /* The beacon we are currently using (AP or IBSS mode). */ - struct sk_buff *current_beacon; - bool beacon0_uploaded; - bool beacon1_uploaded; - bool beacon_templates_virgin; /* Never wrote the templates? */ - struct work_struct beacon_update_trigger; - - /* The current QOS parameters for the 4 queues. */ - struct b43_qos_params qos_params[4]; - - /* Work for adjustment of the transmission power. - * This is scheduled when we determine that the actual TX output - * power doesn't match what we want. */ - struct work_struct txpower_adjust_work; - - /* Packet transmit work */ - struct work_struct tx_work; - /* Queue of packets to be transmitted. */ - struct sk_buff_head tx_queue; -}; +struct b43_wl; /* The type of the firmware file. */ enum b43_firmware_file_type { @@ -768,13 +689,10 @@ struct b43_wldev { /* The device initialization status. * Use b43_status() to query. */ atomic_t __init_status; - /* Saved init status for handling suspend. */ - int suspend_init_status; bool bad_frames_preempt; /* Use "Bad Frames Preemption" (default off) */ bool dfq_valid; /* Directed frame queue valid (IBSS PS mode, ATIM) */ bool radio_hw_enable; /* saved state of radio hardware enabled state */ - bool suspend_in_progress; /* TRUE, if we are in a suspend/resume cycle */ bool qos_enabled; /* TRUE, if QoS is used. */ bool hwcrypto_enabled; /* TRUE, if HW crypto acceleration is enabled. */ @@ -794,12 +712,6 @@ struct b43_wldev { /* Various statistics about the physical device. */ struct b43_stats stats; - /* The device LEDs. */ - struct b43_led led_tx; - struct b43_led led_rx; - struct b43_led led_assoc; - struct b43_led led_radio; - /* Reason code of the last interrupt. */ u32 irq_reason; u32 dma_reason[6]; @@ -830,9 +742,104 @@ struct b43_wldev { /* Debugging stuff follows. */ #ifdef CONFIG_B43_DEBUG struct b43_dfsentry *dfsentry; + unsigned int irq_count; + unsigned int irq_bit_count[32]; + unsigned int tx_count; + unsigned int rx_count; #endif }; +/* + * Include goes here to avoid a dependency problem. + * A better fix would be to integrate xmit.h into b43.h. + */ +#include "xmit.h" + +/* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */ +struct b43_wl { + /* Pointer to the active wireless device on this chip */ + struct b43_wldev *current_dev; + /* Pointer to the ieee80211 hardware data structure */ + struct ieee80211_hw *hw; + + /* Global driver mutex. Every operation must run with this mutex locked. */ + struct mutex mutex; + /* Hard-IRQ spinlock. This lock protects things used in the hard-IRQ + * handler, only. This basically is just the IRQ mask register. */ + spinlock_t hardirq_lock; + + /* The number of queues that were registered with the mac80211 subsystem + * initially. This is a backup copy of hw->queues in case hw->queues has + * to be dynamically lowered at runtime (Firmware does not support QoS). + * hw->queues has to be restored to the original value before unregistering + * from the mac80211 subsystem. */ + u16 mac80211_initially_registered_queues; + + /* We can only have one operating interface (802.11 core) + * at a time. General information about this interface follows. + */ + + struct ieee80211_vif *vif; + /* The MAC address of the operating interface. */ + u8 mac_addr[ETH_ALEN]; + /* Current BSSID */ + u8 bssid[ETH_ALEN]; + /* Interface type. (NL80211_IFTYPE_XXX) */ + int if_type; + /* Is the card operating in AP, STA or IBSS mode? */ + bool operating; + /* filter flags */ + unsigned int filter_flags; + /* Stats about the wireless interface */ + struct ieee80211_low_level_stats ieee_stats; + +#ifdef CONFIG_B43_HWRNG + struct hwrng rng; + bool rng_initialized; + char rng_name[30 + 1]; +#endif /* CONFIG_B43_HWRNG */ + + /* List of all wireless devices on this chip */ + struct list_head devlist; + u8 nr_devs; + + bool radiotap_enabled; + bool radio_enabled; + + /* The beacon we are currently using (AP or IBSS mode). */ + struct sk_buff *current_beacon; + bool beacon0_uploaded; + bool beacon1_uploaded; + bool beacon_templates_virgin; /* Never wrote the templates? */ + struct work_struct beacon_update_trigger; + + /* The current QOS parameters for the 4 queues. */ + struct b43_qos_params qos_params[4]; + + /* Work for adjustment of the transmission power. + * This is scheduled when we determine that the actual TX output + * power doesn't match what we want. */ + struct work_struct txpower_adjust_work; + + /* Packet transmit work */ + struct work_struct tx_work; + /* Queue of packets to be transmitted. */ + struct sk_buff_head tx_queue; + + /* The device LEDs. */ + struct b43_leds leds; + +#ifdef CONFIG_B43_PIO + /* + * RX/TX header/tail buffers used by the frame transmit functions. + */ + struct b43_rxhdr_fw4 rxhdr; + struct b43_txhdr txhdr; + u8 rx_tail[4]; + u8 tx_tail[4]; +#endif /* CONFIG_B43_PIO */ +}; + static inline struct b43_wl *hw_to_b43_wl(struct ieee80211_hw *hw) { return hw->priv; diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/b43/debugfs.c index 8f64943e3f6..80b19a44a40 100644 --- a/drivers/net/wireless/b43/debugfs.c +++ b/drivers/net/wireless/b43/debugfs.c @@ -689,6 +689,7 @@ static void b43_add_dynamic_debug(struct b43_wldev *dev) add_dyn_dbg("debug_lo", B43_DBG_LO, 0); add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, 0); add_dyn_dbg("debug_keys", B43_DBG_KEYS, 0); + add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, 0); #undef add_dyn_dbg } diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/b43/debugfs.h index e47b4b488b0..822aad8842f 100644 --- a/drivers/net/wireless/b43/debugfs.h +++ b/drivers/net/wireless/b43/debugfs.h @@ -13,6 +13,7 @@ enum b43_dyndbg { /* Dynamic debugging features */ B43_DBG_LO, B43_DBG_FIRMWARE, B43_DBG_KEYS, + B43_DBG_VERBOSESTATS, __B43_NR_DYNDBG, }; diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index a467ee260a1..de4e804bedf 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -1157,8 +1157,9 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot) } static int dma_tx_fragment(struct b43_dmaring *ring, - struct sk_buff *skb) + struct sk_buff **in_skb) { + struct sk_buff *skb = *in_skb; const struct b43_dma_ops *ops = ring->ops; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u8 *header; @@ -1224,8 +1225,14 @@ static int dma_tx_fragment(struct b43_dmaring *ring, } memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len); + memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb)); + bounce_skb->dev = skb->dev; + skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb)); + info = IEEE80211_SKB_CB(bounce_skb); + dev_kfree_skb_any(skb); skb = bounce_skb; + *in_skb = bounce_skb; meta->skb = skb; meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { @@ -1355,7 +1362,11 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) * static, so we don't need to store it per frame. */ ring->queue_prio = skb_get_queue_mapping(skb); - err = dma_tx_fragment(ring, skb); + /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing + * into the skb data or cb now. */ + hdr = NULL; + info = NULL; + err = dma_tx_fragment(ring, &skb); if (unlikely(err == -ENOKEY)) { /* Drop this packet, as we don't have the encryption key * anymore and must not transmit it unencrypted. */ @@ -1428,9 +1439,9 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ring->nr_failed_tx_packets++; ring->nr_total_packet_tries += status->frame_count; #endif /* DEBUG */ - ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb); + ieee80211_tx_status(dev->wl->hw, meta->skb); - /* skb is freed by ieee80211_tx_status_irqsafe() */ + /* skb is freed by ieee80211_tx_status() */ meta->skb = NULL; } else { /* No need to call free_descriptor_buffer here, as diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index c8b317094c3..1e8dba48800 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -34,57 +34,88 @@ static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index, bool activelow) { - struct b43_wl *wl = dev->wl; - unsigned long flags; u16 ctl; - spin_lock_irqsave(&wl->leds_lock, flags); ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); if (activelow) ctl &= ~(1 << led_index); else ctl |= (1 << led_index); b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); - spin_unlock_irqrestore(&wl->leds_lock, flags); } static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index, bool activelow) { - struct b43_wl *wl = dev->wl; - unsigned long flags; u16 ctl; - spin_lock_irqsave(&wl->leds_lock, flags); ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL); if (activelow) ctl |= (1 << led_index); else ctl &= ~(1 << led_index); b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl); - spin_unlock_irqrestore(&wl->leds_lock, flags); } -/* Callback from the LED subsystem. */ -static void b43_led_brightness_set(struct led_classdev *led_dev, - enum led_brightness brightness) +static void b43_led_update(struct b43_wldev *dev, + struct b43_led *led) { - struct b43_led *led = container_of(led_dev, struct b43_led, led_dev); - struct b43_wldev *dev = led->dev; bool radio_enabled; + bool turn_on; - if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) + if (!led->wl) return; - /* Checking the radio-enabled status here is slightly racy, - * but we want to avoid the locking overhead and we don't care - * whether the LED has the wrong state for a second. */ radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable); - if (brightness == LED_OFF || !radio_enabled) - b43_led_turn_off(dev, led->index, led->activelow); + /* The led->state read is racy, but we don't care. In case we raced + * with the brightness_set handler, we will be called again soon + * to fixup our state. */ + if (radio_enabled) + turn_on = atomic_read(&led->state) != LED_OFF; else + turn_on = 0; + if (turn_on == led->hw_state) + return; + led->hw_state = turn_on; + + if (turn_on) b43_led_turn_on(dev, led->index, led->activelow); + else + b43_led_turn_off(dev, led->index, led->activelow); +} + +static void b43_leds_work(struct work_struct *work) +{ + struct b43_leds *leds = container_of(work, struct b43_leds, work); + struct b43_wl *wl = container_of(leds, struct b43_wl, leds); + struct b43_wldev *dev; + + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED)) + goto out_unlock; + + b43_led_update(dev, &wl->leds.led_tx); + b43_led_update(dev, &wl->leds.led_rx); + b43_led_update(dev, &wl->leds.led_radio); + b43_led_update(dev, &wl->leds.led_assoc); + +out_unlock: + mutex_unlock(&wl->mutex); +} + +/* Callback from the LED subsystem. */ +static void b43_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct b43_led *led = container_of(led_dev, struct b43_led, led_dev); + struct b43_wl *wl = led->wl; + + if (likely(!wl->leds.stop)) { + atomic_set(&led->state, brightness); + ieee80211_queue_work(wl->hw, &wl->leds.work); + } } static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, @@ -93,15 +124,15 @@ static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, { int err; - b43_led_turn_off(dev, led_index, activelow); - if (led->dev) + if (led->wl) return -EEXIST; if (!default_trigger) return -EINVAL; - led->dev = dev; + led->wl = dev->wl; led->index = led_index; led->activelow = activelow; strncpy(led->name, name, sizeof(led->name)); + atomic_set(&led->state, 0); led->led_dev.name = led->name; led->led_dev.default_trigger = default_trigger; @@ -110,19 +141,19 @@ static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, err = led_classdev_register(dev->dev->dev, &led->led_dev); if (err) { b43warn(dev->wl, "LEDs: Failed to register %s\n", name); - led->dev = NULL; + led->wl = NULL; return err; } + return 0; } static void b43_unregister_led(struct b43_led *led) { - if (!led->dev) + if (!led->wl) return; led_classdev_unregister(&led->led_dev); - b43_led_turn_off(led->dev, led->index, led->activelow); - led->dev = NULL; + led->wl = NULL; } static void b43_map_led(struct b43_wldev *dev, @@ -137,24 +168,20 @@ static void b43_map_led(struct b43_wldev *dev, * generic LED triggers. */ switch (behaviour) { case B43_LED_INACTIVE: - break; case B43_LED_OFF: - b43_led_turn_off(dev, led_index, activelow); - break; case B43_LED_ON: - b43_led_turn_on(dev, led_index, activelow); break; case B43_LED_ACTIVITY: case B43_LED_TRANSFER: case B43_LED_APTRANSFER: snprintf(name, sizeof(name), "b43-%s::tx", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_tx, name, + b43_register_led(dev, &dev->wl->leds.led_tx, name, ieee80211_get_tx_led_name(hw), led_index, activelow); snprintf(name, sizeof(name), "b43-%s::rx", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_rx, name, + b43_register_led(dev, &dev->wl->leds.led_rx, name, ieee80211_get_rx_led_name(hw), led_index, activelow); break; @@ -164,18 +191,15 @@ static void b43_map_led(struct b43_wldev *dev, case B43_LED_MODE_BG: snprintf(name, sizeof(name), "b43-%s::radio", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_radio, name, + b43_register_led(dev, &dev->wl->leds.led_radio, name, ieee80211_get_radio_led_name(hw), led_index, activelow); - /* Sync the RF-kill LED state with radio and switch states. */ - if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) - b43_led_turn_on(dev, led_index, activelow); break; case B43_LED_WEIRD: case B43_LED_ASSOC: snprintf(name, sizeof(name), "b43-%s::assoc", wiphy_name(hw->wiphy)); - b43_register_led(dev, &dev->led_assoc, name, + b43_register_led(dev, &dev->wl->leds.led_assoc, name, ieee80211_get_assoc_led_name(hw), led_index, activelow); break; @@ -186,58 +210,150 @@ static void b43_map_led(struct b43_wldev *dev, } } -void b43_leds_init(struct b43_wldev *dev) +static void b43_led_get_sprominfo(struct b43_wldev *dev, + unsigned int led_index, + enum b43_led_behaviour *behaviour, + bool *activelow) { struct ssb_bus *bus = dev->dev->bus; u8 sprom[4]; - int i; - enum b43_led_behaviour behaviour; - bool activelow; sprom[0] = bus->sprom.gpio0; sprom[1] = bus->sprom.gpio1; sprom[2] = bus->sprom.gpio2; sprom[3] = bus->sprom.gpio3; - for (i = 0; i < 4; i++) { - if (sprom[i] == 0xFF) { - /* There is no LED information in the SPROM - * for this LED. Hardcode it here. */ - activelow = 0; - switch (i) { - case 0: - behaviour = B43_LED_ACTIVITY; - activelow = 1; - if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ) - behaviour = B43_LED_RADIO_ALL; - break; - case 1: - behaviour = B43_LED_RADIO_B; - if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK) - behaviour = B43_LED_ASSOC; - break; - case 2: - behaviour = B43_LED_RADIO_A; - break; - case 3: - behaviour = B43_LED_OFF; - break; - default: - B43_WARN_ON(1); - return; - } + if (sprom[led_index] == 0xFF) { + /* There is no LED information in the SPROM + * for this LED. Hardcode it here. */ + *activelow = 0; + switch (led_index) { + case 0: + *behaviour = B43_LED_ACTIVITY; + *activelow = 1; + if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ) + *behaviour = B43_LED_RADIO_ALL; + break; + case 1: + *behaviour = B43_LED_RADIO_B; + if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK) + *behaviour = B43_LED_ASSOC; + break; + case 2: + *behaviour = B43_LED_RADIO_A; + break; + case 3: + *behaviour = B43_LED_OFF; + break; + default: + B43_WARN_ON(1); + return; + } + } else { + *behaviour = sprom[led_index] & B43_LED_BEHAVIOUR; + *activelow = !!(sprom[led_index] & B43_LED_ACTIVELOW); + } +} + +void b43_leds_init(struct b43_wldev *dev) +{ + struct b43_led *led; + unsigned int i; + enum b43_led_behaviour behaviour; + bool activelow; + + /* Sync the RF-kill LED state (if we have one) with radio and switch states. */ + led = &dev->wl->leds.led_radio; + if (led->wl) { + if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) { + b43_led_turn_on(dev, led->index, led->activelow); + led->hw_state = 1; + atomic_set(&led->state, 1); } else { - behaviour = sprom[i] & B43_LED_BEHAVIOUR; - activelow = !!(sprom[i] & B43_LED_ACTIVELOW); + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); } - b43_map_led(dev, i, behaviour, activelow); } + + /* Initialize TX/RX/ASSOC leds */ + led = &dev->wl->leds.led_tx; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); + } + led = &dev->wl->leds.led_rx; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); + } + led = &dev->wl->leds.led_assoc; + if (led->wl) { + b43_led_turn_off(dev, led->index, led->activelow); + led->hw_state = 0; + atomic_set(&led->state, 0); + } + + /* Initialize other LED states. */ + for (i = 0; i < B43_MAX_NR_LEDS; i++) { + b43_led_get_sprominfo(dev, i, &behaviour, &activelow); + switch (behaviour) { + case B43_LED_OFF: + b43_led_turn_off(dev, i, activelow); + break; + case B43_LED_ON: + b43_led_turn_on(dev, i, activelow); + break; + default: + /* Leave others as-is. */ + break; + } + } + + dev->wl->leds.stop = 0; } void b43_leds_exit(struct b43_wldev *dev) { - b43_unregister_led(&dev->led_tx); - b43_unregister_led(&dev->led_rx); - b43_unregister_led(&dev->led_assoc); - b43_unregister_led(&dev->led_radio); + struct b43_leds *leds = &dev->wl->leds; + + b43_led_turn_off(dev, leds->led_tx.index, leds->led_tx.activelow); + b43_led_turn_off(dev, leds->led_rx.index, leds->led_rx.activelow); + b43_led_turn_off(dev, leds->led_assoc.index, leds->led_assoc.activelow); + b43_led_turn_off(dev, leds->led_radio.index, leds->led_radio.activelow); +} + +void b43_leds_stop(struct b43_wldev *dev) +{ + struct b43_leds *leds = &dev->wl->leds; + + leds->stop = 1; + cancel_work_sync(&leds->work); +} + +void b43_leds_register(struct b43_wldev *dev) +{ + unsigned int i; + enum b43_led_behaviour behaviour; + bool activelow; + + INIT_WORK(&dev->wl->leds.work, b43_leds_work); + + /* Register the LEDs to the LED subsystem. */ + for (i = 0; i < B43_MAX_NR_LEDS; i++) { + b43_led_get_sprominfo(dev, i, &behaviour, &activelow); + b43_map_led(dev, i, behaviour, activelow); + } +} + +void b43_leds_unregister(struct b43_wl *wl) +{ + struct b43_leds *leds = &wl->leds; + + b43_unregister_led(&leds->led_tx); + b43_unregister_led(&leds->led_rx); + b43_unregister_led(&leds->led_assoc); + b43_unregister_led(&leds->led_radio); } diff --git a/drivers/net/wireless/b43/leds.h b/drivers/net/wireless/b43/leds.h index b8b1dd52124..32b66d53cda 100644 --- a/drivers/net/wireless/b43/leds.h +++ b/drivers/net/wireless/b43/leds.h @@ -1,18 +1,20 @@ #ifndef B43_LEDS_H_ #define B43_LEDS_H_ +struct b43_wl; struct b43_wldev; #ifdef CONFIG_B43_LEDS #include <linux/types.h> #include <linux/leds.h> +#include <linux/workqueue.h> #define B43_LED_MAX_NAME_LEN 31 struct b43_led { - struct b43_wldev *dev; + struct b43_wl *wl; /* The LED class device */ struct led_classdev led_dev; /* The index number of the LED. */ @@ -22,8 +24,24 @@ struct b43_led { bool activelow; /* The unique name string for this LED device. */ char name[B43_LED_MAX_NAME_LEN + 1]; + /* The current status of the LED. This is updated locklessly. */ + atomic_t state; + /* The active state in hardware. */ + bool hw_state; }; +struct b43_leds { + struct b43_led led_tx; + struct b43_led led_rx; + struct b43_led led_radio; + struct b43_led led_assoc; + + bool stop; + struct work_struct work; +}; + +#define B43_MAX_NR_LEDS 4 + #define B43_LED_BEHAVIOUR 0x7F #define B43_LED_ACTIVELOW 0x80 /* LED behaviour values */ @@ -42,23 +60,35 @@ enum b43_led_behaviour { B43_LED_INACTIVE, }; +void b43_leds_register(struct b43_wldev *dev); +void b43_leds_unregister(struct b43_wl *wl); void b43_leds_init(struct b43_wldev *dev); void b43_leds_exit(struct b43_wldev *dev); +void b43_leds_stop(struct b43_wldev *dev); #else /* CONFIG_B43_LEDS */ /* LED support disabled */ -struct b43_led { +struct b43_leds { /* empty */ }; +static inline void b43_leds_register(struct b43_wldev *dev) +{ +} +static inline void b43_leds_unregister(struct b43_wl *wl) +{ +} static inline void b43_leds_init(struct b43_wldev *dev) { } static inline void b43_leds_exit(struct b43_wldev *dev) { } +static inline void b43_leds_stop(struct b43_wldev *dev) +{ +} #endif /* CONFIG_B43_LEDS */ #endif /* B43_LEDS_H_ */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index e789792a36b..098dda1a67c 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -8,6 +8,9 @@ Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org> Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch> + SDIO support + Copyright (c) 2009 Albert Herranz <albert_herranz@yahoo.es> + Some parts of the code in this file are derived from the ipw2200 driver Copyright(c) 2003 - 2004 Intel Corporation. @@ -53,6 +56,8 @@ #include "xmit.h" #include "lo.h" #include "pcmcia.h" +#include "sdio.h" +#include <linux/mmc/sdio_func.h> MODULE_DESCRIPTION("Broadcom B43 wireless driver"); MODULE_AUTHOR("Martin Langer"); @@ -1587,7 +1592,7 @@ static void b43_beacon_update_trigger_work(struct work_struct *work) mutex_lock(&wl->mutex); dev = wl->current_dev; if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) { - if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) { + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { /* wl->mutex is enough. */ b43_do_beacon_update_trigger_work(dev); mmiowb(); @@ -1825,6 +1830,16 @@ static void b43_do_interrupt_thread(struct b43_wldev *dev) /* Re-enable interrupts on the device by restoring the current interrupt mask. */ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); + +#if B43_DEBUG + if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { + dev->irq_count++; + for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { + if (reason & (1 << i)) + dev->irq_bit_count[i]++; + } + } +#endif } /* Interrupt thread handler. Handles device interrupts in thread context. */ @@ -1905,6 +1920,21 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id) return ret; } +/* SDIO interrupt handler. This runs in process context. */ +static void b43_sdio_interrupt_handler(struct b43_wldev *dev) +{ + struct b43_wl *wl = dev->wl; + irqreturn_t ret; + + mutex_lock(&wl->mutex); + + ret = b43_do_interrupt(dev); + if (ret == IRQ_WAKE_THREAD) + b43_do_interrupt_thread(dev); + + mutex_unlock(&wl->mutex); +} + void b43_do_release_fw(struct b43_firmware_file *fw) { release_firmware(fw->data); @@ -2645,6 +2675,20 @@ static void b43_adjust_opmode(struct b43_wldev *dev) cfp_pretbtt = 50; } b43_write16(dev, 0x612, cfp_pretbtt); + + /* FIXME: We don't currently implement the PMQ mechanism, + * so always disable it. If we want to implement PMQ, + * we need to enable it here (clear DISCPMQ) in AP mode. + */ + if (0 /* ctl & B43_MACCTL_AP */) { + b43_write32(dev, B43_MMIO_MACCTL, + b43_read32(dev, B43_MMIO_MACCTL) + & ~B43_MACCTL_DISCPMQ); + } else { + b43_write32(dev, B43_MMIO_MACCTL, + b43_read32(dev, B43_MMIO_MACCTL) + | B43_MACCTL_DISCPMQ); + } } static void b43_rate_memory_write(struct b43_wldev *dev, u16 rate, int is_ofdm) @@ -2873,6 +2917,27 @@ static void b43_periodic_every15sec(struct b43_wldev *dev) atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); wmb(); + +#if B43_DEBUG + if (b43_debug(dev, B43_DBG_VERBOSESTATS)) { + unsigned int i; + + b43dbg(dev->wl, "Stats: %7u IRQs/sec, %7u TX/sec, %7u RX/sec\n", + dev->irq_count / 15, + dev->tx_count / 15, + dev->rx_count / 15); + dev->irq_count = 0; + dev->tx_count = 0; + dev->rx_count = 0; + for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) { + if (dev->irq_bit_count[i]) { + b43dbg(dev->wl, "Stats: %7u IRQ-%02u/sec (0x%08X)\n", + dev->irq_bit_count[i] / 15, i, (1 << i)); + dev->irq_bit_count[i] = 0; + } + } + } +#endif } static void do_periodic_work(struct b43_wldev *dev) @@ -3002,14 +3067,18 @@ static void b43_security_init(struct b43_wldev *dev) static int b43_rng_read(struct hwrng *rng, u32 *data) { struct b43_wl *wl = (struct b43_wl *)rng->priv; + struct b43_wldev *dev; + int count = -ENODEV; - /* FIXME: We need to take wl->mutex here to make sure the device - * is not going away from under our ass. However it could deadlock - * with hwrng internal locking. */ - - *data = b43_read16(wl->current_dev, B43_MMIO_RNG); + mutex_lock(&wl->mutex); + dev = wl->current_dev; + if (likely(dev && b43_status(dev) >= B43_STAT_INITIALIZED)) { + *data = b43_read16(dev, B43_MMIO_RNG); + count = sizeof(u16); + } + mutex_unlock(&wl->mutex); - return (sizeof(u16)); + return count; } #endif /* CONFIG_B43_HWRNG */ @@ -3068,6 +3137,9 @@ static void b43_tx_work(struct work_struct *work) dev_kfree_skb(skb); /* Drop it */ } +#if B43_DEBUG + dev->tx_count++; +#endif mutex_unlock(&wl->mutex); } @@ -3802,6 +3874,7 @@ static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; struct b43_wldev *orig_dev; + u32 mask; redo: if (!dev || b43_status(dev) < B43_STAT_STARTED) @@ -3820,7 +3893,7 @@ redo: /* Disable interrupts on the device. */ b43_set_status(dev, B43_STAT_INITIALIZED); - if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) { + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { /* wl->mutex is locked. That is enough. */ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ @@ -3830,10 +3903,15 @@ redo: b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ spin_unlock_irq(&wl->hardirq_lock); } - /* Synchronize the interrupt handlers. Unlock to avoid deadlocks. */ + /* Synchronize and free the interrupt handlers. Unlock to avoid deadlocks. */ orig_dev = dev; mutex_unlock(&wl->mutex); - synchronize_irq(dev->dev->irq); + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { + b43_sdio_free_irq(dev); + } else { + synchronize_irq(dev->dev->irq); + free_irq(dev->dev->irq, dev); + } mutex_lock(&wl->mutex); dev = wl->current_dev; if (!dev) @@ -3843,14 +3921,15 @@ redo: goto redo; return dev; } - B43_WARN_ON(b43_read32(dev, B43_MMIO_GEN_IRQ_MASK)); + mask = b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); + B43_WARN_ON(mask != 0xFFFFFFFF && mask); /* Drain the TX queue */ while (skb_queue_len(&wl->tx_queue)) dev_kfree_skb(skb_dequeue(&wl->tx_queue)); b43_mac_suspend(dev); - free_irq(dev->dev->irq, dev); + b43_leds_exit(dev); b43dbg(wl, "Wireless interface stopped\n"); return dev; @@ -3864,12 +3943,20 @@ static int b43_wireless_core_start(struct b43_wldev *dev) B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED); drain_txstatus_queue(dev); - err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, - b43_interrupt_thread_handler, - IRQF_SHARED, KBUILD_MODNAME, dev); - if (err) { - b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); - goto out; + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { + err = b43_sdio_request_irq(dev, b43_sdio_interrupt_handler); + if (err) { + b43err(dev->wl, "Cannot request SDIO IRQ\n"); + goto out; + } + } else { + err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, + b43_interrupt_thread_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (err) { + b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); + goto out; + } } /* We are ready to run. */ @@ -3882,8 +3969,10 @@ static int b43_wireless_core_start(struct b43_wldev *dev) /* Start maintainance work */ b43_periodic_tasks_setup(dev); + b43_leds_init(dev); + b43dbg(dev->wl, "Wireless interface started\n"); - out: +out: return err; } @@ -4160,10 +4249,6 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) macctl |= B43_MACCTL_PSM_JMP0; b43_write32(dev, B43_MMIO_MACCTL, macctl); - if (!dev->suspend_in_progress) { - b43_leds_exit(dev); - b43_rng_exit(dev->wl); - } b43_dma_free(dev); b43_pio_free(dev); b43_chip_exit(dev); @@ -4180,7 +4265,6 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) /* Initialize a wireless core */ static int b43_wireless_core_init(struct b43_wldev *dev) { - struct b43_wl *wl = dev->wl; struct ssb_bus *bus = dev->dev->bus; struct ssb_sprom *sprom = &bus->sprom; struct b43_phy *phy = &dev->phy; @@ -4264,7 +4348,9 @@ static int b43_wireless_core_init(struct b43_wldev *dev) /* Maximum Contention Window */ b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF); - if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || B43_FORCE_PIO) { + if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || + (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) || + B43_FORCE_PIO) { dev->__using_pio_transfers = 1; err = b43_pio_init(dev); } else { @@ -4280,15 +4366,13 @@ static int b43_wireless_core_init(struct b43_wldev *dev) ssb_bus_powerup(bus, !(sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW)); b43_upload_card_macaddress(dev); b43_security_init(dev); - if (!dev->suspend_in_progress) - b43_rng_init(wl); + + ieee80211_wake_queues(dev->wl->hw); ieee80211_wake_queues(dev->wl->hw); b43_set_status(dev, B43_STAT_INITIALIZED); - if (!dev->suspend_in_progress) - b43_leds_init(dev); out: return err; @@ -4437,9 +4521,8 @@ static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, { struct b43_wl *wl = hw_to_b43_wl(hw); - mutex_lock(&wl->mutex); + /* FIXME: add locking */ b43_update_templates(wl); - mutex_unlock(&wl->mutex); return 0; } @@ -4837,7 +4920,6 @@ static int b43_wireless_init(struct ssb_device *dev) /* Initialize struct b43_wl */ wl->hw = hw; - spin_lock_init(&wl->leds_lock); mutex_init(&wl->mutex); spin_lock_init(&wl->hardirq_lock); INIT_LIST_HEAD(&wl->devlist); @@ -4878,6 +4960,8 @@ static int b43_probe(struct ssb_device *dev, const struct ssb_device_id *id) err = ieee80211_register_hw(wl->hw); if (err) goto err_one_core_detach; + b43_leds_register(wl->current_dev); + b43_rng_init(wl); } out: @@ -4906,12 +4990,15 @@ static void b43_remove(struct ssb_device *dev) * might have modified it. Restoring is important, so the networking * stack can properly free resources. */ wl->hw->queues = wl->mac80211_initially_registered_queues; + b43_leds_stop(wldev); ieee80211_unregister_hw(wl->hw); } b43_one_core_detach(dev); if (list_empty(&wl->devlist)) { + b43_rng_exit(wl); + b43_leds_unregister(wl); /* Last core on the chip unregistered. * We can destroy common struct b43_wl. */ @@ -4929,80 +5016,17 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason) ieee80211_queue_work(dev->wl->hw, &dev->restart_work); } -#ifdef CONFIG_PM - -static int b43_suspend(struct ssb_device *dev, pm_message_t state) -{ - struct b43_wldev *wldev = ssb_get_drvdata(dev); - struct b43_wl *wl = wldev->wl; - - b43dbg(wl, "Suspending...\n"); - - mutex_lock(&wl->mutex); - wldev->suspend_in_progress = true; - wldev->suspend_init_status = b43_status(wldev); - if (wldev->suspend_init_status >= B43_STAT_STARTED) - wldev = b43_wireless_core_stop(wldev); - if (wldev && wldev->suspend_init_status >= B43_STAT_INITIALIZED) - b43_wireless_core_exit(wldev); - mutex_unlock(&wl->mutex); - - b43dbg(wl, "Device suspended.\n"); - - return 0; -} - -static int b43_resume(struct ssb_device *dev) -{ - struct b43_wldev *wldev = ssb_get_drvdata(dev); - struct b43_wl *wl = wldev->wl; - int err = 0; - - b43dbg(wl, "Resuming...\n"); - - mutex_lock(&wl->mutex); - if (wldev->suspend_init_status >= B43_STAT_INITIALIZED) { - err = b43_wireless_core_init(wldev); - if (err) { - b43err(wl, "Resume failed at core init\n"); - goto out; - } - } - if (wldev->suspend_init_status >= B43_STAT_STARTED) { - err = b43_wireless_core_start(wldev); - if (err) { - b43_leds_exit(wldev); - b43_rng_exit(wldev->wl); - b43_wireless_core_exit(wldev); - b43err(wl, "Resume failed at core start\n"); - goto out; - } - } - b43dbg(wl, "Device resumed.\n"); - out: - wldev->suspend_in_progress = false; - mutex_unlock(&wl->mutex); - return err; -} - -#else /* CONFIG_PM */ -# define b43_suspend NULL -# define b43_resume NULL -#endif /* CONFIG_PM */ - static struct ssb_driver b43_ssb_driver = { .name = KBUILD_MODNAME, .id_table = b43_ssb_tbl, .probe = b43_probe, .remove = b43_remove, - .suspend = b43_suspend, - .resume = b43_resume, }; static void b43_print_driverinfo(void) { const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "", - *feat_leds = ""; + *feat_leds = "", *feat_sdio = ""; #ifdef CONFIG_B43_PCI_AUTOSELECT feat_pci = "P"; @@ -5016,11 +5040,14 @@ static void b43_print_driverinfo(void) #ifdef CONFIG_B43_LEDS feat_leds = "L"; #endif +#ifdef CONFIG_B43_SDIO + feat_sdio = "S"; +#endif printk(KERN_INFO "Broadcom 43xx driver loaded " - "[ Features: %s%s%s%s, Firmware-ID: " + "[ Features: %s%s%s%s%s, Firmware-ID: " B43_SUPPORTED_FIRMWARE_ID " ]\n", feat_pci, feat_pcmcia, feat_nphy, - feat_leds); + feat_leds, feat_sdio); } static int __init b43_init(void) @@ -5031,13 +5058,18 @@ static int __init b43_init(void) err = b43_pcmcia_init(); if (err) goto err_dfs_exit; - err = ssb_driver_register(&b43_ssb_driver); + err = b43_sdio_init(); if (err) goto err_pcmcia_exit; + err = ssb_driver_register(&b43_ssb_driver); + if (err) + goto err_sdio_exit; b43_print_driverinfo(); return err; +err_sdio_exit: + b43_sdio_exit(); err_pcmcia_exit: b43_pcmcia_exit(); err_dfs_exit: @@ -5048,6 +5080,7 @@ err_dfs_exit: static void __exit b43_exit(void) { ssb_driver_unregister(&b43_ssb_driver); + b43_sdio_exit(); b43_pcmcia_exit(); b43_debugfs_exit(); } diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index 3e02d969f68..1e318d815a5 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -2228,6 +2228,16 @@ static enum b43_txpwr_result b43_lpphy_op_recalc_txpower(struct b43_wldev *dev, return B43_TXPWR_RES_DONE; } +void b43_lpphy_op_switch_analog(struct b43_wldev *dev, bool on) +{ + if (on) { + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xfff8); + } else { + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0x0007); + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x0007); + } +} + const struct b43_phy_operations b43_phyops_lp = { .allocate = b43_lpphy_op_allocate, .free = b43_lpphy_op_free, @@ -2239,7 +2249,7 @@ const struct b43_phy_operations b43_phyops_lp = { .radio_read = b43_lpphy_op_radio_read, .radio_write = b43_lpphy_op_radio_write, .software_rfkill = b43_lpphy_op_software_rfkill, - .switch_analog = b43_phyop_switch_analog_generic, + .switch_analog = b43_lpphy_op_switch_analog, .switch_channel = b43_lpphy_op_switch_channel, .get_default_chan = b43_lpphy_op_get_default_chan, .set_rx_antenna = b43_lpphy_op_set_rx_antenna, diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c index 3498b68385e..9b904440021 100644 --- a/drivers/net/wireless/b43/pio.c +++ b/drivers/net/wireless/b43/pio.c @@ -30,6 +30,7 @@ #include "xmit.h" #include <linux/delay.h> +#include <linux/sched.h> static u16 generate_cookie(struct b43_pio_txqueue *q, @@ -331,6 +332,7 @@ static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q, unsigned int data_len) { struct b43_wldev *dev = q->dev; + struct b43_wl *wl = dev->wl; const u8 *data = _data; ctl |= B43_PIO_TXCTL_WRITELO | B43_PIO_TXCTL_WRITEHI; @@ -343,7 +345,11 @@ static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q, /* Write the last byte. */ ctl &= ~B43_PIO_TXCTL_WRITEHI; b43_piotx_write16(q, B43_PIO_TXCTL, ctl); - b43_piotx_write16(q, B43_PIO_TXDATA, data[data_len - 1]); + wl->tx_tail[0] = data[data_len - 1]; + wl->tx_tail[1] = 0; + ssb_block_write(dev->dev, wl->tx_tail, 2, + q->mmio_base + B43_PIO_TXDATA, + sizeof(u16)); } return ctl; @@ -376,6 +382,7 @@ static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q, unsigned int data_len) { struct b43_wldev *dev = q->dev; + struct b43_wl *wl = dev->wl; const u8 *data = _data; ctl |= B43_PIO8_TXCTL_0_7 | B43_PIO8_TXCTL_8_15 | @@ -386,26 +393,33 @@ static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q, q->mmio_base + B43_PIO8_TXDATA, sizeof(u32)); if (data_len & 3) { - u32 value = 0; - + wl->tx_tail[3] = 0; /* Write the last few bytes. */ ctl &= ~(B43_PIO8_TXCTL_8_15 | B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_24_31); - data = &(data[data_len - 1]); switch (data_len & 3) { case 3: - ctl |= B43_PIO8_TXCTL_16_23; - value |= (u32)(*data) << 16; - data--; + ctl |= B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_8_15; + wl->tx_tail[0] = data[data_len - 3]; + wl->tx_tail[1] = data[data_len - 2]; + wl->tx_tail[2] = data[data_len - 1]; + break; case 2: ctl |= B43_PIO8_TXCTL_8_15; - value |= (u32)(*data) << 8; - data--; + wl->tx_tail[0] = data[data_len - 2]; + wl->tx_tail[1] = data[data_len - 1]; + wl->tx_tail[2] = 0; + break; case 1: - value |= (u32)(*data); + wl->tx_tail[0] = data[data_len - 1]; + wl->tx_tail[1] = 0; + wl->tx_tail[2] = 0; + break; } b43_piotx_write32(q, B43_PIO8_TXCTL, ctl); - b43_piotx_write32(q, B43_PIO8_TXDATA, value); + ssb_block_write(dev->dev, wl->tx_tail, 4, + q->mmio_base + B43_PIO8_TXDATA, + sizeof(u32)); } return ctl; @@ -435,8 +449,9 @@ static void pio_tx_frame_4byte_queue(struct b43_pio_txpacket *pack, static int pio_tx_frame(struct b43_pio_txqueue *q, struct sk_buff *skb) { + struct b43_wldev *dev = q->dev; + struct b43_wl *wl = dev->wl; struct b43_pio_txpacket *pack; - struct b43_txhdr txhdr; u16 cookie; int err; unsigned int hdrlen; @@ -447,8 +462,8 @@ static int pio_tx_frame(struct b43_pio_txqueue *q, struct b43_pio_txpacket, list); cookie = generate_cookie(q, pack); - hdrlen = b43_txhdr_size(q->dev); - err = b43_generate_txhdr(q->dev, (u8 *)&txhdr, skb, + hdrlen = b43_txhdr_size(dev); + err = b43_generate_txhdr(dev, (u8 *)&wl->txhdr, skb, info, cookie); if (err) return err; @@ -456,15 +471,15 @@ static int pio_tx_frame(struct b43_pio_txqueue *q, if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { /* Tell the firmware about the cookie of the last * mcast frame, so it can clear the more-data bit in it. */ - b43_shm_write16(q->dev, B43_SHM_SHARED, + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_MCASTCOOKIE, cookie); } pack->skb = skb; if (q->rev >= 8) - pio_tx_frame_4byte_queue(pack, (const u8 *)&txhdr, hdrlen); + pio_tx_frame_4byte_queue(pack, (const u8 *)&wl->txhdr, hdrlen); else - pio_tx_frame_2byte_queue(pack, (const u8 *)&txhdr, hdrlen); + pio_tx_frame_2byte_queue(pack, (const u8 *)&wl->txhdr, hdrlen); /* Remove it from the list of available packet slots. * It will be put back when we receive the status report. */ @@ -574,7 +589,7 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev, q->buffer_used -= total_len; q->free_packet_slots += 1; - ieee80211_tx_status_irqsafe(dev->wl->hw, pack->skb); + ieee80211_tx_status(dev->wl->hw, pack->skb); pack->skb = NULL; list_add(&pack->list, &q->packets_list); @@ -604,14 +619,14 @@ void b43_pio_get_tx_stats(struct b43_wldev *dev, static bool pio_rx_frame(struct b43_pio_rxqueue *q) { struct b43_wldev *dev = q->dev; - struct b43_rxhdr_fw4 rxhdr; + struct b43_wl *wl = dev->wl; u16 len; u32 macstat; unsigned int i, padding; struct sk_buff *skb; const char *err_msg = NULL; - memset(&rxhdr, 0, sizeof(rxhdr)); + memset(&wl->rxhdr, 0, sizeof(wl->rxhdr)); /* Check if we have data and wait for it to get ready. */ if (q->rev >= 8) { @@ -649,16 +664,16 @@ data_ready: /* Get the preamble (RX header) */ if (q->rev >= 8) { - ssb_block_read(dev->dev, &rxhdr, sizeof(rxhdr), + ssb_block_read(dev->dev, &wl->rxhdr, sizeof(wl->rxhdr), q->mmio_base + B43_PIO8_RXDATA, sizeof(u32)); } else { - ssb_block_read(dev->dev, &rxhdr, sizeof(rxhdr), + ssb_block_read(dev->dev, &wl->rxhdr, sizeof(wl->rxhdr), q->mmio_base + B43_PIO_RXDATA, sizeof(u16)); } /* Sanity checks. */ - len = le16_to_cpu(rxhdr.frame_len); + len = le16_to_cpu(wl->rxhdr.frame_len); if (unlikely(len > 0x700)) { err_msg = "len > 0x700"; goto rx_error; @@ -668,7 +683,7 @@ data_ready: goto rx_error; } - macstat = le32_to_cpu(rxhdr.mac_status); + macstat = le32_to_cpu(wl->rxhdr.mac_status); if (macstat & B43_RX_MAC_FCSERR) { if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) { /* Drop frames with failed FCS. */ @@ -693,21 +708,23 @@ data_ready: q->mmio_base + B43_PIO8_RXDATA, sizeof(u32)); if (len & 3) { - u32 value; - char *data; - /* Read the last few bytes. */ - value = b43_piorx_read32(q, B43_PIO8_RXDATA); - data = &(skb->data[len + padding - 1]); + ssb_block_read(dev->dev, wl->rx_tail, 4, + q->mmio_base + B43_PIO8_RXDATA, + sizeof(u32)); switch (len & 3) { case 3: - *data = (value >> 16); - data--; + skb->data[len + padding - 3] = wl->rx_tail[0]; + skb->data[len + padding - 2] = wl->rx_tail[1]; + skb->data[len + padding - 1] = wl->rx_tail[2]; + break; case 2: - *data = (value >> 8); - data--; + skb->data[len + padding - 2] = wl->rx_tail[0]; + skb->data[len + padding - 1] = wl->rx_tail[1]; + break; case 1: - *data = value; + skb->data[len + padding - 1] = wl->rx_tail[0]; + break; } } } else { @@ -715,15 +732,15 @@ data_ready: q->mmio_base + B43_PIO_RXDATA, sizeof(u16)); if (len & 1) { - u16 value; - /* Read the last byte. */ - value = b43_piorx_read16(q, B43_PIO_RXDATA); - skb->data[len + padding - 1] = value; + ssb_block_read(dev->dev, wl->rx_tail, 2, + q->mmio_base + B43_PIO_RXDATA, + sizeof(u16)); + skb->data[len + padding - 1] = wl->rx_tail[0]; } } - b43_rx(q->dev, skb, &rxhdr); + b43_rx(q->dev, skb, &wl->rxhdr); return 1; diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c index 31e55999893..ffdce6f3c90 100644 --- a/drivers/net/wireless/b43/rfkill.c +++ b/drivers/net/wireless/b43/rfkill.c @@ -28,12 +28,13 @@ /* Returns TRUE, if the radio is enabled in hardware. */ bool b43_is_hw_radio_enabled(struct b43_wldev *dev) { - if (dev->phy.rev >= 3) { + if (dev->phy.rev >= 3 || dev->phy.type == B43_PHYTYPE_LP) { if (!(b43_read32(dev, B43_MMIO_RADIO_HWENABLED_HI) & B43_MMIO_RADIO_HWENABLED_HI_MASK)) return 1; } else { - if (b43_read16(dev, B43_MMIO_RADIO_HWENABLED_LO) + if (b43_status(dev) >= B43_STAT_STARTED && + b43_read16(dev, B43_MMIO_RADIO_HWENABLED_LO) & B43_MMIO_RADIO_HWENABLED_LO_MASK) return 1; } diff --git a/drivers/net/wireless/b43/sdio.c b/drivers/net/wireless/b43/sdio.c new file mode 100644 index 00000000000..0d3ac64147a --- /dev/null +++ b/drivers/net/wireless/b43/sdio.c @@ -0,0 +1,202 @@ +/* + * Broadcom B43 wireless driver + * + * SDIO over Sonics Silicon Backplane bus glue for b43. + * + * Copyright (C) 2009 Albert Herranz + * Copyright (C) 2009 Michael Buesch <mb@bu3sch.de> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/mmc/card.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/ssb/ssb.h> + +#include "sdio.h" +#include "b43.h" + + +#define HNBU_CHIPID 0x01 /* vendor & device id */ + +#define B43_SDIO_BLOCK_SIZE 64 /* rx fifo max size in bytes */ + + +static const struct b43_sdio_quirk { + u16 vendor; + u16 device; + unsigned int quirks; +} b43_sdio_quirks[] = { + { 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32, }, + { }, +}; + + +static unsigned int b43_sdio_get_quirks(u16 vendor, u16 device) +{ + const struct b43_sdio_quirk *q; + + for (q = b43_sdio_quirks; q->quirks; q++) { + if (vendor == q->vendor && device == q->device) + return q->quirks; + } + + return 0; +} + +static void b43_sdio_interrupt_dispatcher(struct sdio_func *func) +{ + struct b43_sdio *sdio = sdio_get_drvdata(func); + struct b43_wldev *dev = sdio->irq_handler_opaque; + + if (unlikely(b43_status(dev) < B43_STAT_STARTED)) + return; + + sdio_release_host(func); + sdio->irq_handler(dev); + sdio_claim_host(func); +} + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)) +{ + struct ssb_bus *bus = dev->dev->bus; + struct sdio_func *func = bus->host_sdio; + struct b43_sdio *sdio = sdio_get_drvdata(func); + int err; + + sdio->irq_handler_opaque = dev; + sdio->irq_handler = handler; + sdio_claim_host(func); + err = sdio_claim_irq(func, b43_sdio_interrupt_dispatcher); + sdio_release_host(func); + + return err; +} + +void b43_sdio_free_irq(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct sdio_func *func = bus->host_sdio; + struct b43_sdio *sdio = sdio_get_drvdata(func); + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_release_host(func); + sdio->irq_handler_opaque = NULL; + sdio->irq_handler = NULL; +} + +static int b43_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct b43_sdio *sdio; + struct sdio_func_tuple *tuple; + u16 vendor = 0, device = 0; + int error; + + /* Look for the card chip identifier. */ + tuple = func->tuples; + while (tuple) { + switch (tuple->code) { + case 0x80: + switch (tuple->data[0]) { + case HNBU_CHIPID: + if (tuple->size != 5) + break; + vendor = tuple->data[1] | (tuple->data[2]<<8); + device = tuple->data[3] | (tuple->data[4]<<8); + dev_info(&func->dev, "Chip ID %04x:%04x\n", + vendor, device); + break; + default: + break; + } + break; + default: + break; + } + tuple = tuple->next; + } + if (!vendor || !device) { + error = -ENODEV; + goto out; + } + + sdio_claim_host(func); + error = sdio_set_block_size(func, B43_SDIO_BLOCK_SIZE); + if (error) { + dev_err(&func->dev, "failed to set block size to %u bytes," + " error %d\n", B43_SDIO_BLOCK_SIZE, error); + goto err_release_host; + } + error = sdio_enable_func(func); + if (error) { + dev_err(&func->dev, "failed to enable func, error %d\n", error); + goto err_release_host; + } + sdio_release_host(func); + + sdio = kzalloc(sizeof(*sdio), GFP_KERNEL); + if (!sdio) { + error = -ENOMEM; + dev_err(&func->dev, "failed to allocate ssb bus\n"); + goto err_disable_func; + } + error = ssb_bus_sdiobus_register(&sdio->ssb, func, + b43_sdio_get_quirks(vendor, device)); + if (error) { + dev_err(&func->dev, "failed to register ssb sdio bus," + " error %d\n", error); + goto err_free_ssb; + } + sdio_set_drvdata(func, sdio); + + return 0; + +err_free_ssb: + kfree(sdio); +err_disable_func: + sdio_disable_func(func); +err_release_host: + sdio_release_host(func); +out: + return error; +} + +static void b43_sdio_remove(struct sdio_func *func) +{ + struct b43_sdio *sdio = sdio_get_drvdata(func); + + ssb_bus_unregister(&sdio->ssb); + sdio_disable_func(func); + kfree(sdio); + sdio_set_drvdata(func, NULL); +} + +static const struct sdio_device_id b43_sdio_ids[] = { + { SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */ + { }, +}; + +static struct sdio_driver b43_sdio_driver = { + .name = "b43-sdio", + .id_table = b43_sdio_ids, + .probe = b43_sdio_probe, + .remove = b43_sdio_remove, +}; + +int b43_sdio_init(void) +{ + return sdio_register_driver(&b43_sdio_driver); +} + +void b43_sdio_exit(void) +{ + sdio_unregister_driver(&b43_sdio_driver); +} diff --git a/drivers/net/wireless/b43/sdio.h b/drivers/net/wireless/b43/sdio.h new file mode 100644 index 00000000000..fb633094403 --- /dev/null +++ b/drivers/net/wireless/b43/sdio.h @@ -0,0 +1,45 @@ +#ifndef B43_SDIO_H_ +#define B43_SDIO_H_ + +#include <linux/ssb/ssb.h> + +struct b43_wldev; + + +#ifdef CONFIG_B43_SDIO + +struct b43_sdio { + struct ssb_bus ssb; + void *irq_handler_opaque; + void (*irq_handler)(struct b43_wldev *dev); +}; + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)); +void b43_sdio_free_irq(struct b43_wldev *dev); + +int b43_sdio_init(void); +void b43_sdio_exit(void); + + +#else /* CONFIG_B43_SDIO */ + + +int b43_sdio_request_irq(struct b43_wldev *dev, + void (*handler)(struct b43_wldev *dev)) +{ + return -ENODEV; +} +void b43_sdio_free_irq(struct b43_wldev *dev) +{ +} +static inline int b43_sdio_init(void) +{ + return 0; +} +static inline void b43_sdio_exit(void) +{ +} + +#endif /* CONFIG_B43_SDIO */ +#endif /* B43_SDIO_H_ */ diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 14f541248b5..f4e9695ec18 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -27,7 +27,7 @@ */ -#include "xmit.h" +#include "b43.h" #include "phy_common.h" #include "dma.h" #include "pio.h" @@ -690,8 +690,14 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) } memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); - ieee80211_rx_irqsafe(dev->wl->hw, skb); + local_bh_disable(); + ieee80211_rx(dev->wl->hw, skb); + local_bh_enable(); + +#if B43_DEBUG + dev->rx_count++; +#endif return; drop: b43dbg(dev->wl, "RX: Packet dropped\n"); |