xref: /linux/drivers/scsi/leapraid/leapraid_app.c (revision 08dbfad3f5040f5bdb6c529da20d6d4e81fefd72)
15597088cSDongdong Hao // SPDX-License-Identifier: GPL-2.0
25597088cSDongdong Hao /*
35597088cSDongdong Hao  * Copyright (C) 2026 LeapIO Tech Inc.
45597088cSDongdong Hao  *
55597088cSDongdong Hao  * LeapRAID storage and RAID controller driver.
65597088cSDongdong Hao  */
75597088cSDongdong Hao #include <linux/compat.h>
85597088cSDongdong Hao #include <linux/module.h>
95597088cSDongdong Hao #include <linux/miscdevice.h>
105597088cSDongdong Hao 
115597088cSDongdong Hao #include "leapraid_func.h"
125597088cSDongdong Hao 
135597088cSDongdong Hao /* IOCTL device file name. */
145597088cSDongdong Hao #define LEAPRAID_DEV_NAME       "leapraid_ctl"
155597088cSDongdong Hao 
165597088cSDongdong Hao /* IOCTL version. */
175597088cSDongdong Hao #define LEAPRAID_IOCTL_VERSION  0x07
185597088cSDongdong Hao 
195597088cSDongdong Hao /* IOCTL commands. */
205597088cSDongdong Hao #define LEAPRAID_ADAPTER_INFO   17
215597088cSDongdong Hao #define LEAPRAID_COMMAND        20
225597088cSDongdong Hao #define LEAPRAID_EVENTQUERY     21
235597088cSDongdong Hao #define LEAPRAID_EVENTREPORT    23
245597088cSDongdong Hao 
255597088cSDongdong Hao /**
265597088cSDongdong Hao  * struct leapraid_ioctl_header - IOCTL command header
275597088cSDongdong Hao  *
285597088cSDongdong Hao  * @adapter_id : Adapter identifier.
295597088cSDongdong Hao  * @port_number: Port identifier.
305597088cSDongdong Hao  * @max_data_size: Maximum data size for transfer.
315597088cSDongdong Hao  */
325597088cSDongdong Hao struct leapraid_ioctl_header {
335597088cSDongdong Hao 	u32 adapter_id;
345597088cSDongdong Hao 	u32 port_number;
355597088cSDongdong Hao 	u32 max_data_size;
365597088cSDongdong Hao };
375597088cSDongdong Hao 
385597088cSDongdong Hao /**
395597088cSDongdong Hao  * struct leapraid_ioctl_diag_reset - Diagnostic reset request
405597088cSDongdong Hao  *
415597088cSDongdong Hao  * @hdr: Common IOCTL header.
425597088cSDongdong Hao  */
435597088cSDongdong Hao struct leapraid_ioctl_diag_reset {
445597088cSDongdong Hao 	struct leapraid_ioctl_header hdr;
455597088cSDongdong Hao };
465597088cSDongdong Hao 
475597088cSDongdong Hao /**
485597088cSDongdong Hao  * struct leapraid_ioctl_pci_info - PCI device information
495597088cSDongdong Hao  *
505597088cSDongdong Hao  * @u: Union holding PCI bus/device/function information.
515597088cSDongdong Hao  * @u.bits.dev: PCI device number.
525597088cSDongdong Hao  * @u.bits.func: PCI function number.
535597088cSDongdong Hao  * @u.bits.bus: PCI bus number.
545597088cSDongdong Hao  * @u.word: Combined representation of PCI BDF.
555597088cSDongdong Hao  * @seg_id: PCI segment identifier.
565597088cSDongdong Hao  */
575597088cSDongdong Hao struct leapraid_ioctl_pci_info {
585597088cSDongdong Hao 	union {
595597088cSDongdong Hao 		struct {
605597088cSDongdong Hao 			u32 dev:5;
615597088cSDongdong Hao 			u32 func:3;
625597088cSDongdong Hao 			u32 bus:24;
635597088cSDongdong Hao 		} bits;
645597088cSDongdong Hao 		u32 word;
655597088cSDongdong Hao 	} u;
665597088cSDongdong Hao 	u32 seg_id;
675597088cSDongdong Hao };
685597088cSDongdong Hao 
695597088cSDongdong Hao /**
705597088cSDongdong Hao  * struct leapraid_ioctl_adapter_info - Adapter information for IOCTL
715597088cSDongdong Hao  *
725597088cSDongdong Hao  * @hdr: IOCTL header.
735597088cSDongdong Hao  * @adapter_type: Adapter type identifier.
745597088cSDongdong Hao  * @port_number: Port number.
755597088cSDongdong Hao  * @pci_id: PCI device ID.
765597088cSDongdong Hao  * @revision: Revision number.
775597088cSDongdong Hao  * @sub_dev: Subsystem device ID.
785597088cSDongdong Hao  * @sub_vendor: Subsystem vendor ID.
795597088cSDongdong Hao  * @r0: Reserved.
805597088cSDongdong Hao  * @fw_ver: Firmware version.
815597088cSDongdong Hao  * @bios_ver: BIOS version.
825597088cSDongdong Hao  * @driver_ver: Driver version.
835597088cSDongdong Hao  * @r1: Reserved.
845597088cSDongdong Hao  * @scsi_id: SCSI ID.
855597088cSDongdong Hao  * @r2: Reserved.
865597088cSDongdong Hao  * @pci_info: PCI information structure.
875597088cSDongdong Hao  */
885597088cSDongdong Hao struct leapraid_ioctl_adapter_info {
895597088cSDongdong Hao 	struct leapraid_ioctl_header hdr;
905597088cSDongdong Hao 	u32 adapter_type;
915597088cSDongdong Hao 	u32 port_number;
925597088cSDongdong Hao 	u32 pci_id;
935597088cSDongdong Hao 	u32 revision;
945597088cSDongdong Hao 	u32 sub_dev;
955597088cSDongdong Hao 	u32 sub_vendor;
965597088cSDongdong Hao 	u32 r0;
975597088cSDongdong Hao 	u32 fw_ver;
985597088cSDongdong Hao 	u32 bios_ver;
995597088cSDongdong Hao 	u8 driver_ver[32];
1005597088cSDongdong Hao 	u8 r1;
1015597088cSDongdong Hao 	u8 scsi_id;
1025597088cSDongdong Hao 	u16 r2;
1035597088cSDongdong Hao 	struct leapraid_ioctl_pci_info pci_info;
1045597088cSDongdong Hao };
1055597088cSDongdong Hao 
1065597088cSDongdong Hao /**
1075597088cSDongdong Hao  * struct leapraid_ioctl_command - IOCTL command structure
1085597088cSDongdong Hao  *
1095597088cSDongdong Hao  * @hdr: IOCTL header.
1105597088cSDongdong Hao  * @timeout: Command timeout.
1115597088cSDongdong Hao  * @rep_msg_buf_ptr: User pointer to reply message buffer.
1125597088cSDongdong Hao  * @c2h_buf_ptr: User pointer to card-to-host data buffer.
1135597088cSDongdong Hao  * @h2c_buf_ptr: User pointer to host-to-card data buffer.
1145597088cSDongdong Hao  * @sense_data_ptr: User pointer to sense data buffer.
1155597088cSDongdong Hao  * @max_rep_bytes: Maximum reply bytes.
1165597088cSDongdong Hao  * @c2h_size: Card-to-host data size.
1175597088cSDongdong Hao  * @h2c_size: Host-to-card data size.
1185597088cSDongdong Hao  * @max_sense_bytes: Maximum sense data bytes.
1195597088cSDongdong Hao  * @data_sge_offset: Data SGE offset.
1205597088cSDongdong Hao  * @mf: Message frame data (flexible array).
1215597088cSDongdong Hao  */
1225597088cSDongdong Hao struct leapraid_ioctl_command {
1235597088cSDongdong Hao 	struct leapraid_ioctl_header hdr;
1245597088cSDongdong Hao 	u32 timeout;
1255597088cSDongdong Hao 	void __user *rep_msg_buf_ptr;
1265597088cSDongdong Hao 	void __user *c2h_buf_ptr;
1275597088cSDongdong Hao 	void __user *h2c_buf_ptr;
1285597088cSDongdong Hao 	void __user *sense_data_ptr;
1295597088cSDongdong Hao 	u32 max_rep_bytes;
1305597088cSDongdong Hao 	u32 c2h_size;
1315597088cSDongdong Hao 	u32 h2c_size;
1325597088cSDongdong Hao 	u32 max_sense_bytes;
1335597088cSDongdong Hao 	u32 data_sge_offset;
1345597088cSDongdong Hao 	u8 mf[];
1355597088cSDongdong Hao };
1365597088cSDongdong Hao 
leapraid_ctl_validate_sge_offset(struct leapraid_adapter * adapter,const struct leapraid_req * req,u32 offset_bytes,size_t h2c_size,size_t c2h_size)1375597088cSDongdong Hao static int leapraid_ctl_validate_sge_offset(struct leapraid_adapter *adapter,
1385597088cSDongdong Hao 					    const struct leapraid_req *req,
1395597088cSDongdong Hao 					    u32 offset_bytes,
1405597088cSDongdong Hao 					    size_t h2c_size,
1415597088cSDongdong Hao 					    size_t c2h_size)
1425597088cSDongdong Hao {
1435597088cSDongdong Hao 	size_t sge_bytes;
1445597088cSDongdong Hao 
1455597088cSDongdong Hao 	switch (req->func) {
1465597088cSDongdong Hao 	case LEAPRAID_FUNC_SCSIIO:
1475597088cSDongdong Hao 	case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH:
1485597088cSDongdong Hao 	case LEAPRAID_FUNC_SMP_PASSTHROUGH:
1495597088cSDongdong Hao 	case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH:
1505597088cSDongdong Hao 	case LEAPRAID_FUNC_FW_DOWNLOAD:
1515597088cSDongdong Hao 	case LEAPRAID_FUNC_FW_UPLOAD:
1525597088cSDongdong Hao 		sge_bytes = LEAPRAID_IEEE_SGE64_ENTRY_SIZE;
1535597088cSDongdong Hao 		break;
1545597088cSDongdong Hao 	default:
1555597088cSDongdong Hao 		sge_bytes = adapter->adapter_attr.use_32_dma_mask ?
1565597088cSDongdong Hao 			sizeof(struct leapraid_sge_simple32) :
1575597088cSDongdong Hao 			sizeof(struct leapraid_sge_simple64);
1585597088cSDongdong Hao 		break;
1595597088cSDongdong Hao 	}
1605597088cSDongdong Hao 
1615597088cSDongdong Hao 	if (h2c_size && c2h_size)
1625597088cSDongdong Hao 		sge_bytes *= 2;
1635597088cSDongdong Hao 
1645597088cSDongdong Hao 	if (offset_bytes > LEAPRAID_REQUEST_SIZE - sge_bytes) {
1655597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
1665597088cSDongdong Hao 			"%s: Invalid offset_bytes=%u for func=0x%x\n",
1675597088cSDongdong Hao 			__func__, offset_bytes, req->func);
1685597088cSDongdong Hao 		return -EINVAL;
1695597088cSDongdong Hao 	}
1705597088cSDongdong Hao 
1715597088cSDongdong Hao 	return 0;
1725597088cSDongdong Hao }
1735597088cSDongdong Hao 
leapraid_ctl_lookup_adapter(int adapter_id,bool track_mmap)174*00b7c8d4SLinmao Li static struct leapraid_adapter *leapraid_ctl_lookup_adapter(int adapter_id,
175*00b7c8d4SLinmao Li 							    bool track_mmap)
1765597088cSDongdong Hao {
1775597088cSDongdong Hao 	struct leapraid_adapter *adapter;
1785597088cSDongdong Hao 	struct Scsi_Host *shost;
1795597088cSDongdong Hao 
1805597088cSDongdong Hao 	spin_lock(&leapraid_adapter_lock);
1815597088cSDongdong Hao 	list_for_each_entry(adapter, &leapraid_adapter_list, list) {
1825597088cSDongdong Hao 		if (adapter->adapter_attr.id == adapter_id) {
1835597088cSDongdong Hao 			if (READ_ONCE(adapter->access_ctrl.host_removing))
1845597088cSDongdong Hao 				break;
1855597088cSDongdong Hao 			shost = adapter->shost;
1865597088cSDongdong Hao 			if (!shost || !scsi_host_get(shost))
1875597088cSDongdong Hao 				break;
188*00b7c8d4SLinmao Li 			if (track_mmap)
189*00b7c8d4SLinmao Li 				atomic_inc(&adapter->fw_log_desc.mmap_refcnt);
1905597088cSDongdong Hao 			spin_unlock(&leapraid_adapter_lock);
1915597088cSDongdong Hao 			return adapter;
1925597088cSDongdong Hao 		}
1935597088cSDongdong Hao 	}
1945597088cSDongdong Hao 	spin_unlock(&leapraid_adapter_lock);
1955597088cSDongdong Hao 
1965597088cSDongdong Hao 	return NULL;
1975597088cSDongdong Hao }
1985597088cSDongdong Hao 
leapraid_ctl_put_adapter(struct leapraid_adapter * adapter)1995597088cSDongdong Hao static void leapraid_ctl_put_adapter(struct leapraid_adapter *adapter)
2005597088cSDongdong Hao {
2015597088cSDongdong Hao 	if (adapter && adapter->shost)
2025597088cSDongdong Hao 		scsi_host_put(adapter->shost);
2035597088cSDongdong Hao }
2045597088cSDongdong Hao 
leapraid_ctl_scsiio_cmd(struct leapraid_adapter * adapter,void * ctl_sp_mpi_req,u16 taskid,dma_addr_t h2c_dma_addr,size_t h2c_size,dma_addr_t c2h_dma_addr,size_t c2h_size,u16 dev_hdl,void * psge)2055597088cSDongdong Hao static void leapraid_ctl_scsiio_cmd(struct leapraid_adapter *adapter,
2065597088cSDongdong Hao 				    void *ctl_sp_mpi_req,
2075597088cSDongdong Hao 				    u16 taskid,
2085597088cSDongdong Hao 				    dma_addr_t h2c_dma_addr, size_t h2c_size,
2095597088cSDongdong Hao 				    dma_addr_t c2h_dma_addr, size_t c2h_size,
2105597088cSDongdong Hao 				    u16 dev_hdl, void *psge)
2115597088cSDongdong Hao {
2125597088cSDongdong Hao 	struct leapraid_mpi_scsiio_req *scsiio_request = ctl_sp_mpi_req;
2135597088cSDongdong Hao 
2145597088cSDongdong Hao 	scsiio_request->sense_buffer_len = SCSI_SENSE_BUFFERSIZE;
2155597088cSDongdong Hao 	scsiio_request->sense_buffer_low_add =
2165597088cSDongdong Hao 		leapraid_get_sense_buffer_dma(adapter, taskid);
2175597088cSDongdong Hao 	memset(&adapter->driver_cmds.ctl_cmd.sense,
2185597088cSDongdong Hao 	       0, SCSI_SENSE_BUFFERSIZE);
2195597088cSDongdong Hao 	leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr,
2205597088cSDongdong Hao 			       h2c_size, c2h_dma_addr, c2h_size);
2215597088cSDongdong Hao 	if (scsiio_request->func == LEAPRAID_FUNC_SCSIIO)
2225597088cSDongdong Hao 		leapraid_fire_scsi_io(adapter, taskid, dev_hdl);
2235597088cSDongdong Hao 	else
2245597088cSDongdong Hao 		leapraid_fire_task(adapter, taskid);
2255597088cSDongdong Hao }
2265597088cSDongdong Hao 
leapraid_ctl_smp_passthrough_cmd(struct leapraid_adapter * adapter,void * ctl_sp_req,u16 taskid,dma_addr_t h2c_dma_addr,size_t h2c_size,dma_addr_t c2h_dma_addr,size_t c2h_size,void * psge,void * h2c)2275597088cSDongdong Hao static int leapraid_ctl_smp_passthrough_cmd(struct leapraid_adapter *adapter,
2285597088cSDongdong Hao 					    void *ctl_sp_req,
2295597088cSDongdong Hao 					    u16 taskid,
2305597088cSDongdong Hao 					    dma_addr_t h2c_dma_addr,
2315597088cSDongdong Hao 					    size_t h2c_size,
2325597088cSDongdong Hao 					    dma_addr_t c2h_dma_addr,
2335597088cSDongdong Hao 					    size_t c2h_size,
2345597088cSDongdong Hao 					    void *psge, void *h2c)
2355597088cSDongdong Hao {
2365597088cSDongdong Hao 	struct leapraid_smp_passthrough_req *smp_pt_req = ctl_sp_req;
2375597088cSDongdong Hao 	u8 *data;
2385597088cSDongdong Hao 
2395597088cSDongdong Hao 	if (!adapter->adapter_attr.enable_mp)
2405597088cSDongdong Hao 		smp_pt_req->physical_port = LEAPRAID_DISABLE_MP_PORT_ID;
2415597088cSDongdong Hao 	if (smp_pt_req->passthrough_flg & LEAPRAID_SMP_PT_FLAG_SGL_PTR)
2425597088cSDongdong Hao 		data = (u8 *)&smp_pt_req->sgl;
2435597088cSDongdong Hao 	else
2445597088cSDongdong Hao 		data = h2c;
2455597088cSDongdong Hao 
2465597088cSDongdong Hao 	if (!(smp_pt_req->passthrough_flg & LEAPRAID_SMP_PT_FLAG_SGL_PTR) &&
2475597088cSDongdong Hao 	    h2c_size <= 10) {
2485597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
2495597088cSDongdong Hao 			"%s: Invalid request size=%zu\n",
2505597088cSDongdong Hao 			__func__, h2c_size);
2515597088cSDongdong Hao 		return -EINVAL;
2525597088cSDongdong Hao 	}
2535597088cSDongdong Hao 
2545597088cSDongdong Hao 	if (data[1] == SMP_PHY_CONTROL &&
2555597088cSDongdong Hao 	    (data[10] == SMP_PHY_CONTROL_LINK_RESET ||
2565597088cSDongdong Hao 	     data[10] == SMP_PHY_CONTROL_HARD_RESET))
2575597088cSDongdong Hao 		adapter->reset_desc.adapter_link_resetting = 1;
2585597088cSDongdong Hao 
2595597088cSDongdong Hao 	leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr,
2605597088cSDongdong Hao 			       h2c_size, c2h_dma_addr, c2h_size);
2615597088cSDongdong Hao 	leapraid_fire_task(adapter, taskid);
2625597088cSDongdong Hao 	return 0;
2635597088cSDongdong Hao }
2645597088cSDongdong Hao 
leapraid_ctl_sas_io_unit_ctrl_cmd(struct leapraid_adapter * adapter,void * ctl_sp_req,dma_addr_t h2c_dma_addr,size_t h2c_size,dma_addr_t c2h_dma_addr,size_t c2h_size,void * psge,u16 taskid)2655597088cSDongdong Hao static void leapraid_ctl_sas_io_unit_ctrl_cmd(struct leapraid_adapter *adapter,
2665597088cSDongdong Hao 					      void *ctl_sp_req,
2675597088cSDongdong Hao 					      dma_addr_t h2c_dma_addr,
2685597088cSDongdong Hao 					      size_t h2c_size,
2695597088cSDongdong Hao 					      dma_addr_t c2h_dma_addr,
2705597088cSDongdong Hao 					      size_t c2h_size,
2715597088cSDongdong Hao 					      void *psge, u16 taskid)
2725597088cSDongdong Hao {
2735597088cSDongdong Hao 	struct leapraid_sas_io_unit_ctrl_req *sas_io_unit_ctl_req = ctl_sp_req;
2745597088cSDongdong Hao 
2755597088cSDongdong Hao 	if (sas_io_unit_ctl_req->op == LEAPRAID_SAS_OP_PHY_HARD_RESET ||
2765597088cSDongdong Hao 	    sas_io_unit_ctl_req->op == LEAPRAID_SAS_OP_PHY_LINK_RESET)
2775597088cSDongdong Hao 		adapter->reset_desc.adapter_link_resetting = 1;
2785597088cSDongdong Hao 	leapraid_build_mpi_sg(adapter, psge, h2c_dma_addr,
2795597088cSDongdong Hao 			      h2c_size, c2h_dma_addr, c2h_size);
2805597088cSDongdong Hao 	leapraid_fire_task(adapter, taskid);
2815597088cSDongdong Hao }
2825597088cSDongdong Hao 
leapraid_ctl_do_command(struct leapraid_adapter * adapter,struct leapraid_ioctl_command * karg,void __user * mf)2835597088cSDongdong Hao static int leapraid_ctl_do_command(struct leapraid_adapter *adapter,
2845597088cSDongdong Hao 				   struct leapraid_ioctl_command *karg,
2855597088cSDongdong Hao 				   void __user *mf)
2865597088cSDongdong Hao {
2875597088cSDongdong Hao 	struct leapraid_req *leap_mpi_req;
2885597088cSDongdong Hao 	struct leapraid_req *ctl_sp_mpi_req;
2895597088cSDongdong Hao 	u16 taskid;
2905597088cSDongdong Hao 	void *h2c = NULL;
2915597088cSDongdong Hao 	size_t h2c_size = 0;
2925597088cSDongdong Hao 	dma_addr_t h2c_dma_addr = 0;
2935597088cSDongdong Hao 	void *c2h = NULL;
2945597088cSDongdong Hao 	size_t c2h_size = 0;
2955597088cSDongdong Hao 	dma_addr_t c2h_dma_addr = 0;
2965597088cSDongdong Hao 	void *psge;
2975597088cSDongdong Hao 	unsigned long timeout;
2985597088cSDongdong Hao 	u16 dev_hdl = LEAPRAID_INVALID_DEV_HANDLE;
2995597088cSDongdong Hao 	bool issue_reset = false;
3005597088cSDongdong Hao 	u32 data_sge_offset_bytes;
3015597088cSDongdong Hao 	u32 sz;
3025597088cSDongdong Hao 	int rc;
3035597088cSDongdong Hao 
3045597088cSDongdong Hao 	rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT,
3055597088cSDongdong Hao 					  __func__);
3065597088cSDongdong Hao 	if (rc)
3075597088cSDongdong Hao 		return rc;
3085597088cSDongdong Hao 
3095597088cSDongdong Hao 	leap_mpi_req = kzalloc(LEAPRAID_REQUEST_SIZE, GFP_KERNEL);
3105597088cSDongdong Hao 	if (!leap_mpi_req)
3115597088cSDongdong Hao 		return -ENOMEM;
3125597088cSDongdong Hao 
3135597088cSDongdong Hao 	if (karg->data_sge_offset > (UINT_MAX / LEAPRAID_SGE_OFFSET_SIZE)) {
3145597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
3155597088cSDongdong Hao 			"%s: Invalid data_sge_offset=%u\n",
3165597088cSDongdong Hao 			__func__, karg->data_sge_offset);
3175597088cSDongdong Hao 		rc = -EINVAL;
3185597088cSDongdong Hao 		goto out_cleanup;
3195597088cSDongdong Hao 	}
3205597088cSDongdong Hao 
3215597088cSDongdong Hao 	data_sge_offset_bytes = karg->data_sge_offset *
3225597088cSDongdong Hao 				LEAPRAID_SGE_OFFSET_SIZE;
3235597088cSDongdong Hao 	if (data_sge_offset_bytes > LEAPRAID_REQUEST_SIZE) {
3245597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
3255597088cSDongdong Hao 			"%s: Invalid data_sge_offset=%u\n",
3265597088cSDongdong Hao 			__func__, karg->data_sge_offset);
3275597088cSDongdong Hao 		rc = -EINVAL;
3285597088cSDongdong Hao 		goto out_cleanup;
3295597088cSDongdong Hao 	}
3305597088cSDongdong Hao 
3315597088cSDongdong Hao 	if (copy_from_user(leap_mpi_req, mf, data_sge_offset_bytes)) {
3325597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
3335597088cSDongdong Hao 			"%s: Failed to copy request message from user\n",
3345597088cSDongdong Hao 			__func__);
3355597088cSDongdong Hao 		rc = -EFAULT;
3365597088cSDongdong Hao 		goto out_cleanup;
3375597088cSDongdong Hao 	}
3385597088cSDongdong Hao 
3395597088cSDongdong Hao 	h2c_size = karg->h2c_size;
3405597088cSDongdong Hao 	c2h_size = karg->c2h_size;
3415597088cSDongdong Hao 	rc = leapraid_ctl_validate_sge_offset(adapter, leap_mpi_req,
3425597088cSDongdong Hao 					      data_sge_offset_bytes,
3435597088cSDongdong Hao 					      h2c_size, c2h_size);
3445597088cSDongdong Hao 	if (rc)
3455597088cSDongdong Hao 		goto out_cleanup;
3465597088cSDongdong Hao 
3475597088cSDongdong Hao 	taskid = adapter->driver_cmds.ctl_cmd.taskid;
3485597088cSDongdong Hao 
3495597088cSDongdong Hao 	adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_PENDING;
3505597088cSDongdong Hao 	memset(&adapter->driver_cmds.ctl_cmd.reply, 0, LEAPRAID_REPLY_SIZE);
3515597088cSDongdong Hao 	ctl_sp_mpi_req = leapraid_get_task_desc(adapter, taskid);
3525597088cSDongdong Hao 	memset(ctl_sp_mpi_req, 0, LEAPRAID_REQUEST_SIZE);
3535597088cSDongdong Hao 	memcpy(ctl_sp_mpi_req, leap_mpi_req, data_sge_offset_bytes);
3545597088cSDongdong Hao 
3555597088cSDongdong Hao 	if (ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
3565597088cSDongdong Hao 	    ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH ||
3575597088cSDongdong Hao 	    ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH) {
3585597088cSDongdong Hao 		dev_hdl = le16_to_cpu(ctl_sp_mpi_req->func_dep1);
3595597088cSDongdong Hao 		if (!dev_hdl ||
3605597088cSDongdong Hao 		    dev_hdl > adapter->adapter_attr.features.max_dev_handle) {
3615597088cSDongdong Hao 			dev_err(&adapter->pdev->dev,
3625597088cSDongdong Hao 				"%s: Invalid device handle\n", __func__);
3635597088cSDongdong Hao 			rc = -EINVAL;
3645597088cSDongdong Hao 			goto out_cleanup;
3655597088cSDongdong Hao 		}
3665597088cSDongdong Hao 	}
3675597088cSDongdong Hao 
3685597088cSDongdong Hao 	if (WARN_ON(ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSI_TMF)) {
3695597088cSDongdong Hao 		rc = -EINVAL;
3705597088cSDongdong Hao 		goto out_cleanup;
3715597088cSDongdong Hao 	}
3725597088cSDongdong Hao 
3735597088cSDongdong Hao 	if (h2c_size) {
3745597088cSDongdong Hao 		h2c = dma_alloc_coherent(&adapter->pdev->dev, h2c_size,
3755597088cSDongdong Hao 					 &h2c_dma_addr, GFP_KERNEL);
3765597088cSDongdong Hao 		if (!h2c) {
3775597088cSDongdong Hao 			rc = -ENOMEM;
3785597088cSDongdong Hao 			goto out_cleanup;
3795597088cSDongdong Hao 		}
3805597088cSDongdong Hao 		if (copy_from_user(h2c, karg->h2c_buf_ptr, h2c_size)) {
3815597088cSDongdong Hao 			dev_err(&adapter->pdev->dev,
3825597088cSDongdong Hao 				"%s: Failed to copy h2c from user\n",
3835597088cSDongdong Hao 				__func__);
3845597088cSDongdong Hao 			rc = -EFAULT;
3855597088cSDongdong Hao 			goto out_cleanup;
3865597088cSDongdong Hao 		}
3875597088cSDongdong Hao 	}
3885597088cSDongdong Hao 	if (c2h_size) {
3895597088cSDongdong Hao 		c2h = dma_alloc_coherent(&adapter->pdev->dev, c2h_size,
3905597088cSDongdong Hao 					 &c2h_dma_addr, GFP_KERNEL);
3915597088cSDongdong Hao 		if (!c2h) {
3925597088cSDongdong Hao 			rc = -ENOMEM;
3935597088cSDongdong Hao 			goto out_cleanup;
3945597088cSDongdong Hao 		}
3955597088cSDongdong Hao 	}
3965597088cSDongdong Hao 
3975597088cSDongdong Hao 	psge = (void *)ctl_sp_mpi_req + data_sge_offset_bytes;
3985597088cSDongdong Hao 	init_completion(&adapter->driver_cmds.ctl_cmd.done);
3995597088cSDongdong Hao 
4005597088cSDongdong Hao 	switch (ctl_sp_mpi_req->func) {
4015597088cSDongdong Hao 	case LEAPRAID_FUNC_SCSIIO:
4025597088cSDongdong Hao 	case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH:
4035597088cSDongdong Hao 		leapraid_ctl_scsiio_cmd(adapter, ctl_sp_mpi_req, taskid,
4045597088cSDongdong Hao 					h2c_dma_addr, h2c_size,
4055597088cSDongdong Hao 					c2h_dma_addr, c2h_size,
4065597088cSDongdong Hao 					dev_hdl, psge);
4075597088cSDongdong Hao 		break;
4085597088cSDongdong Hao 	case LEAPRAID_FUNC_SMP_PASSTHROUGH:
4095597088cSDongdong Hao 		if (!h2c) {
4105597088cSDongdong Hao 			rc = -EINVAL;
4115597088cSDongdong Hao 			goto out_cleanup;
4125597088cSDongdong Hao 		}
4135597088cSDongdong Hao 		rc = leapraid_ctl_smp_passthrough_cmd(adapter,
4145597088cSDongdong Hao 						      ctl_sp_mpi_req, taskid,
4155597088cSDongdong Hao 						      h2c_dma_addr, h2c_size,
4165597088cSDongdong Hao 						      c2h_dma_addr, c2h_size,
4175597088cSDongdong Hao 						      psge, h2c);
4185597088cSDongdong Hao 		if (rc)
4195597088cSDongdong Hao 			goto out_cleanup;
4205597088cSDongdong Hao 		break;
4215597088cSDongdong Hao 	case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH:
4225597088cSDongdong Hao 		leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size,
4235597088cSDongdong Hao 				       c2h_dma_addr, c2h_size);
4245597088cSDongdong Hao 		leapraid_fire_task(adapter, taskid);
4255597088cSDongdong Hao 		break;
4265597088cSDongdong Hao 	case LEAPRAID_FUNC_FW_DOWNLOAD:
4275597088cSDongdong Hao 	case LEAPRAID_FUNC_FW_UPLOAD:
4285597088cSDongdong Hao 		leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size,
4295597088cSDongdong Hao 				       c2h_dma_addr, c2h_size);
4305597088cSDongdong Hao 		leapraid_fire_task(adapter, taskid);
4315597088cSDongdong Hao 		break;
4325597088cSDongdong Hao 	case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL:
4335597088cSDongdong Hao 		leapraid_ctl_sas_io_unit_ctrl_cmd(adapter, ctl_sp_mpi_req,
4345597088cSDongdong Hao 						  h2c_dma_addr, h2c_size,
4355597088cSDongdong Hao 						  c2h_dma_addr, c2h_size,
4365597088cSDongdong Hao 						  psge, taskid);
4375597088cSDongdong Hao 		break;
4385597088cSDongdong Hao 	default:
4395597088cSDongdong Hao 		leapraid_build_mpi_sg(adapter, psge, h2c_dma_addr,
4405597088cSDongdong Hao 				      h2c_size, c2h_dma_addr, c2h_size);
4415597088cSDongdong Hao 		leapraid_fire_task(adapter, taskid);
4425597088cSDongdong Hao 		break;
4435597088cSDongdong Hao 	}
4445597088cSDongdong Hao 
4455597088cSDongdong Hao 	timeout = karg->timeout;
4465597088cSDongdong Hao 	if (timeout < LEAPRAID_CTL_CMD_TIMEOUT)
4475597088cSDongdong Hao 		timeout = LEAPRAID_CTL_CMD_TIMEOUT;
4485597088cSDongdong Hao 	if (!wait_for_completion_timeout(&adapter->driver_cmds.ctl_cmd.done,
4495597088cSDongdong Hao 					 timeout * HZ)) {
4505597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
4515597088cSDongdong Hao 			"%s: ctl_cmd timeout, status=0x%x\n",
4525597088cSDongdong Hao 			__func__, adapter->driver_cmds.ctl_cmd.status);
4535597088cSDongdong Hao 		leapraid_log_req_context(adapter, taskid, ctl_sp_mpi_req);
4545597088cSDongdong Hao 	}
4555597088cSDongdong Hao 
4565597088cSDongdong Hao 	if ((leap_mpi_req->func == LEAPRAID_FUNC_SMP_PASSTHROUGH ||
4575597088cSDongdong Hao 	     leap_mpi_req->func == LEAPRAID_FUNC_SAS_IO_UNIT_CTRL) &&
4585597088cSDongdong Hao 	    adapter->reset_desc.adapter_link_resetting)
4595597088cSDongdong Hao 		adapter->reset_desc.adapter_link_resetting = 0;
4605597088cSDongdong Hao 
4615597088cSDongdong Hao 	if (!(adapter->driver_cmds.ctl_cmd.status & LEAPRAID_CMD_DONE)) {
4625597088cSDongdong Hao 		issue_reset =
4635597088cSDongdong Hao 			leapraid_check_reset(
4645597088cSDongdong Hao 				adapter->driver_cmds.ctl_cmd.status);
4655597088cSDongdong Hao 		goto reset;
4665597088cSDongdong Hao 	}
4675597088cSDongdong Hao 
4685597088cSDongdong Hao 	if (c2h_size && copy_to_user(karg->c2h_buf_ptr, c2h, c2h_size)) {
4695597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
4705597088cSDongdong Hao 			"%s: Failed to copy c2h to user\n", __func__);
4715597088cSDongdong Hao 		rc = -ENODATA;
4725597088cSDongdong Hao 		goto out_cleanup;
4735597088cSDongdong Hao 	}
4745597088cSDongdong Hao 	if (karg->max_rep_bytes) {
4755597088cSDongdong Hao 		sz = min_t(u32, karg->max_rep_bytes, LEAPRAID_REPLY_SIZE);
4765597088cSDongdong Hao 		if (copy_to_user(karg->rep_msg_buf_ptr,
4775597088cSDongdong Hao 				 &adapter->driver_cmds.ctl_cmd.reply,
4785597088cSDongdong Hao 				 sz)) {
4795597088cSDongdong Hao 			dev_err(&adapter->pdev->dev,
4805597088cSDongdong Hao 				"%s: Failed to copy reply to user\n",
4815597088cSDongdong Hao 				__func__);
4825597088cSDongdong Hao 			rc = -ENODATA;
4835597088cSDongdong Hao 			goto out_cleanup;
4845597088cSDongdong Hao 		}
4855597088cSDongdong Hao 	}
4865597088cSDongdong Hao 
4875597088cSDongdong Hao 	if (karg->max_sense_bytes &&
4885597088cSDongdong Hao 	    (leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
4895597088cSDongdong Hao 	     leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH)) {
4905597088cSDongdong Hao 		if (!karg->sense_data_ptr)
4915597088cSDongdong Hao 			goto out_cleanup;
4925597088cSDongdong Hao 
4935597088cSDongdong Hao 		sz = min_t(u32, karg->max_sense_bytes, SCSI_SENSE_BUFFERSIZE);
4945597088cSDongdong Hao 		if (copy_to_user(karg->sense_data_ptr,
4955597088cSDongdong Hao 				 &adapter->driver_cmds.ctl_cmd.sense,
4965597088cSDongdong Hao 				 sz)) {
4975597088cSDongdong Hao 			dev_err(&adapter->pdev->dev,
4985597088cSDongdong Hao 				"%s: Failed to copy sense data to user\n",
4995597088cSDongdong Hao 				__func__);
5005597088cSDongdong Hao 			rc = -ENODATA;
5015597088cSDongdong Hao 			goto out_cleanup;
5025597088cSDongdong Hao 		}
5035597088cSDongdong Hao 	}
5045597088cSDongdong Hao reset:
5055597088cSDongdong Hao 	if (issue_reset) {
5065597088cSDongdong Hao 		rc = -ENODATA;
5075597088cSDongdong Hao 		if (leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
5085597088cSDongdong Hao 		    leap_mpi_req->func ==
5095597088cSDongdong Hao 		    LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH ||
5105597088cSDongdong Hao 		    leap_mpi_req->func ==
5115597088cSDongdong Hao 		    LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH) {
5125597088cSDongdong Hao 			dev_err(&adapter->pdev->dev,
5135597088cSDongdong Hao 				"Fire tgt reset, hdl=0x%04x\n",
5145597088cSDongdong Hao 				le16_to_cpu(leap_mpi_req->func_dep1));
5155597088cSDongdong Hao 			leapraid_issue_locked_tm(
5165597088cSDongdong Hao 				adapter,
5175597088cSDongdong Hao 				le16_to_cpu(leap_mpi_req->func_dep1), 0, 0, 0,
5185597088cSDongdong Hao 				LEAPRAID_TM_TASKTYPE_TARGET_RESET, taskid,
5195597088cSDongdong Hao 				LEAPRAID_TM_MSGFLAGS_LINK_RESET);
5205597088cSDongdong Hao 		} else {
5215597088cSDongdong Hao 			dev_info(&adapter->pdev->dev,
5225597088cSDongdong Hao 				 "%s:%d: call hard_reset\n",
5235597088cSDongdong Hao 				 __func__, __LINE__);
5245597088cSDongdong Hao 			leapraid_hard_reset_handler(adapter, FULL_RESET);
5255597088cSDongdong Hao 		}
5265597088cSDongdong Hao 	}
5275597088cSDongdong Hao out_cleanup:
5285597088cSDongdong Hao 	if (c2h)
5295597088cSDongdong Hao 		dma_free_coherent(&adapter->pdev->dev, c2h_size,
5305597088cSDongdong Hao 				  c2h, c2h_dma_addr);
5315597088cSDongdong Hao 	if (h2c)
5325597088cSDongdong Hao 		dma_free_coherent(&adapter->pdev->dev, h2c_size,
5335597088cSDongdong Hao 				  h2c, h2c_dma_addr);
5345597088cSDongdong Hao 	kfree(leap_mpi_req);
5355597088cSDongdong Hao 	adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_NOT_USED;
5365597088cSDongdong Hao 	return rc;
5375597088cSDongdong Hao }
5385597088cSDongdong Hao 
leapraid_ctl_get_adapter_info(struct leapraid_adapter * adapter,void __user * arg)5395597088cSDongdong Hao static int leapraid_ctl_get_adapter_info(struct leapraid_adapter *adapter,
5405597088cSDongdong Hao 					 void __user *arg)
5415597088cSDongdong Hao {
5425597088cSDongdong Hao 	struct leapraid_ioctl_adapter_info *karg;
5435597088cSDongdong Hao 	u8 revision;
5445597088cSDongdong Hao 	int rc = 0;
5455597088cSDongdong Hao 
5465597088cSDongdong Hao 	karg = kzalloc_obj(*karg);
5475597088cSDongdong Hao 	if (!karg)
5485597088cSDongdong Hao 		return -ENOMEM;
5495597088cSDongdong Hao 
5505597088cSDongdong Hao 	pci_read_config_byte(adapter->pdev, PCI_CLASS_REVISION, &revision);
5515597088cSDongdong Hao 	karg->revision = revision;
5525597088cSDongdong Hao 	karg->pci_id = adapter->pdev->device;
5535597088cSDongdong Hao 	karg->sub_dev = adapter->pdev->subsystem_device;
5545597088cSDongdong Hao 	karg->sub_vendor = adapter->pdev->subsystem_vendor;
5555597088cSDongdong Hao 	karg->pci_info.u.bits.bus = adapter->pdev->bus->number;
5565597088cSDongdong Hao 	karg->pci_info.u.bits.dev = PCI_SLOT(adapter->pdev->devfn);
5575597088cSDongdong Hao 	karg->pci_info.u.bits.func = PCI_FUNC(adapter->pdev->devfn);
5585597088cSDongdong Hao 	karg->pci_info.seg_id = pci_domain_nr(adapter->pdev->bus);
5595597088cSDongdong Hao 	karg->fw_ver = adapter->adapter_attr.features.fw_version;
5605597088cSDongdong Hao 
5615597088cSDongdong Hao 	snprintf(karg->driver_ver, sizeof(karg->driver_ver), "%s-%s",
5625597088cSDongdong Hao 		 LEAPRAID_DRIVER_NAME, LEAPRAID_DRIVER_VERSION);
5635597088cSDongdong Hao 
5645597088cSDongdong Hao 	karg->adapter_type = LEAPRAID_IOCTL_VERSION;
5655597088cSDongdong Hao 	karg->bios_ver = adapter->adapter_attr.bios_version;
5665597088cSDongdong Hao 	if (copy_to_user(arg, karg,
5675597088cSDongdong Hao 			 sizeof(struct leapraid_ioctl_adapter_info))) {
5685597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
5695597088cSDongdong Hao 			"%s: Failed to copy info to user\n", __func__);
5705597088cSDongdong Hao 		rc = -EFAULT;
5715597088cSDongdong Hao 		goto free_buf;
5725597088cSDongdong Hao 	}
5735597088cSDongdong Hao 
5745597088cSDongdong Hao free_buf:
5755597088cSDongdong Hao 	kfree(karg);
5765597088cSDongdong Hao 	return rc;
5775597088cSDongdong Hao }
5785597088cSDongdong Hao 
leapraid_ctl_ioctl_main(struct file * file,unsigned int cmd,void __user * arg)5795597088cSDongdong Hao static int leapraid_ctl_ioctl_main(struct file *file, unsigned int cmd,
5805597088cSDongdong Hao 				   void __user *arg)
5815597088cSDongdong Hao {
5825597088cSDongdong Hao 	struct leapraid_ioctl_header ioctl_header;
5835597088cSDongdong Hao 	struct leapraid_adapter *adapter = NULL;
5845597088cSDongdong Hao 	struct leapraid_ioctl_command __user *uarg;
5855597088cSDongdong Hao 	struct leapraid_ioctl_command karg;
5865597088cSDongdong Hao 	int rc = -ENOIOCTLCMD;
5875597088cSDongdong Hao 
5885597088cSDongdong Hao 	if (copy_from_user(&ioctl_header, arg,
5895597088cSDongdong Hao 			   sizeof(struct leapraid_ioctl_header))) {
5905597088cSDongdong Hao 		pr_err("%s:%s: Failed to copy header from user\n",
5915597088cSDongdong Hao 		       LEAPRAID_DRIVER_NAME, __func__);
5925597088cSDongdong Hao 		return -EFAULT;
5935597088cSDongdong Hao 	}
5945597088cSDongdong Hao 
595*00b7c8d4SLinmao Li 	adapter = leapraid_ctl_lookup_adapter(ioctl_header.adapter_id, false);
5965597088cSDongdong Hao 	if (!adapter)
5975597088cSDongdong Hao 		return -EFAULT;
5985597088cSDongdong Hao 
5995597088cSDongdong Hao 	if (atomic_read(&adapter->overheat_desc.thermal_alert)) {
6005597088cSDongdong Hao 		dev_warn(&adapter->pdev->dev,
6015597088cSDongdong Hao 			 "%s: Failed, thermal_alert=%d\n",
6025597088cSDongdong Hao 			 __func__,
6035597088cSDongdong Hao 			 atomic_read(&adapter->overheat_desc.thermal_alert));
6045597088cSDongdong Hao 		rc = -EFAULT;
6055597088cSDongdong Hao 		goto out_put;
6065597088cSDongdong Hao 	}
6075597088cSDongdong Hao 
6085597088cSDongdong Hao 	mutex_lock(&adapter->access_ctrl.pci_access_lock);
6095597088cSDongdong Hao 
6105597088cSDongdong Hao 	rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_LONG,
6115597088cSDongdong Hao 					  __func__);
6125597088cSDongdong Hao 	if (rc)
6135597088cSDongdong Hao 		goto unlock;
6145597088cSDongdong Hao 
6155597088cSDongdong Hao 	if (!wait_event_timeout(adapter->access_ctrl.shost_recover_wq,
6165597088cSDongdong Hao 				!adapter->access_ctrl.shost_recover_async,
6175597088cSDongdong Hao 				LEAPRAID_WAIT_SHOST_RECOVERY * HZ)) {
6185597088cSDongdong Hao 		dev_warn(&adapter->pdev->dev,
6195597088cSDongdong Hao 			 "Timeout waiting for shost recovery async\n");
6205597088cSDongdong Hao 		rc = -EAGAIN;
6215597088cSDongdong Hao 		goto unlock;
6225597088cSDongdong Hao 	}
6235597088cSDongdong Hao 
6245597088cSDongdong Hao 	if (adapter->access_ctrl.pcie_recovering ||
6255597088cSDongdong Hao 	    adapter->scan_dev_desc.driver_loading ||
6265597088cSDongdong Hao 	    adapter->access_ctrl.host_removing) {
6275597088cSDongdong Hao 		rc = -EAGAIN;
6285597088cSDongdong Hao 		goto unlock;
6295597088cSDongdong Hao 	}
6305597088cSDongdong Hao 
6315597088cSDongdong Hao 	if (file->f_flags & O_NONBLOCK) {
6325597088cSDongdong Hao 		if (!mutex_trylock(&adapter->driver_cmds.ctl_cmd.mutex)) {
6335597088cSDongdong Hao 			rc = -EAGAIN;
6345597088cSDongdong Hao 			goto unlock;
6355597088cSDongdong Hao 		}
6365597088cSDongdong Hao 	} else if (mutex_lock_interruptible(&adapter->driver_cmds
6375597088cSDongdong Hao 					    .ctl_cmd.mutex)) {
6385597088cSDongdong Hao 		rc = -ERESTARTSYS;
6395597088cSDongdong Hao 		goto unlock;
6405597088cSDongdong Hao 	}
6415597088cSDongdong Hao 
6425597088cSDongdong Hao 	switch (_IOC_NR(cmd)) {
6435597088cSDongdong Hao 	case LEAPRAID_ADAPTER_INFO:
6445597088cSDongdong Hao 		if (_IOC_SIZE(cmd) ==
6455597088cSDongdong Hao 		    sizeof(struct leapraid_ioctl_adapter_info))
6465597088cSDongdong Hao 			rc = leapraid_ctl_get_adapter_info(adapter, arg);
6475597088cSDongdong Hao 		break;
6485597088cSDongdong Hao 	case LEAPRAID_COMMAND:
6495597088cSDongdong Hao 		if (copy_from_user(&karg, arg, sizeof(karg))) {
6505597088cSDongdong Hao 			dev_err(&adapter->pdev->dev,
6515597088cSDongdong Hao 				"%s: Failed to copy data from user\n",
6525597088cSDongdong Hao 				__func__);
6535597088cSDongdong Hao 			rc = -EFAULT;
6545597088cSDongdong Hao 			break;
6555597088cSDongdong Hao 		}
6565597088cSDongdong Hao 
6575597088cSDongdong Hao 		if (karg.hdr.adapter_id != ioctl_header.adapter_id) {
6585597088cSDongdong Hao 			rc = -EINVAL;
6595597088cSDongdong Hao 			break;
6605597088cSDongdong Hao 		}
6615597088cSDongdong Hao 
6625597088cSDongdong Hao 		if (_IOC_SIZE(cmd) == sizeof(struct leapraid_ioctl_command)) {
6635597088cSDongdong Hao 			uarg = arg;
6645597088cSDongdong Hao 			rc = leapraid_ctl_do_command(adapter, &karg,
6655597088cSDongdong Hao 						     &uarg->mf);
6665597088cSDongdong Hao 			if (rc)
6675597088cSDongdong Hao 				dev_warn(&adapter->pdev->dev,
6685597088cSDongdong Hao 					 "%s: IOCTL cmd failed rc=%d\n",
6695597088cSDongdong Hao 					 __func__, rc);
6705597088cSDongdong Hao 		}
6715597088cSDongdong Hao 		break;
6725597088cSDongdong Hao 	case LEAPRAID_EVENTQUERY:
6735597088cSDongdong Hao 	case LEAPRAID_EVENTREPORT:
6745597088cSDongdong Hao 		rc = 0;
6755597088cSDongdong Hao 		break;
6765597088cSDongdong Hao 	default:
6775597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
6785597088cSDongdong Hao 			"Unknown IOCTL opcode=0x%08x\n", cmd);
6795597088cSDongdong Hao 		break;
6805597088cSDongdong Hao 	}
6815597088cSDongdong Hao 	mutex_unlock(&adapter->driver_cmds.ctl_cmd.mutex);
6825597088cSDongdong Hao 
6835597088cSDongdong Hao unlock:
6845597088cSDongdong Hao 	mutex_unlock(&adapter->access_ctrl.pci_access_lock);
6855597088cSDongdong Hao out_put:
6865597088cSDongdong Hao 	leapraid_ctl_put_adapter(adapter);
6875597088cSDongdong Hao 	return rc;
6885597088cSDongdong Hao }
6895597088cSDongdong Hao 
leapraid_ctl_ioctl(struct file * file,unsigned int cmd,unsigned long arg)6905597088cSDongdong Hao static long leapraid_ctl_ioctl(struct file *file, unsigned int cmd,
6915597088cSDongdong Hao 			       unsigned long arg)
6925597088cSDongdong Hao {
6935597088cSDongdong Hao 	return leapraid_ctl_ioctl_main(file, cmd, (void __user *)arg);
6945597088cSDongdong Hao }
6955597088cSDongdong Hao 
leapraid_fw_mmap_open(struct vm_area_struct * vma)6965597088cSDongdong Hao static void leapraid_fw_mmap_open(struct vm_area_struct *vma)
6975597088cSDongdong Hao {
6985597088cSDongdong Hao 	struct leapraid_adapter *adapter = vma->vm_private_data;
6995597088cSDongdong Hao 
7005597088cSDongdong Hao 	if (!adapter)
7015597088cSDongdong Hao 		return;
7025597088cSDongdong Hao 
703970f69b6SLinmao Li 	get_device(&adapter->shost->shost_gendev);
7045597088cSDongdong Hao 	atomic_inc(&adapter->fw_log_desc.mmap_refcnt);
7055597088cSDongdong Hao }
7065597088cSDongdong Hao 
leapraid_fw_mmap_close(struct vm_area_struct * vma)7075597088cSDongdong Hao static void leapraid_fw_mmap_close(struct vm_area_struct *vma)
7085597088cSDongdong Hao {
7095597088cSDongdong Hao 	struct leapraid_adapter *adapter = vma->vm_private_data;
7105597088cSDongdong Hao 
7115597088cSDongdong Hao 	if (!adapter)
7125597088cSDongdong Hao 		return;
7135597088cSDongdong Hao 
7145597088cSDongdong Hao 	if (atomic_dec_and_test(&adapter->fw_log_desc.mmap_refcnt))
7155597088cSDongdong Hao 		wake_up(&adapter->fw_log_desc.mmap_waitq);
7165597088cSDongdong Hao 	leapraid_ctl_put_adapter(adapter);
7175597088cSDongdong Hao }
7185597088cSDongdong Hao 
7195597088cSDongdong Hao static const struct vm_operations_struct leapraid_fw_mmap_vm_ops = {
7205597088cSDongdong Hao 	.open = leapraid_fw_mmap_open,
7215597088cSDongdong Hao 	.close = leapraid_fw_mmap_close,
7225597088cSDongdong Hao };
7235597088cSDongdong Hao 
leapraid_fw_mmap(struct file * filp,struct vm_area_struct * vma)7245597088cSDongdong Hao static int leapraid_fw_mmap(struct file *filp, struct vm_area_struct *vma)
7255597088cSDongdong Hao {
7265597088cSDongdong Hao 	struct leapraid_adapter *adapter = NULL;
7275597088cSDongdong Hao 	/* Userspace passes the adapter ID via vma->vm_pgoff. */
7285597088cSDongdong Hao 	u32 adapter_id = vma->vm_pgoff;
7295597088cSDongdong Hao 	unsigned long length;
7305597088cSDongdong Hao 	int rc = -EINVAL;
7315597088cSDongdong Hao 
7325597088cSDongdong Hao 	length = vma->vm_end - vma->vm_start;
7335597088cSDongdong Hao 
734*00b7c8d4SLinmao Li 	adapter = leapraid_ctl_lookup_adapter(adapter_id, true);
7355597088cSDongdong Hao 	if (!adapter) {
7365597088cSDongdong Hao 		pr_err("%s: No adapter found!\n", __func__);
7375597088cSDongdong Hao 		return -EINVAL;
7385597088cSDongdong Hao 	}
7395597088cSDongdong Hao 
7405597088cSDongdong Hao 	if (READ_ONCE(adapter->access_ctrl.host_removing)) {
7415597088cSDongdong Hao 		rc = -EAGAIN;
7425597088cSDongdong Hao 		goto out_put;
7435597088cSDongdong Hao 	}
7445597088cSDongdong Hao 
7455597088cSDongdong Hao 	if (length > (LEAPRAID_SYS_LOG_BUF_SIZE +
7465597088cSDongdong Hao 		      LEAPRAID_SYS_LOG_BUF_RESERVE)) {
7475597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
7485597088cSDongdong Hao 			"Requested mapping size is too large!\n");
7495597088cSDongdong Hao 		rc = -EINVAL;
7505597088cSDongdong Hao 		goto out_put;
7515597088cSDongdong Hao 	}
7525597088cSDongdong Hao 
7535597088cSDongdong Hao 	if (!adapter->fw_log_desc.fw_log_buffer) {
7545597088cSDongdong Hao 		dev_err(&adapter->pdev->dev, "No log buffer!\n");
7555597088cSDongdong Hao 		rc = -EINVAL;
7565597088cSDongdong Hao 		goto out_put;
7575597088cSDongdong Hao 	}
7585597088cSDongdong Hao 
7595597088cSDongdong Hao 	vma->vm_pgoff = 0;
7605597088cSDongdong Hao 
7615597088cSDongdong Hao 	rc = dma_mmap_coherent(&adapter->pdev->dev, vma,
7625597088cSDongdong Hao 			       adapter->fw_log_desc.fw_log_buffer,
7635597088cSDongdong Hao 			       adapter->fw_log_desc.fw_log_buffer_dma,
7645597088cSDongdong Hao 			       length);
7655597088cSDongdong Hao 	if (rc) {
7665597088cSDongdong Hao 		dev_err(&adapter->pdev->dev,
7675597088cSDongdong Hao 			"Failed to map memory to user space!\n");
7685597088cSDongdong Hao 		goto out_put;
7695597088cSDongdong Hao 	}
7705597088cSDongdong Hao 
7715597088cSDongdong Hao 	vma->vm_private_data = adapter;
7725597088cSDongdong Hao 	vma->vm_ops = &leapraid_fw_mmap_vm_ops;
7735597088cSDongdong Hao 	leapraid_fw_mmap_open(vma);
7745597088cSDongdong Hao 
7755597088cSDongdong Hao 	rc = 0;
7765597088cSDongdong Hao out_put:
777*00b7c8d4SLinmao Li 	if (adapter &&
778*00b7c8d4SLinmao Li 	    atomic_dec_and_test(&adapter->fw_log_desc.mmap_refcnt))
779*00b7c8d4SLinmao Li 		wake_up(&adapter->fw_log_desc.mmap_waitq);
7805597088cSDongdong Hao 	leapraid_ctl_put_adapter(adapter);
7815597088cSDongdong Hao 	return rc;
7825597088cSDongdong Hao }
7835597088cSDongdong Hao 
7845597088cSDongdong Hao static const struct file_operations leapraid_ctl_fops = {
7855597088cSDongdong Hao 	.owner = THIS_MODULE,
7865597088cSDongdong Hao 	.unlocked_ioctl = leapraid_ctl_ioctl,
7875597088cSDongdong Hao 	.mmap = leapraid_fw_mmap,
7885597088cSDongdong Hao };
7895597088cSDongdong Hao 
7905597088cSDongdong Hao static struct miscdevice leapraid_ctl_dev = {
7915597088cSDongdong Hao 	.minor = MISC_DYNAMIC_MINOR,
7925597088cSDongdong Hao 	.name = LEAPRAID_DEV_NAME,
7935597088cSDongdong Hao 	.fops = &leapraid_ctl_fops,
7945597088cSDongdong Hao };
7955597088cSDongdong Hao 
leapraid_ctl_init(void)7965597088cSDongdong Hao int leapraid_ctl_init(void)
7975597088cSDongdong Hao {
7985597088cSDongdong Hao 	if (misc_register(&leapraid_ctl_dev) < 0) {
7995597088cSDongdong Hao 		pr_err("%s Can't register misc device\n",
8005597088cSDongdong Hao 		       LEAPRAID_DRIVER_NAME);
8015597088cSDongdong Hao 		return -ENODEV;
8025597088cSDongdong Hao 	}
8035597088cSDongdong Hao 	return 0;
8045597088cSDongdong Hao }
8055597088cSDongdong Hao 
leapraid_ctl_exit(void)8065597088cSDongdong Hao void leapraid_ctl_exit(void)
8075597088cSDongdong Hao {
8085597088cSDongdong Hao 	misc_deregister(&leapraid_ctl_dev);
8095597088cSDongdong Hao }
810