162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
462306a36Sopenharmony_ci/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <asm/byteorder.h>
762306a36Sopenharmony_ci#include <linux/completion.h>
862306a36Sopenharmony_ci#include <linux/crc32.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/kref.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/mhi.h>
1462306a36Sopenharmony_ci#include <linux/mm.h>
1562306a36Sopenharmony_ci#include <linux/moduleparam.h>
1662306a36Sopenharmony_ci#include <linux/mutex.h>
1762306a36Sopenharmony_ci#include <linux/overflow.h>
1862306a36Sopenharmony_ci#include <linux/pci.h>
1962306a36Sopenharmony_ci#include <linux/scatterlist.h>
2062306a36Sopenharmony_ci#include <linux/types.h>
2162306a36Sopenharmony_ci#include <linux/uaccess.h>
2262306a36Sopenharmony_ci#include <linux/workqueue.h>
2362306a36Sopenharmony_ci#include <linux/wait.h>
2462306a36Sopenharmony_ci#include <drm/drm_device.h>
2562306a36Sopenharmony_ci#include <drm/drm_file.h>
2662306a36Sopenharmony_ci#include <uapi/drm/qaic_accel.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "qaic.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define MANAGE_MAGIC_NUMBER		((__force __le32)0x43494151) /* "QAIC" in little endian */
3162306a36Sopenharmony_ci#define QAIC_DBC_Q_GAP			SZ_256
3262306a36Sopenharmony_ci#define QAIC_DBC_Q_BUF_ALIGN		SZ_4K
3362306a36Sopenharmony_ci#define QAIC_MANAGE_EXT_MSG_LENGTH	SZ_64K /* Max DMA message length */
3462306a36Sopenharmony_ci#define QAIC_WRAPPER_MAX_SIZE		SZ_4K
3562306a36Sopenharmony_ci#define QAIC_MHI_RETRY_WAIT_MS		100
3662306a36Sopenharmony_ci#define QAIC_MHI_RETRY_MAX		20
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic unsigned int control_resp_timeout_s = 60; /* 60 sec default */
3962306a36Sopenharmony_cimodule_param(control_resp_timeout_s, uint, 0600);
4062306a36Sopenharmony_ciMODULE_PARM_DESC(control_resp_timeout_s, "Timeout for NNC responses from QSM");
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct manage_msg {
4362306a36Sopenharmony_ci	u32 len;
4462306a36Sopenharmony_ci	u32 count;
4562306a36Sopenharmony_ci	u8 data[];
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * wire encoding structures for the manage protocol.
5062306a36Sopenharmony_ci * All fields are little endian on the wire
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_cistruct wire_msg_hdr {
5362306a36Sopenharmony_ci	__le32 crc32; /* crc of everything following this field in the message */
5462306a36Sopenharmony_ci	__le32 magic_number;
5562306a36Sopenharmony_ci	__le32 sequence_number;
5662306a36Sopenharmony_ci	__le32 len; /* length of this message */
5762306a36Sopenharmony_ci	__le32 count; /* number of transactions in this message */
5862306a36Sopenharmony_ci	__le32 handle; /* unique id to track the resources consumed */
5962306a36Sopenharmony_ci	__le32 partition_id; /* partition id for the request (signed) */
6062306a36Sopenharmony_ci	__le32 padding; /* must be 0 */
6162306a36Sopenharmony_ci} __packed;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct wire_msg {
6462306a36Sopenharmony_ci	struct wire_msg_hdr hdr;
6562306a36Sopenharmony_ci	u8 data[];
6662306a36Sopenharmony_ci} __packed;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct wire_trans_hdr {
6962306a36Sopenharmony_ci	__le32 type;
7062306a36Sopenharmony_ci	__le32 len;
7162306a36Sopenharmony_ci} __packed;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* Each message sent from driver to device are organized in a list of wrapper_msg */
7462306a36Sopenharmony_cistruct wrapper_msg {
7562306a36Sopenharmony_ci	struct list_head list;
7662306a36Sopenharmony_ci	struct kref ref_count;
7762306a36Sopenharmony_ci	u32 len; /* length of data to transfer */
7862306a36Sopenharmony_ci	struct wrapper_list *head;
7962306a36Sopenharmony_ci	union {
8062306a36Sopenharmony_ci		struct wire_msg msg;
8162306a36Sopenharmony_ci		struct wire_trans_hdr trans;
8262306a36Sopenharmony_ci	};
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct wrapper_list {
8662306a36Sopenharmony_ci	struct list_head list;
8762306a36Sopenharmony_ci	spinlock_t lock; /* Protects the list state during additions and removals */
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistruct wire_trans_passthrough {
9162306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
9262306a36Sopenharmony_ci	u8 data[];
9362306a36Sopenharmony_ci} __packed;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct wire_addr_size_pair {
9662306a36Sopenharmony_ci	__le64 addr;
9762306a36Sopenharmony_ci	__le64 size;
9862306a36Sopenharmony_ci} __packed;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistruct wire_trans_dma_xfer {
10162306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
10262306a36Sopenharmony_ci	__le32 tag;
10362306a36Sopenharmony_ci	__le32 count;
10462306a36Sopenharmony_ci	__le32 dma_chunk_id;
10562306a36Sopenharmony_ci	__le32 padding;
10662306a36Sopenharmony_ci	struct wire_addr_size_pair data[];
10762306a36Sopenharmony_ci} __packed;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/* Initiated by device to continue the DMA xfer of a large piece of data */
11062306a36Sopenharmony_cistruct wire_trans_dma_xfer_cont {
11162306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
11262306a36Sopenharmony_ci	__le32 dma_chunk_id;
11362306a36Sopenharmony_ci	__le32 padding;
11462306a36Sopenharmony_ci	__le64 xferred_size;
11562306a36Sopenharmony_ci} __packed;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistruct wire_trans_activate_to_dev {
11862306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
11962306a36Sopenharmony_ci	__le64 req_q_addr;
12062306a36Sopenharmony_ci	__le64 rsp_q_addr;
12162306a36Sopenharmony_ci	__le32 req_q_size;
12262306a36Sopenharmony_ci	__le32 rsp_q_size;
12362306a36Sopenharmony_ci	__le32 buf_len;
12462306a36Sopenharmony_ci	__le32 options; /* unused, but BIT(16) has meaning to the device */
12562306a36Sopenharmony_ci} __packed;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistruct wire_trans_activate_from_dev {
12862306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
12962306a36Sopenharmony_ci	__le32 status;
13062306a36Sopenharmony_ci	__le32 dbc_id;
13162306a36Sopenharmony_ci	__le64 options; /* unused */
13262306a36Sopenharmony_ci} __packed;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistruct wire_trans_deactivate_from_dev {
13562306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
13662306a36Sopenharmony_ci	__le32 status;
13762306a36Sopenharmony_ci	__le32 dbc_id;
13862306a36Sopenharmony_ci} __packed;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistruct wire_trans_terminate_to_dev {
14162306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
14262306a36Sopenharmony_ci	__le32 handle;
14362306a36Sopenharmony_ci	__le32 padding;
14462306a36Sopenharmony_ci} __packed;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistruct wire_trans_terminate_from_dev {
14762306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
14862306a36Sopenharmony_ci	__le32 status;
14962306a36Sopenharmony_ci	__le32 padding;
15062306a36Sopenharmony_ci} __packed;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistruct wire_trans_status_to_dev {
15362306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
15462306a36Sopenharmony_ci} __packed;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistruct wire_trans_status_from_dev {
15762306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
15862306a36Sopenharmony_ci	__le16 major;
15962306a36Sopenharmony_ci	__le16 minor;
16062306a36Sopenharmony_ci	__le32 status;
16162306a36Sopenharmony_ci	__le64 status_flags;
16262306a36Sopenharmony_ci} __packed;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistruct wire_trans_validate_part_to_dev {
16562306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
16662306a36Sopenharmony_ci	__le32 part_id;
16762306a36Sopenharmony_ci	__le32 padding;
16862306a36Sopenharmony_ci} __packed;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistruct wire_trans_validate_part_from_dev {
17162306a36Sopenharmony_ci	struct wire_trans_hdr hdr;
17262306a36Sopenharmony_ci	__le32 status;
17362306a36Sopenharmony_ci	__le32 padding;
17462306a36Sopenharmony_ci} __packed;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistruct xfer_queue_elem {
17762306a36Sopenharmony_ci	/*
17862306a36Sopenharmony_ci	 * Node in list of ongoing transfer request on control channel.
17962306a36Sopenharmony_ci	 * Maintained by root device struct.
18062306a36Sopenharmony_ci	 */
18162306a36Sopenharmony_ci	struct list_head list;
18262306a36Sopenharmony_ci	/* Sequence number of this transfer request */
18362306a36Sopenharmony_ci	u32 seq_num;
18462306a36Sopenharmony_ci	/* This is used to wait on until completion of transfer request */
18562306a36Sopenharmony_ci	struct completion xfer_done;
18662306a36Sopenharmony_ci	/* Received data from device */
18762306a36Sopenharmony_ci	void *buf;
18862306a36Sopenharmony_ci};
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistruct dma_xfer {
19162306a36Sopenharmony_ci	/* Node in list of DMA transfers which is used for cleanup */
19262306a36Sopenharmony_ci	struct list_head list;
19362306a36Sopenharmony_ci	/* SG table of memory used for DMA */
19462306a36Sopenharmony_ci	struct sg_table *sgt;
19562306a36Sopenharmony_ci	/* Array pages used for DMA */
19662306a36Sopenharmony_ci	struct page **page_list;
19762306a36Sopenharmony_ci	/* Number of pages used for DMA */
19862306a36Sopenharmony_ci	unsigned long nr_pages;
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistruct ioctl_resources {
20262306a36Sopenharmony_ci	/* List of all DMA transfers which is used later for cleanup */
20362306a36Sopenharmony_ci	struct list_head dma_xfers;
20462306a36Sopenharmony_ci	/* Base address of request queue which belongs to a DBC */
20562306a36Sopenharmony_ci	void *buf;
20662306a36Sopenharmony_ci	/*
20762306a36Sopenharmony_ci	 * Base bus address of request queue which belongs to a DBC. Response
20862306a36Sopenharmony_ci	 * queue base bus address can be calculated by adding size of request
20962306a36Sopenharmony_ci	 * queue to base bus address of request queue.
21062306a36Sopenharmony_ci	 */
21162306a36Sopenharmony_ci	dma_addr_t dma_addr;
21262306a36Sopenharmony_ci	/* Total size of request queue and response queue in byte */
21362306a36Sopenharmony_ci	u32 total_size;
21462306a36Sopenharmony_ci	/* Total number of elements that can be queued in each of request and response queue */
21562306a36Sopenharmony_ci	u32 nelem;
21662306a36Sopenharmony_ci	/* Base address of response queue which belongs to a DBC */
21762306a36Sopenharmony_ci	void *rsp_q_base;
21862306a36Sopenharmony_ci	/* Status of the NNC message received */
21962306a36Sopenharmony_ci	u32 status;
22062306a36Sopenharmony_ci	/* DBC id of the DBC received from device */
22162306a36Sopenharmony_ci	u32 dbc_id;
22262306a36Sopenharmony_ci	/*
22362306a36Sopenharmony_ci	 * DMA transfer request messages can be big in size and it may not be
22462306a36Sopenharmony_ci	 * possible to send them in one shot. In such cases the messages are
22562306a36Sopenharmony_ci	 * broken into chunks, this field stores ID of such chunks.
22662306a36Sopenharmony_ci	 */
22762306a36Sopenharmony_ci	u32 dma_chunk_id;
22862306a36Sopenharmony_ci	/* Total number of bytes transferred for a DMA xfer request */
22962306a36Sopenharmony_ci	u64 xferred_dma_size;
23062306a36Sopenharmony_ci	/* Header of transaction message received from user. Used during DMA xfer request. */
23162306a36Sopenharmony_ci	void *trans_hdr;
23262306a36Sopenharmony_ci};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistruct resp_work {
23562306a36Sopenharmony_ci	struct work_struct work;
23662306a36Sopenharmony_ci	struct qaic_device *qdev;
23762306a36Sopenharmony_ci	void *buf;
23862306a36Sopenharmony_ci};
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/*
24162306a36Sopenharmony_ci * Since we're working with little endian messages, its useful to be able to
24262306a36Sopenharmony_ci * increment without filling a whole line with conversions back and forth just
24362306a36Sopenharmony_ci * to add one(1) to a message count.
24462306a36Sopenharmony_ci */
24562306a36Sopenharmony_cistatic __le32 incr_le32(__le32 val)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	return cpu_to_le32(le32_to_cpu(val) + 1);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic u32 gen_crc(void *msg)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct wrapper_list *wrappers = msg;
25362306a36Sopenharmony_ci	struct wrapper_msg *w;
25462306a36Sopenharmony_ci	u32 crc = ~0;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	list_for_each_entry(w, &wrappers->list, list)
25762306a36Sopenharmony_ci		crc = crc32(crc, &w->msg, w->len);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return crc ^ ~0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic u32 gen_crc_stub(void *msg)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	return 0;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic bool valid_crc(void *msg)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct wire_msg_hdr *hdr = msg;
27062306a36Sopenharmony_ci	bool ret;
27162306a36Sopenharmony_ci	u32 crc;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/*
27462306a36Sopenharmony_ci	 * The output of this algorithm is always converted to the native
27562306a36Sopenharmony_ci	 * endianness.
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	crc = le32_to_cpu(hdr->crc32);
27862306a36Sopenharmony_ci	hdr->crc32 = 0;
27962306a36Sopenharmony_ci	ret = (crc32(~0, msg, le32_to_cpu(hdr->len)) ^ ~0) == crc;
28062306a36Sopenharmony_ci	hdr->crc32 = cpu_to_le32(crc);
28162306a36Sopenharmony_ci	return ret;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic bool valid_crc_stub(void *msg)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	return true;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic void free_wrapper(struct kref *ref)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct wrapper_msg *wrapper = container_of(ref, struct wrapper_msg, ref_count);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	list_del(&wrapper->list);
29462306a36Sopenharmony_ci	kfree(wrapper);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic void save_dbc_buf(struct qaic_device *qdev, struct ioctl_resources *resources,
29862306a36Sopenharmony_ci			 struct qaic_user *usr)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	u32 dbc_id = resources->dbc_id;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (resources->buf) {
30362306a36Sopenharmony_ci		wait_event_interruptible(qdev->dbc[dbc_id].dbc_release, !qdev->dbc[dbc_id].in_use);
30462306a36Sopenharmony_ci		qdev->dbc[dbc_id].req_q_base = resources->buf;
30562306a36Sopenharmony_ci		qdev->dbc[dbc_id].rsp_q_base = resources->rsp_q_base;
30662306a36Sopenharmony_ci		qdev->dbc[dbc_id].dma_addr = resources->dma_addr;
30762306a36Sopenharmony_ci		qdev->dbc[dbc_id].total_size = resources->total_size;
30862306a36Sopenharmony_ci		qdev->dbc[dbc_id].nelem = resources->nelem;
30962306a36Sopenharmony_ci		enable_dbc(qdev, dbc_id, usr);
31062306a36Sopenharmony_ci		qdev->dbc[dbc_id].in_use = true;
31162306a36Sopenharmony_ci		resources->buf = NULL;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic void free_dbc_buf(struct qaic_device *qdev, struct ioctl_resources *resources)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	if (resources->buf)
31862306a36Sopenharmony_ci		dma_free_coherent(&qdev->pdev->dev, resources->total_size, resources->buf,
31962306a36Sopenharmony_ci				  resources->dma_addr);
32062306a36Sopenharmony_ci	resources->buf = NULL;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic void free_dma_xfers(struct qaic_device *qdev, struct ioctl_resources *resources)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct dma_xfer *xfer;
32662306a36Sopenharmony_ci	struct dma_xfer *x;
32762306a36Sopenharmony_ci	int i;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	list_for_each_entry_safe(xfer, x, &resources->dma_xfers, list) {
33062306a36Sopenharmony_ci		dma_unmap_sgtable(&qdev->pdev->dev, xfer->sgt, DMA_TO_DEVICE, 0);
33162306a36Sopenharmony_ci		sg_free_table(xfer->sgt);
33262306a36Sopenharmony_ci		kfree(xfer->sgt);
33362306a36Sopenharmony_ci		for (i = 0; i < xfer->nr_pages; ++i)
33462306a36Sopenharmony_ci			put_page(xfer->page_list[i]);
33562306a36Sopenharmony_ci		kfree(xfer->page_list);
33662306a36Sopenharmony_ci		list_del(&xfer->list);
33762306a36Sopenharmony_ci		kfree(xfer);
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic struct wrapper_msg *add_wrapper(struct wrapper_list *wrappers, u32 size)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct wrapper_msg *w = kzalloc(size, GFP_KERNEL);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (!w)
34662306a36Sopenharmony_ci		return NULL;
34762306a36Sopenharmony_ci	list_add_tail(&w->list, &wrappers->list);
34862306a36Sopenharmony_ci	kref_init(&w->ref_count);
34962306a36Sopenharmony_ci	w->head = wrappers;
35062306a36Sopenharmony_ci	return w;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic int encode_passthrough(struct qaic_device *qdev, void *trans, struct wrapper_list *wrappers,
35462306a36Sopenharmony_ci			      u32 *user_len)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct qaic_manage_trans_passthrough *in_trans = trans;
35762306a36Sopenharmony_ci	struct wire_trans_passthrough *out_trans;
35862306a36Sopenharmony_ci	struct wrapper_msg *trans_wrapper;
35962306a36Sopenharmony_ci	struct wrapper_msg *wrapper;
36062306a36Sopenharmony_ci	struct wire_msg *msg;
36162306a36Sopenharmony_ci	u32 msg_hdr_len;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
36462306a36Sopenharmony_ci	msg = &wrapper->msg;
36562306a36Sopenharmony_ci	msg_hdr_len = le32_to_cpu(msg->hdr.len);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (in_trans->hdr.len % 8 != 0)
36862306a36Sopenharmony_ci		return -EINVAL;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_EXT_MSG_LENGTH)
37162306a36Sopenharmony_ci		return -ENOSPC;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	trans_wrapper = add_wrapper(wrappers,
37462306a36Sopenharmony_ci				    offsetof(struct wrapper_msg, trans) + in_trans->hdr.len);
37562306a36Sopenharmony_ci	if (!trans_wrapper)
37662306a36Sopenharmony_ci		return -ENOMEM;
37762306a36Sopenharmony_ci	trans_wrapper->len = in_trans->hdr.len;
37862306a36Sopenharmony_ci	out_trans = (struct wire_trans_passthrough *)&trans_wrapper->trans;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	memcpy(out_trans->data, in_trans->data, in_trans->hdr.len - sizeof(in_trans->hdr));
38162306a36Sopenharmony_ci	msg->hdr.len = cpu_to_le32(msg_hdr_len + in_trans->hdr.len);
38262306a36Sopenharmony_ci	msg->hdr.count = incr_le32(msg->hdr.count);
38362306a36Sopenharmony_ci	*user_len += in_trans->hdr.len;
38462306a36Sopenharmony_ci	out_trans->hdr.type = cpu_to_le32(QAIC_TRANS_PASSTHROUGH_TO_DEV);
38562306a36Sopenharmony_ci	out_trans->hdr.len = cpu_to_le32(in_trans->hdr.len);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return 0;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci/* returns error code for failure, 0 if enough pages alloc'd, 1 if dma_cont is needed */
39162306a36Sopenharmony_cistatic int find_and_map_user_pages(struct qaic_device *qdev,
39262306a36Sopenharmony_ci				   struct qaic_manage_trans_dma_xfer *in_trans,
39362306a36Sopenharmony_ci				   struct ioctl_resources *resources, struct dma_xfer *xfer)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	u64 xfer_start_addr, remaining, end, total;
39662306a36Sopenharmony_ci	unsigned long need_pages;
39762306a36Sopenharmony_ci	struct page **page_list;
39862306a36Sopenharmony_ci	unsigned long nr_pages;
39962306a36Sopenharmony_ci	struct sg_table *sgt;
40062306a36Sopenharmony_ci	int ret;
40162306a36Sopenharmony_ci	int i;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (check_add_overflow(in_trans->addr, resources->xferred_dma_size, &xfer_start_addr))
40462306a36Sopenharmony_ci		return -EINVAL;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (in_trans->size < resources->xferred_dma_size)
40762306a36Sopenharmony_ci		return -EINVAL;
40862306a36Sopenharmony_ci	remaining = in_trans->size - resources->xferred_dma_size;
40962306a36Sopenharmony_ci	if (remaining == 0)
41062306a36Sopenharmony_ci		return 0;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (check_add_overflow(xfer_start_addr, remaining, &end))
41362306a36Sopenharmony_ci		return -EINVAL;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	total = remaining + offset_in_page(xfer_start_addr);
41662306a36Sopenharmony_ci	if (total >= SIZE_MAX)
41762306a36Sopenharmony_ci		return -EINVAL;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	need_pages = DIV_ROUND_UP(total, PAGE_SIZE);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	nr_pages = need_pages;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	while (1) {
42462306a36Sopenharmony_ci		page_list = kmalloc_array(nr_pages, sizeof(*page_list), GFP_KERNEL | __GFP_NOWARN);
42562306a36Sopenharmony_ci		if (!page_list) {
42662306a36Sopenharmony_ci			nr_pages = nr_pages / 2;
42762306a36Sopenharmony_ci			if (!nr_pages)
42862306a36Sopenharmony_ci				return -ENOMEM;
42962306a36Sopenharmony_ci		} else {
43062306a36Sopenharmony_ci			break;
43162306a36Sopenharmony_ci		}
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	ret = get_user_pages_fast(xfer_start_addr, nr_pages, 0, page_list);
43562306a36Sopenharmony_ci	if (ret < 0)
43662306a36Sopenharmony_ci		goto free_page_list;
43762306a36Sopenharmony_ci	if (ret != nr_pages) {
43862306a36Sopenharmony_ci		nr_pages = ret;
43962306a36Sopenharmony_ci		ret = -EFAULT;
44062306a36Sopenharmony_ci		goto put_pages;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
44462306a36Sopenharmony_ci	if (!sgt) {
44562306a36Sopenharmony_ci		ret = -ENOMEM;
44662306a36Sopenharmony_ci		goto put_pages;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	ret = sg_alloc_table_from_pages(sgt, page_list, nr_pages,
45062306a36Sopenharmony_ci					offset_in_page(xfer_start_addr),
45162306a36Sopenharmony_ci					remaining, GFP_KERNEL);
45262306a36Sopenharmony_ci	if (ret) {
45362306a36Sopenharmony_ci		ret = -ENOMEM;
45462306a36Sopenharmony_ci		goto free_sgt;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	ret = dma_map_sgtable(&qdev->pdev->dev, sgt, DMA_TO_DEVICE, 0);
45862306a36Sopenharmony_ci	if (ret)
45962306a36Sopenharmony_ci		goto free_table;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	xfer->sgt = sgt;
46262306a36Sopenharmony_ci	xfer->page_list = page_list;
46362306a36Sopenharmony_ci	xfer->nr_pages = nr_pages;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return need_pages > nr_pages ? 1 : 0;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cifree_table:
46862306a36Sopenharmony_ci	sg_free_table(sgt);
46962306a36Sopenharmony_cifree_sgt:
47062306a36Sopenharmony_ci	kfree(sgt);
47162306a36Sopenharmony_ciput_pages:
47262306a36Sopenharmony_ci	for (i = 0; i < nr_pages; ++i)
47362306a36Sopenharmony_ci		put_page(page_list[i]);
47462306a36Sopenharmony_cifree_page_list:
47562306a36Sopenharmony_ci	kfree(page_list);
47662306a36Sopenharmony_ci	return ret;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci/* returns error code for failure, 0 if everything was encoded, 1 if dma_cont is needed */
48062306a36Sopenharmony_cistatic int encode_addr_size_pairs(struct dma_xfer *xfer, struct wrapper_list *wrappers,
48162306a36Sopenharmony_ci				  struct ioctl_resources *resources, u32 msg_hdr_len, u32 *size,
48262306a36Sopenharmony_ci				  struct wire_trans_dma_xfer **out_trans)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct wrapper_msg *trans_wrapper;
48562306a36Sopenharmony_ci	struct sg_table *sgt = xfer->sgt;
48662306a36Sopenharmony_ci	struct wire_addr_size_pair *asp;
48762306a36Sopenharmony_ci	struct scatterlist *sg;
48862306a36Sopenharmony_ci	struct wrapper_msg *w;
48962306a36Sopenharmony_ci	unsigned int dma_len;
49062306a36Sopenharmony_ci	u64 dma_chunk_len;
49162306a36Sopenharmony_ci	void *boundary;
49262306a36Sopenharmony_ci	int nents_dma;
49362306a36Sopenharmony_ci	int nents;
49462306a36Sopenharmony_ci	int i;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	nents = sgt->nents;
49762306a36Sopenharmony_ci	nents_dma = nents;
49862306a36Sopenharmony_ci	*size = QAIC_MANAGE_EXT_MSG_LENGTH - msg_hdr_len - sizeof(**out_trans);
49962306a36Sopenharmony_ci	for_each_sgtable_sg(sgt, sg, i) {
50062306a36Sopenharmony_ci		*size -= sizeof(*asp);
50162306a36Sopenharmony_ci		/* Save 1K for possible follow-up transactions. */
50262306a36Sopenharmony_ci		if (*size < SZ_1K) {
50362306a36Sopenharmony_ci			nents_dma = i;
50462306a36Sopenharmony_ci			break;
50562306a36Sopenharmony_ci		}
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	trans_wrapper = add_wrapper(wrappers, QAIC_WRAPPER_MAX_SIZE);
50962306a36Sopenharmony_ci	if (!trans_wrapper)
51062306a36Sopenharmony_ci		return -ENOMEM;
51162306a36Sopenharmony_ci	*out_trans = (struct wire_trans_dma_xfer *)&trans_wrapper->trans;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	asp = (*out_trans)->data;
51462306a36Sopenharmony_ci	boundary = (void *)trans_wrapper + QAIC_WRAPPER_MAX_SIZE;
51562306a36Sopenharmony_ci	*size = 0;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	dma_len = 0;
51862306a36Sopenharmony_ci	w = trans_wrapper;
51962306a36Sopenharmony_ci	dma_chunk_len = 0;
52062306a36Sopenharmony_ci	for_each_sg(sgt->sgl, sg, nents_dma, i) {
52162306a36Sopenharmony_ci		asp->size = cpu_to_le64(dma_len);
52262306a36Sopenharmony_ci		dma_chunk_len += dma_len;
52362306a36Sopenharmony_ci		if (dma_len) {
52462306a36Sopenharmony_ci			asp++;
52562306a36Sopenharmony_ci			if ((void *)asp + sizeof(*asp) > boundary) {
52662306a36Sopenharmony_ci				w->len = (void *)asp - (void *)&w->msg;
52762306a36Sopenharmony_ci				*size += w->len;
52862306a36Sopenharmony_ci				w = add_wrapper(wrappers, QAIC_WRAPPER_MAX_SIZE);
52962306a36Sopenharmony_ci				if (!w)
53062306a36Sopenharmony_ci					return -ENOMEM;
53162306a36Sopenharmony_ci				boundary = (void *)w + QAIC_WRAPPER_MAX_SIZE;
53262306a36Sopenharmony_ci				asp = (struct wire_addr_size_pair *)&w->msg;
53362306a36Sopenharmony_ci			}
53462306a36Sopenharmony_ci		}
53562306a36Sopenharmony_ci		asp->addr = cpu_to_le64(sg_dma_address(sg));
53662306a36Sopenharmony_ci		dma_len = sg_dma_len(sg);
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci	/* finalize the last segment */
53962306a36Sopenharmony_ci	asp->size = cpu_to_le64(dma_len);
54062306a36Sopenharmony_ci	w->len = (void *)asp + sizeof(*asp) - (void *)&w->msg;
54162306a36Sopenharmony_ci	*size += w->len;
54262306a36Sopenharmony_ci	dma_chunk_len += dma_len;
54362306a36Sopenharmony_ci	resources->xferred_dma_size += dma_chunk_len;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return nents_dma < nents ? 1 : 0;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic void cleanup_xfer(struct qaic_device *qdev, struct dma_xfer *xfer)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	int i;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	dma_unmap_sgtable(&qdev->pdev->dev, xfer->sgt, DMA_TO_DEVICE, 0);
55362306a36Sopenharmony_ci	sg_free_table(xfer->sgt);
55462306a36Sopenharmony_ci	kfree(xfer->sgt);
55562306a36Sopenharmony_ci	for (i = 0; i < xfer->nr_pages; ++i)
55662306a36Sopenharmony_ci		put_page(xfer->page_list[i]);
55762306a36Sopenharmony_ci	kfree(xfer->page_list);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic int encode_dma(struct qaic_device *qdev, void *trans, struct wrapper_list *wrappers,
56162306a36Sopenharmony_ci		      u32 *user_len, struct ioctl_resources *resources, struct qaic_user *usr)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	struct qaic_manage_trans_dma_xfer *in_trans = trans;
56462306a36Sopenharmony_ci	struct wire_trans_dma_xfer *out_trans;
56562306a36Sopenharmony_ci	struct wrapper_msg *wrapper;
56662306a36Sopenharmony_ci	struct dma_xfer *xfer;
56762306a36Sopenharmony_ci	struct wire_msg *msg;
56862306a36Sopenharmony_ci	bool need_cont_dma;
56962306a36Sopenharmony_ci	u32 msg_hdr_len;
57062306a36Sopenharmony_ci	u32 size;
57162306a36Sopenharmony_ci	int ret;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
57462306a36Sopenharmony_ci	msg = &wrapper->msg;
57562306a36Sopenharmony_ci	msg_hdr_len = le32_to_cpu(msg->hdr.len);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* There should be enough space to hold at least one ASP entry. */
57862306a36Sopenharmony_ci	if (size_add(msg_hdr_len, sizeof(*out_trans) + sizeof(struct wire_addr_size_pair)) >
57962306a36Sopenharmony_ci	    QAIC_MANAGE_EXT_MSG_LENGTH)
58062306a36Sopenharmony_ci		return -ENOMEM;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	xfer = kmalloc(sizeof(*xfer), GFP_KERNEL);
58362306a36Sopenharmony_ci	if (!xfer)
58462306a36Sopenharmony_ci		return -ENOMEM;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	ret = find_and_map_user_pages(qdev, in_trans, resources, xfer);
58762306a36Sopenharmony_ci	if (ret < 0)
58862306a36Sopenharmony_ci		goto free_xfer;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	need_cont_dma = (bool)ret;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	ret = encode_addr_size_pairs(xfer, wrappers, resources, msg_hdr_len, &size, &out_trans);
59362306a36Sopenharmony_ci	if (ret < 0)
59462306a36Sopenharmony_ci		goto cleanup_xfer;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	need_cont_dma = need_cont_dma || (bool)ret;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	msg->hdr.len = cpu_to_le32(msg_hdr_len + size);
59962306a36Sopenharmony_ci	msg->hdr.count = incr_le32(msg->hdr.count);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	out_trans->hdr.type = cpu_to_le32(QAIC_TRANS_DMA_XFER_TO_DEV);
60262306a36Sopenharmony_ci	out_trans->hdr.len = cpu_to_le32(size);
60362306a36Sopenharmony_ci	out_trans->tag = cpu_to_le32(in_trans->tag);
60462306a36Sopenharmony_ci	out_trans->count = cpu_to_le32((size - sizeof(*out_trans)) /
60562306a36Sopenharmony_ci								sizeof(struct wire_addr_size_pair));
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	*user_len += in_trans->hdr.len;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	if (resources->dma_chunk_id) {
61062306a36Sopenharmony_ci		out_trans->dma_chunk_id = cpu_to_le32(resources->dma_chunk_id);
61162306a36Sopenharmony_ci	} else if (need_cont_dma) {
61262306a36Sopenharmony_ci		while (resources->dma_chunk_id == 0)
61362306a36Sopenharmony_ci			resources->dma_chunk_id = atomic_inc_return(&usr->chunk_id);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		out_trans->dma_chunk_id = cpu_to_le32(resources->dma_chunk_id);
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci	resources->trans_hdr = trans;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	list_add(&xfer->list, &resources->dma_xfers);
62062306a36Sopenharmony_ci	return 0;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cicleanup_xfer:
62362306a36Sopenharmony_ci	cleanup_xfer(qdev, xfer);
62462306a36Sopenharmony_cifree_xfer:
62562306a36Sopenharmony_ci	kfree(xfer);
62662306a36Sopenharmony_ci	return ret;
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic int encode_activate(struct qaic_device *qdev, void *trans, struct wrapper_list *wrappers,
63062306a36Sopenharmony_ci			   u32 *user_len, struct ioctl_resources *resources)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	struct qaic_manage_trans_activate_to_dev *in_trans = trans;
63362306a36Sopenharmony_ci	struct wire_trans_activate_to_dev *out_trans;
63462306a36Sopenharmony_ci	struct wrapper_msg *trans_wrapper;
63562306a36Sopenharmony_ci	struct wrapper_msg *wrapper;
63662306a36Sopenharmony_ci	struct wire_msg *msg;
63762306a36Sopenharmony_ci	dma_addr_t dma_addr;
63862306a36Sopenharmony_ci	u32 msg_hdr_len;
63962306a36Sopenharmony_ci	void *buf;
64062306a36Sopenharmony_ci	u32 nelem;
64162306a36Sopenharmony_ci	u32 size;
64262306a36Sopenharmony_ci	int ret;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
64562306a36Sopenharmony_ci	msg = &wrapper->msg;
64662306a36Sopenharmony_ci	msg_hdr_len = le32_to_cpu(msg->hdr.len);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (size_add(msg_hdr_len, sizeof(*out_trans)) > QAIC_MANAGE_MAX_MSG_LENGTH)
64962306a36Sopenharmony_ci		return -ENOSPC;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (!in_trans->queue_size)
65262306a36Sopenharmony_ci		return -EINVAL;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (in_trans->pad)
65562306a36Sopenharmony_ci		return -EINVAL;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	nelem = in_trans->queue_size;
65862306a36Sopenharmony_ci	size = (get_dbc_req_elem_size() + get_dbc_rsp_elem_size()) * nelem;
65962306a36Sopenharmony_ci	if (size / nelem != get_dbc_req_elem_size() + get_dbc_rsp_elem_size())
66062306a36Sopenharmony_ci		return -EINVAL;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (size + QAIC_DBC_Q_GAP + QAIC_DBC_Q_BUF_ALIGN < size)
66362306a36Sopenharmony_ci		return -EINVAL;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	size = ALIGN((size + QAIC_DBC_Q_GAP), QAIC_DBC_Q_BUF_ALIGN);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	buf = dma_alloc_coherent(&qdev->pdev->dev, size, &dma_addr, GFP_KERNEL);
66862306a36Sopenharmony_ci	if (!buf)
66962306a36Sopenharmony_ci		return -ENOMEM;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	trans_wrapper = add_wrapper(wrappers,
67262306a36Sopenharmony_ci				    offsetof(struct wrapper_msg, trans) + sizeof(*out_trans));
67362306a36Sopenharmony_ci	if (!trans_wrapper) {
67462306a36Sopenharmony_ci		ret = -ENOMEM;
67562306a36Sopenharmony_ci		goto free_dma;
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci	trans_wrapper->len = sizeof(*out_trans);
67862306a36Sopenharmony_ci	out_trans = (struct wire_trans_activate_to_dev *)&trans_wrapper->trans;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	out_trans->hdr.type = cpu_to_le32(QAIC_TRANS_ACTIVATE_TO_DEV);
68162306a36Sopenharmony_ci	out_trans->hdr.len = cpu_to_le32(sizeof(*out_trans));
68262306a36Sopenharmony_ci	out_trans->buf_len = cpu_to_le32(size);
68362306a36Sopenharmony_ci	out_trans->req_q_addr = cpu_to_le64(dma_addr);
68462306a36Sopenharmony_ci	out_trans->req_q_size = cpu_to_le32(nelem);
68562306a36Sopenharmony_ci	out_trans->rsp_q_addr = cpu_to_le64(dma_addr + size - nelem * get_dbc_rsp_elem_size());
68662306a36Sopenharmony_ci	out_trans->rsp_q_size = cpu_to_le32(nelem);
68762306a36Sopenharmony_ci	out_trans->options = cpu_to_le32(in_trans->options);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	*user_len += in_trans->hdr.len;
69062306a36Sopenharmony_ci	msg->hdr.len = cpu_to_le32(msg_hdr_len + sizeof(*out_trans));
69162306a36Sopenharmony_ci	msg->hdr.count = incr_le32(msg->hdr.count);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	resources->buf = buf;
69462306a36Sopenharmony_ci	resources->dma_addr = dma_addr;
69562306a36Sopenharmony_ci	resources->total_size = size;
69662306a36Sopenharmony_ci	resources->nelem = nelem;
69762306a36Sopenharmony_ci	resources->rsp_q_base = buf + size - nelem * get_dbc_rsp_elem_size();
69862306a36Sopenharmony_ci	return 0;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cifree_dma:
70162306a36Sopenharmony_ci	dma_free_coherent(&qdev->pdev->dev, size, buf, dma_addr);
70262306a36Sopenharmony_ci	return ret;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic int encode_deactivate(struct qaic_device *qdev, void *trans,
70662306a36Sopenharmony_ci			     u32 *user_len, struct qaic_user *usr)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct qaic_manage_trans_deactivate *in_trans = trans;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (in_trans->dbc_id >= qdev->num_dbc || in_trans->pad)
71162306a36Sopenharmony_ci		return -EINVAL;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	*user_len += in_trans->hdr.len;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	return disable_dbc(qdev, in_trans->dbc_id, usr);
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_cistatic int encode_status(struct qaic_device *qdev, void *trans, struct wrapper_list *wrappers,
71962306a36Sopenharmony_ci			 u32 *user_len)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct qaic_manage_trans_status_to_dev *in_trans = trans;
72262306a36Sopenharmony_ci	struct wire_trans_status_to_dev *out_trans;
72362306a36Sopenharmony_ci	struct wrapper_msg *trans_wrapper;
72462306a36Sopenharmony_ci	struct wrapper_msg *wrapper;
72562306a36Sopenharmony_ci	struct wire_msg *msg;
72662306a36Sopenharmony_ci	u32 msg_hdr_len;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
72962306a36Sopenharmony_ci	msg = &wrapper->msg;
73062306a36Sopenharmony_ci	msg_hdr_len = le32_to_cpu(msg->hdr.len);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_MAX_MSG_LENGTH)
73362306a36Sopenharmony_ci		return -ENOSPC;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	trans_wrapper = add_wrapper(wrappers, sizeof(*trans_wrapper));
73662306a36Sopenharmony_ci	if (!trans_wrapper)
73762306a36Sopenharmony_ci		return -ENOMEM;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	trans_wrapper->len = sizeof(*out_trans);
74062306a36Sopenharmony_ci	out_trans = (struct wire_trans_status_to_dev *)&trans_wrapper->trans;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	out_trans->hdr.type = cpu_to_le32(QAIC_TRANS_STATUS_TO_DEV);
74362306a36Sopenharmony_ci	out_trans->hdr.len = cpu_to_le32(in_trans->hdr.len);
74462306a36Sopenharmony_ci	msg->hdr.len = cpu_to_le32(msg_hdr_len + in_trans->hdr.len);
74562306a36Sopenharmony_ci	msg->hdr.count = incr_le32(msg->hdr.count);
74662306a36Sopenharmony_ci	*user_len += in_trans->hdr.len;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	return 0;
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cistatic int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
75262306a36Sopenharmony_ci			  struct wrapper_list *wrappers, struct ioctl_resources *resources,
75362306a36Sopenharmony_ci			  struct qaic_user *usr)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct qaic_manage_trans_hdr *trans_hdr;
75662306a36Sopenharmony_ci	struct wrapper_msg *wrapper;
75762306a36Sopenharmony_ci	struct wire_msg *msg;
75862306a36Sopenharmony_ci	u32 user_len = 0;
75962306a36Sopenharmony_ci	int ret;
76062306a36Sopenharmony_ci	int i;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	if (!user_msg->count ||
76362306a36Sopenharmony_ci	    user_msg->len < sizeof(*trans_hdr)) {
76462306a36Sopenharmony_ci		ret = -EINVAL;
76562306a36Sopenharmony_ci		goto out;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
76962306a36Sopenharmony_ci	msg = &wrapper->msg;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	msg->hdr.len = cpu_to_le32(sizeof(msg->hdr));
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (resources->dma_chunk_id) {
77462306a36Sopenharmony_ci		ret = encode_dma(qdev, resources->trans_hdr, wrappers, &user_len, resources, usr);
77562306a36Sopenharmony_ci		msg->hdr.count = cpu_to_le32(1);
77662306a36Sopenharmony_ci		goto out;
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	for (i = 0; i < user_msg->count; ++i) {
78062306a36Sopenharmony_ci		if (user_len > user_msg->len - sizeof(*trans_hdr)) {
78162306a36Sopenharmony_ci			ret = -EINVAL;
78262306a36Sopenharmony_ci			break;
78362306a36Sopenharmony_ci		}
78462306a36Sopenharmony_ci		trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len);
78562306a36Sopenharmony_ci		if (trans_hdr->len < sizeof(trans_hdr) ||
78662306a36Sopenharmony_ci		    size_add(user_len, trans_hdr->len) > user_msg->len) {
78762306a36Sopenharmony_ci			ret = -EINVAL;
78862306a36Sopenharmony_ci			break;
78962306a36Sopenharmony_ci		}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		switch (trans_hdr->type) {
79262306a36Sopenharmony_ci		case QAIC_TRANS_PASSTHROUGH_FROM_USR:
79362306a36Sopenharmony_ci			ret = encode_passthrough(qdev, trans_hdr, wrappers, &user_len);
79462306a36Sopenharmony_ci			break;
79562306a36Sopenharmony_ci		case QAIC_TRANS_DMA_XFER_FROM_USR:
79662306a36Sopenharmony_ci			ret = encode_dma(qdev, trans_hdr, wrappers, &user_len, resources, usr);
79762306a36Sopenharmony_ci			break;
79862306a36Sopenharmony_ci		case QAIC_TRANS_ACTIVATE_FROM_USR:
79962306a36Sopenharmony_ci			ret = encode_activate(qdev, trans_hdr, wrappers, &user_len, resources);
80062306a36Sopenharmony_ci			break;
80162306a36Sopenharmony_ci		case QAIC_TRANS_DEACTIVATE_FROM_USR:
80262306a36Sopenharmony_ci			ret = encode_deactivate(qdev, trans_hdr, &user_len, usr);
80362306a36Sopenharmony_ci			break;
80462306a36Sopenharmony_ci		case QAIC_TRANS_STATUS_FROM_USR:
80562306a36Sopenharmony_ci			ret = encode_status(qdev, trans_hdr, wrappers, &user_len);
80662306a36Sopenharmony_ci			break;
80762306a36Sopenharmony_ci		default:
80862306a36Sopenharmony_ci			ret = -EINVAL;
80962306a36Sopenharmony_ci			break;
81062306a36Sopenharmony_ci		}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		if (ret)
81362306a36Sopenharmony_ci			break;
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (user_len != user_msg->len)
81762306a36Sopenharmony_ci		ret = -EINVAL;
81862306a36Sopenharmony_ciout:
81962306a36Sopenharmony_ci	if (ret) {
82062306a36Sopenharmony_ci		free_dma_xfers(qdev, resources);
82162306a36Sopenharmony_ci		free_dbc_buf(qdev, resources);
82262306a36Sopenharmony_ci		return ret;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	return 0;
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic int decode_passthrough(struct qaic_device *qdev, void *trans, struct manage_msg *user_msg,
82962306a36Sopenharmony_ci			      u32 *msg_len)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	struct qaic_manage_trans_passthrough *out_trans;
83262306a36Sopenharmony_ci	struct wire_trans_passthrough *in_trans = trans;
83362306a36Sopenharmony_ci	u32 len;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	out_trans = (void *)user_msg->data + user_msg->len;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	len = le32_to_cpu(in_trans->hdr.len);
83862306a36Sopenharmony_ci	if (len % 8 != 0)
83962306a36Sopenharmony_ci		return -EINVAL;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (user_msg->len + len > QAIC_MANAGE_MAX_MSG_LENGTH)
84262306a36Sopenharmony_ci		return -ENOSPC;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	memcpy(out_trans->data, in_trans->data, len - sizeof(in_trans->hdr));
84562306a36Sopenharmony_ci	user_msg->len += len;
84662306a36Sopenharmony_ci	*msg_len += len;
84762306a36Sopenharmony_ci	out_trans->hdr.type = le32_to_cpu(in_trans->hdr.type);
84862306a36Sopenharmony_ci	out_trans->hdr.len = len;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	return 0;
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic int decode_activate(struct qaic_device *qdev, void *trans, struct manage_msg *user_msg,
85462306a36Sopenharmony_ci			   u32 *msg_len, struct ioctl_resources *resources, struct qaic_user *usr)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	struct qaic_manage_trans_activate_from_dev *out_trans;
85762306a36Sopenharmony_ci	struct wire_trans_activate_from_dev *in_trans = trans;
85862306a36Sopenharmony_ci	u32 len;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	out_trans = (void *)user_msg->data + user_msg->len;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	len = le32_to_cpu(in_trans->hdr.len);
86362306a36Sopenharmony_ci	if (user_msg->len + len > QAIC_MANAGE_MAX_MSG_LENGTH)
86462306a36Sopenharmony_ci		return -ENOSPC;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	user_msg->len += len;
86762306a36Sopenharmony_ci	*msg_len += len;
86862306a36Sopenharmony_ci	out_trans->hdr.type = le32_to_cpu(in_trans->hdr.type);
86962306a36Sopenharmony_ci	out_trans->hdr.len = len;
87062306a36Sopenharmony_ci	out_trans->status = le32_to_cpu(in_trans->status);
87162306a36Sopenharmony_ci	out_trans->dbc_id = le32_to_cpu(in_trans->dbc_id);
87262306a36Sopenharmony_ci	out_trans->options = le64_to_cpu(in_trans->options);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (!resources->buf)
87562306a36Sopenharmony_ci		/* how did we get an activate response without a request? */
87662306a36Sopenharmony_ci		return -EINVAL;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if (out_trans->dbc_id >= qdev->num_dbc)
87962306a36Sopenharmony_ci		/*
88062306a36Sopenharmony_ci		 * The device assigned an invalid resource, which should never
88162306a36Sopenharmony_ci		 * happen. Return an error so the user can try to recover.
88262306a36Sopenharmony_ci		 */
88362306a36Sopenharmony_ci		return -ENODEV;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	if (out_trans->status)
88662306a36Sopenharmony_ci		/*
88762306a36Sopenharmony_ci		 * Allocating resources failed on device side. This is not an
88862306a36Sopenharmony_ci		 * expected behaviour, user is expected to handle this situation.
88962306a36Sopenharmony_ci		 */
89062306a36Sopenharmony_ci		return -ECANCELED;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	resources->status = out_trans->status;
89362306a36Sopenharmony_ci	resources->dbc_id = out_trans->dbc_id;
89462306a36Sopenharmony_ci	save_dbc_buf(qdev, resources, usr);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	return 0;
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic int decode_deactivate(struct qaic_device *qdev, void *trans, u32 *msg_len,
90062306a36Sopenharmony_ci			     struct qaic_user *usr)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct wire_trans_deactivate_from_dev *in_trans = trans;
90362306a36Sopenharmony_ci	u32 dbc_id = le32_to_cpu(in_trans->dbc_id);
90462306a36Sopenharmony_ci	u32 status = le32_to_cpu(in_trans->status);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	if (dbc_id >= qdev->num_dbc)
90762306a36Sopenharmony_ci		/*
90862306a36Sopenharmony_ci		 * The device assigned an invalid resource, which should never
90962306a36Sopenharmony_ci		 * happen. Inject an error so the user can try to recover.
91062306a36Sopenharmony_ci		 */
91162306a36Sopenharmony_ci		return -ENODEV;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (status) {
91462306a36Sopenharmony_ci		/*
91562306a36Sopenharmony_ci		 * Releasing resources failed on the device side, which puts
91662306a36Sopenharmony_ci		 * us in a bind since they may still be in use, so enable the
91762306a36Sopenharmony_ci		 * dbc. User is expected to retry deactivation.
91862306a36Sopenharmony_ci		 */
91962306a36Sopenharmony_ci		enable_dbc(qdev, dbc_id, usr);
92062306a36Sopenharmony_ci		return -ECANCELED;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	release_dbc(qdev, dbc_id);
92462306a36Sopenharmony_ci	*msg_len += sizeof(*in_trans);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	return 0;
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic int decode_status(struct qaic_device *qdev, void *trans, struct manage_msg *user_msg,
93062306a36Sopenharmony_ci			 u32 *user_len, struct wire_msg *msg)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	struct qaic_manage_trans_status_from_dev *out_trans;
93362306a36Sopenharmony_ci	struct wire_trans_status_from_dev *in_trans = trans;
93462306a36Sopenharmony_ci	u32 len;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	out_trans = (void *)user_msg->data + user_msg->len;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	len = le32_to_cpu(in_trans->hdr.len);
93962306a36Sopenharmony_ci	if (user_msg->len + len > QAIC_MANAGE_MAX_MSG_LENGTH)
94062306a36Sopenharmony_ci		return -ENOSPC;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	out_trans->hdr.type = QAIC_TRANS_STATUS_FROM_DEV;
94362306a36Sopenharmony_ci	out_trans->hdr.len = len;
94462306a36Sopenharmony_ci	out_trans->major = le16_to_cpu(in_trans->major);
94562306a36Sopenharmony_ci	out_trans->minor = le16_to_cpu(in_trans->minor);
94662306a36Sopenharmony_ci	out_trans->status_flags = le64_to_cpu(in_trans->status_flags);
94762306a36Sopenharmony_ci	out_trans->status = le32_to_cpu(in_trans->status);
94862306a36Sopenharmony_ci	*user_len += le32_to_cpu(in_trans->hdr.len);
94962306a36Sopenharmony_ci	user_msg->len += len;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	if (out_trans->status)
95262306a36Sopenharmony_ci		return -ECANCELED;
95362306a36Sopenharmony_ci	if (out_trans->status_flags & BIT(0) && !valid_crc(msg))
95462306a36Sopenharmony_ci		return -EPIPE;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	return 0;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
96062306a36Sopenharmony_ci			  struct wire_msg *msg, struct ioctl_resources *resources,
96162306a36Sopenharmony_ci			  struct qaic_user *usr)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	u32 msg_hdr_len = le32_to_cpu(msg->hdr.len);
96462306a36Sopenharmony_ci	struct wire_trans_hdr *trans_hdr;
96562306a36Sopenharmony_ci	u32 msg_len = 0;
96662306a36Sopenharmony_ci	int ret;
96762306a36Sopenharmony_ci	int i;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	if (msg_hdr_len < sizeof(*trans_hdr) ||
97062306a36Sopenharmony_ci	    msg_hdr_len > QAIC_MANAGE_MAX_MSG_LENGTH)
97162306a36Sopenharmony_ci		return -EINVAL;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	user_msg->len = 0;
97462306a36Sopenharmony_ci	user_msg->count = le32_to_cpu(msg->hdr.count);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	for (i = 0; i < user_msg->count; ++i) {
97762306a36Sopenharmony_ci		u32 hdr_len;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci		if (msg_len > msg_hdr_len - sizeof(*trans_hdr))
98062306a36Sopenharmony_ci			return -EINVAL;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		trans_hdr = (struct wire_trans_hdr *)(msg->data + msg_len);
98362306a36Sopenharmony_ci		hdr_len = le32_to_cpu(trans_hdr->len);
98462306a36Sopenharmony_ci		if (hdr_len < sizeof(*trans_hdr) ||
98562306a36Sopenharmony_ci		    size_add(msg_len, hdr_len) > msg_hdr_len)
98662306a36Sopenharmony_ci			return -EINVAL;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		switch (le32_to_cpu(trans_hdr->type)) {
98962306a36Sopenharmony_ci		case QAIC_TRANS_PASSTHROUGH_FROM_DEV:
99062306a36Sopenharmony_ci			ret = decode_passthrough(qdev, trans_hdr, user_msg, &msg_len);
99162306a36Sopenharmony_ci			break;
99262306a36Sopenharmony_ci		case QAIC_TRANS_ACTIVATE_FROM_DEV:
99362306a36Sopenharmony_ci			ret = decode_activate(qdev, trans_hdr, user_msg, &msg_len, resources, usr);
99462306a36Sopenharmony_ci			break;
99562306a36Sopenharmony_ci		case QAIC_TRANS_DEACTIVATE_FROM_DEV:
99662306a36Sopenharmony_ci			ret = decode_deactivate(qdev, trans_hdr, &msg_len, usr);
99762306a36Sopenharmony_ci			break;
99862306a36Sopenharmony_ci		case QAIC_TRANS_STATUS_FROM_DEV:
99962306a36Sopenharmony_ci			ret = decode_status(qdev, trans_hdr, user_msg, &msg_len, msg);
100062306a36Sopenharmony_ci			break;
100162306a36Sopenharmony_ci		default:
100262306a36Sopenharmony_ci			return -EINVAL;
100362306a36Sopenharmony_ci		}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci		if (ret)
100662306a36Sopenharmony_ci			return ret;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (msg_len != (msg_hdr_len - sizeof(msg->hdr)))
101062306a36Sopenharmony_ci		return -EINVAL;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	return 0;
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cistatic void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u32 seq_num,
101662306a36Sopenharmony_ci		      bool ignore_signal)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	struct xfer_queue_elem elem;
101962306a36Sopenharmony_ci	struct wire_msg *out_buf;
102062306a36Sopenharmony_ci	struct wrapper_msg *w;
102162306a36Sopenharmony_ci	long ret = -EAGAIN;
102262306a36Sopenharmony_ci	int xfer_count = 0;
102362306a36Sopenharmony_ci	int retry_count;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	if (qdev->in_reset) {
102662306a36Sopenharmony_ci		mutex_unlock(&qdev->cntl_mutex);
102762306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	/* Attempt to avoid a partial commit of a message */
103162306a36Sopenharmony_ci	list_for_each_entry(w, &wrappers->list, list)
103262306a36Sopenharmony_ci		xfer_count++;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	for (retry_count = 0; retry_count < QAIC_MHI_RETRY_MAX; retry_count++) {
103562306a36Sopenharmony_ci		if (xfer_count <= mhi_get_free_desc_count(qdev->cntl_ch, DMA_TO_DEVICE)) {
103662306a36Sopenharmony_ci			ret = 0;
103762306a36Sopenharmony_ci			break;
103862306a36Sopenharmony_ci		}
103962306a36Sopenharmony_ci		msleep_interruptible(QAIC_MHI_RETRY_WAIT_MS);
104062306a36Sopenharmony_ci		if (signal_pending(current))
104162306a36Sopenharmony_ci			break;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	if (ret) {
104562306a36Sopenharmony_ci		mutex_unlock(&qdev->cntl_mutex);
104662306a36Sopenharmony_ci		return ERR_PTR(ret);
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	elem.seq_num = seq_num;
105062306a36Sopenharmony_ci	elem.buf = NULL;
105162306a36Sopenharmony_ci	init_completion(&elem.xfer_done);
105262306a36Sopenharmony_ci	if (likely(!qdev->cntl_lost_buf)) {
105362306a36Sopenharmony_ci		/*
105462306a36Sopenharmony_ci		 * The max size of request to device is QAIC_MANAGE_EXT_MSG_LENGTH.
105562306a36Sopenharmony_ci		 * The max size of response from device is QAIC_MANAGE_MAX_MSG_LENGTH.
105662306a36Sopenharmony_ci		 */
105762306a36Sopenharmony_ci		out_buf = kmalloc(QAIC_MANAGE_MAX_MSG_LENGTH, GFP_KERNEL);
105862306a36Sopenharmony_ci		if (!out_buf) {
105962306a36Sopenharmony_ci			mutex_unlock(&qdev->cntl_mutex);
106062306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
106162306a36Sopenharmony_ci		}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci		ret = mhi_queue_buf(qdev->cntl_ch, DMA_FROM_DEVICE, out_buf,
106462306a36Sopenharmony_ci				    QAIC_MANAGE_MAX_MSG_LENGTH, MHI_EOT);
106562306a36Sopenharmony_ci		if (ret) {
106662306a36Sopenharmony_ci			mutex_unlock(&qdev->cntl_mutex);
106762306a36Sopenharmony_ci			return ERR_PTR(ret);
106862306a36Sopenharmony_ci		}
106962306a36Sopenharmony_ci	} else {
107062306a36Sopenharmony_ci		/*
107162306a36Sopenharmony_ci		 * we lost a buffer because we queued a recv buf, but then
107262306a36Sopenharmony_ci		 * queuing the corresponding tx buf failed. To try to avoid
107362306a36Sopenharmony_ci		 * a memory leak, lets reclaim it and use it for this
107462306a36Sopenharmony_ci		 * transaction.
107562306a36Sopenharmony_ci		 */
107662306a36Sopenharmony_ci		qdev->cntl_lost_buf = false;
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	list_for_each_entry(w, &wrappers->list, list) {
108062306a36Sopenharmony_ci		kref_get(&w->ref_count);
108162306a36Sopenharmony_ci		retry_count = 0;
108262306a36Sopenharmony_ci		ret = mhi_queue_buf(qdev->cntl_ch, DMA_TO_DEVICE, &w->msg, w->len,
108362306a36Sopenharmony_ci				    list_is_last(&w->list, &wrappers->list) ? MHI_EOT : MHI_CHAIN);
108462306a36Sopenharmony_ci		if (ret) {
108562306a36Sopenharmony_ci			qdev->cntl_lost_buf = true;
108662306a36Sopenharmony_ci			kref_put(&w->ref_count, free_wrapper);
108762306a36Sopenharmony_ci			mutex_unlock(&qdev->cntl_mutex);
108862306a36Sopenharmony_ci			return ERR_PTR(ret);
108962306a36Sopenharmony_ci		}
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	list_add_tail(&elem.list, &qdev->cntl_xfer_list);
109362306a36Sopenharmony_ci	mutex_unlock(&qdev->cntl_mutex);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	if (ignore_signal)
109662306a36Sopenharmony_ci		ret = wait_for_completion_timeout(&elem.xfer_done, control_resp_timeout_s * HZ);
109762306a36Sopenharmony_ci	else
109862306a36Sopenharmony_ci		ret = wait_for_completion_interruptible_timeout(&elem.xfer_done,
109962306a36Sopenharmony_ci								control_resp_timeout_s * HZ);
110062306a36Sopenharmony_ci	/*
110162306a36Sopenharmony_ci	 * not using _interruptable because we have to cleanup or we'll
110262306a36Sopenharmony_ci	 * likely cause memory corruption
110362306a36Sopenharmony_ci	 */
110462306a36Sopenharmony_ci	mutex_lock(&qdev->cntl_mutex);
110562306a36Sopenharmony_ci	if (!list_empty(&elem.list))
110662306a36Sopenharmony_ci		list_del(&elem.list);
110762306a36Sopenharmony_ci	if (!ret && !elem.buf)
110862306a36Sopenharmony_ci		ret = -ETIMEDOUT;
110962306a36Sopenharmony_ci	else if (ret > 0 && !elem.buf)
111062306a36Sopenharmony_ci		ret = -EIO;
111162306a36Sopenharmony_ci	mutex_unlock(&qdev->cntl_mutex);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (ret < 0) {
111462306a36Sopenharmony_ci		kfree(elem.buf);
111562306a36Sopenharmony_ci		return ERR_PTR(ret);
111662306a36Sopenharmony_ci	} else if (!qdev->valid_crc(elem.buf)) {
111762306a36Sopenharmony_ci		kfree(elem.buf);
111862306a36Sopenharmony_ci		return ERR_PTR(-EPIPE);
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	return elem.buf;
112262306a36Sopenharmony_ci}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci/* Add a transaction to abort the outstanding DMA continuation */
112562306a36Sopenharmony_cistatic int abort_dma_cont(struct qaic_device *qdev, struct wrapper_list *wrappers, u32 dma_chunk_id)
112662306a36Sopenharmony_ci{
112762306a36Sopenharmony_ci	struct wire_trans_dma_xfer *out_trans;
112862306a36Sopenharmony_ci	u32 size = sizeof(*out_trans);
112962306a36Sopenharmony_ci	struct wrapper_msg *wrapper;
113062306a36Sopenharmony_ci	struct wrapper_msg *w;
113162306a36Sopenharmony_ci	struct wire_msg *msg;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	wrapper = list_first_entry(&wrappers->list, struct wrapper_msg, list);
113462306a36Sopenharmony_ci	msg = &wrapper->msg;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	/* Remove all but the first wrapper which has the msg header */
113762306a36Sopenharmony_ci	list_for_each_entry_safe(wrapper, w, &wrappers->list, list)
113862306a36Sopenharmony_ci		if (!list_is_first(&wrapper->list, &wrappers->list))
113962306a36Sopenharmony_ci			kref_put(&wrapper->ref_count, free_wrapper);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	wrapper = add_wrapper(wrappers, offsetof(struct wrapper_msg, trans) + sizeof(*out_trans));
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	if (!wrapper)
114462306a36Sopenharmony_ci		return -ENOMEM;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	out_trans = (struct wire_trans_dma_xfer *)&wrapper->trans;
114762306a36Sopenharmony_ci	out_trans->hdr.type = cpu_to_le32(QAIC_TRANS_DMA_XFER_TO_DEV);
114862306a36Sopenharmony_ci	out_trans->hdr.len = cpu_to_le32(size);
114962306a36Sopenharmony_ci	out_trans->tag = cpu_to_le32(0);
115062306a36Sopenharmony_ci	out_trans->count = cpu_to_le32(0);
115162306a36Sopenharmony_ci	out_trans->dma_chunk_id = cpu_to_le32(dma_chunk_id);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	msg->hdr.len = cpu_to_le32(size + sizeof(*msg));
115462306a36Sopenharmony_ci	msg->hdr.count = cpu_to_le32(1);
115562306a36Sopenharmony_ci	wrapper->len = size;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	return 0;
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_cistatic struct wrapper_list *alloc_wrapper_list(void)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	struct wrapper_list *wrappers;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	wrappers = kmalloc(sizeof(*wrappers), GFP_KERNEL);
116562306a36Sopenharmony_ci	if (!wrappers)
116662306a36Sopenharmony_ci		return NULL;
116762306a36Sopenharmony_ci	INIT_LIST_HEAD(&wrappers->list);
116862306a36Sopenharmony_ci	spin_lock_init(&wrappers->lock);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	return wrappers;
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic int qaic_manage_msg_xfer(struct qaic_device *qdev, struct qaic_user *usr,
117462306a36Sopenharmony_ci				struct manage_msg *user_msg, struct ioctl_resources *resources,
117562306a36Sopenharmony_ci				struct wire_msg **rsp)
117662306a36Sopenharmony_ci{
117762306a36Sopenharmony_ci	struct wrapper_list *wrappers;
117862306a36Sopenharmony_ci	struct wrapper_msg *wrapper;
117962306a36Sopenharmony_ci	struct wrapper_msg *w;
118062306a36Sopenharmony_ci	bool all_done = false;
118162306a36Sopenharmony_ci	struct wire_msg *msg;
118262306a36Sopenharmony_ci	int ret;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	wrappers = alloc_wrapper_list();
118562306a36Sopenharmony_ci	if (!wrappers)
118662306a36Sopenharmony_ci		return -ENOMEM;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	wrapper = add_wrapper(wrappers, sizeof(*wrapper));
118962306a36Sopenharmony_ci	if (!wrapper) {
119062306a36Sopenharmony_ci		kfree(wrappers);
119162306a36Sopenharmony_ci		return -ENOMEM;
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	msg = &wrapper->msg;
119562306a36Sopenharmony_ci	wrapper->len = sizeof(*msg);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	ret = encode_message(qdev, user_msg, wrappers, resources, usr);
119862306a36Sopenharmony_ci	if (ret && resources->dma_chunk_id)
119962306a36Sopenharmony_ci		ret = abort_dma_cont(qdev, wrappers, resources->dma_chunk_id);
120062306a36Sopenharmony_ci	if (ret)
120162306a36Sopenharmony_ci		goto encode_failed;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	ret = mutex_lock_interruptible(&qdev->cntl_mutex);
120462306a36Sopenharmony_ci	if (ret)
120562306a36Sopenharmony_ci		goto lock_failed;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	msg->hdr.magic_number = MANAGE_MAGIC_NUMBER;
120862306a36Sopenharmony_ci	msg->hdr.sequence_number = cpu_to_le32(qdev->next_seq_num++);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	if (usr) {
121162306a36Sopenharmony_ci		msg->hdr.handle = cpu_to_le32(usr->handle);
121262306a36Sopenharmony_ci		msg->hdr.partition_id = cpu_to_le32(usr->qddev->partition_id);
121362306a36Sopenharmony_ci	} else {
121462306a36Sopenharmony_ci		msg->hdr.handle = 0;
121562306a36Sopenharmony_ci		msg->hdr.partition_id = cpu_to_le32(QAIC_NO_PARTITION);
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	msg->hdr.padding = cpu_to_le32(0);
121962306a36Sopenharmony_ci	msg->hdr.crc32 = cpu_to_le32(qdev->gen_crc(wrappers));
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	/* msg_xfer releases the mutex */
122262306a36Sopenharmony_ci	*rsp = msg_xfer(qdev, wrappers, qdev->next_seq_num - 1, false);
122362306a36Sopenharmony_ci	if (IS_ERR(*rsp))
122462306a36Sopenharmony_ci		ret = PTR_ERR(*rsp);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_cilock_failed:
122762306a36Sopenharmony_ci	free_dma_xfers(qdev, resources);
122862306a36Sopenharmony_ciencode_failed:
122962306a36Sopenharmony_ci	spin_lock(&wrappers->lock);
123062306a36Sopenharmony_ci	list_for_each_entry_safe(wrapper, w, &wrappers->list, list)
123162306a36Sopenharmony_ci		kref_put(&wrapper->ref_count, free_wrapper);
123262306a36Sopenharmony_ci	all_done = list_empty(&wrappers->list);
123362306a36Sopenharmony_ci	spin_unlock(&wrappers->lock);
123462306a36Sopenharmony_ci	if (all_done)
123562306a36Sopenharmony_ci		kfree(wrappers);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	return ret;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic int qaic_manage(struct qaic_device *qdev, struct qaic_user *usr, struct manage_msg *user_msg)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct wire_trans_dma_xfer_cont *dma_cont = NULL;
124362306a36Sopenharmony_ci	struct ioctl_resources resources;
124462306a36Sopenharmony_ci	struct wire_msg *rsp = NULL;
124562306a36Sopenharmony_ci	int ret;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	memset(&resources, 0, sizeof(struct ioctl_resources));
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	INIT_LIST_HEAD(&resources.dma_xfers);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	if (user_msg->len > QAIC_MANAGE_MAX_MSG_LENGTH ||
125262306a36Sopenharmony_ci	    user_msg->count > QAIC_MANAGE_MAX_MSG_LENGTH / sizeof(struct qaic_manage_trans_hdr))
125362306a36Sopenharmony_ci		return -EINVAL;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_cidma_xfer_continue:
125662306a36Sopenharmony_ci	ret = qaic_manage_msg_xfer(qdev, usr, user_msg, &resources, &rsp);
125762306a36Sopenharmony_ci	if (ret)
125862306a36Sopenharmony_ci		return ret;
125962306a36Sopenharmony_ci	/* dma_cont should be the only transaction if present */
126062306a36Sopenharmony_ci	if (le32_to_cpu(rsp->hdr.count) == 1) {
126162306a36Sopenharmony_ci		dma_cont = (struct wire_trans_dma_xfer_cont *)rsp->data;
126262306a36Sopenharmony_ci		if (le32_to_cpu(dma_cont->hdr.type) != QAIC_TRANS_DMA_XFER_CONT)
126362306a36Sopenharmony_ci			dma_cont = NULL;
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci	if (dma_cont) {
126662306a36Sopenharmony_ci		if (le32_to_cpu(dma_cont->dma_chunk_id) == resources.dma_chunk_id &&
126762306a36Sopenharmony_ci		    le64_to_cpu(dma_cont->xferred_size) == resources.xferred_dma_size) {
126862306a36Sopenharmony_ci			kfree(rsp);
126962306a36Sopenharmony_ci			goto dma_xfer_continue;
127062306a36Sopenharmony_ci		}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci		ret = -EINVAL;
127362306a36Sopenharmony_ci		goto dma_cont_failed;
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	ret = decode_message(qdev, user_msg, rsp, &resources, usr);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_cidma_cont_failed:
127962306a36Sopenharmony_ci	free_dbc_buf(qdev, &resources);
128062306a36Sopenharmony_ci	kfree(rsp);
128162306a36Sopenharmony_ci	return ret;
128262306a36Sopenharmony_ci}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ciint qaic_manage_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv)
128562306a36Sopenharmony_ci{
128662306a36Sopenharmony_ci	struct qaic_manage_msg *user_msg = data;
128762306a36Sopenharmony_ci	struct qaic_device *qdev;
128862306a36Sopenharmony_ci	struct manage_msg *msg;
128962306a36Sopenharmony_ci	struct qaic_user *usr;
129062306a36Sopenharmony_ci	u8 __user *user_data;
129162306a36Sopenharmony_ci	int qdev_rcu_id;
129262306a36Sopenharmony_ci	int usr_rcu_id;
129362306a36Sopenharmony_ci	int ret;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	if (user_msg->len > QAIC_MANAGE_MAX_MSG_LENGTH)
129662306a36Sopenharmony_ci		return -EINVAL;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	usr = file_priv->driver_priv;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
130162306a36Sopenharmony_ci	if (!usr->qddev) {
130262306a36Sopenharmony_ci		srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
130362306a36Sopenharmony_ci		return -ENODEV;
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	qdev = usr->qddev->qdev;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	qdev_rcu_id = srcu_read_lock(&qdev->dev_lock);
130962306a36Sopenharmony_ci	if (qdev->in_reset) {
131062306a36Sopenharmony_ci		srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
131162306a36Sopenharmony_ci		srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
131262306a36Sopenharmony_ci		return -ENODEV;
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	msg = kzalloc(QAIC_MANAGE_MAX_MSG_LENGTH + sizeof(*msg), GFP_KERNEL);
131662306a36Sopenharmony_ci	if (!msg) {
131762306a36Sopenharmony_ci		ret = -ENOMEM;
131862306a36Sopenharmony_ci		goto out;
131962306a36Sopenharmony_ci	}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	msg->len = user_msg->len;
132262306a36Sopenharmony_ci	msg->count = user_msg->count;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	user_data = u64_to_user_ptr(user_msg->data);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	if (copy_from_user(msg->data, user_data, user_msg->len)) {
132762306a36Sopenharmony_ci		ret = -EFAULT;
132862306a36Sopenharmony_ci		goto free_msg;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	ret = qaic_manage(qdev, usr, msg);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	/*
133462306a36Sopenharmony_ci	 * If the qaic_manage() is successful then we copy the message onto
133562306a36Sopenharmony_ci	 * userspace memory but we have an exception for -ECANCELED.
133662306a36Sopenharmony_ci	 * For -ECANCELED, it means that device has NACKed the message with a
133762306a36Sopenharmony_ci	 * status error code which userspace would like to know.
133862306a36Sopenharmony_ci	 */
133962306a36Sopenharmony_ci	if (ret == -ECANCELED || !ret) {
134062306a36Sopenharmony_ci		if (copy_to_user(user_data, msg->data, msg->len)) {
134162306a36Sopenharmony_ci			ret = -EFAULT;
134262306a36Sopenharmony_ci		} else {
134362306a36Sopenharmony_ci			user_msg->len = msg->len;
134462306a36Sopenharmony_ci			user_msg->count = msg->count;
134562306a36Sopenharmony_ci		}
134662306a36Sopenharmony_ci	}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_cifree_msg:
134962306a36Sopenharmony_ci	kfree(msg);
135062306a36Sopenharmony_ciout:
135162306a36Sopenharmony_ci	srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
135262306a36Sopenharmony_ci	srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
135362306a36Sopenharmony_ci	return ret;
135462306a36Sopenharmony_ci}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ciint get_cntl_version(struct qaic_device *qdev, struct qaic_user *usr, u16 *major, u16 *minor)
135762306a36Sopenharmony_ci{
135862306a36Sopenharmony_ci	struct qaic_manage_trans_status_from_dev *status_result;
135962306a36Sopenharmony_ci	struct qaic_manage_trans_status_to_dev *status_query;
136062306a36Sopenharmony_ci	struct manage_msg *user_msg;
136162306a36Sopenharmony_ci	int ret;
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	user_msg = kmalloc(sizeof(*user_msg) + sizeof(*status_result), GFP_KERNEL);
136462306a36Sopenharmony_ci	if (!user_msg) {
136562306a36Sopenharmony_ci		ret = -ENOMEM;
136662306a36Sopenharmony_ci		goto out;
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci	user_msg->len = sizeof(*status_query);
136962306a36Sopenharmony_ci	user_msg->count = 1;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	status_query = (struct qaic_manage_trans_status_to_dev *)user_msg->data;
137262306a36Sopenharmony_ci	status_query->hdr.type = QAIC_TRANS_STATUS_FROM_USR;
137362306a36Sopenharmony_ci	status_query->hdr.len = sizeof(status_query->hdr);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	ret = qaic_manage(qdev, usr, user_msg);
137662306a36Sopenharmony_ci	if (ret)
137762306a36Sopenharmony_ci		goto kfree_user_msg;
137862306a36Sopenharmony_ci	status_result = (struct qaic_manage_trans_status_from_dev *)user_msg->data;
137962306a36Sopenharmony_ci	*major = status_result->major;
138062306a36Sopenharmony_ci	*minor = status_result->minor;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	if (status_result->status_flags & BIT(0)) { /* device is using CRC */
138362306a36Sopenharmony_ci		/* By default qdev->gen_crc is programmed to generate CRC */
138462306a36Sopenharmony_ci		qdev->valid_crc = valid_crc;
138562306a36Sopenharmony_ci	} else {
138662306a36Sopenharmony_ci		/* By default qdev->valid_crc is programmed to bypass CRC */
138762306a36Sopenharmony_ci		qdev->gen_crc = gen_crc_stub;
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cikfree_user_msg:
139162306a36Sopenharmony_ci	kfree(user_msg);
139262306a36Sopenharmony_ciout:
139362306a36Sopenharmony_ci	return ret;
139462306a36Sopenharmony_ci}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_cistatic void resp_worker(struct work_struct *work)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	struct resp_work *resp = container_of(work, struct resp_work, work);
139962306a36Sopenharmony_ci	struct qaic_device *qdev = resp->qdev;
140062306a36Sopenharmony_ci	struct wire_msg *msg = resp->buf;
140162306a36Sopenharmony_ci	struct xfer_queue_elem *elem;
140262306a36Sopenharmony_ci	struct xfer_queue_elem *i;
140362306a36Sopenharmony_ci	bool found = false;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	mutex_lock(&qdev->cntl_mutex);
140662306a36Sopenharmony_ci	list_for_each_entry_safe(elem, i, &qdev->cntl_xfer_list, list) {
140762306a36Sopenharmony_ci		if (elem->seq_num == le32_to_cpu(msg->hdr.sequence_number)) {
140862306a36Sopenharmony_ci			found = true;
140962306a36Sopenharmony_ci			list_del_init(&elem->list);
141062306a36Sopenharmony_ci			elem->buf = msg;
141162306a36Sopenharmony_ci			complete_all(&elem->xfer_done);
141262306a36Sopenharmony_ci			break;
141362306a36Sopenharmony_ci		}
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci	mutex_unlock(&qdev->cntl_mutex);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	if (!found)
141862306a36Sopenharmony_ci		/* request must have timed out, drop packet */
141962306a36Sopenharmony_ci		kfree(msg);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	kfree(resp);
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_cistatic void free_wrapper_from_list(struct wrapper_list *wrappers, struct wrapper_msg *wrapper)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	bool all_done = false;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	spin_lock(&wrappers->lock);
142962306a36Sopenharmony_ci	kref_put(&wrapper->ref_count, free_wrapper);
143062306a36Sopenharmony_ci	all_done = list_empty(&wrappers->list);
143162306a36Sopenharmony_ci	spin_unlock(&wrappers->lock);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	if (all_done)
143462306a36Sopenharmony_ci		kfree(wrappers);
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_civoid qaic_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
143862306a36Sopenharmony_ci{
143962306a36Sopenharmony_ci	struct wire_msg *msg = mhi_result->buf_addr;
144062306a36Sopenharmony_ci	struct wrapper_msg *wrapper = container_of(msg, struct wrapper_msg, msg);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	free_wrapper_from_list(wrapper->head, wrapper);
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_civoid qaic_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
144862306a36Sopenharmony_ci	struct wire_msg *msg = mhi_result->buf_addr;
144962306a36Sopenharmony_ci	struct resp_work *resp;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	if (mhi_result->transaction_status || msg->hdr.magic_number != MANAGE_MAGIC_NUMBER) {
145262306a36Sopenharmony_ci		kfree(msg);
145362306a36Sopenharmony_ci		return;
145462306a36Sopenharmony_ci	}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	resp = kmalloc(sizeof(*resp), GFP_ATOMIC);
145762306a36Sopenharmony_ci	if (!resp) {
145862306a36Sopenharmony_ci		kfree(msg);
145962306a36Sopenharmony_ci		return;
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	INIT_WORK(&resp->work, resp_worker);
146362306a36Sopenharmony_ci	resp->qdev = qdev;
146462306a36Sopenharmony_ci	resp->buf = msg;
146562306a36Sopenharmony_ci	queue_work(qdev->cntl_wq, &resp->work);
146662306a36Sopenharmony_ci}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ciint qaic_control_open(struct qaic_device *qdev)
146962306a36Sopenharmony_ci{
147062306a36Sopenharmony_ci	if (!qdev->cntl_ch)
147162306a36Sopenharmony_ci		return -ENODEV;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	qdev->cntl_lost_buf = false;
147462306a36Sopenharmony_ci	/*
147562306a36Sopenharmony_ci	 * By default qaic should assume that device has CRC enabled.
147662306a36Sopenharmony_ci	 * Qaic comes to know if device has CRC enabled or disabled during the
147762306a36Sopenharmony_ci	 * device status transaction, which is the first transaction performed
147862306a36Sopenharmony_ci	 * on control channel.
147962306a36Sopenharmony_ci	 *
148062306a36Sopenharmony_ci	 * So CRC validation of first device status transaction response is
148162306a36Sopenharmony_ci	 * ignored (by calling valid_crc_stub) and is done later during decoding
148262306a36Sopenharmony_ci	 * if device has CRC enabled.
148362306a36Sopenharmony_ci	 * Now that qaic knows whether device has CRC enabled or not it acts
148462306a36Sopenharmony_ci	 * accordingly.
148562306a36Sopenharmony_ci	 */
148662306a36Sopenharmony_ci	qdev->gen_crc = gen_crc;
148762306a36Sopenharmony_ci	qdev->valid_crc = valid_crc_stub;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	return mhi_prepare_for_transfer(qdev->cntl_ch);
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_civoid qaic_control_close(struct qaic_device *qdev)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	mhi_unprepare_from_transfer(qdev->cntl_ch);
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_civoid qaic_release_usr(struct qaic_device *qdev, struct qaic_user *usr)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	struct wire_trans_terminate_to_dev *trans;
150062306a36Sopenharmony_ci	struct wrapper_list *wrappers;
150162306a36Sopenharmony_ci	struct wrapper_msg *wrapper;
150262306a36Sopenharmony_ci	struct wire_msg *msg;
150362306a36Sopenharmony_ci	struct wire_msg *rsp;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	wrappers = alloc_wrapper_list();
150662306a36Sopenharmony_ci	if (!wrappers)
150762306a36Sopenharmony_ci		return;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	wrapper = add_wrapper(wrappers, sizeof(*wrapper) + sizeof(*msg) + sizeof(*trans));
151062306a36Sopenharmony_ci	if (!wrapper)
151162306a36Sopenharmony_ci		return;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	msg = &wrapper->msg;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	trans = (struct wire_trans_terminate_to_dev *)msg->data;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	trans->hdr.type = cpu_to_le32(QAIC_TRANS_TERMINATE_TO_DEV);
151862306a36Sopenharmony_ci	trans->hdr.len = cpu_to_le32(sizeof(*trans));
151962306a36Sopenharmony_ci	trans->handle = cpu_to_le32(usr->handle);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	mutex_lock(&qdev->cntl_mutex);
152262306a36Sopenharmony_ci	wrapper->len = sizeof(msg->hdr) + sizeof(*trans);
152362306a36Sopenharmony_ci	msg->hdr.magic_number = MANAGE_MAGIC_NUMBER;
152462306a36Sopenharmony_ci	msg->hdr.sequence_number = cpu_to_le32(qdev->next_seq_num++);
152562306a36Sopenharmony_ci	msg->hdr.len = cpu_to_le32(wrapper->len);
152662306a36Sopenharmony_ci	msg->hdr.count = cpu_to_le32(1);
152762306a36Sopenharmony_ci	msg->hdr.handle = cpu_to_le32(usr->handle);
152862306a36Sopenharmony_ci	msg->hdr.padding = cpu_to_le32(0);
152962306a36Sopenharmony_ci	msg->hdr.crc32 = cpu_to_le32(qdev->gen_crc(wrappers));
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	/*
153262306a36Sopenharmony_ci	 * msg_xfer releases the mutex
153362306a36Sopenharmony_ci	 * We don't care about the return of msg_xfer since we will not do
153462306a36Sopenharmony_ci	 * anything different based on what happens.
153562306a36Sopenharmony_ci	 * We ignore pending signals since one will be set if the user is
153662306a36Sopenharmony_ci	 * killed, and we need give the device a chance to cleanup, otherwise
153762306a36Sopenharmony_ci	 * DMA may still be in progress when we return.
153862306a36Sopenharmony_ci	 */
153962306a36Sopenharmony_ci	rsp = msg_xfer(qdev, wrappers, qdev->next_seq_num - 1, true);
154062306a36Sopenharmony_ci	if (!IS_ERR(rsp))
154162306a36Sopenharmony_ci		kfree(rsp);
154262306a36Sopenharmony_ci	free_wrapper_from_list(wrappers, wrapper);
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_civoid wake_all_cntl(struct qaic_device *qdev)
154662306a36Sopenharmony_ci{
154762306a36Sopenharmony_ci	struct xfer_queue_elem *elem;
154862306a36Sopenharmony_ci	struct xfer_queue_elem *i;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	mutex_lock(&qdev->cntl_mutex);
155162306a36Sopenharmony_ci	list_for_each_entry_safe(elem, i, &qdev->cntl_xfer_list, list) {
155262306a36Sopenharmony_ci		list_del_init(&elem->list);
155362306a36Sopenharmony_ci		complete_all(&elem->xfer_done);
155462306a36Sopenharmony_ci	}
155562306a36Sopenharmony_ci	mutex_unlock(&qdev->cntl_mutex);
155662306a36Sopenharmony_ci}
1557