* [PATCH v1 2/2] Bluetooth: hci_qca: Add qcomm devcoredump support
@ 2023-04-28 7:18 Sai Teja Aluvala
2023-04-28 23:54 ` kernel test robot
0 siblings, 1 reply; 3+ messages in thread
From: Sai Teja Aluvala @ 2023-04-28 7:18 UTC (permalink / raw)
To: marcel, johan.hedberg
Cc: linux-kernel, linux-bluetooth, quic_hemantg, quic_bgodavar,
quic_rameshn, jiangzp, mmandlik, Sai Teja Aluvala
Intercept debug exception events from QCA controller and put them into
a devcoredump using hci devcoredump APIs of hci_core
Signed-off-by: Sai Teja Aluvala <quic_saluvala@quicinc.com>
Reviewed-by: Manish Mandlik <mmandlik@google.com>
---
drivers/bluetooth/hci_qca.c | 188 ++++++++++++++++++++++++++++++++------------
1 file changed, 136 insertions(+), 52 deletions(-)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index e21180d..90f1e91 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -77,6 +77,7 @@ enum qca_flags {
QCA_MEMDUMP_COLLECTION,
QCA_HW_ERROR_EVENT,
QCA_SSR_TRIGGERED,
+ QCA_COREDUMP_TRIGGERED,
QCA_BT_OFF,
QCA_ROM_FW
};
@@ -116,9 +117,7 @@ enum qca_memdump_states {
QCA_MEMDUMP_TIMEOUT,
};
-struct qca_memdump_data {
- char *memdump_buf_head;
- char *memdump_buf_tail;
+struct qca_memdump_info {
u32 current_seq_no;
u32 received_dump;
u32 ram_dump_size;
@@ -159,13 +158,15 @@ struct qca_data {
struct work_struct ws_tx_vote_off;
struct work_struct ctrl_memdump_evt;
struct delayed_work ctrl_memdump_timeout;
- struct qca_memdump_data *qca_memdump;
+ struct qca_memdump_info *qca_memdump;
unsigned long flags;
struct completion drop_ev_comp;
wait_queue_head_t suspend_wait_q;
enum qca_memdump_states memdump_state;
struct mutex hci_memdump_lock;
+ u16 fw_version;
+ u16 controller_id;
/* For debugging purpose */
u64 ibs_sent_wacks;
u64 ibs_sent_slps;
@@ -543,7 +544,8 @@ static void qca_controller_memdump_timeout(struct work_struct *work)
mutex_lock(&qca->hci_memdump_lock);
if (test_bit(QCA_MEMDUMP_COLLECTION, &qca->flags)) {
qca->memdump_state = QCA_MEMDUMP_TIMEOUT;
- if (!test_bit(QCA_HW_ERROR_EVENT, &qca->flags)) {
+ if ((!test_bit(QCA_HW_ERROR_EVENT, &qca->flags)) ||
+ (!test_bit(QCA_COREDUMP_TRIGGERED, &qca->flags))) {
/* Inject hw error event to reset the device
* and driver.
*/
@@ -976,6 +978,27 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb)
return hci_recv_frame(hdev, skb);
}
+int qca_dmp_hdr(struct hci_dev *hdev, char *buf, size_t size)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct qca_data *qca = hu->priv;
+ int len = 0;
+
+ len += snprintf(buf + len, size-len, "Controller Name: 0x%x\n",
+ qca->controller_id);
+
+ len += snprintf(buf + len, size-len, "Firmware Version: 0x%x\n",
+ qca->fw_version);
+
+ len += snprintf(buf + len, size-len, "Vendor:Qualcomm\n");
+
+ len += snprintf(buf + len, size-len, "Driver: %s\n",
+ hu->serdev->dev.driver->name);
+
+ bt_dev_info(hdev, "vendor dump hdr size(%d)", len);
+ return len;
+}
+
static void qca_controller_memdump(struct work_struct *work)
{
struct qca_data *qca = container_of(work, struct qca_data,
@@ -983,13 +1006,11 @@ static void qca_controller_memdump(struct work_struct *work)
struct hci_uart *hu = qca->hu;
struct sk_buff *skb;
struct qca_memdump_event_hdr *cmd_hdr;
- struct qca_memdump_data *qca_memdump = qca->qca_memdump;
+ struct qca_memdump_info *qca_memdump = qca->qca_memdump;
struct qca_dump_size *dump;
- char *memdump_buf;
- char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 };
u16 seq_no;
- u32 dump_size;
u32 rx_size;
+ int ret = 0;
enum qca_btsoc_type soc_type = qca_soc_type(hu);
while ((skb = skb_dequeue(&qca->rx_memdump_q))) {
@@ -1005,7 +1026,7 @@ static void qca_controller_memdump(struct work_struct *work)
}
if (!qca_memdump) {
- qca_memdump = kzalloc(sizeof(struct qca_memdump_data),
+ qca_memdump = kzalloc(sizeof(struct qca_memdump_info),
GFP_ATOMIC);
if (!qca_memdump) {
mutex_unlock(&qca->hci_memdump_lock);
@@ -1031,44 +1052,49 @@ static void qca_controller_memdump(struct work_struct *work)
set_bit(QCA_IBS_DISABLED, &qca->flags);
set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
dump = (void *) skb->data;
- dump_size = __le32_to_cpu(dump->dump_size);
- if (!(dump_size)) {
+ qca_memdump->ram_dump_size = __le32_to_cpu(dump->dump_size);
+ if (!(qca_memdump->ram_dump_size)) {
bt_dev_err(hu->hdev, "Rx invalid memdump size");
kfree(qca_memdump);
kfree_skb(skb);
- qca->qca_memdump = NULL;
mutex_unlock(&qca->hci_memdump_lock);
return;
}
- bt_dev_info(hu->hdev, "QCA collecting dump of size:%u",
- dump_size);
queue_delayed_work(qca->workqueue,
&qca->ctrl_memdump_timeout,
- msecs_to_jiffies(MEMDUMP_TIMEOUT_MS)
- );
-
- skb_pull(skb, sizeof(dump_size));
- memdump_buf = vmalloc(dump_size);
- qca_memdump->ram_dump_size = dump_size;
- qca_memdump->memdump_buf_head = memdump_buf;
- qca_memdump->memdump_buf_tail = memdump_buf;
- }
+ msecs_to_jiffies(MEMDUMP_TIMEOUT_MS));
+ skb_pull(skb, sizeof(qca_memdump->ram_dump_size));
+ qca_memdump->current_seq_no = 0;
+ qca_memdump->received_dump = 0;
+ ret = hci_devcoredump_init(hu->hdev, qca_memdump->ram_dump_size);
+ bt_dev_info(hu->hdev, "hci_devcoredump_init Return:%d",
+ ret);
+ if (ret < 0) {
+ kfree(qca->qca_memdump);
+ qca->qca_memdump = NULL;
+ qca->memdump_state = QCA_MEMDUMP_COLLECTED;
+ cancel_delayed_work(&qca->ctrl_memdump_timeout);
+ clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
+ mutex_unlock(&qca->hci_memdump_lock);
+ return;
+ }
+
+ bt_dev_info(hu->hdev, "QCA collecting dump of size:%u",
+ qca_memdump->ram_dump_size);
- memdump_buf = qca_memdump->memdump_buf_tail;
+ }
/* If sequence no 0 is missed then there is no point in
* accepting the other sequences.
*/
- if (!memdump_buf) {
+ if (!test_bit(QCA_MEMDUMP_COLLECTION, &qca->flags)) {
bt_dev_err(hu->hdev, "QCA: Discarding other packets");
kfree(qca_memdump);
kfree_skb(skb);
- qca->qca_memdump = NULL;
mutex_unlock(&qca->hci_memdump_lock);
return;
}
-
/* There could be chance of missing some packets from
* the controller. In such cases let us store the dummy
* packets in the buffer.
@@ -1078,8 +1104,8 @@ static void qca_controller_memdump(struct work_struct *work)
* bits, so skip this checking for missing packet.
*/
while ((seq_no > qca_memdump->current_seq_no + 1) &&
- (soc_type != QCA_QCA6390) &&
- seq_no != QCA_LAST_SEQUENCE_NUM) {
+ (soc_type != QCA_QCA6390) &&
+ seq_no != QCA_LAST_SEQUENCE_NUM) {
bt_dev_err(hu->hdev, "QCA controller missed packet:%d",
qca_memdump->current_seq_no);
rx_size = qca_memdump->received_dump;
@@ -1090,43 +1116,38 @@ static void qca_controller_memdump(struct work_struct *work)
qca_memdump->received_dump);
break;
}
- memcpy(memdump_buf, nullBuff, QCA_DUMP_PACKET_SIZE);
- memdump_buf = memdump_buf + QCA_DUMP_PACKET_SIZE;
+ hci_devcoredump_append_pattern(hu->hdev, 0x00,
+ QCA_DUMP_PACKET_SIZE);
qca_memdump->received_dump += QCA_DUMP_PACKET_SIZE;
qca_memdump->current_seq_no++;
}
- rx_size = qca_memdump->received_dump + skb->len;
+ rx_size = qca_memdump->received_dump + skb->len;
if (rx_size <= qca_memdump->ram_dump_size) {
if ((seq_no != QCA_LAST_SEQUENCE_NUM) &&
- (seq_no != qca_memdump->current_seq_no))
+ (seq_no != qca_memdump->current_seq_no)) {
bt_dev_err(hu->hdev,
"QCA memdump unexpected packet %d",
seq_no);
+ }
bt_dev_dbg(hu->hdev,
"QCA memdump packet %d with length %d",
seq_no, skb->len);
- memcpy(memdump_buf, (unsigned char *)skb->data,
- skb->len);
- memdump_buf = memdump_buf + skb->len;
- qca_memdump->memdump_buf_tail = memdump_buf;
- qca_memdump->current_seq_no = seq_no + 1;
- qca_memdump->received_dump += skb->len;
+ hci_devcoredump_append(hu->hdev, skb);
+ qca_memdump->current_seq_no += 1;
+ qca_memdump->received_dump = rx_size;
} else {
bt_dev_err(hu->hdev,
- "QCA memdump received %d, no space for packet %d",
- qca_memdump->received_dump, seq_no);
+ "QCA memdump received no space for packet %d",
+ qca_memdump->current_seq_no);
}
- qca->qca_memdump = qca_memdump;
- kfree_skb(skb);
+
if (seq_no == QCA_LAST_SEQUENCE_NUM) {
bt_dev_info(hu->hdev,
- "QCA memdump Done, received %d, total %d",
- qca_memdump->received_dump,
- qca_memdump->ram_dump_size);
- memdump_buf = qca_memdump->memdump_buf_head;
- dev_coredumpv(&hu->serdev->dev, memdump_buf,
- qca_memdump->received_dump, GFP_KERNEL);
+ "QCA memdump Done, received %d, total %d",
+ qca_memdump->received_dump,
+ qca_memdump->ram_dump_size);
+ hci_devcoredump_complete(hu->hdev);
cancel_delayed_work(&qca->ctrl_memdump_timeout);
kfree(qca->qca_memdump);
qca->qca_memdump = NULL;
@@ -1537,8 +1558,8 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code)
mutex_lock(&qca->hci_memdump_lock);
if (qca->memdump_state != QCA_MEMDUMP_COLLECTED) {
bt_dev_err(hu->hdev, "clearing allocated memory due to memdump timeout");
+ hci_devcoredump_abort(hu->hdev);
if (qca->qca_memdump) {
- vfree(qca->qca_memdump->memdump_buf_head);
kfree(qca->qca_memdump);
qca->qca_memdump = NULL;
}
@@ -1577,7 +1598,8 @@ static void qca_cmd_timeout(struct hci_dev *hdev)
mutex_lock(&qca->hci_memdump_lock);
if (qca->memdump_state != QCA_MEMDUMP_COLLECTED) {
qca->memdump_state = QCA_MEMDUMP_TIMEOUT;
- if (!test_bit(QCA_HW_ERROR_EVENT, &qca->flags)) {
+ if ((!test_bit(QCA_HW_ERROR_EVENT, &qca->flags)) ||
+ (!test_bit(QCA_COREDUMP_TRIGGERED, &qca->flags))) {
/* Inject hw error event to reset the device
* and driver.
*/
@@ -1711,6 +1733,65 @@ static bool hciqca_coredump_enabled(struct hci_dev *hdev)
}
#endif
+static void hci_coredump_qca(struct hci_dev *hdev)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct qca_data *qca = hu->priv;
+ struct sk_buff *skb;
+
+
+ set_bit(QCA_COREDUMP_TRIGGERED, &qca->flags);
+ bt_dev_info(hdev, "Enter mem_dump_status: %d", qca->memdump_state);
+
+ if (qca->memdump_state == QCA_MEMDUMP_IDLE) {
+ /* we need to crash the SOC
+ * and wait here for 8 seconds to get the dump packets.
+ * This will block main thread to be on hold until we
+ * collect dump.
+ */
+ set_bit(QCA_SSR_TRIGGERED, &qca->flags);
+ set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
+
+ skb = bt_skb_alloc(QCA_CRASHBYTE_PACKET_LEN, GFP_KERNEL);
+ if (!skb) {
+ bt_dev_err(hu->hdev, "Failed to allocate memory for skb packet");
+ return;
+ }
+
+ /* We forcefully crash the controller, by sending 0xfb byte for
+ * 1024 times. We also might have chance of losing data, To be
+ * on safer side we send 1096 bytes to the SoC.
+ */
+ memset(skb_put(skb, QCA_CRASHBYTE_PACKET_LEN), QCA_MEMDUMP_BYTE,
+ QCA_CRASHBYTE_PACKET_LEN);
+ hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
+ bt_dev_info(hu->hdev, "crash the soc to collect controller dump");
+
+ switch (qca->tx_ibs_state) {
+ case HCI_IBS_TX_WAKING:
+ /* Transient state; just keep packet for later */
+ skb_queue_tail(&qca->tx_wait_q, skb);
+ break;
+ case HCI_IBS_TX_AWAKE:
+ skb_queue_tail(&qca->txq, skb);
+ hci_uart_tx_wakeup(hu);
+ break;
+ case HCI_IBS_TX_ASLEEP:
+ skb_queue_tail(&qca->tx_wait_q, skb);
+ qca->tx_ibs_state = HCI_IBS_TX_WAKING;
+ /* Schedule a work queue to wake up device */
+ queue_work(qca->workqueue, &qca->ws_awake_device);
+ break;
+ }
+ } else if (qca->memdump_state == QCA_MEMDUMP_COLLECTING) {
+ /* Let us wait here until memory dump collected or
+ * memory dump timer expired.
+ */
+ bt_dev_info(hdev, "waiting for dump to complete");
+ }
+ clear_bit(QCA_COREDUMP_TRIGGERED, &qca->flags);
+}
+
static int qca_setup(struct hci_uart *hu)
{
struct hci_dev *hdev = hu->hdev;
@@ -1828,6 +1909,9 @@ static int qca_setup(struct hci_uart *hu)
hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
else
hu->hdev->set_bdaddr = qca_set_bdaddr;
+ qca->fw_version = le16_to_cpu(ver.patch_ver);
+ qca->controller_id = le16_to_cpu(ver.rom_ver);
+ hci_devcoredump_register(hdev, hci_coredump_qca, qca_dmp_hdr, NULL);
return ret;
}
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc.
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v1 2/2] Bluetooth: hci_qca: Add qcomm devcoredump support
2023-04-28 7:18 [PATCH v1 2/2] Bluetooth: hci_qca: Add qcomm devcoredump support Sai Teja Aluvala
@ 2023-04-28 23:54 ` kernel test robot
0 siblings, 0 replies; 3+ messages in thread
From: kernel test robot @ 2023-04-28 23:54 UTC (permalink / raw)
To: Sai Teja Aluvala, marcel, johan.hedberg
Cc: oe-kbuild-all, linux-kernel, linux-bluetooth, quic_hemantg,
quic_bgodavar, quic_rameshn, jiangzp, mmandlik, Sai Teja Aluvala
Hi Sai,
kernel test robot noticed the following build warnings:
[auto build test WARNING on bluetooth/master]
[also build test WARNING on bluetooth-next/master linus/master v6.3 next-20230428]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sai-Teja-Aluvala/Bluetooth-hci_qca-Add-qcomm-devcoredump-support/20230428-152145
base: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git master
patch link: https://lore.kernel.org/r/1682666311-8452-1-git-send-email-quic_saluvala%40quicinc.com
patch subject: [PATCH v1 2/2] Bluetooth: hci_qca: Add qcomm devcoredump support
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20230429/202304290720.TfpPCFY6-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/a3c3d43057a99f13198754e0b8eaf944d1853142
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Sai-Teja-Aluvala/Bluetooth-hci_qca-Add-qcomm-devcoredump-support/20230428-152145
git checkout a3c3d43057a99f13198754e0b8eaf944d1853142
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash drivers/
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202304290720.TfpPCFY6-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/bluetooth/hci_qca.c:981:5: warning: no previous prototype for 'qca_dmp_hdr' [-Wmissing-prototypes]
981 | int qca_dmp_hdr(struct hci_dev *hdev, char *buf, size_t size)
| ^~~~~~~~~~~
drivers/bluetooth/hci_qca.c: In function 'qca_controller_memdump':
drivers/bluetooth/hci_qca.c:1070:31: error: implicit declaration of function 'hci_devcoredump_init' [-Werror=implicit-function-declaration]
1070 | ret = hci_devcoredump_init(hu->hdev, qca_memdump->ram_dump_size);
| ^~~~~~~~~~~~~~~~~~~~
drivers/bluetooth/hci_qca.c:1119:25: error: implicit declaration of function 'hci_devcoredump_append_pattern' [-Werror=implicit-function-declaration]
1119 | hci_devcoredump_append_pattern(hu->hdev, 0x00,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/bluetooth/hci_qca.c:1136:25: error: implicit declaration of function 'hci_devcoredump_append' [-Werror=implicit-function-declaration]
1136 | hci_devcoredump_append(hu->hdev, skb);
| ^~~~~~~~~~~~~~~~~~~~~~
drivers/bluetooth/hci_qca.c:1150:25: error: implicit declaration of function 'hci_devcoredump_complete' [-Werror=implicit-function-declaration]
1150 | hci_devcoredump_complete(hu->hdev);
| ^~~~~~~~~~~~~~~~~~~~~~~~
drivers/bluetooth/hci_qca.c: In function 'qca_hw_error':
drivers/bluetooth/hci_qca.c:1557:17: error: implicit declaration of function 'hci_devcoredump_abort' [-Werror=implicit-function-declaration]
1557 | hci_devcoredump_abort(hu->hdev);
| ^~~~~~~~~~~~~~~~~~~~~
drivers/bluetooth/hci_qca.c: In function 'hciqca_coredump_enabled':
drivers/bluetooth/hci_qca.c:1727:32: error: 'struct device' has no member named 'coredump_disabled'
1727 | return !hu->serdev->dev.coredump_disabled;
| ^
drivers/bluetooth/hci_qca.c: In function 'qca_setup':
drivers/bluetooth/hci_qca.c:1866:25: error: 'struct hci_dev' has no member named 'dump'
1866 | hu->hdev->dump.enabled = hciqca_coredump_enabled;
| ^~
drivers/bluetooth/hci_qca.c:1906:9: error: implicit declaration of function 'hci_devcoredump_register' [-Werror=implicit-function-declaration]
1906 | hci_devcoredump_register(hdev, hci_coredump_qca, qca_dmp_hdr, NULL);
| ^~~~~~~~~~~~~~~~~~~~~~~~
drivers/bluetooth/hci_qca.c: In function 'hciqca_coredump':
drivers/bluetooth/hci_qca.c:2458:17: error: 'struct device' has no member named 'coredump_disabled'
2458 | if (!dev->coredump_disabled && hdev->dump.coredump)
| ^~
drivers/bluetooth/hci_qca.c:2458:44: error: 'struct hci_dev' has no member named 'dump'
2458 | if (!dev->coredump_disabled && hdev->dump.coredump)
| ^~
drivers/bluetooth/hci_qca.c:2459:21: error: 'struct hci_dev' has no member named 'dump'
2459 | hdev->dump.coredump(hdev);
| ^~
cc1: some warnings being treated as errors
vim +/qca_dmp_hdr +981 drivers/bluetooth/hci_qca.c
980
> 981 int qca_dmp_hdr(struct hci_dev *hdev, char *buf, size_t size)
982 {
983 struct hci_uart *hu = hci_get_drvdata(hdev);
984 struct qca_data *qca = hu->priv;
985 int len = 0;
986
987 len += snprintf(buf + len, size-len, "Controller Name: 0x%x\n",
988 qca->controller_id);
989
990 len += snprintf(buf + len, size-len, "Firmware Version: 0x%x\n",
991 qca->fw_version);
992
993 len += snprintf(buf + len, size-len, "Vendor:Qualcomm\n");
994
995 len += snprintf(buf + len, size-len, "Driver: %s\n",
996 hu->serdev->dev.driver->name);
997
998 bt_dev_info(hdev, "vendor dump hdr size(%d)", len);
999 return len;
1000 }
1001
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v1 2/2] Bluetooth: hci_qca: Add qcomm devcoredump support
@ 2023-05-01 12:03 Sai Teja Aluvala
0 siblings, 0 replies; 3+ messages in thread
From: Sai Teja Aluvala @ 2023-05-01 12:03 UTC (permalink / raw)
To: marcel, johan.hedberg, luiz.dentz
Cc: linux-kernel, linux-bluetooth, quic_hemantg, quic_bgodavar,
jiangzp, mmandlik, Sai Teja Aluvala
Intercept debug exception events from QCA controller and put them into
a devcoredump using hci devcoredump APIs of hci_core
Signed-off-by: Sai Teja Aluvala <quic_saluvala@quicinc.com>
Reviewed-by: Manish Mandlik <mmandlik@google.com>
v1:Initial Patch
---
drivers/bluetooth/hci_qca.c | 189 ++++++++++++++++++++++++++++++++------------
1 file changed, 137 insertions(+), 52 deletions(-)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index ca98f6d..907eaa1 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -77,6 +77,7 @@ enum qca_flags {
QCA_MEMDUMP_COLLECTION,
QCA_HW_ERROR_EVENT,
QCA_SSR_TRIGGERED,
+ QCA_COREDUMP_TRIGGERED,
QCA_BT_OFF,
QCA_ROM_FW
};
@@ -116,9 +117,7 @@ enum qca_memdump_states {
QCA_MEMDUMP_TIMEOUT,
};
-struct qca_memdump_data {
- char *memdump_buf_head;
- char *memdump_buf_tail;
+struct qca_memdump_info {
u32 current_seq_no;
u32 received_dump;
u32 ram_dump_size;
@@ -159,13 +158,15 @@ struct qca_data {
struct work_struct ws_tx_vote_off;
struct work_struct ctrl_memdump_evt;
struct delayed_work ctrl_memdump_timeout;
- struct qca_memdump_data *qca_memdump;
+ struct qca_memdump_info *qca_memdump;
unsigned long flags;
struct completion drop_ev_comp;
wait_queue_head_t suspend_wait_q;
enum qca_memdump_states memdump_state;
struct mutex hci_memdump_lock;
+ u16 fw_version;
+ u16 controller_id;
/* For debugging purpose */
u64 ibs_sent_wacks;
u64 ibs_sent_slps;
@@ -232,6 +233,7 @@ static void qca_regulator_disable(struct qca_serdev *qcadev);
static void qca_power_shutdown(struct hci_uart *hu);
static int qca_power_off(struct hci_dev *hdev);
static void qca_controller_memdump(struct work_struct *work);
+static int qca_dmp_hdr(struct hci_dev *hdev, char *buf, size_t size);
static enum qca_btsoc_type qca_soc_type(struct hci_uart *hu)
{
@@ -543,7 +545,8 @@ static void qca_controller_memdump_timeout(struct work_struct *work)
mutex_lock(&qca->hci_memdump_lock);
if (test_bit(QCA_MEMDUMP_COLLECTION, &qca->flags)) {
qca->memdump_state = QCA_MEMDUMP_TIMEOUT;
- if (!test_bit(QCA_HW_ERROR_EVENT, &qca->flags)) {
+ if ((!test_bit(QCA_HW_ERROR_EVENT, &qca->flags)) ||
+ (!test_bit(QCA_COREDUMP_TRIGGERED, &qca->flags))) {
/* Inject hw error event to reset the device
* and driver.
*/
@@ -976,6 +979,27 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb)
return hci_recv_frame(hdev, skb);
}
+static int qca_dmp_hdr(struct hci_dev *hdev, char *buf, size_t size)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct qca_data *qca = hu->priv;
+ int len = 0;
+
+ len += snprintf(buf + len, size-len, "Controller Name: 0x%x\n",
+ qca->controller_id);
+
+ len += snprintf(buf + len, size-len, "Firmware Version: 0x%x\n",
+ qca->fw_version);
+
+ len += snprintf(buf + len, size-len, "Vendor:Qualcomm\n");
+
+ len += snprintf(buf + len, size-len, "Driver: %s\n",
+ hu->serdev->dev.driver->name);
+
+ bt_dev_info(hdev, "vendor dump hdr size(%d)", len);
+ return len;
+}
+
static void qca_controller_memdump(struct work_struct *work)
{
struct qca_data *qca = container_of(work, struct qca_data,
@@ -983,13 +1007,11 @@ static void qca_controller_memdump(struct work_struct *work)
struct hci_uart *hu = qca->hu;
struct sk_buff *skb;
struct qca_memdump_event_hdr *cmd_hdr;
- struct qca_memdump_data *qca_memdump = qca->qca_memdump;
+ struct qca_memdump_info *qca_memdump = qca->qca_memdump;
struct qca_dump_size *dump;
- char *memdump_buf;
- char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 };
u16 seq_no;
- u32 dump_size;
u32 rx_size;
+ int ret = 0;
enum qca_btsoc_type soc_type = qca_soc_type(hu);
while ((skb = skb_dequeue(&qca->rx_memdump_q))) {
@@ -1005,7 +1027,7 @@ static void qca_controller_memdump(struct work_struct *work)
}
if (!qca_memdump) {
- qca_memdump = kzalloc(sizeof(struct qca_memdump_data),
+ qca_memdump = kzalloc(sizeof(struct qca_memdump_info),
GFP_ATOMIC);
if (!qca_memdump) {
mutex_unlock(&qca->hci_memdump_lock);
@@ -1031,44 +1053,49 @@ static void qca_controller_memdump(struct work_struct *work)
set_bit(QCA_IBS_DISABLED, &qca->flags);
set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
dump = (void *) skb->data;
- dump_size = __le32_to_cpu(dump->dump_size);
- if (!(dump_size)) {
+ qca_memdump->ram_dump_size = __le32_to_cpu(dump->dump_size);
+ if (!(qca_memdump->ram_dump_size)) {
bt_dev_err(hu->hdev, "Rx invalid memdump size");
kfree(qca_memdump);
kfree_skb(skb);
- qca->qca_memdump = NULL;
mutex_unlock(&qca->hci_memdump_lock);
return;
}
- bt_dev_info(hu->hdev, "QCA collecting dump of size:%u",
- dump_size);
queue_delayed_work(qca->workqueue,
&qca->ctrl_memdump_timeout,
- msecs_to_jiffies(MEMDUMP_TIMEOUT_MS)
- );
-
- skb_pull(skb, sizeof(dump_size));
- memdump_buf = vmalloc(dump_size);
- qca_memdump->ram_dump_size = dump_size;
- qca_memdump->memdump_buf_head = memdump_buf;
- qca_memdump->memdump_buf_tail = memdump_buf;
- }
+ msecs_to_jiffies(MEMDUMP_TIMEOUT_MS));
+ skb_pull(skb, sizeof(qca_memdump->ram_dump_size));
+ qca_memdump->current_seq_no = 0;
+ qca_memdump->received_dump = 0;
+ ret = hci_devcd_init(hu->hdev, qca_memdump->ram_dump_size);
+ bt_dev_info(hu->hdev, "hci_devcd_init Return:%d",
+ ret);
+ if (ret < 0) {
+ kfree(qca->qca_memdump);
+ qca->qca_memdump = NULL;
+ qca->memdump_state = QCA_MEMDUMP_COLLECTED;
+ cancel_delayed_work(&qca->ctrl_memdump_timeout);
+ clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
+ mutex_unlock(&qca->hci_memdump_lock);
+ return;
+ }
+
+ bt_dev_info(hu->hdev, "QCA collecting dump of size:%u",
+ qca_memdump->ram_dump_size);
- memdump_buf = qca_memdump->memdump_buf_tail;
+ }
/* If sequence no 0 is missed then there is no point in
* accepting the other sequences.
*/
- if (!memdump_buf) {
+ if (!test_bit(QCA_MEMDUMP_COLLECTION, &qca->flags)) {
bt_dev_err(hu->hdev, "QCA: Discarding other packets");
kfree(qca_memdump);
kfree_skb(skb);
- qca->qca_memdump = NULL;
mutex_unlock(&qca->hci_memdump_lock);
return;
}
-
/* There could be chance of missing some packets from
* the controller. In such cases let us store the dummy
* packets in the buffer.
@@ -1078,8 +1105,8 @@ static void qca_controller_memdump(struct work_struct *work)
* bits, so skip this checking for missing packet.
*/
while ((seq_no > qca_memdump->current_seq_no + 1) &&
- (soc_type != QCA_QCA6390) &&
- seq_no != QCA_LAST_SEQUENCE_NUM) {
+ (soc_type != QCA_QCA6390) &&
+ seq_no != QCA_LAST_SEQUENCE_NUM) {
bt_dev_err(hu->hdev, "QCA controller missed packet:%d",
qca_memdump->current_seq_no);
rx_size = qca_memdump->received_dump;
@@ -1090,43 +1117,38 @@ static void qca_controller_memdump(struct work_struct *work)
qca_memdump->received_dump);
break;
}
- memcpy(memdump_buf, nullBuff, QCA_DUMP_PACKET_SIZE);
- memdump_buf = memdump_buf + QCA_DUMP_PACKET_SIZE;
+ hci_devcd_append_pattern(hu->hdev, 0x00,
+ QCA_DUMP_PACKET_SIZE);
qca_memdump->received_dump += QCA_DUMP_PACKET_SIZE;
qca_memdump->current_seq_no++;
}
- rx_size = qca_memdump->received_dump + skb->len;
+ rx_size = qca_memdump->received_dump + skb->len;
if (rx_size <= qca_memdump->ram_dump_size) {
if ((seq_no != QCA_LAST_SEQUENCE_NUM) &&
- (seq_no != qca_memdump->current_seq_no))
+ (seq_no != qca_memdump->current_seq_no)) {
bt_dev_err(hu->hdev,
"QCA memdump unexpected packet %d",
seq_no);
+ }
bt_dev_dbg(hu->hdev,
"QCA memdump packet %d with length %d",
seq_no, skb->len);
- memcpy(memdump_buf, (unsigned char *)skb->data,
- skb->len);
- memdump_buf = memdump_buf + skb->len;
- qca_memdump->memdump_buf_tail = memdump_buf;
- qca_memdump->current_seq_no = seq_no + 1;
- qca_memdump->received_dump += skb->len;
+ hci_devcd_append(hu->hdev, skb);
+ qca_memdump->current_seq_no += 1;
+ qca_memdump->received_dump = rx_size;
} else {
bt_dev_err(hu->hdev,
- "QCA memdump received %d, no space for packet %d",
- qca_memdump->received_dump, seq_no);
+ "QCA memdump received no space for packet %d",
+ qca_memdump->current_seq_no);
}
- qca->qca_memdump = qca_memdump;
- kfree_skb(skb);
+
if (seq_no == QCA_LAST_SEQUENCE_NUM) {
bt_dev_info(hu->hdev,
- "QCA memdump Done, received %d, total %d",
- qca_memdump->received_dump,
- qca_memdump->ram_dump_size);
- memdump_buf = qca_memdump->memdump_buf_head;
- dev_coredumpv(&hu->serdev->dev, memdump_buf,
- qca_memdump->received_dump, GFP_KERNEL);
+ "QCA memdump Done, received %d, total %d",
+ qca_memdump->received_dump,
+ qca_memdump->ram_dump_size);
+ hci_devcd_complete(hu->hdev);
cancel_delayed_work(&qca->ctrl_memdump_timeout);
kfree(qca->qca_memdump);
qca->qca_memdump = NULL;
@@ -1537,8 +1559,8 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code)
mutex_lock(&qca->hci_memdump_lock);
if (qca->memdump_state != QCA_MEMDUMP_COLLECTED) {
bt_dev_err(hu->hdev, "clearing allocated memory due to memdump timeout");
+ hci_devcd_abort(hu->hdev);
if (qca->qca_memdump) {
- vfree(qca->qca_memdump->memdump_buf_head);
kfree(qca->qca_memdump);
qca->qca_memdump = NULL;
}
@@ -1577,7 +1599,8 @@ static void qca_cmd_timeout(struct hci_dev *hdev)
mutex_lock(&qca->hci_memdump_lock);
if (qca->memdump_state != QCA_MEMDUMP_COLLECTED) {
qca->memdump_state = QCA_MEMDUMP_TIMEOUT;
- if (!test_bit(QCA_HW_ERROR_EVENT, &qca->flags)) {
+ if ((!test_bit(QCA_HW_ERROR_EVENT, &qca->flags)) ||
+ (!test_bit(QCA_COREDUMP_TRIGGERED, &qca->flags))) {
/* Inject hw error event to reset the device
* and driver.
*/
@@ -1702,6 +1725,65 @@ static int qca_power_on(struct hci_dev *hdev)
return ret;
}
+static void hci_coredump_qca(struct hci_dev *hdev)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct qca_data *qca = hu->priv;
+ struct sk_buff *skb;
+
+
+ set_bit(QCA_COREDUMP_TRIGGERED, &qca->flags);
+ bt_dev_info(hdev, "Enter mem_dump_status: %d", qca->memdump_state);
+
+ if (qca->memdump_state == QCA_MEMDUMP_IDLE) {
+ /* we need to crash the SOC
+ * and wait here for 8 seconds to get the dump packets.
+ * This will block main thread to be on hold until we
+ * collect dump.
+ */
+ set_bit(QCA_SSR_TRIGGERED, &qca->flags);
+ set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
+
+ skb = bt_skb_alloc(QCA_CRASHBYTE_PACKET_LEN, GFP_KERNEL);
+ if (!skb) {
+ bt_dev_err(hu->hdev, "Failed to allocate memory for skb packet");
+ return;
+ }
+
+ /* We forcefully crash the controller, by sending 0xfb byte for
+ * 1024 times. We also might have chance of losing data, To be
+ * on safer side we send 1096 bytes to the SoC.
+ */
+ memset(skb_put(skb, QCA_CRASHBYTE_PACKET_LEN), QCA_MEMDUMP_BYTE,
+ QCA_CRASHBYTE_PACKET_LEN);
+ hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
+ bt_dev_info(hu->hdev, "crash the soc to collect controller dump");
+
+ switch (qca->tx_ibs_state) {
+ case HCI_IBS_TX_WAKING:
+ /* Transient state; just keep packet for later */
+ skb_queue_tail(&qca->tx_wait_q, skb);
+ break;
+ case HCI_IBS_TX_AWAKE:
+ skb_queue_tail(&qca->txq, skb);
+ hci_uart_tx_wakeup(hu);
+ break;
+ case HCI_IBS_TX_ASLEEP:
+ skb_queue_tail(&qca->tx_wait_q, skb);
+ qca->tx_ibs_state = HCI_IBS_TX_WAKING;
+ /* Schedule a work queue to wake up device */
+ queue_work(qca->workqueue, &qca->ws_awake_device);
+ break;
+ }
+ } else if (qca->memdump_state == QCA_MEMDUMP_COLLECTING) {
+ /* Let us wait here until memory dump collected or
+ * memory dump timer expired.
+ */
+ bt_dev_info(hdev, "waiting for dump to complete");
+ }
+ clear_bit(QCA_COREDUMP_TRIGGERED, &qca->flags);
+}
+
static int qca_setup(struct hci_uart *hu)
{
struct hci_dev *hdev = hu->hdev;
@@ -1816,6 +1898,9 @@ static int qca_setup(struct hci_uart *hu)
hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
else
hu->hdev->set_bdaddr = qca_set_bdaddr;
+ qca->fw_version = le16_to_cpu(ver.patch_ver);
+ qca->controller_id = le16_to_cpu(ver.rom_ver);
+ hci_devcd_register(hdev, hci_coredump_qca, qca_dmp_hdr, NULL);
return ret;
}
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc.
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2023-05-01 12:04 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-28 7:18 [PATCH v1 2/2] Bluetooth: hci_qca: Add qcomm devcoredump support Sai Teja Aluvala
2023-04-28 23:54 ` kernel test robot
2023-05-01 12:03 Sai Teja Aluvala
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).