xref: /illumos-gate/usr/src/uts/common/io/cpqary3/cpqary3_ioctl.c (revision 8c69cc8fbe729fa7b091e901c4b50508ccc6bb33)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
14  */
15 
16 /*
17  * Supported IOCTLs :
18  *	CPQARY3_IOCTL_DRIVER_INFO	- to get driver details
19  *	CPQARY3_IOCTL_CTLR_INFO		- to get controller details
20  *	CPQARY3_IOCTL_BMIC_PASS		- to pass BMIC commands
21  *	CPQARY3_IOCTL_SCSI_PASS		- to pass SCSI commands
22  */
23 
24 #include "cpqary3.h"
25 
26 /*
27  * Local Functions Declaration
28  */
29 
30 static int32_t cpqary3_ioctl_send_bmiccmd(cpqary3_t *, cpqary3_bmic_pass_t *,
31     int);
32 static void cpqary3_ioctl_fil_bmic(CommandList_t *, cpqary3_bmic_pass_t *);
33 static void cpqary3_ioctl_fil_bmic_sas(CommandList_t *, cpqary3_bmic_pass_t *);
34 static int32_t cpqary3_ioctl_send_scsicmd(cpqary3_t *, cpqary3_scsi_pass_t *,
35     int);
36 static void cpqary3_ioctl_fil_scsi(CommandList_t *, cpqary3_scsi_pass_t *);
37 
38 /*
39  * Global Variables Definitions
40  */
41 
42 cpqary3_driver_info_t gdriver_info = {0};
43 
44 /* Function Definitions  */
45 
46 /*
47  * Function	:	cpqary3_ioctl_driver_info
48  * Description	:	This routine will get major/ minor versions, Number of
49  *			controllers detected & MAX Number of controllers
50  *			supported
51  * Called By	:	cpqary3_ioctl
52  * Parameters	: 	ioctl_reqp	- address of the parameter sent from
53  *					  the application
54  *			cpqary3p	- address of the PerController structure
55  *			mode		- mode which comes from application
56  * Return Values:	EFAULT on Failure, 0 on SUCCESS
57  */
58 int32_t
59 cpqary3_ioctl_driver_info(uintptr_t ioctl_reqp, int mode)
60 {
61 	cpqary3_ioctl_request_t *request;
62 
63 	request = (cpqary3_ioctl_request_t *)
64 	    MEM_ZALLOC(sizeof (cpqary3_ioctl_request_t));
65 
66 	if (NULL == request)
67 		return (FAILURE);
68 
69 	/*
70 	 * First let us copyin the ioctl_reqp user buffer to request(kernel)
71 	 * memory.  This is very much recomended before we access any of the
72 	 * fields.
73 	 */
74 	if (ddi_copyin((void *)ioctl_reqp, (void *)request,
75 	    sizeof (cpqary3_ioctl_request_t), mode)) {
76 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
77 		return (EFAULT);
78 	}
79 
80 	/*
81 	 * Fill up the global structure "gdriver_info" memory.
82 	 * Fill this structure with available info, which will be copied
83 	 * back later
84 	 */
85 
86 	(void) strcpy(gdriver_info.name, "cpqary3");
87 	gdriver_info.version.minor = CPQARY3_MINOR_REV_NO;
88 	gdriver_info.version.major = CPQARY3_MAJOR_REV_NO;
89 	gdriver_info.version.dd = CPQARY3_REV_MONTH;
90 	gdriver_info.version.mm = CPQARY3_REV_DATE;
91 	gdriver_info.version.yyyy = CPQARY3_REV_YEAR;
92 	gdriver_info.max_num_ctlr = MAX_CTLRS;
93 
94 	/*
95 	 * First Copy out the driver_info structure
96 	 */
97 
98 	if (ddi_copyout((void *)&gdriver_info, (void *)(uintptr_t)request->argp,
99 	    sizeof (cpqary3_driver_info_t), mode)) {
100 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
101 		return (EFAULT);
102 	}
103 
104 	/*
105 	 * Copy out the request structure back
106 	 */
107 
108 	if (ddi_copyout((void *)request, (void *)ioctl_reqp,
109 	    sizeof (cpqary3_ioctl_request_t), mode)) {
110 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
111 		return (EFAULT);
112 	}
113 
114 	MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
115 
116 	/*
117 	 * Everything looks fine. So return SUCCESS
118 	 */
119 
120 	return (SUCCESS);
121 }
122 
123 /*
124  * Function	:	cpqary3_ioctl_ctlr_info
125  * Description	:	This routine will get the controller related info, like
126  * 			board-id, subsystem-id, num of logical drives,
127  * 			slot number
128  * Called By	:	cpqary3_ioctl
129  * Parameters	: 	ioctl_reqp - address of the parameter sent form the
130  *				     application
131  *			cpqary3p   - address of the PerController structure
132  *			mode	   - mode which comes from application
133  * Return Values:	EFAULT on Failure, 0 on SUCCESS
134  */
135 int32_t
136 cpqary3_ioctl_ctlr_info(uintptr_t ioctl_reqp, cpqary3_t *cpqary3p, int mode)
137 {
138 	cpqary3_ioctl_request_t	*request;
139 	cpqary3_ctlr_info_t	*ctlr_info;
140 
141 	request = (cpqary3_ioctl_request_t *)
142 	    MEM_ZALLOC(sizeof (cpqary3_ioctl_request_t));
143 
144 	if (NULL == request)
145 		return (FAILURE);
146 
147 	/*
148 	 * First let us copyin the buffer to kernel memory. This is very much
149 	 * recomended before we access any of the fields.
150 	 */
151 
152 	if (ddi_copyin((void *) ioctl_reqp, (void *)request,
153 	    sizeof (cpqary3_ioctl_request_t), mode)) {
154 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
155 		return (EFAULT);
156 	}
157 
158 	ctlr_info = (cpqary3_ctlr_info_t *)
159 	    MEM_ZALLOC(sizeof (cpqary3_ctlr_info_t));
160 
161 	if (NULL == ctlr_info) {
162 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
163 		return (FAILURE);
164 	}
165 
166 	/*
167 	 * in the driver, board_id is actually subsystem_id
168 	 */
169 
170 	ctlr_info->subsystem_id = cpqary3p->board_id;
171 	ctlr_info->bus = cpqary3p->bus;
172 	ctlr_info->dev = cpqary3p->dev;
173 	ctlr_info->fun = cpqary3p->fun;
174 	ctlr_info->num_of_tgts = cpqary3p->num_of_targets;
175 	ctlr_info->controller_instance = cpqary3p->instance;
176 
177 	/*
178 	 * TODO: ctlr_info.slot_num has to be implemented
179 	 * state & board_id fields are kept for future implementation i
180 	 * if required!
181 	 */
182 
183 	/*
184 	 * First Copy out the ctlr_info structure
185 	 */
186 
187 	if (ddi_copyout((void *)ctlr_info, (void *)(uintptr_t)request->argp,
188 	    sizeof (cpqary3_ctlr_info_t), mode)) {
189 		MEM_SFREE(ctlr_info, sizeof (cpqary3_ctlr_info_t));
190 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
191 		return (EFAULT);
192 	}
193 
194 	/*
195 	 * Copy out the request structure back
196 	 */
197 
198 	if (ddi_copyout((void *)request, (void *)ioctl_reqp,
199 	    sizeof (cpqary3_ioctl_request_t), mode)) {
200 		MEM_SFREE(ctlr_info, sizeof (cpqary3_ctlr_info_t));
201 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
202 		return (EFAULT);
203 	}
204 
205 	MEM_SFREE(ctlr_info, sizeof (cpqary3_ctlr_info_t));
206 	MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
207 
208 	/*
209 	 * Everything looks fine. So return SUCCESS
210 	 */
211 
212 	return (SUCCESS);
213 }
214 
215 /*
216  * Function	:	cpqary3_ioctl_bmic_pass
217  * Description	:	This routine will pass the BMIC commands to controller
218  * Called By	:	cpqary3_ioctl
219  * Parameters	: 	ioctl_reqp - address of the parameter sent from the
220  *				     application
221  *			cpqary3p   - address of the PerController structure
222  *			mode	   - mode which comes directly from application
223  * Return Values:	EFAULT on Failure, 0 on SUCCESS
224  */
225 int32_t
226 cpqary3_ioctl_bmic_pass(uintptr_t ioctl_reqp, cpqary3_t *cpqary3p, int mode)
227 {
228 	cpqary3_ioctl_request_t	*request;
229 	cpqary3_bmic_pass_t 	*bmic_pass;
230 	int32_t			retval = SUCCESS;
231 
232 	request = (cpqary3_ioctl_request_t *)
233 	    MEM_ZALLOC(sizeof (cpqary3_ioctl_request_t));
234 
235 	if (NULL == request)
236 		return (FAILURE);
237 
238 	/*
239 	 * First let us copyin the ioctl_reqp(user) buffer to request(kernel)
240 	 * memory.  This is very much recommended before we access any of the
241 	 * fields.
242 	 */
243 
244 	if (ddi_copyin((void *)ioctl_reqp, (void *)request,
245 	    sizeof (cpqary3_ioctl_request_t), mode)) {
246 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
247 		return (EFAULT);
248 	}
249 
250 	bmic_pass = (cpqary3_bmic_pass_t *)
251 	    MEM_ZALLOC(sizeof (cpqary3_bmic_pass_t));
252 
253 	if (NULL == bmic_pass) {
254 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
255 		return (FAILURE);
256 	}
257 
258 	/*
259 	 * Copy in "cpqary3_bmic_pass_t" structure from argp member
260 	 * of ioctl_reqp.
261 	 */
262 
263 	if (ddi_copyin((void *)(uintptr_t)request->argp, (void *)bmic_pass,
264 	    sizeof (cpqary3_bmic_pass_t), mode)) {
265 		MEM_SFREE(bmic_pass, sizeof (cpqary3_bmic_pass_t));
266 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
267 		return (EFAULT);
268 	}
269 
270 	/*
271 	 * Get the free command list, fill in the bmic command and send it
272 	 * to the controller. This will return 0 on success.
273 	 */
274 
275 	retval = cpqary3_ioctl_send_bmiccmd(cpqary3p, bmic_pass, mode);
276 
277 	/*
278 	 * Now copy the  bmic_pass (kernel) to the user argp
279 	 */
280 
281 	if (ddi_copyout((void *) bmic_pass, (void *)(uintptr_t)request->argp,
282 	    sizeof (cpqary3_bmic_pass_t), mode)) {
283 		MEM_SFREE(bmic_pass, sizeof (cpqary3_bmic_pass_t));
284 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
285 		retval = EFAULT; /* copyout failed */
286 	}
287 
288 	/*
289 	 * Now copy the  request(kernel) to ioctl_reqp(user)
290 	 */
291 
292 	if (ddi_copyout((void *) request, (void *)ioctl_reqp,
293 	    sizeof (cpqary3_ioctl_request_t), mode)) {
294 		MEM_SFREE(bmic_pass, sizeof (cpqary3_bmic_pass_t));
295 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
296 		retval = EFAULT;
297 	}
298 
299 	MEM_SFREE(bmic_pass, sizeof (cpqary3_bmic_pass_t));
300 	MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
301 
302 	return (retval);
303 }
304 
305 /*
306  * Function	:	cpqary3_ioctl_send_bmiccmd
307  * Description	:	This routine will get the free command,
308  *			allocate memory and send it to controller.
309  * Called By	:	cpqary3_ioctl_bmic_pass
310  * Parameters	: 	cpqary3_t - PerController structure
311  *			cpqary3_bmic_pass_t - bmic structure
312  *			mode - mode value sent from application
313  * Return Values:	0 on success
314  *			FAILURE, EFAULT, ETIMEOUT based on the failure
315  */
316 
317 uint32_t cpqary3_ioctl_wait_ms = 30000;
318 
319 static int32_t
320 cpqary3_ioctl_send_bmiccmd(cpqary3_t *cpqary3p,
321     cpqary3_bmic_pass_t *bmic_pass, int mode)
322 {
323 	cpqary3_cmdpvt_t	*memp    = NULL;
324 	CommandList_t		*cmdlist = NULL;
325 	int8_t			*databuf = NULL;
326 	uint8_t			retval  = 0;
327 
328 	/* allocate a command with a dma buffer */
329 	memp = cpqary3_synccmd_alloc(cpqary3p, bmic_pass->buf_len);
330 	if (memp == NULL)
331 		return (FAILURE);
332 
333 	/* Get the databuf when buf_len is greater than zero */
334 	if (bmic_pass->buf_len > 0) {
335 		databuf = memp->driverdata->sg;
336 	}
337 
338 	cmdlist	= memp->cmdlist_memaddr;
339 
340 	/*
341 	 * If io_direction is CPQARY3_SCSI_OUT, we have to copy user buffer
342 	 * to databuf
343 	 */
344 
345 	if (bmic_pass->io_direction == CPQARY3_SCSI_OUT) {
346 		/* Do a copyin when buf_len is greater than zero */
347 		if (bmic_pass->buf_len > 0) {
348 			if (ddi_copyin((void*)(uintptr_t)(bmic_pass->buf),
349 			    (void*)databuf, bmic_pass->buf_len, mode)) {
350 				cpqary3_synccmd_free(cpqary3p, memp);
351 				return (EFAULT);
352 			}
353 		}
354 	}
355 
356 	/*
357 	 * Now fill the command as per the BMIC
358 	 */
359 	if (cpqary3p->bddef->bd_flags & SA_BD_SAS) {
360 		cpqary3_ioctl_fil_bmic_sas(cmdlist, bmic_pass);
361 	} else {
362 		cpqary3_ioctl_fil_bmic(cmdlist, bmic_pass);
363 	}
364 
365 
366 	/* PERF */
367 
368 	memp->complete = cpqary3_synccmd_complete;
369 
370 	/* PERF */
371 
372 	/* send command to controller and wait for a reply */
373 	if (cpqary3_synccmd_send(cpqary3p, memp, cpqary3_ioctl_wait_ms,
374 	    CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) {
375 		cpqary3_synccmd_free(cpqary3p, memp);
376 		return (ETIMEDOUT);
377 	}
378 
379 	/*
380 	 * Now the command is completed and copy the buffers back
381 	 * First copy the buffer databuf to bmic_pass.buf
382 	 * which is used as a buffer before passing the command to the
383 	 * controller.
384 	 */
385 
386 	if (bmic_pass->io_direction == CPQARY3_SCSI_IN) {
387 		/* Do a copyout when buf_len is greater than zero */
388 		if (bmic_pass->buf_len > 0) {
389 			if (ddi_copyout((void *)databuf,
390 			    (void *)(uintptr_t)bmic_pass->buf,
391 			    bmic_pass->buf_len, mode)) {
392 				retval = EFAULT;
393 			}
394 		}
395 	}
396 
397 	/*
398 	 * This is case where the command completes with error,
399 	 * Then tag would have set its 1st(10) bit.
400 	 */
401 
402 	if (cmdlist->Header.Tag.drvinfo_n_err == CPQARY3_SYNCCMD_FAILURE) {
403 		bmic_pass->err_status = 1;
404 		bcopy((caddr_t)memp->errorinfop, &bmic_pass->err_info,
405 		    sizeof (ErrorInfo_t));
406 		switch (memp->errorinfop->CommandStatus) {
407 		case CISS_CMD_DATA_OVERRUN :
408 		case CISS_CMD_DATA_UNDERRUN :
409 		case CISS_CMD_SUCCESS :
410 		case CISS_CMD_TARGET_STATUS :
411 			retval = SUCCESS;
412 			break;
413 		default :
414 			retval = EIO;
415 			break;
416 		}
417 	}
418 
419 	cpqary3_synccmd_free(cpqary3p, memp);
420 
421 	return (retval);
422 }
423 
424 /*
425  * Function	:	cpqary3_ioctl_fil_bmic
426  * Description	:	This routine will fill the cmdlist with BMIC details
427  * Called By	:	cpqary3_ioctl_send_bmiccmd
428  * Parameters	: 	cmdlist 	- command packet
429  *			bmic_pass 	- bmic structure
430  * Return Values:	void
431  */
432 static void
433 cpqary3_ioctl_fil_bmic(CommandList_t *cmdlist,
434     cpqary3_bmic_pass_t *bmic_pass)
435 {
436 	cmdlist->Header.SGTotal = 1;
437 	cmdlist->Header.SGList = 1;
438 	cmdlist->Request.CDBLen = bmic_pass->cmd_len;
439 	cmdlist->Request.Timeout = bmic_pass->timeout;
440 	cmdlist->Request.Type.Type = CISS_TYPE_CMD;
441 	cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
442 
443 	switch (bmic_pass->io_direction) {
444 	case CPQARY3_SCSI_OUT:
445 		cmdlist->Request.Type.Direction = CISS_XFER_WRITE;
446 		break;
447 	case CPQARY3_SCSI_IN:
448 		cmdlist->Request.Type.Direction = CISS_XFER_READ;
449 		break;
450 	case CPQARY3_NODATA_XFER:
451 		cmdlist->Request.Type.Direction = CISS_XFER_NONE;
452 		break;
453 	default:
454 		cmdlist->Request.Type.Direction = CISS_XFER_RSVD;
455 		break;
456 	}
457 
458 	cmdlist ->Request.CDB[0] =
459 	    (bmic_pass->io_direction == CPQARY3_SCSI_IN) ? 0x26: 0x27;
460 	cmdlist ->Request.CDB[1] = bmic_pass->unit_number; /* Unit Number */
461 
462 	/*
463 	 * BMIC Detail - bytes 2[MSB] to 5[LSB]
464 	 */
465 
466 	cmdlist->Request.CDB[2] = (bmic_pass->blk_number >> 24) & 0xff;
467 	cmdlist->Request.CDB[3] = (bmic_pass->blk_number >> 16) & 0xff;
468 	cmdlist->Request.CDB[4] = (bmic_pass->blk_number >> 8) & 0xff;
469 	cmdlist->Request.CDB[5] = bmic_pass->blk_number;
470 
471 	cmdlist->Request.CDB[6] = bmic_pass->cmd; /* BMIC Command */
472 
473 	/* Transfer Length - bytes 7[MSB] to 8[LSB] */
474 
475 	cmdlist->Request.CDB[7] = (bmic_pass->buf_len >> 8) & 0xff;
476 	cmdlist->Request.CDB[8] = bmic_pass->buf_len & 0xff;
477 	cmdlist->Request.CDB[9] = 0x00; /* Reserved */
478 
479 	/*
480 	 * Copy the Lun address from the request
481 	 */
482 
483 	bcopy(&bmic_pass->lun_addr[0], &(cmdlist->Header.LUN),
484 	    sizeof (LUNAddr_t));
485 	cmdlist->SG[0].Len = bmic_pass->buf_len;
486 }
487 
488 /*
489  * Function	:	cpqary3_ioctl_scsi_pass
490  * Description	:	This routine will pass the SCSI commands to controller
491  * Called By	:	cpqary3_ioctl
492  * Parameters	:  	ioctl_reqp - address of the parameter sent
493  *				     from the application
494  *			cpqary3p   - Addess of the percontroller stucture
495  *			mode       - mode which comes directly from application
496  * Return Values:	EFAULT on Failure, 0 on SUCCESS
497  */
498 int32_t
499 cpqary3_ioctl_scsi_pass(uintptr_t ioctl_reqp, cpqary3_t *cpqary3p, int mode)
500 {
501 	cpqary3_ioctl_request_t	*request;
502 	cpqary3_scsi_pass_t 	*scsi_pass;
503 	int32_t			retval = SUCCESS;
504 
505 	request = (cpqary3_ioctl_request_t *)
506 	    MEM_ZALLOC(sizeof (cpqary3_ioctl_request_t));
507 
508 	if (NULL == request)
509 		return (FAILURE);
510 
511 	/*
512 	 * First let us copyin the ioctl_reqp(user) buffer to request(kernel)
513 	 * memory.  * This is very much recommended before we access any of
514 	 * the fields.
515 	 */
516 
517 	if (ddi_copyin((void *)ioctl_reqp, (void *)request,
518 	    sizeof (cpqary3_ioctl_request_t), mode)) {
519 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
520 		return (EFAULT);
521 	}
522 
523 	scsi_pass = (cpqary3_scsi_pass_t *)
524 	    MEM_ZALLOC(sizeof (cpqary3_scsi_pass_t));
525 
526 	if (NULL == scsi_pass) {
527 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
528 		return (FAILURE);
529 	}
530 
531 	/*
532 	 * Copy in "cpqary3_scsi_pass_t" structure from argp member
533 	 * of ioctl_reqp.
534 	 */
535 
536 	if (ddi_copyin((void *)(uintptr_t)request->argp, (void *)scsi_pass,
537 	    sizeof (cpqary3_scsi_pass_t), mode)) {
538 		MEM_SFREE(scsi_pass, sizeof (cpqary3_scsi_pass_t));
539 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
540 		return (EFAULT);
541 	}
542 
543 	/*
544 	 * Get the free command list, fill in the scsi command and send it
545 	 * to the controller. This will return 0 on success.
546 	 */
547 
548 	retval = cpqary3_ioctl_send_scsicmd(cpqary3p, scsi_pass, mode);
549 
550 	/*
551 	 * Now copy the  scsi_pass (kernel) to the user argp
552 	 */
553 
554 	if (ddi_copyout((void *)scsi_pass, (void *)(uintptr_t)request->argp,
555 	    sizeof (cpqary3_scsi_pass_t), mode)) {
556 		MEM_SFREE(scsi_pass, sizeof (cpqary3_scsi_pass_t));
557 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
558 		retval = EFAULT; /* copyout failed */
559 	}
560 
561 	/*
562 	 * Now copy the  request(kernel) to ioctl_reqp(user)
563 	 */
564 
565 	if (ddi_copyout((void *)request, (void *)ioctl_reqp,
566 	    sizeof (cpqary3_ioctl_request_t), mode)) {
567 		MEM_SFREE(scsi_pass, sizeof (cpqary3_scsi_pass_t));
568 		MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
569 		retval = EFAULT;
570 	}
571 
572 	MEM_SFREE(scsi_pass, sizeof (cpqary3_scsi_pass_t));
573 	MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
574 
575 	return (retval);
576 }
577 
578 /*
579  * Function	:	cpqary3_ioctl_send_scsiccmd
580  * Description	:	This routine will pass the SCSI commands to controller
581  * Called By	:	cpqary3_ioctl_scsi_pass
582  * Parameters	: 	cpqary3_t		- PerController structure,
583  *			cpqary3_scsi_pass_t	- scsi parameter
584  *			mode			- sent from the application
585  * Return Values:	0 on success
586  *			FAILURE, EFAULT, ETIMEOUT based on the failure
587  */
588 static int32_t
589 cpqary3_ioctl_send_scsicmd(cpqary3_t *cpqary3p,
590     cpqary3_scsi_pass_t *scsi_pass, int mode)
591 {
592 	cpqary3_cmdpvt_t	*memp    = NULL;
593 	CommandList_t		*cmdlist = NULL;
594 	int8_t			*databuf = NULL;
595 	uint8_t			retval  = 0;
596 	NoeBuffer		*evt;
597 	uint16_t		drive = 0;
598 
599 	/* allocate a command with a dma buffer */
600 	memp = cpqary3_synccmd_alloc(cpqary3p, scsi_pass->buf_len);
601 	if (memp == NULL)
602 		return (FAILURE);
603 
604 	/* Get the databuf when buf_len is greater than zero */
605 	if (scsi_pass->buf_len > 0) {
606 		databuf = memp->driverdata->sg;
607 	}
608 
609 	cmdlist	= memp->cmdlist_memaddr;
610 
611 	if (scsi_pass->io_direction == CPQARY3_SCSI_OUT) {
612 		/* Do a copyin when buf_len is greater than zero */
613 		if (scsi_pass->buf_len > 0) {
614 			if (ddi_copyin((void*)(uintptr_t)(scsi_pass->buf),
615 			    (void*)databuf, scsi_pass->buf_len, mode)) {
616 				cpqary3_synccmd_free(cpqary3p, memp);
617 				return (EFAULT);
618 			}
619 		}
620 	}
621 
622 	/*
623 	 * Fill the scsi command
624 	 */
625 	cpqary3_ioctl_fil_scsi(cmdlist, scsi_pass);
626 
627 	/* PERF */
628 	memp->complete = cpqary3_synccmd_complete;
629 	/* PERF */
630 
631 	/* send command to controller and wait for a reply */
632 	if (cpqary3_synccmd_send(cpqary3p, memp, cpqary3_ioctl_wait_ms,
633 	    CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) {
634 		cpqary3_synccmd_free(cpqary3p, memp);
635 		return (ETIMEDOUT);
636 	}
637 
638 	/*
639 	 * If the command sent is NOE
640 	 * if the event class is CLASS_LOGICAL_DRIVE
641 	 * if the subcalls code is zero and if detail change is zero
642 	 * if the event specific data[3] is either 1 or 2 ie., if
643 	 * if the logical drive is failed set the target type to
644 	 * CPQARY3_TARGET_NONE
645 	 */
646 
647 	/* NOE */
648 	if (cpqary3p->noe_support == 0 &&
649 	    cmdlist->Request.CDB[0] == 0x26 &&
650 	    cmdlist->Request.CDB[6] == BMIC_NOTIFY_ON_EVENT) {
651 
652 		evt = (NoeBuffer*)MEM2DRVPVT(memp)->sg;
653 
654 		if (evt->event_class_code == CLASS_LOGICAL_DRIVE &&
655 		    evt->event_subclass_code == SUB_CLASS_STATUS &&
656 		    evt->event_detail_code == DETAIL_CHANGE &&
657 		    evt->event_specific_data[3] == 1) {
658 			/* LINTED: alignment */
659 			drive =	*(uint16_t *)(&evt->event_specific_data[0]);
660 			drive = ((drive < CTLR_SCSI_ID) ?
661 			    drive : drive + CPQARY3_TGT_ALIGNMENT);
662 
663 			if (cpqary3p && cpqary3p->cpqary3_tgtp[drive]) {
664 				cpqary3p->cpqary3_tgtp[drive]->type =
665 				    CPQARY3_TARGET_NONE;
666 			}
667 		}
668 	}
669 
670 	/*
671 	 * Now the command is completed and copy the buffers back
672 	 * First copy the buffer databuf to scsi_pass->buf
673 	 * which is used as a buffer before passing the command to the
674 	 * controller.
675 	 */
676 
677 	if (scsi_pass->io_direction == CPQARY3_SCSI_IN) {
678 		if (scsi_pass->buf_len > 0) {
679 			if (ddi_copyout((void *)databuf,
680 			    (void *)(uintptr_t)scsi_pass->buf,
681 			    scsi_pass->buf_len, mode)) {
682 				retval = EFAULT;
683 			}
684 		}
685 	}
686 
687 	/*
688 	 * This is case where the command completes with error,
689 	 * Then tag would have set its 1st(10) bit.
690 	 */
691 
692 	if (cmdlist->Header.Tag.drvinfo_n_err == CPQARY3_SYNCCMD_FAILURE) {
693 		scsi_pass->err_status = 1;
694 		bcopy((caddr_t)memp->errorinfop, &scsi_pass->err_info,
695 		    sizeof (ErrorInfo_t));
696 		switch (memp->errorinfop->CommandStatus) {
697 		case CISS_CMD_DATA_OVERRUN:
698 		case CISS_CMD_DATA_UNDERRUN:
699 		case CISS_CMD_SUCCESS:
700 		case CISS_CMD_TARGET_STATUS:
701 			retval = SUCCESS;
702 			break;
703 		default:
704 			retval = EIO;
705 			break;
706 		}
707 	}
708 
709 	cpqary3_synccmd_free(cpqary3p, memp);
710 
711 	return (retval);
712 }
713 
714 /*
715  * Function	:	cpqary3_ioctl_fil_scsi_
716  * Description	:	This routine will fill the cmdlist with SCSI CDB
717  * Called By	:	cpqary3_ioctl_send_scsicmd
718  * Parameters	: 	cmdlist			- command packet
719  *			cpqary3_scsi_pass_t	- scsi parameter
720  * Return Values:	void
721  */
722 static void
723 cpqary3_ioctl_fil_scsi(CommandList_t *cmdlist,
724 			cpqary3_scsi_pass_t *scsi_pass)
725 {
726 	cmdlist->Header.SGTotal = 1;
727 	cmdlist->Header.SGList = 1;
728 	cmdlist->Request.CDBLen = scsi_pass->cdb_len;
729 	cmdlist->Request.Timeout = scsi_pass->timeout;
730 	cmdlist->Request.Type.Type = CISS_TYPE_CMD;
731 	cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
732 
733 	switch (scsi_pass->io_direction) {
734 	case CPQARY3_SCSI_OUT:
735 		cmdlist->Request.Type.Direction = CISS_XFER_WRITE;
736 		break;
737 	case CPQARY3_SCSI_IN:
738 		cmdlist->Request.Type.Direction = CISS_XFER_READ;
739 		break;
740 	case CPQARY3_NODATA_XFER:
741 		cmdlist->Request.Type.Direction = CISS_XFER_NONE;
742 		break;
743 	default:
744 		cmdlist->Request.Type.Direction = CISS_XFER_RSVD;
745 		break;
746 	}
747 
748 	/*
749 	 * Copy the SCSI CDB as is
750 	 */
751 
752 	bcopy(&scsi_pass->cdb[0], &cmdlist->Request.CDB[0],
753 	    scsi_pass->cdb_len);
754 
755 	/*
756 	 * Copy the Lun address from the request
757 	 */
758 
759 	bcopy(&scsi_pass->lun_addr[0], &(cmdlist->Header.LUN),
760 	    sizeof (LUNAddr_t));
761 
762 	cmdlist->SG[0].Len 	= scsi_pass->buf_len;
763 }
764 
765 /*
766  * Function	:	cpqary3_ioctl_fil_bmic_sas
767  * Description	:	This routine will fill the cmdlist with BMIC details
768  * Called By	:	cpqary3_ioctl_send_bmiccmd
769  * Parameters	: 	cmdlist 	- command packet
770  *			bmic_pass 	- bmic structure
771  * Return Values:	void
772  */
773 static void
774 cpqary3_ioctl_fil_bmic_sas(CommandList_t *cmdlist,
775     cpqary3_bmic_pass_t *bmic_pass)
776 {
777 	cmdlist->Header.SGTotal = 1;
778 	cmdlist->Header.SGList = 1;
779 	cmdlist->Request.CDBLen = bmic_pass->cmd_len;
780 	cmdlist->Request.Timeout = bmic_pass->timeout;
781 	cmdlist->Request.Type.Type = CISS_TYPE_CMD;
782 	cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
783 
784 	switch (bmic_pass->io_direction) {
785 		case CPQARY3_SCSI_OUT:
786 			cmdlist->Request.Type.Direction = CISS_XFER_WRITE;
787 			break;
788 		case CPQARY3_SCSI_IN:
789 			cmdlist->Request.Type.Direction = CISS_XFER_READ;
790 			break;
791 		case CPQARY3_NODATA_XFER:
792 			cmdlist->Request.Type.Direction = CISS_XFER_NONE;
793 			break;
794 		default:
795 			cmdlist->Request.Type.Direction = CISS_XFER_RSVD;
796 			break;
797 	}
798 
799 	cmdlist->Request.CDB[0] =
800 	    (bmic_pass->io_direction == CPQARY3_SCSI_IN) ? 0x26: 0x27;
801 	cmdlist->Request.CDB[1] = bmic_pass->unit_number; /* Unit Number */
802 
803 	/*
804 	 * BMIC Detail - bytes 2[MSB] to 5[LSB]
805 	 */
806 
807 	cmdlist->Request.CDB[2] = (bmic_pass->blk_number >> 24) & 0xff;
808 	cmdlist->Request.CDB[3] = (bmic_pass->blk_number >> 16) & 0xff;
809 	cmdlist->Request.CDB[4] = (bmic_pass->blk_number >> 8) & 0xff;
810 	cmdlist->Request.CDB[5] = bmic_pass->blk_number;
811 
812 	cmdlist->Request.CDB[6] = bmic_pass->cmd; /* BMIC Command */
813 
814 	/* Transfer Length - bytes 7[MSB] to 8[LSB] */
815 
816 	cmdlist->Request.CDB[7] = (bmic_pass->buf_len >> 8) & 0xff;
817 	cmdlist->Request.CDB[8] = bmic_pass->buf_len & 0xff;
818 	cmdlist->Request.CDB[9] = 0x00; /* Reserved */
819 
820 	/* Update CDB[2] = LSB bmix_index and CDB[9] = MSB bmic_index */
821 	switch (bmic_pass->cmd) {
822 	case HPSAS_ID_PHYSICAL_DRIVE:
823 	case HPSAS_TAPE_INQUIRY:
824 	case HPSAS_SENSE_MP_STAT:
825 	case HPSAS_SET_MP_THRESHOLD:
826 	case HPSAS_MP_PARAM_CONTROL:
827 	case HPSAS_SENSE_DRV_ERR_LOG:
828 	case HPSAS_SET_MP_VALUE:
829 		cmdlist -> Request.CDB[2] = bmic_pass->bmic_index & 0xff;
830 		cmdlist -> Request.CDB[9] = (bmic_pass->bmic_index >>8) & 0xff;
831 		break;
832 
833 	case HPSAS_ID_LOG_DRIVE:
834 	case HPSAS_SENSE_LOG_DRIVE:
835 	case HPSAS_READ:
836 	case HPSAS_WRITE:
837 	case HPSAS_WRITE_THROUGH:
838 	case HPSAS_SENSE_CONFIG:
839 	case HPSAS_SET_CONFIG:
840 	case HPSAS_BYPASS_VOL_STATE:
841 	case HPSAS_CHANGE_CONFIG:
842 	case HPSAS_SENSE_ORIG_CONFIG:
843 	case HPSAS_LABEL_LOG_DRIVE:
844 		/* Unit Number MSB */
845 		cmdlist->Request.CDB[9] = (bmic_pass->unit_number >> 8) & 0xff;
846 		break;
847 
848 	default:
849 		break;
850 	}
851 
852 
853 	/*
854 	 * Copy the Lun address from the request
855 	 */
856 
857 	bcopy(&bmic_pass->lun_addr[0], &(cmdlist->Header.LUN),
858 	    sizeof (LUNAddr_t));
859 
860 	cmdlist->SG[0].Len = bmic_pass->buf_len;
861 }
862