xref: /linux/drivers/scsi/leapraid/leapraid_app.c (revision 970f69b6bf71562df5afcefb89c77b4e971d68de)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2026 LeapIO Tech Inc.
4  *
5  * LeapRAID storage and RAID controller driver.
6  */
7 #include <linux/compat.h>
8 #include <linux/module.h>
9 #include <linux/miscdevice.h>
10 
11 #include "leapraid_func.h"
12 
13 /* IOCTL device file name. */
14 #define LEAPRAID_DEV_NAME       "leapraid_ctl"
15 
16 /* IOCTL version. */
17 #define LEAPRAID_IOCTL_VERSION  0x07
18 
19 /* IOCTL commands. */
20 #define LEAPRAID_ADAPTER_INFO   17
21 #define LEAPRAID_COMMAND        20
22 #define LEAPRAID_EVENTQUERY     21
23 #define LEAPRAID_EVENTREPORT    23
24 
25 /**
26  * struct leapraid_ioctl_header - IOCTL command header
27  *
28  * @adapter_id : Adapter identifier.
29  * @port_number: Port identifier.
30  * @max_data_size: Maximum data size for transfer.
31  */
32 struct leapraid_ioctl_header {
33 	u32 adapter_id;
34 	u32 port_number;
35 	u32 max_data_size;
36 };
37 
38 /**
39  * struct leapraid_ioctl_diag_reset - Diagnostic reset request
40  *
41  * @hdr: Common IOCTL header.
42  */
43 struct leapraid_ioctl_diag_reset {
44 	struct leapraid_ioctl_header hdr;
45 };
46 
47 /**
48  * struct leapraid_ioctl_pci_info - PCI device information
49  *
50  * @u: Union holding PCI bus/device/function information.
51  * @u.bits.dev: PCI device number.
52  * @u.bits.func: PCI function number.
53  * @u.bits.bus: PCI bus number.
54  * @u.word: Combined representation of PCI BDF.
55  * @seg_id: PCI segment identifier.
56  */
57 struct leapraid_ioctl_pci_info {
58 	union {
59 		struct {
60 			u32 dev:5;
61 			u32 func:3;
62 			u32 bus:24;
63 		} bits;
64 		u32 word;
65 	} u;
66 	u32 seg_id;
67 };
68 
69 /**
70  * struct leapraid_ioctl_adapter_info - Adapter information for IOCTL
71  *
72  * @hdr: IOCTL header.
73  * @adapter_type: Adapter type identifier.
74  * @port_number: Port number.
75  * @pci_id: PCI device ID.
76  * @revision: Revision number.
77  * @sub_dev: Subsystem device ID.
78  * @sub_vendor: Subsystem vendor ID.
79  * @r0: Reserved.
80  * @fw_ver: Firmware version.
81  * @bios_ver: BIOS version.
82  * @driver_ver: Driver version.
83  * @r1: Reserved.
84  * @scsi_id: SCSI ID.
85  * @r2: Reserved.
86  * @pci_info: PCI information structure.
87  */
88 struct leapraid_ioctl_adapter_info {
89 	struct leapraid_ioctl_header hdr;
90 	u32 adapter_type;
91 	u32 port_number;
92 	u32 pci_id;
93 	u32 revision;
94 	u32 sub_dev;
95 	u32 sub_vendor;
96 	u32 r0;
97 	u32 fw_ver;
98 	u32 bios_ver;
99 	u8 driver_ver[32];
100 	u8 r1;
101 	u8 scsi_id;
102 	u16 r2;
103 	struct leapraid_ioctl_pci_info pci_info;
104 };
105 
106 /**
107  * struct leapraid_ioctl_command - IOCTL command structure
108  *
109  * @hdr: IOCTL header.
110  * @timeout: Command timeout.
111  * @rep_msg_buf_ptr: User pointer to reply message buffer.
112  * @c2h_buf_ptr: User pointer to card-to-host data buffer.
113  * @h2c_buf_ptr: User pointer to host-to-card data buffer.
114  * @sense_data_ptr: User pointer to sense data buffer.
115  * @max_rep_bytes: Maximum reply bytes.
116  * @c2h_size: Card-to-host data size.
117  * @h2c_size: Host-to-card data size.
118  * @max_sense_bytes: Maximum sense data bytes.
119  * @data_sge_offset: Data SGE offset.
120  * @mf: Message frame data (flexible array).
121  */
122 struct leapraid_ioctl_command {
123 	struct leapraid_ioctl_header hdr;
124 	u32 timeout;
125 	void __user *rep_msg_buf_ptr;
126 	void __user *c2h_buf_ptr;
127 	void __user *h2c_buf_ptr;
128 	void __user *sense_data_ptr;
129 	u32 max_rep_bytes;
130 	u32 c2h_size;
131 	u32 h2c_size;
132 	u32 max_sense_bytes;
133 	u32 data_sge_offset;
134 	u8 mf[];
135 };
136 
137 static int leapraid_ctl_validate_sge_offset(struct leapraid_adapter *adapter,
138 					    const struct leapraid_req *req,
139 					    u32 offset_bytes,
140 					    size_t h2c_size,
141 					    size_t c2h_size)
142 {
143 	size_t sge_bytes;
144 
145 	switch (req->func) {
146 	case LEAPRAID_FUNC_SCSIIO:
147 	case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH:
148 	case LEAPRAID_FUNC_SMP_PASSTHROUGH:
149 	case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH:
150 	case LEAPRAID_FUNC_FW_DOWNLOAD:
151 	case LEAPRAID_FUNC_FW_UPLOAD:
152 		sge_bytes = LEAPRAID_IEEE_SGE64_ENTRY_SIZE;
153 		break;
154 	default:
155 		sge_bytes = adapter->adapter_attr.use_32_dma_mask ?
156 			sizeof(struct leapraid_sge_simple32) :
157 			sizeof(struct leapraid_sge_simple64);
158 		break;
159 	}
160 
161 	if (h2c_size && c2h_size)
162 		sge_bytes *= 2;
163 
164 	if (offset_bytes > LEAPRAID_REQUEST_SIZE - sge_bytes) {
165 		dev_err(&adapter->pdev->dev,
166 			"%s: Invalid offset_bytes=%u for func=0x%x\n",
167 			__func__, offset_bytes, req->func);
168 		return -EINVAL;
169 	}
170 
171 	return 0;
172 }
173 
174 static struct leapraid_adapter *leapraid_ctl_lookup_adapter(int adapter_id)
175 {
176 	struct leapraid_adapter *adapter;
177 	struct Scsi_Host *shost;
178 
179 	spin_lock(&leapraid_adapter_lock);
180 	list_for_each_entry(adapter, &leapraid_adapter_list, list) {
181 		if (adapter->adapter_attr.id == adapter_id) {
182 			if (READ_ONCE(adapter->access_ctrl.host_removing))
183 				break;
184 			shost = adapter->shost;
185 			if (!shost || !scsi_host_get(shost))
186 				break;
187 			spin_unlock(&leapraid_adapter_lock);
188 			return adapter;
189 		}
190 	}
191 	spin_unlock(&leapraid_adapter_lock);
192 
193 	return NULL;
194 }
195 
196 static void leapraid_ctl_put_adapter(struct leapraid_adapter *adapter)
197 {
198 	if (adapter && adapter->shost)
199 		scsi_host_put(adapter->shost);
200 }
201 
202 static void leapraid_ctl_scsiio_cmd(struct leapraid_adapter *adapter,
203 				    void *ctl_sp_mpi_req,
204 				    u16 taskid,
205 				    dma_addr_t h2c_dma_addr, size_t h2c_size,
206 				    dma_addr_t c2h_dma_addr, size_t c2h_size,
207 				    u16 dev_hdl, void *psge)
208 {
209 	struct leapraid_mpi_scsiio_req *scsiio_request = ctl_sp_mpi_req;
210 
211 	scsiio_request->sense_buffer_len = SCSI_SENSE_BUFFERSIZE;
212 	scsiio_request->sense_buffer_low_add =
213 		leapraid_get_sense_buffer_dma(adapter, taskid);
214 	memset(&adapter->driver_cmds.ctl_cmd.sense,
215 	       0, SCSI_SENSE_BUFFERSIZE);
216 	leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr,
217 			       h2c_size, c2h_dma_addr, c2h_size);
218 	if (scsiio_request->func == LEAPRAID_FUNC_SCSIIO)
219 		leapraid_fire_scsi_io(adapter, taskid, dev_hdl);
220 	else
221 		leapraid_fire_task(adapter, taskid);
222 }
223 
224 static int leapraid_ctl_smp_passthrough_cmd(struct leapraid_adapter *adapter,
225 					    void *ctl_sp_req,
226 					    u16 taskid,
227 					    dma_addr_t h2c_dma_addr,
228 					    size_t h2c_size,
229 					    dma_addr_t c2h_dma_addr,
230 					    size_t c2h_size,
231 					    void *psge, void *h2c)
232 {
233 	struct leapraid_smp_passthrough_req *smp_pt_req = ctl_sp_req;
234 	u8 *data;
235 
236 	if (!adapter->adapter_attr.enable_mp)
237 		smp_pt_req->physical_port = LEAPRAID_DISABLE_MP_PORT_ID;
238 	if (smp_pt_req->passthrough_flg & LEAPRAID_SMP_PT_FLAG_SGL_PTR)
239 		data = (u8 *)&smp_pt_req->sgl;
240 	else
241 		data = h2c;
242 
243 	if (!(smp_pt_req->passthrough_flg & LEAPRAID_SMP_PT_FLAG_SGL_PTR) &&
244 	    h2c_size <= 10) {
245 		dev_err(&adapter->pdev->dev,
246 			"%s: Invalid request size=%zu\n",
247 			__func__, h2c_size);
248 		return -EINVAL;
249 	}
250 
251 	if (data[1] == SMP_PHY_CONTROL &&
252 	    (data[10] == SMP_PHY_CONTROL_LINK_RESET ||
253 	     data[10] == SMP_PHY_CONTROL_HARD_RESET))
254 		adapter->reset_desc.adapter_link_resetting = 1;
255 
256 	leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr,
257 			       h2c_size, c2h_dma_addr, c2h_size);
258 	leapraid_fire_task(adapter, taskid);
259 	return 0;
260 }
261 
262 static void leapraid_ctl_sas_io_unit_ctrl_cmd(struct leapraid_adapter *adapter,
263 					      void *ctl_sp_req,
264 					      dma_addr_t h2c_dma_addr,
265 					      size_t h2c_size,
266 					      dma_addr_t c2h_dma_addr,
267 					      size_t c2h_size,
268 					      void *psge, u16 taskid)
269 {
270 	struct leapraid_sas_io_unit_ctrl_req *sas_io_unit_ctl_req = ctl_sp_req;
271 
272 	if (sas_io_unit_ctl_req->op == LEAPRAID_SAS_OP_PHY_HARD_RESET ||
273 	    sas_io_unit_ctl_req->op == LEAPRAID_SAS_OP_PHY_LINK_RESET)
274 		adapter->reset_desc.adapter_link_resetting = 1;
275 	leapraid_build_mpi_sg(adapter, psge, h2c_dma_addr,
276 			      h2c_size, c2h_dma_addr, c2h_size);
277 	leapraid_fire_task(adapter, taskid);
278 }
279 
280 static int leapraid_ctl_do_command(struct leapraid_adapter *adapter,
281 				   struct leapraid_ioctl_command *karg,
282 				   void __user *mf)
283 {
284 	struct leapraid_req *leap_mpi_req;
285 	struct leapraid_req *ctl_sp_mpi_req;
286 	u16 taskid;
287 	void *h2c = NULL;
288 	size_t h2c_size = 0;
289 	dma_addr_t h2c_dma_addr = 0;
290 	void *c2h = NULL;
291 	size_t c2h_size = 0;
292 	dma_addr_t c2h_dma_addr = 0;
293 	void *psge;
294 	unsigned long timeout;
295 	u16 dev_hdl = LEAPRAID_INVALID_DEV_HANDLE;
296 	bool issue_reset = false;
297 	u32 data_sge_offset_bytes;
298 	u32 sz;
299 	int rc;
300 
301 	rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT,
302 					  __func__);
303 	if (rc)
304 		return rc;
305 
306 	leap_mpi_req = kzalloc(LEAPRAID_REQUEST_SIZE, GFP_KERNEL);
307 	if (!leap_mpi_req)
308 		return -ENOMEM;
309 
310 	if (karg->data_sge_offset > (UINT_MAX / LEAPRAID_SGE_OFFSET_SIZE)) {
311 		dev_err(&adapter->pdev->dev,
312 			"%s: Invalid data_sge_offset=%u\n",
313 			__func__, karg->data_sge_offset);
314 		rc = -EINVAL;
315 		goto out_cleanup;
316 	}
317 
318 	data_sge_offset_bytes = karg->data_sge_offset *
319 				LEAPRAID_SGE_OFFSET_SIZE;
320 	if (data_sge_offset_bytes > LEAPRAID_REQUEST_SIZE) {
321 		dev_err(&adapter->pdev->dev,
322 			"%s: Invalid data_sge_offset=%u\n",
323 			__func__, karg->data_sge_offset);
324 		rc = -EINVAL;
325 		goto out_cleanup;
326 	}
327 
328 	if (copy_from_user(leap_mpi_req, mf, data_sge_offset_bytes)) {
329 		dev_err(&adapter->pdev->dev,
330 			"%s: Failed to copy request message from user\n",
331 			__func__);
332 		rc = -EFAULT;
333 		goto out_cleanup;
334 	}
335 
336 	h2c_size = karg->h2c_size;
337 	c2h_size = karg->c2h_size;
338 	rc = leapraid_ctl_validate_sge_offset(adapter, leap_mpi_req,
339 					      data_sge_offset_bytes,
340 					      h2c_size, c2h_size);
341 	if (rc)
342 		goto out_cleanup;
343 
344 	taskid = adapter->driver_cmds.ctl_cmd.taskid;
345 
346 	adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_PENDING;
347 	memset(&adapter->driver_cmds.ctl_cmd.reply, 0, LEAPRAID_REPLY_SIZE);
348 	ctl_sp_mpi_req = leapraid_get_task_desc(adapter, taskid);
349 	memset(ctl_sp_mpi_req, 0, LEAPRAID_REQUEST_SIZE);
350 	memcpy(ctl_sp_mpi_req, leap_mpi_req, data_sge_offset_bytes);
351 
352 	if (ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
353 	    ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH ||
354 	    ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH) {
355 		dev_hdl = le16_to_cpu(ctl_sp_mpi_req->func_dep1);
356 		if (!dev_hdl ||
357 		    dev_hdl > adapter->adapter_attr.features.max_dev_handle) {
358 			dev_err(&adapter->pdev->dev,
359 				"%s: Invalid device handle\n", __func__);
360 			rc = -EINVAL;
361 			goto out_cleanup;
362 		}
363 	}
364 
365 	if (WARN_ON(ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSI_TMF)) {
366 		rc = -EINVAL;
367 		goto out_cleanup;
368 	}
369 
370 	if (h2c_size) {
371 		h2c = dma_alloc_coherent(&adapter->pdev->dev, h2c_size,
372 					 &h2c_dma_addr, GFP_KERNEL);
373 		if (!h2c) {
374 			rc = -ENOMEM;
375 			goto out_cleanup;
376 		}
377 		if (copy_from_user(h2c, karg->h2c_buf_ptr, h2c_size)) {
378 			dev_err(&adapter->pdev->dev,
379 				"%s: Failed to copy h2c from user\n",
380 				__func__);
381 			rc = -EFAULT;
382 			goto out_cleanup;
383 		}
384 	}
385 	if (c2h_size) {
386 		c2h = dma_alloc_coherent(&adapter->pdev->dev, c2h_size,
387 					 &c2h_dma_addr, GFP_KERNEL);
388 		if (!c2h) {
389 			rc = -ENOMEM;
390 			goto out_cleanup;
391 		}
392 	}
393 
394 	psge = (void *)ctl_sp_mpi_req + data_sge_offset_bytes;
395 	init_completion(&adapter->driver_cmds.ctl_cmd.done);
396 
397 	switch (ctl_sp_mpi_req->func) {
398 	case LEAPRAID_FUNC_SCSIIO:
399 	case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH:
400 		leapraid_ctl_scsiio_cmd(adapter, ctl_sp_mpi_req, taskid,
401 					h2c_dma_addr, h2c_size,
402 					c2h_dma_addr, c2h_size,
403 					dev_hdl, psge);
404 		break;
405 	case LEAPRAID_FUNC_SMP_PASSTHROUGH:
406 		if (!h2c) {
407 			rc = -EINVAL;
408 			goto out_cleanup;
409 		}
410 		rc = leapraid_ctl_smp_passthrough_cmd(adapter,
411 						      ctl_sp_mpi_req, taskid,
412 						      h2c_dma_addr, h2c_size,
413 						      c2h_dma_addr, c2h_size,
414 						      psge, h2c);
415 		if (rc)
416 			goto out_cleanup;
417 		break;
418 	case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH:
419 		leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size,
420 				       c2h_dma_addr, c2h_size);
421 		leapraid_fire_task(adapter, taskid);
422 		break;
423 	case LEAPRAID_FUNC_FW_DOWNLOAD:
424 	case LEAPRAID_FUNC_FW_UPLOAD:
425 		leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size,
426 				       c2h_dma_addr, c2h_size);
427 		leapraid_fire_task(adapter, taskid);
428 		break;
429 	case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL:
430 		leapraid_ctl_sas_io_unit_ctrl_cmd(adapter, ctl_sp_mpi_req,
431 						  h2c_dma_addr, h2c_size,
432 						  c2h_dma_addr, c2h_size,
433 						  psge, taskid);
434 		break;
435 	default:
436 		leapraid_build_mpi_sg(adapter, psge, h2c_dma_addr,
437 				      h2c_size, c2h_dma_addr, c2h_size);
438 		leapraid_fire_task(adapter, taskid);
439 		break;
440 	}
441 
442 	timeout = karg->timeout;
443 	if (timeout < LEAPRAID_CTL_CMD_TIMEOUT)
444 		timeout = LEAPRAID_CTL_CMD_TIMEOUT;
445 	if (!wait_for_completion_timeout(&adapter->driver_cmds.ctl_cmd.done,
446 					 timeout * HZ)) {
447 		dev_err(&adapter->pdev->dev,
448 			"%s: ctl_cmd timeout, status=0x%x\n",
449 			__func__, adapter->driver_cmds.ctl_cmd.status);
450 		leapraid_log_req_context(adapter, taskid, ctl_sp_mpi_req);
451 	}
452 
453 	if ((leap_mpi_req->func == LEAPRAID_FUNC_SMP_PASSTHROUGH ||
454 	     leap_mpi_req->func == LEAPRAID_FUNC_SAS_IO_UNIT_CTRL) &&
455 	    adapter->reset_desc.adapter_link_resetting)
456 		adapter->reset_desc.adapter_link_resetting = 0;
457 
458 	if (!(adapter->driver_cmds.ctl_cmd.status & LEAPRAID_CMD_DONE)) {
459 		issue_reset =
460 			leapraid_check_reset(
461 				adapter->driver_cmds.ctl_cmd.status);
462 		goto reset;
463 	}
464 
465 	if (c2h_size && copy_to_user(karg->c2h_buf_ptr, c2h, c2h_size)) {
466 		dev_err(&adapter->pdev->dev,
467 			"%s: Failed to copy c2h to user\n", __func__);
468 		rc = -ENODATA;
469 		goto out_cleanup;
470 	}
471 	if (karg->max_rep_bytes) {
472 		sz = min_t(u32, karg->max_rep_bytes, LEAPRAID_REPLY_SIZE);
473 		if (copy_to_user(karg->rep_msg_buf_ptr,
474 				 &adapter->driver_cmds.ctl_cmd.reply,
475 				 sz)) {
476 			dev_err(&adapter->pdev->dev,
477 				"%s: Failed to copy reply to user\n",
478 				__func__);
479 			rc = -ENODATA;
480 			goto out_cleanup;
481 		}
482 	}
483 
484 	if (karg->max_sense_bytes &&
485 	    (leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
486 	     leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH)) {
487 		if (!karg->sense_data_ptr)
488 			goto out_cleanup;
489 
490 		sz = min_t(u32, karg->max_sense_bytes, SCSI_SENSE_BUFFERSIZE);
491 		if (copy_to_user(karg->sense_data_ptr,
492 				 &adapter->driver_cmds.ctl_cmd.sense,
493 				 sz)) {
494 			dev_err(&adapter->pdev->dev,
495 				"%s: Failed to copy sense data to user\n",
496 				__func__);
497 			rc = -ENODATA;
498 			goto out_cleanup;
499 		}
500 	}
501 reset:
502 	if (issue_reset) {
503 		rc = -ENODATA;
504 		if (leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
505 		    leap_mpi_req->func ==
506 		    LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH ||
507 		    leap_mpi_req->func ==
508 		    LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH) {
509 			dev_err(&adapter->pdev->dev,
510 				"Fire tgt reset, hdl=0x%04x\n",
511 				le16_to_cpu(leap_mpi_req->func_dep1));
512 			leapraid_issue_locked_tm(
513 				adapter,
514 				le16_to_cpu(leap_mpi_req->func_dep1), 0, 0, 0,
515 				LEAPRAID_TM_TASKTYPE_TARGET_RESET, taskid,
516 				LEAPRAID_TM_MSGFLAGS_LINK_RESET);
517 		} else {
518 			dev_info(&adapter->pdev->dev,
519 				 "%s:%d: call hard_reset\n",
520 				 __func__, __LINE__);
521 			leapraid_hard_reset_handler(adapter, FULL_RESET);
522 		}
523 	}
524 out_cleanup:
525 	if (c2h)
526 		dma_free_coherent(&adapter->pdev->dev, c2h_size,
527 				  c2h, c2h_dma_addr);
528 	if (h2c)
529 		dma_free_coherent(&adapter->pdev->dev, h2c_size,
530 				  h2c, h2c_dma_addr);
531 	kfree(leap_mpi_req);
532 	adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_NOT_USED;
533 	return rc;
534 }
535 
536 static int leapraid_ctl_get_adapter_info(struct leapraid_adapter *adapter,
537 					 void __user *arg)
538 {
539 	struct leapraid_ioctl_adapter_info *karg;
540 	u8 revision;
541 	int rc = 0;
542 
543 	karg = kzalloc_obj(*karg);
544 	if (!karg)
545 		return -ENOMEM;
546 
547 	pci_read_config_byte(adapter->pdev, PCI_CLASS_REVISION, &revision);
548 	karg->revision = revision;
549 	karg->pci_id = adapter->pdev->device;
550 	karg->sub_dev = adapter->pdev->subsystem_device;
551 	karg->sub_vendor = adapter->pdev->subsystem_vendor;
552 	karg->pci_info.u.bits.bus = adapter->pdev->bus->number;
553 	karg->pci_info.u.bits.dev = PCI_SLOT(adapter->pdev->devfn);
554 	karg->pci_info.u.bits.func = PCI_FUNC(adapter->pdev->devfn);
555 	karg->pci_info.seg_id = pci_domain_nr(adapter->pdev->bus);
556 	karg->fw_ver = adapter->adapter_attr.features.fw_version;
557 
558 	snprintf(karg->driver_ver, sizeof(karg->driver_ver), "%s-%s",
559 		 LEAPRAID_DRIVER_NAME, LEAPRAID_DRIVER_VERSION);
560 
561 	karg->adapter_type = LEAPRAID_IOCTL_VERSION;
562 	karg->bios_ver = adapter->adapter_attr.bios_version;
563 	if (copy_to_user(arg, karg,
564 			 sizeof(struct leapraid_ioctl_adapter_info))) {
565 		dev_err(&adapter->pdev->dev,
566 			"%s: Failed to copy info to user\n", __func__);
567 		rc = -EFAULT;
568 		goto free_buf;
569 	}
570 
571 free_buf:
572 	kfree(karg);
573 	return rc;
574 }
575 
576 static int leapraid_ctl_ioctl_main(struct file *file, unsigned int cmd,
577 				   void __user *arg)
578 {
579 	struct leapraid_ioctl_header ioctl_header;
580 	struct leapraid_adapter *adapter = NULL;
581 	struct leapraid_ioctl_command __user *uarg;
582 	struct leapraid_ioctl_command karg;
583 	int rc = -ENOIOCTLCMD;
584 
585 	if (copy_from_user(&ioctl_header, arg,
586 			   sizeof(struct leapraid_ioctl_header))) {
587 		pr_err("%s:%s: Failed to copy header from user\n",
588 		       LEAPRAID_DRIVER_NAME, __func__);
589 		return -EFAULT;
590 	}
591 
592 	adapter = leapraid_ctl_lookup_adapter(ioctl_header.adapter_id);
593 	if (!adapter)
594 		return -EFAULT;
595 
596 	if (atomic_read(&adapter->overheat_desc.thermal_alert)) {
597 		dev_warn(&adapter->pdev->dev,
598 			 "%s: Failed, thermal_alert=%d\n",
599 			 __func__,
600 			 atomic_read(&adapter->overheat_desc.thermal_alert));
601 		rc = -EFAULT;
602 		goto out_put;
603 	}
604 
605 	mutex_lock(&adapter->access_ctrl.pci_access_lock);
606 
607 	rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_LONG,
608 					  __func__);
609 	if (rc)
610 		goto unlock;
611 
612 	if (!wait_event_timeout(adapter->access_ctrl.shost_recover_wq,
613 				!adapter->access_ctrl.shost_recover_async,
614 				LEAPRAID_WAIT_SHOST_RECOVERY * HZ)) {
615 		dev_warn(&adapter->pdev->dev,
616 			 "Timeout waiting for shost recovery async\n");
617 		rc = -EAGAIN;
618 		goto unlock;
619 	}
620 
621 	if (adapter->access_ctrl.pcie_recovering ||
622 	    adapter->scan_dev_desc.driver_loading ||
623 	    adapter->access_ctrl.host_removing) {
624 		rc = -EAGAIN;
625 		goto unlock;
626 	}
627 
628 	if (file->f_flags & O_NONBLOCK) {
629 		if (!mutex_trylock(&adapter->driver_cmds.ctl_cmd.mutex)) {
630 			rc = -EAGAIN;
631 			goto unlock;
632 		}
633 	} else if (mutex_lock_interruptible(&adapter->driver_cmds
634 					    .ctl_cmd.mutex)) {
635 		rc = -ERESTARTSYS;
636 		goto unlock;
637 	}
638 
639 	switch (_IOC_NR(cmd)) {
640 	case LEAPRAID_ADAPTER_INFO:
641 		if (_IOC_SIZE(cmd) ==
642 		    sizeof(struct leapraid_ioctl_adapter_info))
643 			rc = leapraid_ctl_get_adapter_info(adapter, arg);
644 		break;
645 	case LEAPRAID_COMMAND:
646 		if (copy_from_user(&karg, arg, sizeof(karg))) {
647 			dev_err(&adapter->pdev->dev,
648 				"%s: Failed to copy data from user\n",
649 				__func__);
650 			rc = -EFAULT;
651 			break;
652 		}
653 
654 		if (karg.hdr.adapter_id != ioctl_header.adapter_id) {
655 			rc = -EINVAL;
656 			break;
657 		}
658 
659 		if (_IOC_SIZE(cmd) == sizeof(struct leapraid_ioctl_command)) {
660 			uarg = arg;
661 			rc = leapraid_ctl_do_command(adapter, &karg,
662 						     &uarg->mf);
663 			if (rc)
664 				dev_warn(&adapter->pdev->dev,
665 					 "%s: IOCTL cmd failed rc=%d\n",
666 					 __func__, rc);
667 		}
668 		break;
669 	case LEAPRAID_EVENTQUERY:
670 	case LEAPRAID_EVENTREPORT:
671 		rc = 0;
672 		break;
673 	default:
674 		dev_err(&adapter->pdev->dev,
675 			"Unknown IOCTL opcode=0x%08x\n", cmd);
676 		break;
677 	}
678 	mutex_unlock(&adapter->driver_cmds.ctl_cmd.mutex);
679 
680 unlock:
681 	mutex_unlock(&adapter->access_ctrl.pci_access_lock);
682 out_put:
683 	leapraid_ctl_put_adapter(adapter);
684 	return rc;
685 }
686 
687 static long leapraid_ctl_ioctl(struct file *file, unsigned int cmd,
688 			       unsigned long arg)
689 {
690 	return leapraid_ctl_ioctl_main(file, cmd, (void __user *)arg);
691 }
692 
693 static void leapraid_fw_mmap_open(struct vm_area_struct *vma)
694 {
695 	struct leapraid_adapter *adapter = vma->vm_private_data;
696 
697 	if (!adapter)
698 		return;
699 
700 	get_device(&adapter->shost->shost_gendev);
701 	atomic_inc(&adapter->fw_log_desc.mmap_refcnt);
702 }
703 
704 static void leapraid_fw_mmap_close(struct vm_area_struct *vma)
705 {
706 	struct leapraid_adapter *adapter = vma->vm_private_data;
707 
708 	if (!adapter)
709 		return;
710 
711 	if (atomic_dec_and_test(&adapter->fw_log_desc.mmap_refcnt))
712 		wake_up(&adapter->fw_log_desc.mmap_waitq);
713 	leapraid_ctl_put_adapter(adapter);
714 }
715 
716 static const struct vm_operations_struct leapraid_fw_mmap_vm_ops = {
717 	.open = leapraid_fw_mmap_open,
718 	.close = leapraid_fw_mmap_close,
719 };
720 
721 static int leapraid_fw_mmap(struct file *filp, struct vm_area_struct *vma)
722 {
723 	struct leapraid_adapter *adapter = NULL;
724 	/* Userspace passes the adapter ID via vma->vm_pgoff. */
725 	u32 adapter_id = vma->vm_pgoff;
726 	unsigned long length;
727 	int rc = -EINVAL;
728 
729 	length = vma->vm_end - vma->vm_start;
730 
731 	adapter = leapraid_ctl_lookup_adapter(adapter_id);
732 	if (!adapter) {
733 		pr_err("%s: No adapter found!\n", __func__);
734 		return -EINVAL;
735 	}
736 
737 	if (READ_ONCE(adapter->access_ctrl.host_removing)) {
738 		rc = -EAGAIN;
739 		goto out_put;
740 	}
741 
742 	if (length > (LEAPRAID_SYS_LOG_BUF_SIZE +
743 		      LEAPRAID_SYS_LOG_BUF_RESERVE)) {
744 		dev_err(&adapter->pdev->dev,
745 			"Requested mapping size is too large!\n");
746 		rc = -EINVAL;
747 		goto out_put;
748 	}
749 
750 	if (!adapter->fw_log_desc.fw_log_buffer) {
751 		dev_err(&adapter->pdev->dev, "No log buffer!\n");
752 		rc = -EINVAL;
753 		goto out_put;
754 	}
755 
756 	vma->vm_pgoff = 0;
757 
758 	rc = dma_mmap_coherent(&adapter->pdev->dev, vma,
759 			       adapter->fw_log_desc.fw_log_buffer,
760 			       adapter->fw_log_desc.fw_log_buffer_dma,
761 			       length);
762 	if (rc) {
763 		dev_err(&adapter->pdev->dev,
764 			"Failed to map memory to user space!\n");
765 		goto out_put;
766 	}
767 
768 	vma->vm_private_data = adapter;
769 	vma->vm_ops = &leapraid_fw_mmap_vm_ops;
770 	leapraid_fw_mmap_open(vma);
771 
772 	rc = 0;
773 out_put:
774 	leapraid_ctl_put_adapter(adapter);
775 	return rc;
776 }
777 
778 static const struct file_operations leapraid_ctl_fops = {
779 	.owner = THIS_MODULE,
780 	.unlocked_ioctl = leapraid_ctl_ioctl,
781 	.mmap = leapraid_fw_mmap,
782 };
783 
784 static struct miscdevice leapraid_ctl_dev = {
785 	.minor = MISC_DYNAMIC_MINOR,
786 	.name = LEAPRAID_DEV_NAME,
787 	.fops = &leapraid_ctl_fops,
788 };
789 
790 int leapraid_ctl_init(void)
791 {
792 	if (misc_register(&leapraid_ctl_dev) < 0) {
793 		pr_err("%s Can't register misc device\n",
794 		       LEAPRAID_DRIVER_NAME);
795 		return -ENODEV;
796 	}
797 	return 0;
798 }
799 
800 void leapraid_ctl_exit(void)
801 {
802 	misc_deregister(&leapraid_ctl_dev);
803 }
804