mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 05:41:55 +00:00
brcmsmac: request firmware in .start() callback
The firmware is requested from user-space. To assure the request is handled it is recommended to do the request upon IFF_UP. For a mac80211 driver the .start() callback can be considered the equivalent. Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Reviewed-by: Piotr Haber <phaber@broadcom.com> Reviewed-by: Hante Meuleman <meuleman@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
1527c343c1
commit
25b5632fb3
@ -274,6 +274,130 @@ static void brcms_set_basic_rate(struct brcm_rateset *rs, u16 rate, bool is_br)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function frees the WL per-device resources.
|
||||||
|
*
|
||||||
|
* This function frees resources owned by the WL device pointed to
|
||||||
|
* by the wl parameter.
|
||||||
|
*
|
||||||
|
* precondition: can both be called locked and unlocked
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static void brcms_free(struct brcms_info *wl)
|
||||||
|
{
|
||||||
|
struct brcms_timer *t, *next;
|
||||||
|
|
||||||
|
/* free ucode data */
|
||||||
|
if (wl->fw.fw_cnt)
|
||||||
|
brcms_ucode_data_free(&wl->ucode);
|
||||||
|
if (wl->irq)
|
||||||
|
free_irq(wl->irq, wl);
|
||||||
|
|
||||||
|
/* kill dpc */
|
||||||
|
tasklet_kill(&wl->tasklet);
|
||||||
|
|
||||||
|
if (wl->pub) {
|
||||||
|
brcms_debugfs_detach(wl->pub);
|
||||||
|
brcms_c_module_unregister(wl->pub, "linux", wl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free common resources */
|
||||||
|
if (wl->wlc) {
|
||||||
|
brcms_c_detach(wl->wlc);
|
||||||
|
wl->wlc = NULL;
|
||||||
|
wl->pub = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* virtual interface deletion is deferred so we cannot spinwait */
|
||||||
|
|
||||||
|
/* wait for all pending callbacks to complete */
|
||||||
|
while (atomic_read(&wl->callbacks) > 0)
|
||||||
|
schedule();
|
||||||
|
|
||||||
|
/* free timers */
|
||||||
|
for (t = wl->timers; t; t = next) {
|
||||||
|
next = t->next;
|
||||||
|
#ifdef DEBUG
|
||||||
|
kfree(t->name);
|
||||||
|
#endif
|
||||||
|
kfree(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* called from both kernel as from this kernel module (error flow on attach)
|
||||||
|
* precondition: perimeter lock is not acquired.
|
||||||
|
*/
|
||||||
|
static void brcms_remove(struct bcma_device *pdev)
|
||||||
|
{
|
||||||
|
struct ieee80211_hw *hw = bcma_get_drvdata(pdev);
|
||||||
|
struct brcms_info *wl = hw->priv;
|
||||||
|
|
||||||
|
if (wl->wlc) {
|
||||||
|
wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
|
||||||
|
wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
|
||||||
|
ieee80211_unregister_hw(hw);
|
||||||
|
}
|
||||||
|
|
||||||
|
brcms_free(wl);
|
||||||
|
|
||||||
|
bcma_set_drvdata(pdev, NULL);
|
||||||
|
ieee80211_free_hw(hw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Precondition: Since this function is called in brcms_pci_probe() context,
|
||||||
|
* no locking is required.
|
||||||
|
*/
|
||||||
|
static void brcms_release_fw(struct brcms_info *wl)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_FW_IMAGES; i++) {
|
||||||
|
release_firmware(wl->fw.fw_bin[i]);
|
||||||
|
release_firmware(wl->fw.fw_hdr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Precondition: Since this function is called in brcms_pci_probe() context,
|
||||||
|
* no locking is required.
|
||||||
|
*/
|
||||||
|
static int brcms_request_fw(struct brcms_info *wl, struct bcma_device *pdev)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
struct device *device = &pdev->dev;
|
||||||
|
char fw_name[100];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(&wl->fw, 0, sizeof(struct brcms_firmware));
|
||||||
|
for (i = 0; i < MAX_FW_IMAGES; i++) {
|
||||||
|
if (brcms_firmwares[i] == NULL)
|
||||||
|
break;
|
||||||
|
sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i],
|
||||||
|
UCODE_LOADER_API_VER);
|
||||||
|
status = request_firmware(&wl->fw.fw_bin[i], fw_name, device);
|
||||||
|
if (status) {
|
||||||
|
wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
|
||||||
|
KBUILD_MODNAME, fw_name);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i],
|
||||||
|
UCODE_LOADER_API_VER);
|
||||||
|
status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device);
|
||||||
|
if (status) {
|
||||||
|
wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
|
||||||
|
KBUILD_MODNAME, fw_name);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
wl->fw.hdr_num_entries[i] =
|
||||||
|
wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr));
|
||||||
|
}
|
||||||
|
wl->fw.fw_cnt = i;
|
||||||
|
status = brcms_ucode_data_init(wl, &wl->ucode);
|
||||||
|
brcms_release_fw(wl);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
static void brcms_ops_tx(struct ieee80211_hw *hw,
|
static void brcms_ops_tx(struct ieee80211_hw *hw,
|
||||||
struct ieee80211_tx_control *control,
|
struct ieee80211_tx_control *control,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
@ -306,6 +430,14 @@ static int brcms_ops_start(struct ieee80211_hw *hw)
|
|||||||
if (!blocked)
|
if (!blocked)
|
||||||
wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
|
wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
|
||||||
|
|
||||||
|
if (!wl->ucode.bcm43xx_bomminor) {
|
||||||
|
err = brcms_request_fw(wl, wl->wlc->hw->d11core);
|
||||||
|
if (err) {
|
||||||
|
brcms_remove(wl->wlc->hw->d11core);
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_bh(&wl->lock);
|
spin_lock_bh(&wl->lock);
|
||||||
/* avoid acknowledging frames before a non-monitor device is added */
|
/* avoid acknowledging frames before a non-monitor device is added */
|
||||||
wl->mute_tx = true;
|
wl->mute_tx = true;
|
||||||
@ -793,128 +925,6 @@ void brcms_dpc(unsigned long data)
|
|||||||
wake_up(&wl->tx_flush_wq);
|
wake_up(&wl->tx_flush_wq);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Precondition: Since this function is called in brcms_pci_probe() context,
|
|
||||||
* no locking is required.
|
|
||||||
*/
|
|
||||||
static int brcms_request_fw(struct brcms_info *wl, struct bcma_device *pdev)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
struct device *device = &pdev->dev;
|
|
||||||
char fw_name[100];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
memset(&wl->fw, 0, sizeof(struct brcms_firmware));
|
|
||||||
for (i = 0; i < MAX_FW_IMAGES; i++) {
|
|
||||||
if (brcms_firmwares[i] == NULL)
|
|
||||||
break;
|
|
||||||
sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i],
|
|
||||||
UCODE_LOADER_API_VER);
|
|
||||||
status = request_firmware(&wl->fw.fw_bin[i], fw_name, device);
|
|
||||||
if (status) {
|
|
||||||
wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
|
|
||||||
KBUILD_MODNAME, fw_name);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i],
|
|
||||||
UCODE_LOADER_API_VER);
|
|
||||||
status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device);
|
|
||||||
if (status) {
|
|
||||||
wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
|
|
||||||
KBUILD_MODNAME, fw_name);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
wl->fw.hdr_num_entries[i] =
|
|
||||||
wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr));
|
|
||||||
}
|
|
||||||
wl->fw.fw_cnt = i;
|
|
||||||
return brcms_ucode_data_init(wl, &wl->ucode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Precondition: Since this function is called in brcms_pci_probe() context,
|
|
||||||
* no locking is required.
|
|
||||||
*/
|
|
||||||
static void brcms_release_fw(struct brcms_info *wl)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < MAX_FW_IMAGES; i++) {
|
|
||||||
release_firmware(wl->fw.fw_bin[i]);
|
|
||||||
release_firmware(wl->fw.fw_hdr[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function frees the WL per-device resources.
|
|
||||||
*
|
|
||||||
* This function frees resources owned by the WL device pointed to
|
|
||||||
* by the wl parameter.
|
|
||||||
*
|
|
||||||
* precondition: can both be called locked and unlocked
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static void brcms_free(struct brcms_info *wl)
|
|
||||||
{
|
|
||||||
struct brcms_timer *t, *next;
|
|
||||||
|
|
||||||
/* free ucode data */
|
|
||||||
if (wl->fw.fw_cnt)
|
|
||||||
brcms_ucode_data_free(&wl->ucode);
|
|
||||||
if (wl->irq)
|
|
||||||
free_irq(wl->irq, wl);
|
|
||||||
|
|
||||||
/* kill dpc */
|
|
||||||
tasklet_kill(&wl->tasklet);
|
|
||||||
|
|
||||||
if (wl->pub) {
|
|
||||||
brcms_debugfs_detach(wl->pub);
|
|
||||||
brcms_c_module_unregister(wl->pub, "linux", wl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* free common resources */
|
|
||||||
if (wl->wlc) {
|
|
||||||
brcms_c_detach(wl->wlc);
|
|
||||||
wl->wlc = NULL;
|
|
||||||
wl->pub = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* virtual interface deletion is deferred so we cannot spinwait */
|
|
||||||
|
|
||||||
/* wait for all pending callbacks to complete */
|
|
||||||
while (atomic_read(&wl->callbacks) > 0)
|
|
||||||
schedule();
|
|
||||||
|
|
||||||
/* free timers */
|
|
||||||
for (t = wl->timers; t; t = next) {
|
|
||||||
next = t->next;
|
|
||||||
#ifdef DEBUG
|
|
||||||
kfree(t->name);
|
|
||||||
#endif
|
|
||||||
kfree(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* called from both kernel as from this kernel module (error flow on attach)
|
|
||||||
* precondition: perimeter lock is not acquired.
|
|
||||||
*/
|
|
||||||
static void brcms_remove(struct bcma_device *pdev)
|
|
||||||
{
|
|
||||||
struct ieee80211_hw *hw = bcma_get_drvdata(pdev);
|
|
||||||
struct brcms_info *wl = hw->priv;
|
|
||||||
|
|
||||||
if (wl->wlc) {
|
|
||||||
wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
|
|
||||||
wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
|
|
||||||
ieee80211_unregister_hw(hw);
|
|
||||||
}
|
|
||||||
|
|
||||||
brcms_free(wl);
|
|
||||||
|
|
||||||
bcma_set_drvdata(pdev, NULL);
|
|
||||||
ieee80211_free_hw(hw);
|
|
||||||
}
|
|
||||||
|
|
||||||
static irqreturn_t brcms_isr(int irq, void *dev_id)
|
static irqreturn_t brcms_isr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct brcms_info *wl;
|
struct brcms_info *wl;
|
||||||
@ -1047,18 +1057,8 @@ static struct brcms_info *brcms_attach(struct bcma_device *pdev)
|
|||||||
spin_lock_init(&wl->lock);
|
spin_lock_init(&wl->lock);
|
||||||
spin_lock_init(&wl->isr_lock);
|
spin_lock_init(&wl->isr_lock);
|
||||||
|
|
||||||
/* prepare ucode */
|
|
||||||
if (brcms_request_fw(wl, pdev) < 0) {
|
|
||||||
wiphy_err(wl->wiphy, "%s: Failed to find firmware usually in "
|
|
||||||
"%s\n", KBUILD_MODNAME, "/lib/firmware/brcm");
|
|
||||||
brcms_release_fw(wl);
|
|
||||||
brcms_remove(pdev);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* common load-time initialization */
|
/* common load-time initialization */
|
||||||
wl->wlc = brcms_c_attach((void *)wl, pdev, unit, false, &err);
|
wl->wlc = brcms_c_attach((void *)wl, pdev, unit, false, &err);
|
||||||
brcms_release_fw(wl);
|
|
||||||
if (!wl->wlc) {
|
if (!wl->wlc) {
|
||||||
wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n",
|
wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n",
|
||||||
KBUILD_MODNAME, err);
|
KBUILD_MODNAME, err);
|
||||||
|
Loading…
Reference in New Issue
Block a user