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