xref: /freebsd/sys/dev/smartpqi/smartpqi_ioctl.c (revision 1e66f787)
11e66f787SSean Bruno /*-
21e66f787SSean Bruno  * Copyright (c) 2018 Microsemi Corporation.
31e66f787SSean Bruno  * All rights reserved.
41e66f787SSean Bruno  *
51e66f787SSean Bruno  * Redistribution and use in source and binary forms, with or without
61e66f787SSean Bruno  * modification, are permitted provided that the following conditions
71e66f787SSean Bruno  * are met:
81e66f787SSean Bruno  * 1. Redistributions of source code must retain the above copyright
91e66f787SSean Bruno  *    notice, this list of conditions and the following disclaimer.
101e66f787SSean Bruno  * 2. Redistributions in binary form must reproduce the above copyright
111e66f787SSean Bruno  *    notice, this list of conditions and the following disclaimer in the
121e66f787SSean Bruno  *    documentation and/or other materials provided with the distribution.
131e66f787SSean Bruno  *
141e66f787SSean Bruno  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151e66f787SSean Bruno  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161e66f787SSean Bruno  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171e66f787SSean Bruno  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181e66f787SSean Bruno  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191e66f787SSean Bruno  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201e66f787SSean Bruno  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211e66f787SSean Bruno  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221e66f787SSean Bruno  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231e66f787SSean Bruno  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241e66f787SSean Bruno  * SUCH DAMAGE.
251e66f787SSean Bruno  */
261e66f787SSean Bruno 
271e66f787SSean Bruno /* $FreeBSD$ */
281e66f787SSean Bruno 
291e66f787SSean Bruno /*
301e66f787SSean Bruno  * Management interface for smartpqi driver
311e66f787SSean Bruno  */
321e66f787SSean Bruno 
331e66f787SSean Bruno #include "smartpqi_includes.h"
341e66f787SSean Bruno 
351e66f787SSean Bruno /*
361e66f787SSean Bruno  * Wrapper function to copy to user from kernel
371e66f787SSean Bruno  */
381e66f787SSean Bruno int os_copy_to_user(struct pqisrc_softstate *softs, void *dest_buf,
391e66f787SSean Bruno 		void *src_buf, int size, int mode)
401e66f787SSean Bruno {
411e66f787SSean Bruno 	return(copyout(src_buf, dest_buf, size));
421e66f787SSean Bruno }
431e66f787SSean Bruno 
441e66f787SSean Bruno /*
451e66f787SSean Bruno  * Wrapper function to copy from user to kernel
461e66f787SSean Bruno  */
471e66f787SSean Bruno int os_copy_from_user(struct pqisrc_softstate *softs, void *dest_buf,
481e66f787SSean Bruno 		void *src_buf, int size, int mode)
491e66f787SSean Bruno {
501e66f787SSean Bruno 	return(copyin(src_buf, dest_buf, size));
511e66f787SSean Bruno }
521e66f787SSean Bruno 
531e66f787SSean Bruno /*
541e66f787SSean Bruno  * Device open function for ioctl entry
551e66f787SSean Bruno  */
561e66f787SSean Bruno static int smartpqi_open(struct cdev *cdev, int flags, int devtype,
571e66f787SSean Bruno 		struct thread *td)
581e66f787SSean Bruno {
591e66f787SSean Bruno 	int error = PQI_STATUS_SUCCESS;
601e66f787SSean Bruno 
611e66f787SSean Bruno 	return error;
621e66f787SSean Bruno }
631e66f787SSean Bruno 
641e66f787SSean Bruno /*
651e66f787SSean Bruno  * Device close function for ioctl entry
661e66f787SSean Bruno  */
671e66f787SSean Bruno static int smartpqi_close(struct cdev *cdev, int flags, int devtype,
681e66f787SSean Bruno 		struct thread *td)
691e66f787SSean Bruno {
701e66f787SSean Bruno 	int error = PQI_STATUS_SUCCESS;
711e66f787SSean Bruno 
721e66f787SSean Bruno 	return error;
731e66f787SSean Bruno }
741e66f787SSean Bruno 
751e66f787SSean Bruno /*
761e66f787SSean Bruno  * ioctl for getting driver info
771e66f787SSean Bruno  */
781e66f787SSean Bruno static void smartpqi_get_driver_info_ioctl(caddr_t udata, struct cdev *cdev)
791e66f787SSean Bruno {
801e66f787SSean Bruno 	struct pqisrc_softstate *softs = cdev->si_drv1;
811e66f787SSean Bruno 	pdriver_info driver_info = (pdriver_info)udata;
821e66f787SSean Bruno 
831e66f787SSean Bruno 	DBG_FUNC("IN udata = %p cdev = %p\n", udata, cdev);
841e66f787SSean Bruno 
851e66f787SSean Bruno 	driver_info->major_version = PQISRC_DRIVER_MAJOR;
861e66f787SSean Bruno 	driver_info->minor_version = PQISRC_DRIVER_MINOR;
871e66f787SSean Bruno 	driver_info->release_version = PQISRC_DRIVER_RELEASE;
881e66f787SSean Bruno 	driver_info->build_revision = PQISRC_DRIVER_REVISION;
891e66f787SSean Bruno 	driver_info->max_targets = PQI_MAX_DEVICES - 1;
901e66f787SSean Bruno 	driver_info->max_io = softs->max_io_for_scsi_ml;
911e66f787SSean Bruno 	driver_info->max_transfer_length = softs->pqi_cap.max_transfer_size;
921e66f787SSean Bruno 
931e66f787SSean Bruno 	DBG_FUNC("OUT\n");
941e66f787SSean Bruno }
951e66f787SSean Bruno 
961e66f787SSean Bruno /*
971e66f787SSean Bruno  * ioctl for getting controller info
981e66f787SSean Bruno  */
991e66f787SSean Bruno static void smartpqi_get_pci_info_ioctl(caddr_t udata, struct cdev *cdev)
1001e66f787SSean Bruno {
1011e66f787SSean Bruno 	struct pqisrc_softstate *softs = cdev->si_drv1;
1021e66f787SSean Bruno 	device_t dev = softs->os_specific.pqi_dev;
1031e66f787SSean Bruno 	pqi_pci_info_t *pci_info = (pqi_pci_info_t *)udata;
1041e66f787SSean Bruno 	uint32_t sub_vendor = 0;
1051e66f787SSean Bruno 	uint32_t sub_device = 0;
1061e66f787SSean Bruno 	uint32_t vendor = 0;
1071e66f787SSean Bruno 	uint32_t device = 0;
1081e66f787SSean Bruno 
1091e66f787SSean Bruno 	DBG_FUNC("IN udata = %p cdev = %p\n", udata, cdev);
1101e66f787SSean Bruno 
1111e66f787SSean Bruno 	pci_info->bus = pci_get_bus(dev);
1121e66f787SSean Bruno 	pci_info->dev_fn = pci_get_function(dev);
1131e66f787SSean Bruno 	pci_info->domain = pci_get_domain(dev);
1141e66f787SSean Bruno 	sub_vendor = pci_read_config(dev, PCIR_SUBVEND_0, 2);
1151e66f787SSean Bruno 	sub_device = pci_read_config(dev, PCIR_SUBDEV_0, 2);
1161e66f787SSean Bruno 	pci_info->board_id = ((sub_device << 16) & 0xffff0000) | sub_vendor;
1171e66f787SSean Bruno 	vendor = pci_get_vendor(dev);
1181e66f787SSean Bruno 	device =  pci_get_device(dev);
1191e66f787SSean Bruno 	pci_info->chip_id = ((device << 16) & 0xffff0000) | vendor;
1201e66f787SSean Bruno 	DBG_FUNC("OUT\n");
1211e66f787SSean Bruno }
1221e66f787SSean Bruno 
1231e66f787SSean Bruno 
1241e66f787SSean Bruno /*
1251e66f787SSean Bruno  * ioctl entry point for user
1261e66f787SSean Bruno  */
1271e66f787SSean Bruno static int smartpqi_ioctl(struct cdev *cdev, u_long cmd, caddr_t udata,
1281e66f787SSean Bruno 		int flags, struct thread *td)
1291e66f787SSean Bruno {
1301e66f787SSean Bruno 	int error = PQI_STATUS_SUCCESS;
1311e66f787SSean Bruno 	struct pqisrc_softstate *softs = cdev->si_drv1;
1321e66f787SSean Bruno 
1331e66f787SSean Bruno 	DBG_FUNC("IN cmd = 0x%lx udata = %p cdev = %p\n", cmd, udata, cdev);
1341e66f787SSean Bruno 
1351e66f787SSean Bruno 	if (!udata) {
1361e66f787SSean Bruno 		DBG_ERR("udata is null !!\n");
1371e66f787SSean Bruno 	}
1381e66f787SSean Bruno 
1391e66f787SSean Bruno 	if (pqisrc_ctrl_offline(softs)){
1401e66f787SSean Bruno 		DBG_ERR("Controller s offline !!\n");
1411e66f787SSean Bruno 		return ENOTTY;
1421e66f787SSean Bruno 	}
1431e66f787SSean Bruno 
1441e66f787SSean Bruno 	switch (cmd) {
1451e66f787SSean Bruno 		case CCISS_GETDRIVVER:
1461e66f787SSean Bruno 			smartpqi_get_driver_info_ioctl(udata, cdev);
1471e66f787SSean Bruno 			break;
1481e66f787SSean Bruno 		case CCISS_GETPCIINFO:
1491e66f787SSean Bruno 			smartpqi_get_pci_info_ioctl(udata, cdev);
1501e66f787SSean Bruno 			break;
1511e66f787SSean Bruno 		case SMARTPQI_PASS_THRU:
1521e66f787SSean Bruno 		case CCISS_PASSTHRU:
1531e66f787SSean Bruno 			error = pqisrc_passthru_ioctl(softs, udata, 0);
1541e66f787SSean Bruno 			error = PQI_STATUS_SUCCESS;
1551e66f787SSean Bruno 			break;
1561e66f787SSean Bruno 		case CCISS_REGNEWD:
1571e66f787SSean Bruno 			error = pqisrc_scan_devices(softs);
1581e66f787SSean Bruno 			break;
1591e66f787SSean Bruno 		default:
1601e66f787SSean Bruno 			DBG_WARN( "!IOCTL cmd 0x%lx not supported", cmd);
1611e66f787SSean Bruno 			error = ENOTTY;
1621e66f787SSean Bruno 			break;
1631e66f787SSean Bruno 	}
1641e66f787SSean Bruno 
1651e66f787SSean Bruno 	DBG_FUNC("OUT error = %d\n", error);
1661e66f787SSean Bruno 	return error;
1671e66f787SSean Bruno }
1681e66f787SSean Bruno 
1691e66f787SSean Bruno static d_open_t         smartpqi_open;
1701e66f787SSean Bruno static d_ioctl_t        smartpqi_ioctl;
1711e66f787SSean Bruno static d_close_t        smartpqi_close;
1721e66f787SSean Bruno 
1731e66f787SSean Bruno static struct cdevsw smartpqi_cdevsw =
1741e66f787SSean Bruno {
1751e66f787SSean Bruno 	.d_version = D_VERSION,
1761e66f787SSean Bruno 	.d_open    = smartpqi_open,
1771e66f787SSean Bruno 	.d_close   = smartpqi_close,
1781e66f787SSean Bruno 	.d_ioctl   = smartpqi_ioctl,
1791e66f787SSean Bruno 	.d_name    = "smartpqi",
1801e66f787SSean Bruno };
1811e66f787SSean Bruno 
1821e66f787SSean Bruno /*
1831e66f787SSean Bruno  * Function to create device node for ioctl
1841e66f787SSean Bruno  */
1851e66f787SSean Bruno int create_char_dev(struct pqisrc_softstate *softs, int card_index)
1861e66f787SSean Bruno {
1871e66f787SSean Bruno 	int error = PQI_STATUS_SUCCESS;
1881e66f787SSean Bruno 
1891e66f787SSean Bruno 	DBG_FUNC("IN idx = %d\n", card_index);
1901e66f787SSean Bruno 
1911e66f787SSean Bruno 	softs->os_specific.cdev = make_dev(&smartpqi_cdevsw, card_index,
1921e66f787SSean Bruno 				UID_ROOT, GID_OPERATOR, 0640,
1931e66f787SSean Bruno 				"smartpqi%u", card_index);
1941e66f787SSean Bruno 	if(softs->os_specific.cdev) {
1951e66f787SSean Bruno 		softs->os_specific.cdev->si_drv1 = softs;
1961e66f787SSean Bruno 	} else {
1971e66f787SSean Bruno 		error = PQI_STATUS_FAILURE;
1981e66f787SSean Bruno 	}
1991e66f787SSean Bruno 
2001e66f787SSean Bruno 	DBG_FUNC("OUT error = %d\n", error);
2011e66f787SSean Bruno 	return error;
2021e66f787SSean Bruno }
2031e66f787SSean Bruno 
2041e66f787SSean Bruno /*
2051e66f787SSean Bruno  * Function to destroy device node for ioctl
2061e66f787SSean Bruno  */
2071e66f787SSean Bruno void destroy_char_dev(struct pqisrc_softstate *softs)
2081e66f787SSean Bruno {
2091e66f787SSean Bruno 	DBG_FUNC("IN\n");
2101e66f787SSean Bruno 	if (softs->os_specific.cdev) {
2111e66f787SSean Bruno 		destroy_dev(softs->os_specific.cdev);
2121e66f787SSean Bruno 		softs->os_specific.cdev = NULL;
2131e66f787SSean Bruno 	}
2141e66f787SSean Bruno 	DBG_FUNC("OUT\n");
2151e66f787SSean Bruno }
2161e66f787SSean Bruno 
2171e66f787SSean Bruno /*
2181e66f787SSean Bruno  * Function used to send passthru commands to adapter
2191e66f787SSean Bruno  * to support management tools. For eg. ssacli, sscon.
2201e66f787SSean Bruno  */
2211e66f787SSean Bruno int
2221e66f787SSean Bruno pqisrc_passthru_ioctl(struct pqisrc_softstate *softs, void *arg, int mode)
2231e66f787SSean Bruno {
2241e66f787SSean Bruno 	int ret = PQI_STATUS_SUCCESS;
2251e66f787SSean Bruno 	char *drv_buf = NULL;
2261e66f787SSean Bruno 	uint32_t tag = 0;
2271e66f787SSean Bruno 	IOCTL_Command_struct *iocommand = (IOCTL_Command_struct *)arg;
2281e66f787SSean Bruno 	dma_mem_t ioctl_dma_buf;
2291e66f787SSean Bruno 	pqisrc_raid_req_t request;
2301e66f787SSean Bruno 	raid_path_error_info_elem_t error_info;
2311e66f787SSean Bruno 	ib_queue_t *ib_q = &softs->op_raid_ib_q[PQI_DEFAULT_IB_QUEUE];
2321e66f787SSean Bruno 	ob_queue_t *ob_q = &softs->op_ob_q[PQI_DEFAULT_IB_QUEUE];
2331e66f787SSean Bruno 	rcb_t *rcb = NULL;
2341e66f787SSean Bruno 
2351e66f787SSean Bruno 	memset(&request, 0, sizeof(request));
2361e66f787SSean Bruno 	memset(&error_info, 0, sizeof(error_info));
2371e66f787SSean Bruno 
2381e66f787SSean Bruno 	DBG_FUNC("IN");
2391e66f787SSean Bruno 
2401e66f787SSean Bruno 	if (pqisrc_ctrl_offline(softs))
2411e66f787SSean Bruno 		return PQI_STATUS_FAILURE;
2421e66f787SSean Bruno 
2431e66f787SSean Bruno 	if (!arg)
2441e66f787SSean Bruno 		return (PQI_STATUS_FAILURE);
2451e66f787SSean Bruno 
2461e66f787SSean Bruno 	if (iocommand->buf_size < 1 &&
2471e66f787SSean Bruno 		iocommand->Request.Type.Direction != PQIIOCTL_NONE)
2481e66f787SSean Bruno 		return PQI_STATUS_FAILURE;
2491e66f787SSean Bruno 	if (iocommand->Request.CDBLen > sizeof(request.cdb))
2501e66f787SSean Bruno 		return PQI_STATUS_FAILURE;
2511e66f787SSean Bruno 
2521e66f787SSean Bruno 	switch (iocommand->Request.Type.Direction) {
2531e66f787SSean Bruno 		case PQIIOCTL_NONE:
2541e66f787SSean Bruno 		case PQIIOCTL_WRITE:
2551e66f787SSean Bruno 		case PQIIOCTL_READ:
2561e66f787SSean Bruno 		case PQIIOCTL_BIDIRECTIONAL:
2571e66f787SSean Bruno 			break;
2581e66f787SSean Bruno 		default:
2591e66f787SSean Bruno 			return PQI_STATUS_FAILURE;
2601e66f787SSean Bruno 	}
2611e66f787SSean Bruno 
2621e66f787SSean Bruno 	if (iocommand->buf_size > 0) {
2631e66f787SSean Bruno 		memset(&ioctl_dma_buf, 0, sizeof(struct dma_mem));
2641e66f787SSean Bruno 		ioctl_dma_buf.tag = "Ioctl_PassthruCmd_Buffer";
2651e66f787SSean Bruno 		ioctl_dma_buf.size = iocommand->buf_size;
2661e66f787SSean Bruno 		ioctl_dma_buf.align = PQISRC_DEFAULT_DMA_ALIGN;
2671e66f787SSean Bruno 		/* allocate memory */
2681e66f787SSean Bruno 		ret = os_dma_mem_alloc(softs, &ioctl_dma_buf);
2691e66f787SSean Bruno 		if (ret) {
2701e66f787SSean Bruno 			DBG_ERR("Failed to Allocate dma mem for Ioctl PassthruCmd Buffer : %d\n", ret);
2711e66f787SSean Bruno 			ret = PQI_STATUS_FAILURE;
2721e66f787SSean Bruno 			goto out;
2731e66f787SSean Bruno 		}
2741e66f787SSean Bruno 
2751e66f787SSean Bruno 		DBG_INFO("ioctl_dma_buf.dma_addr  = %p\n",(void*)ioctl_dma_buf.dma_addr);
2761e66f787SSean Bruno 		DBG_INFO("ioctl_dma_buf.virt_addr = %p\n",(void*)ioctl_dma_buf.virt_addr);
2771e66f787SSean Bruno 
2781e66f787SSean Bruno 		drv_buf = (char *)ioctl_dma_buf.virt_addr;
2791e66f787SSean Bruno 		if (iocommand->Request.Type.Direction & PQIIOCTL_WRITE) {
2801e66f787SSean Bruno         		if ((ret = os_copy_from_user(softs, (void *)drv_buf, (void *)iocommand->buf,
2811e66f787SSean Bruno 						iocommand->buf_size, mode)) != 0) {
2821e66f787SSean Bruno 				ret = PQI_STATUS_FAILURE;
2831e66f787SSean Bruno 				goto free_mem;
2841e66f787SSean Bruno 			}
2851e66f787SSean Bruno 		}
2861e66f787SSean Bruno 	}
2871e66f787SSean Bruno 
2881e66f787SSean Bruno 	request.header.iu_type = PQI_IU_TYPE_RAID_PATH_IO_REQUEST;
2891e66f787SSean Bruno 	request.header.iu_length = offsetof(pqisrc_raid_req_t, sg_descriptors[1]) -
2901e66f787SSean Bruno 									PQI_REQUEST_HEADER_LENGTH;
2911e66f787SSean Bruno 	memcpy(request.lun_number, iocommand->LUN_info.LunAddrBytes,
2921e66f787SSean Bruno 		sizeof(request.lun_number));
2931e66f787SSean Bruno 	memcpy(request.cdb, iocommand->Request.CDB, iocommand->Request.CDBLen);
2941e66f787SSean Bruno 	request.additional_cdb_bytes_usage = PQI_ADDITIONAL_CDB_BYTES_0;
2951e66f787SSean Bruno 
2961e66f787SSean Bruno 	switch (iocommand->Request.Type.Direction) {
2971e66f787SSean Bruno 	case PQIIOCTL_NONE:
2981e66f787SSean Bruno 		request.data_direction = SOP_DATA_DIR_NONE;
2991e66f787SSean Bruno 		break;
3001e66f787SSean Bruno 	case PQIIOCTL_WRITE:
3011e66f787SSean Bruno 		request.data_direction = SOP_DATA_DIR_FROM_DEVICE;
3021e66f787SSean Bruno 		break;
3031e66f787SSean Bruno 	case PQIIOCTL_READ:
3041e66f787SSean Bruno 		request.data_direction = SOP_DATA_DIR_TO_DEVICE;
3051e66f787SSean Bruno 		break;
3061e66f787SSean Bruno 	case PQIIOCTL_BIDIRECTIONAL:
3071e66f787SSean Bruno 		request.data_direction = SOP_DATA_DIR_BIDIRECTIONAL;
3081e66f787SSean Bruno 		break;
3091e66f787SSean Bruno 	}
3101e66f787SSean Bruno 
3111e66f787SSean Bruno 	request.task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE;
3121e66f787SSean Bruno 	if (iocommand->buf_size > 0) {
3131e66f787SSean Bruno 		request.buffer_length = iocommand->buf_size;
3141e66f787SSean Bruno 		request.sg_descriptors[0].addr = ioctl_dma_buf.dma_addr;
3151e66f787SSean Bruno 		request.sg_descriptors[0].len = iocommand->buf_size;
3161e66f787SSean Bruno 		request.sg_descriptors[0].flags =  SG_FLAG_LAST;
3171e66f787SSean Bruno 	}
3181e66f787SSean Bruno 	tag = pqisrc_get_tag(&softs->taglist);
3191e66f787SSean Bruno 	request.request_id = tag;
3201e66f787SSean Bruno 	request.response_queue_id = ob_q->q_id;
3211e66f787SSean Bruno 	request.error_index = request.request_id;
3221e66f787SSean Bruno 	rcb = &softs->rcb[tag];
3231e66f787SSean Bruno 
3241e66f787SSean Bruno 	rcb->success_cmp_callback = pqisrc_process_internal_raid_response_success;
3251e66f787SSean Bruno 	rcb->error_cmp_callback = pqisrc_process_internal_raid_response_error;
3261e66f787SSean Bruno 	rcb->tag = tag;
3271e66f787SSean Bruno 	rcb->req_pending = true;
3281e66f787SSean Bruno 	/* Submit Command */
3291e66f787SSean Bruno 	ret = pqisrc_submit_cmnd(softs, ib_q, &request);
3301e66f787SSean Bruno 	if (ret != PQI_STATUS_SUCCESS) {
3311e66f787SSean Bruno 		DBG_ERR("Unable to submit command\n");
3321e66f787SSean Bruno 		goto err_out;
3331e66f787SSean Bruno 	}
3341e66f787SSean Bruno 
3351e66f787SSean Bruno 	ret = pqisrc_wait_on_condition(softs, rcb);
3361e66f787SSean Bruno 	if (ret != PQI_STATUS_SUCCESS) {
3371e66f787SSean Bruno 		DBG_ERR("Passthru IOCTL cmd timed out !!\n");
3381e66f787SSean Bruno 		goto err_out;
3391e66f787SSean Bruno 	}
3401e66f787SSean Bruno 
3411e66f787SSean Bruno 	memset(&iocommand->error_info, 0, sizeof(iocommand->error_info));
3421e66f787SSean Bruno 
3431e66f787SSean Bruno 
3441e66f787SSean Bruno 	if (rcb->status) {
3451e66f787SSean Bruno 		size_t sense_data_length;
3461e66f787SSean Bruno 
3471e66f787SSean Bruno 		memcpy(&error_info, rcb->error_info, sizeof(error_info));
3481e66f787SSean Bruno 		iocommand->error_info.ScsiStatus = error_info.status;
3491e66f787SSean Bruno 		sense_data_length = error_info.sense_data_len;
3501e66f787SSean Bruno 
3511e66f787SSean Bruno 		if (!sense_data_length)
3521e66f787SSean Bruno 			sense_data_length = error_info.resp_data_len;
3531e66f787SSean Bruno 
3541e66f787SSean Bruno 		if (sense_data_length &&
3551e66f787SSean Bruno 			(sense_data_length > sizeof(error_info.data)))
3561e66f787SSean Bruno 				sense_data_length = sizeof(error_info.data);
3571e66f787SSean Bruno 
3581e66f787SSean Bruno 		if (sense_data_length) {
3591e66f787SSean Bruno 			if (sense_data_length >
3601e66f787SSean Bruno 				sizeof(iocommand->error_info.SenseInfo))
3611e66f787SSean Bruno 				sense_data_length =
3621e66f787SSean Bruno 					sizeof(iocommand->error_info.SenseInfo);
3631e66f787SSean Bruno 			memcpy (iocommand->error_info.SenseInfo,
3641e66f787SSean Bruno 					error_info.data, sense_data_length);
3651e66f787SSean Bruno 			iocommand->error_info.SenseLen = sense_data_length;
3661e66f787SSean Bruno 		}
3671e66f787SSean Bruno 
3681e66f787SSean Bruno 		if (error_info.data_out_result ==
3691e66f787SSean Bruno 				PQI_RAID_DATA_IN_OUT_UNDERFLOW){
3701e66f787SSean Bruno 			rcb->status = REQUEST_SUCCESS;
3711e66f787SSean Bruno 		}
3721e66f787SSean Bruno 	}
3731e66f787SSean Bruno 
3741e66f787SSean Bruno 	if (rcb->status == REQUEST_SUCCESS && iocommand->buf_size > 0 &&
3751e66f787SSean Bruno 		(iocommand->Request.Type.Direction & PQIIOCTL_READ)) {
3761e66f787SSean Bruno 
3771e66f787SSean Bruno 		if ((ret = os_copy_to_user(softs, (void*)iocommand->buf,
3781e66f787SSean Bruno 			(void*)drv_buf, iocommand->buf_size, mode)) != 0) {
3791e66f787SSean Bruno 				DBG_ERR("Failed to copy the response\n");
3801e66f787SSean Bruno 				goto err_out;
3811e66f787SSean Bruno 		}
3821e66f787SSean Bruno 	}
3831e66f787SSean Bruno 
3841e66f787SSean Bruno 	os_reset_rcb(rcb);
3851e66f787SSean Bruno 	pqisrc_put_tag(&softs->taglist, request.request_id);
3861e66f787SSean Bruno 	if (iocommand->buf_size > 0)
3871e66f787SSean Bruno 			os_dma_mem_free(softs,&ioctl_dma_buf);
3881e66f787SSean Bruno 
3891e66f787SSean Bruno 	DBG_FUNC("OUT\n");
3901e66f787SSean Bruno 	return ret;
3911e66f787SSean Bruno err_out:
3921e66f787SSean Bruno 	os_reset_rcb(rcb);
3931e66f787SSean Bruno 	pqisrc_put_tag(&softs->taglist, request.request_id);
3941e66f787SSean Bruno 
3951e66f787SSean Bruno free_mem:
3961e66f787SSean Bruno 	if (iocommand->buf_size > 0)
3971e66f787SSean Bruno 		os_dma_mem_free(softs, &ioctl_dma_buf);
3981e66f787SSean Bruno 
3991e66f787SSean Bruno out:
4001e66f787SSean Bruno 	DBG_FUNC("Failed OUT\n");
4011e66f787SSean Bruno 	return PQI_STATUS_FAILURE;
4021e66f787SSean Bruno }
403