static void qca_controller_memdump()

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);
	}

}