in hci_qca.c [973:1134]
static void qca_controller_memdump(struct work_struct *work)
{
struct qca_data *qca = container_of(work, struct qca_data,
ctrl_memdump_evt);
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_dump_size *dump;
char *memdump_buf;
char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 };
u16 seq_no;
u32 dump_size;
u32 rx_size;
enum qca_btsoc_type soc_type = qca_soc_type(hu);
while ((skb = skb_dequeue(&qca->rx_memdump_q))) {
mutex_lock(&qca->hci_memdump_lock);
/* Skip processing the received packets if timeout detected
* or memdump collection completed.
*/
if (qca->memdump_state == QCA_MEMDUMP_TIMEOUT ||
qca->memdump_state == QCA_MEMDUMP_COLLECTED) {
mutex_unlock(&qca->hci_memdump_lock);
return;
}
if (!qca_memdump) {
qca_memdump = kzalloc(sizeof(struct qca_memdump_data),
GFP_ATOMIC);
if (!qca_memdump) {
mutex_unlock(&qca->hci_memdump_lock);
return;
}
qca->qca_memdump = qca_memdump;
}
qca->memdump_state = QCA_MEMDUMP_COLLECTING;
cmd_hdr = (void *) skb->data;
seq_no = __le16_to_cpu(cmd_hdr->seq_no);
skb_pull(skb, sizeof(struct qca_memdump_event_hdr));
if (!seq_no) {
/* This is the first frame of memdump packet from
* the controller, Disable IBS to recevie dump
* with out any interruption, ideally time required for
* the controller to send the dump is 8 seconds. let us
* start timer to handle this asynchronous activity.
*/
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)) {
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;
}
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) {
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.
*/
/* For QCA6390, controller does not lost packets but
* sequence number field of packet sometimes has error
* 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) {
bt_dev_err(hu->hdev, "QCA controller missed packet:%d",
qca_memdump->current_seq_no);
rx_size = qca_memdump->received_dump;
rx_size += QCA_DUMP_PACKET_SIZE;
if (rx_size > qca_memdump->ram_dump_size) {
bt_dev_err(hu->hdev,
"QCA memdump received %d, no space for missed packet",
qca_memdump->received_dump);
break;
}
memcpy(memdump_buf, nullBuff, QCA_DUMP_PACKET_SIZE);
memdump_buf = memdump_buf + 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;
if (rx_size <= qca_memdump->ram_dump_size) {
if ((seq_no != QCA_LAST_SEQUENCE_NUM) &&
(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;
} else {
bt_dev_err(hu->hdev,
"QCA memdump received %d, no space for packet %d",
qca_memdump->received_dump, 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);
cancel_delayed_work(&qca->ctrl_memdump_timeout);
kfree(qca->qca_memdump);
qca->qca_memdump = NULL;
qca->memdump_state = QCA_MEMDUMP_COLLECTED;
clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
}
mutex_unlock(&qca->hci_memdump_lock);
}
}