diff options
Diffstat (limited to 'drivers/net/wireless/ath/wil6210')
-rw-r--r-- | drivers/net/wireless/ath/wil6210/debugfs.c | 12 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/interrupt.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/main.c | 24 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/netdev.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/pcie_bus.c | 30 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/rx_reorder.c | 12 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx.c | 5 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wil6210.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wil_crash_dump.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wil_platform.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wil_platform.h | 38 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wmi.c | 4 |
12 files changed, 113 insertions, 29 deletions
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 97bc186f9..a1d10b859 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -580,16 +580,10 @@ static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf, long channel; bool on; - char *kbuf = kmalloc(len + 1, GFP_KERNEL); - - if (!kbuf) - return -ENOMEM; - if (copy_from_user(kbuf, buf, len)) { - kfree(kbuf); - return -EIO; - } + char *kbuf = memdup_user_nul(buf, len); - kbuf[len] = '\0'; + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); rc = kstrtol(kbuf, 0, &channel); kfree(kbuf); if (rc) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 50c136e84..4f2ffa5c6 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -394,9 +394,13 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) wil_fw_core_dump(wil); wil_notify_fw_error(wil); isr &= ~ISR_MISC_FW_ERROR; - wil_fw_error_recovery(wil); + if (wil->platform_ops.notify_crash) { + wil_err(wil, "notify platform driver about FW crash"); + wil->platform_ops.notify_crash(wil->platform_handle); + } else { + wil_fw_error_recovery(wil); + } } - if (isr & ISR_MISC_MBOX_EVT) { wil_dbg_irq(wil, "MBOX event\n"); wmi_recv_cmd(wil); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index bb69a5949..b39f0bfc5 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -401,20 +401,26 @@ void wil_bcast_fini(struct wil6210_priv *wil) static void wil_connect_worker(struct work_struct *work) { - int rc; + int rc, cid, ringid; struct wil6210_priv *wil = container_of(work, struct wil6210_priv, connect_worker); struct net_device *ndev = wil_to_ndev(wil); - int cid = wil->pending_connect_cid; - int ringid = wil_find_free_vring(wil); + mutex_lock(&wil->mutex); + cid = wil->pending_connect_cid; if (cid < 0) { wil_err(wil, "No connection pending\n"); - return; + goto out; + } + ringid = wil_find_free_vring(wil); + if (ringid < 0) { + wil_err(wil, "No free vring found\n"); + goto out; } - wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); + wil_dbg_wmi(wil, "Configure for connection CID %d vring %d\n", + cid, ringid); rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0); wil->pending_connect_cid = -1; @@ -424,6 +430,8 @@ static void wil_connect_worker(struct work_struct *work) } else { wil_disconnect_cid(wil, cid, WLAN_REASON_UNSPECIFIED, true); } +out: + mutex_unlock(&wil->mutex); } int wil_priv_init(struct wil6210_priv *wil) @@ -773,8 +781,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); wil_bcast_fini(wil); - /* prevent NAPI from being scheduled */ + /* prevent NAPI from being scheduled and prevent wmi commands */ + mutex_lock(&wil->wmi_mutex); bitmap_zero(wil->status, wil_status_last); + mutex_unlock(&wil->wmi_mutex); if (wil->scan_request) { wil_dbg_misc(wil, "Abort scan_request 0x%p\n", @@ -977,7 +987,7 @@ int __wil_down(struct wil6210_priv *wil) } mutex_lock(&wil->mutex); - if (!iter) + if (iter < 0) wil_err(wil, "timeout waiting for idle FW/HW\n"); wil_reset(wil, false); diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index e3b3c8fb4..56aaa2d4f 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -183,7 +183,7 @@ void *wil_if_alloc(struct device *dev) netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx, WIL6210_NAPI_BUDGET); - netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx, + netif_tx_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx, WIL6210_NAPI_BUDGET); netif_tx_stop_all_queues(ndev); diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 1a3142c33..e36f2a0c8 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -125,11 +125,37 @@ static int wil_if_pcie_disable(struct wil6210_priv *wil) return 0; } +static int wil_platform_rop_ramdump(void *wil_handle, void *buf, uint32_t size) +{ + struct wil6210_priv *wil = wil_handle; + + if (!wil) + return -EINVAL; + + return wil_fw_copy_crash_dump(wil, buf, size); +} + +static int wil_platform_rop_fw_recovery(void *wil_handle) +{ + struct wil6210_priv *wil = wil_handle; + + if (!wil) + return -EINVAL; + + wil_fw_error_recovery(wil); + + return 0; +} + static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct wil6210_priv *wil; struct device *dev = &pdev->dev; int rc; + const struct wil_platform_rops rops = { + .ramdump = wil_platform_rop_ramdump, + .fw_recovery = wil_platform_rop_fw_recovery, + }; /* check HW */ dev_info(&pdev->dev, WIL_NAME @@ -154,7 +180,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* rollback to if_free */ wil->platform_handle = - wil_platform_init(&pdev->dev, &wil->platform_ops); + wil_platform_init(&pdev->dev, &wil->platform_ops, &rops, wil); if (!wil->platform_handle) { rc = -ENODEV; wil_err(wil, "wil_platform_init failed\n"); diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index e3d1be82f..32031e7a1 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -261,9 +261,19 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, struct wil_tid_ampdu_rx *r) { + int i; + if (!r) return; - wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size); + + /* Do not pass remaining frames to the network stack - it may be + * not expecting to get any more Rx. Rx from here may lead to + * kernel OOPS since some per-socket accounting info was already + * released. + */ + for (i = 0; i < r->buf_size; i++) + kfree_skb(r->reorder_buf[i]); + kfree(r->reorder_buf); kfree(r->reorder_time); kfree(r); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 3bc9bc0ef..7887e6cfd 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -160,6 +160,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, struct device *dev = wil_to_dev(wil); size_t sz = vring->size * sizeof(vring->va[0]); + lockdep_assert_held(&wil->mutex); if (tx) { int vring_index = vring - wil->vring_tx; @@ -749,6 +750,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__, cmd.vring_cfg.tx_sw_ring.max_mpdu_size); + lockdep_assert_held(&wil->mutex); if (vring->va) { wil_err(wil, "Tx ring [%d] already allocated\n", id); @@ -821,6 +823,7 @@ int wil_vring_init_bcast(struct wil6210_priv *wil, int id, int size) wil_dbg_misc(wil, "%s() max_mpdu_size %d\n", __func__, cmd.vring_cfg.tx_sw_ring.max_mpdu_size); + lockdep_assert_held(&wil->mutex); if (vring->va) { wil_err(wil, "Tx ring [%d] already allocated\n", id); @@ -872,7 +875,7 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id) struct vring *vring = &wil->vring_tx[id]; struct vring_tx_data *txdata = &wil->vring_tx_data[id]; - WARN_ON(!mutex_is_locked(&wil->mutex)); + lockdep_assert_held(&wil->mutex); if (!vring->va) return; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 244d80fd4..467bf4b6f 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -828,6 +828,7 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime); int wil_suspend(struct wil6210_priv *wil, bool is_runtime); int wil_resume(struct wil6210_priv *wil, bool is_runtime); +int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size); void wil_fw_core_dump(struct wil6210_priv *wil); #endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c index 7e7093499..b57d28094 100644 --- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c +++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c @@ -51,8 +51,7 @@ static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil, return 0; } -static int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, - u32 size) +int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size) { int i; const struct fw_map *map; diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.c b/drivers/net/wireless/ath/wil6210/wil_platform.c index 2e831bf20..4eed05bdd 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.c +++ b/drivers/net/wireless/ath/wil6210/wil_platform.c @@ -33,7 +33,8 @@ void wil_platform_modexit(void) * It returns a handle which is used with the rest of the API * */ -void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops) +void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops, + const struct wil_platform_rops *rops, void *wil_handle) { void *handle = ops; /* to return some non-NULL for 'void' impl. */ diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h index d7fa19b78..9a949d910 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.h +++ b/drivers/net/wireless/ath/wil6210/wil_platform.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Qualcomm Atheros, Inc. + * Copyright (c) 2014-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,16 +20,48 @@ struct device; /** - * struct wil_platform_ops - wil platform module callbacks + * struct wil_platform_ops - wil platform module calls from this + * driver to platform driver */ struct wil_platform_ops { int (*bus_request)(void *handle, uint32_t kbps /* KBytes/Sec */); int (*suspend)(void *handle); int (*resume)(void *handle); void (*uninit)(void *handle); + int (*notify_crash)(void *handle); }; -void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops); +/** + * struct wil_platform_rops - wil platform module callbacks from + * platform driver to this driver + * @ramdump: store a ramdump from the wil firmware. The platform + * driver may add additional data to the ramdump to + * generate the final crash dump. + * @fw_recovery: start a firmware recovery process. Called as + * part of a crash recovery process which may include other + * related platform subsystems. + */ +struct wil_platform_rops { + int (*ramdump)(void *wil_handle, void *buf, uint32_t size); + int (*fw_recovery)(void *wil_handle); +}; + +/** + * wil_platform_init - initialize the platform driver + * + * @dev - pointer to the wil6210 device + * @ops - structure with platform driver operations. Platform + * driver will fill this structure with function pointers. + * @rops - structure with callbacks from platform driver to + * this driver. The platform driver copies the structure to + * its own storage. Can be NULL if this driver does not + * support crash recovery. + * @wil_handle - context for this driver that will be passed + * when platform driver invokes one of the callbacks in + * rops. May be NULL if rops is NULL. + */ +void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops, + const struct wil_platform_rops *rops, void *wil_handle); int __init wil_platform_modinit(void); void wil_platform_modexit(void); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 6ed26baca..e3ea74cdd 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -228,6 +228,10 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); /* wait till FW finish with previous command */ for (retry = 5; retry > 0; retry--) { + if (!test_bit(wil_status_fwready, wil->status)) { + wil_err(wil, "WMI: cannot send command while FW not ready\n"); + return -EAGAIN; + } r->tail = wil_r(wil, RGF_MBOX + offsetof(struct wil6210_mbox_ctl, tx.tail)); if (next_head != r->tail) |