mps_user.c revision 216088
1/*-
2 * Copyright (c) 2008 Yahoo!, Inc.
3 * All rights reserved.
4 * Written by: John Baldwin <jhb@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the names of any co-contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * LSI MPS-Fusion Host Adapter FreeBSD userland interface
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: head/sys/dev/mps/mps_user.c 216088 2010-11-30 22:39:46Z ken $");
35
36#include "opt_compat.h"
37
38#include <sys/types.h>
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/kernel.h>
42#include <sys/selinfo.h>
43#include <sys/module.h>
44#include <sys/bus.h>
45#include <sys/conf.h>
46#include <sys/bio.h>
47#include <sys/malloc.h>
48#include <sys/uio.h>
49#include <sys/sysctl.h>
50#include <sys/ioccom.h>
51#include <sys/endian.h>
52#include <sys/proc.h>
53#include <sys/sysent.h>
54
55#include <machine/bus.h>
56#include <machine/resource.h>
57#include <sys/rman.h>
58
59#include <cam/scsi/scsi_all.h>
60
61#include <dev/mps/mpi/mpi2_type.h>
62#include <dev/mps/mpi/mpi2.h>
63#include <dev/mps/mpi/mpi2_ioc.h>
64#include <dev/mps/mpi/mpi2_cnfg.h>
65#include <dev/mps/mpsvar.h>
66#include <dev/mps/mps_table.h>
67#include <dev/mps/mps_ioctl.h>
68
69static d_open_t		mps_open;
70static d_close_t	mps_close;
71static d_ioctl_t	mps_ioctl_devsw;
72
73static struct cdevsw mps_cdevsw = {
74	.d_version =	D_VERSION,
75	.d_flags =	0,
76	.d_open =	mps_open,
77	.d_close =	mps_close,
78	.d_ioctl =	mps_ioctl_devsw,
79	.d_name =	"mps",
80};
81
82typedef int (mps_user_f)(struct mps_command *, struct mps_usr_command *);
83static mps_user_f	mpi_pre_ioc_facts;
84static mps_user_f	mpi_pre_port_facts;
85static mps_user_f	mpi_pre_fw_download;
86static mps_user_f	mpi_pre_fw_upload;
87static mps_user_f	mpi_pre_sata_passthrough;
88static mps_user_f	mpi_pre_smp_passthrough;
89static mps_user_f	mpi_pre_config;
90static mps_user_f	mpi_pre_sas_io_unit_control;
91
92static int mps_user_read_cfg_header(struct mps_softc *,
93				    struct mps_cfg_page_req *);
94static int mps_user_read_cfg_page(struct mps_softc *,
95				  struct mps_cfg_page_req *, void *);
96static int mps_user_read_extcfg_header(struct mps_softc *,
97				     struct mps_ext_cfg_page_req *);
98static int mps_user_read_extcfg_page(struct mps_softc *,
99				     struct mps_ext_cfg_page_req *, void *);
100static int mps_user_write_cfg_page(struct mps_softc *,
101				   struct mps_cfg_page_req *, void *);
102static int mps_user_setup_request(struct mps_command *,
103				  struct mps_usr_command *);
104static int mps_user_command(struct mps_softc *, struct mps_usr_command *);
105
106static MALLOC_DEFINE(M_MPSUSER, "mps_user", "Buffers for mps(4) ioctls");
107
108int
109mps_attach_user(struct mps_softc *sc)
110{
111	int unit;
112
113	unit = device_get_unit(sc->mps_dev);
114	sc->mps_cdev = make_dev(&mps_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640,
115	    "mps%d", unit);
116	if (sc->mps_cdev == NULL) {
117		return (ENOMEM);
118	}
119	sc->mps_cdev->si_drv1 = sc;
120	return (0);
121}
122
123void
124mps_detach_user(struct mps_softc *sc)
125{
126
127	/* XXX: do a purge of pending requests? */
128	destroy_dev(sc->mps_cdev);
129
130}
131
132static int
133mps_open(struct cdev *dev, int flags, int fmt, struct thread *td)
134{
135
136	return (0);
137}
138
139static int
140mps_close(struct cdev *dev, int flags, int fmt, struct thread *td)
141{
142
143	return (0);
144}
145
146static int
147mps_user_read_cfg_header(struct mps_softc *sc,
148    struct mps_cfg_page_req *page_req)
149{
150	MPI2_CONFIG_PAGE_HEADER *hdr;
151	struct mps_config_params params;
152	int	    error;
153
154	hdr = &params.hdr.Struct;
155	params.action = MPI2_CONFIG_ACTION_PAGE_HEADER;
156	params.page_address = le32toh(page_req->page_address);
157	hdr->PageVersion = 0;
158	hdr->PageLength = 0;
159	hdr->PageNumber = page_req->header.PageNumber;
160	hdr->PageType = page_req->header.PageType;
161	params.buffer = NULL;
162	params.length = 0;
163	params.callback = NULL;
164
165	if ((error = mps_read_config_page(sc, &params)) != 0) {
166		/*
167		 * Leave the request. Without resetting the chip, it's
168		 * still owned by it and we'll just get into trouble
169		 * freeing it now. Mark it as abandoned so that if it
170		 * shows up later it can be freed.
171		 */
172		mps_printf(sc, "read_cfg_header timed out\n");
173		return (ETIMEDOUT);
174	}
175
176	page_req->ioc_status = htole16(params.status);
177	if ((page_req->ioc_status & MPI2_IOCSTATUS_MASK) ==
178	    MPI2_IOCSTATUS_SUCCESS) {
179		bcopy(hdr, &page_req->header, sizeof(page_req->header));
180	}
181
182	return (0);
183}
184
185static int
186mps_user_read_cfg_page(struct mps_softc *sc, struct mps_cfg_page_req *page_req,
187    void *buf)
188{
189	MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr;
190	struct mps_config_params params;
191	int	      error;
192
193	reqhdr = buf;
194	hdr = &params.hdr.Struct;
195	hdr->PageVersion = reqhdr->PageVersion;
196	hdr->PageLength = reqhdr->PageLength;
197	hdr->PageNumber = reqhdr->PageNumber;
198	hdr->PageType = reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK;
199	params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
200	params.page_address = le32toh(page_req->page_address);
201	params.buffer = buf;
202	params.length = le32toh(page_req->len);
203	params.callback = NULL;
204
205	if ((error = mps_read_config_page(sc, &params)) != 0) {
206		mps_printf(sc, "mps_user_read_cfg_page timed out\n");
207		return (ETIMEDOUT);
208	}
209
210	page_req->ioc_status = htole16(params.status);
211	return (0);
212}
213
214static int
215mps_user_read_extcfg_header(struct mps_softc *sc,
216    struct mps_ext_cfg_page_req *ext_page_req)
217{
218	MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr;
219	struct mps_config_params params;
220	int	    error;
221
222	hdr = &params.hdr.Ext;
223	params.action = MPI2_CONFIG_ACTION_PAGE_HEADER;
224	hdr->PageVersion = ext_page_req->header.PageVersion;
225	hdr->ExtPageLength = 0;
226	hdr->PageNumber = ext_page_req->header.PageNumber;
227	hdr->ExtPageType = ext_page_req->header.ExtPageType;
228	params.page_address = le32toh(ext_page_req->page_address);
229	if ((error = mps_read_config_page(sc, &params)) != 0) {
230		/*
231		 * Leave the request. Without resetting the chip, it's
232		 * still owned by it and we'll just get into trouble
233		 * freeing it now. Mark it as abandoned so that if it
234		 * shows up later it can be freed.
235		 */
236		mps_printf(sc, "mps_user_read_extcfg_header timed out\n");
237		return (ETIMEDOUT);
238	}
239
240	ext_page_req->ioc_status = htole16(params.status);
241	if ((ext_page_req->ioc_status & MPI2_IOCSTATUS_MASK) ==
242	    MPI2_IOCSTATUS_SUCCESS) {
243		ext_page_req->header.PageVersion = hdr->PageVersion;
244		ext_page_req->header.PageNumber = hdr->PageNumber;
245		ext_page_req->header.PageType = hdr->PageType;
246		ext_page_req->header.ExtPageLength = hdr->ExtPageLength;
247		ext_page_req->header.ExtPageType = hdr->ExtPageType;
248	}
249
250	return (0);
251}
252
253static int
254mps_user_read_extcfg_page(struct mps_softc *sc,
255    struct mps_ext_cfg_page_req *ext_page_req, void *buf)
256{
257	MPI2_CONFIG_EXTENDED_PAGE_HEADER *reqhdr, *hdr;
258	struct mps_config_params params;
259	int error;
260
261	reqhdr = buf;
262	hdr = &params.hdr.Ext;
263	params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
264	params.page_address = le32toh(ext_page_req->page_address);
265	hdr->PageVersion = reqhdr->PageVersion;
266	hdr->PageNumber = reqhdr->PageNumber;
267	hdr->ExtPageType = reqhdr->ExtPageType;
268	hdr->ExtPageLength = reqhdr->ExtPageLength;
269	params.buffer = buf;
270	params.length = le32toh(ext_page_req->len);
271	params.callback = NULL;
272
273	if ((error = mps_read_config_page(sc, &params)) != 0) {
274		mps_printf(sc, "mps_user_read_extcfg_page timed out\n");
275		return (ETIMEDOUT);
276	}
277
278	ext_page_req->ioc_status = htole16(params.status);
279	return (0);
280}
281
282static int
283mps_user_write_cfg_page(struct mps_softc *sc,
284    struct mps_cfg_page_req *page_req, void *buf)
285{
286	MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr;
287	struct mps_config_params params;
288	u_int	      hdr_attr;
289	int	      error;
290
291	reqhdr = buf;
292	hdr = &params.hdr.Struct;
293	hdr_attr = reqhdr->PageType & MPI2_CONFIG_PAGEATTR_MASK;
294	if (hdr_attr != MPI2_CONFIG_PAGEATTR_CHANGEABLE &&
295	    hdr_attr != MPI2_CONFIG_PAGEATTR_PERSISTENT) {
296		mps_printf(sc, "page type 0x%x not changeable\n",
297			reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK);
298		return (EINVAL);
299	}
300
301	/*
302	 * There isn't any point in restoring stripped out attributes
303	 * if you then mask them going down to issue the request.
304	 */
305
306	hdr->PageVersion = reqhdr->PageVersion;
307	hdr->PageLength = reqhdr->PageLength;
308	hdr->PageNumber = reqhdr->PageNumber;
309	hdr->PageType = reqhdr->PageType;
310	params.action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
311	params.page_address = le32toh(page_req->page_address);
312	params.buffer = buf;
313	params.length = le32toh(page_req->len);
314	params.callback = NULL;
315
316	if ((error = mps_write_config_page(sc, &params)) != 0) {
317		mps_printf(sc, "mps_write_cfg_page timed out\n");
318		return (ETIMEDOUT);
319	}
320
321	page_req->ioc_status = htole16(params.status);
322	return (0);
323}
324
325void
326mpi_init_sge(struct mps_command *cm, void *req, void *sge)
327{
328	int off, space;
329
330	space = (int)cm->cm_sc->facts->IOCRequestFrameSize * 4;
331	off = (uintptr_t)sge - (uintptr_t)req;
332
333	KASSERT(off < space, ("bad pointers %p %p, off %d, space %d",
334            req, sge, off, space));
335
336	cm->cm_sge = sge;
337	cm->cm_sglsize = space - off;
338}
339
340/*
341 * Prepare the mps_command for an IOC_FACTS request.
342 */
343static int
344mpi_pre_ioc_facts(struct mps_command *cm, struct mps_usr_command *cmd)
345{
346	MPI2_IOC_FACTS_REQUEST *req = (void *)cm->cm_req;
347	MPI2_IOC_FACTS_REPLY *rpl;
348
349	if (cmd->req_len != sizeof *req)
350		return (EINVAL);
351	if (cmd->rpl_len != sizeof *rpl)
352		return (EINVAL);
353
354	cm->cm_sge = NULL;
355	cm->cm_sglsize = 0;
356	return (0);
357}
358
359/*
360 * Prepare the mps_command for a PORT_FACTS request.
361 */
362static int
363mpi_pre_port_facts(struct mps_command *cm, struct mps_usr_command *cmd)
364{
365	MPI2_PORT_FACTS_REQUEST *req = (void *)cm->cm_req;
366	MPI2_PORT_FACTS_REPLY *rpl;
367
368	if (cmd->req_len != sizeof *req)
369		return (EINVAL);
370	if (cmd->rpl_len != sizeof *rpl)
371		return (EINVAL);
372
373	cm->cm_sge = NULL;
374	cm->cm_sglsize = 0;
375	return (0);
376}
377
378/*
379 * Prepare the mps_command for a FW_DOWNLOAD request.
380 */
381static int
382mpi_pre_fw_download(struct mps_command *cm, struct mps_usr_command *cmd)
383{
384	MPI2_FW_DOWNLOAD_REQUEST *req = (void *)cm->cm_req;
385	MPI2_FW_DOWNLOAD_REPLY *rpl;
386	MPI2_FW_DOWNLOAD_TCSGE tc;
387	int error;
388
389	/*
390	 * This code assumes there is room in the request's SGL for
391	 * the TransactionContext plus at least a SGL chain element.
392	 */
393	CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE);
394
395	if (cmd->req_len != sizeof *req)
396		return (EINVAL);
397	if (cmd->rpl_len != sizeof *rpl)
398		return (EINVAL);
399
400	if (cmd->len == 0)
401		return (EINVAL);
402
403	error = copyin(cmd->buf, cm->cm_data, cmd->len);
404	if (error != 0)
405		return (error);
406
407	mpi_init_sge(cm, req, &req->SGL);
408	bzero(&tc, sizeof tc);
409
410	/*
411	 * For now, the F/W image must be provided in a single request.
412	 */
413	if ((req->MsgFlags & MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT) == 0)
414		return (EINVAL);
415	if (req->TotalImageSize != cmd->len)
416		return (EINVAL);
417
418	/*
419	 * The value of the first two elements is specified in the
420	 * Fusion-MPT Message Passing Interface document.
421	 */
422	tc.ContextSize = 0;
423	tc.DetailsLength = 12;
424	tc.ImageOffset = 0;
425	tc.ImageSize = cmd->len;
426
427	cm->cm_flags |= MPS_CM_FLAGS_DATAOUT;
428
429	return (mps_push_sge(cm, &tc, sizeof tc, 0));
430}
431
432/*
433 * Prepare the mps_command for a FW_UPLOAD request.
434 */
435static int
436mpi_pre_fw_upload(struct mps_command *cm, struct mps_usr_command *cmd)
437{
438	MPI2_FW_UPLOAD_REQUEST *req = (void *)cm->cm_req;
439	MPI2_FW_UPLOAD_REPLY *rpl;
440	MPI2_FW_UPLOAD_TCSGE tc;
441
442	/*
443	 * This code assumes there is room in the request's SGL for
444	 * the TransactionContext plus at least a SGL chain element.
445	 */
446	CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE);
447
448	if (cmd->req_len != sizeof *req)
449		return (EINVAL);
450	if (cmd->rpl_len != sizeof *rpl)
451		return (EINVAL);
452
453	mpi_init_sge(cm, req, &req->SGL);
454	if (cmd->len == 0) {
455		/* Perhaps just asking what the size of the fw is? */
456		return (0);
457	}
458
459	bzero(&tc, sizeof tc);
460
461	/*
462	 * The value of the first two elements is specified in the
463	 * Fusion-MPT Message Passing Interface document.
464	 */
465	tc.ContextSize = 0;
466	tc.DetailsLength = 12;
467	/*
468	 * XXX Is there any reason to fetch a partial image?  I.e. to
469	 * set ImageOffset to something other than 0?
470	 */
471	tc.ImageOffset = 0;
472	tc.ImageSize = cmd->len;
473
474	return (mps_push_sge(cm, &tc, sizeof tc, 0));
475}
476
477/*
478 * Prepare the mps_command for a SATA_PASSTHROUGH request.
479 */
480static int
481mpi_pre_sata_passthrough(struct mps_command *cm, struct mps_usr_command *cmd)
482{
483	MPI2_SATA_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req;
484	MPI2_SATA_PASSTHROUGH_REPLY *rpl;
485
486	if (cmd->req_len != sizeof *req)
487		return (EINVAL);
488	if (cmd->rpl_len != sizeof *rpl)
489		return (EINVAL);
490
491	mpi_init_sge(cm, req, &req->SGL);
492	return (0);
493}
494
495/*
496 * Prepare the mps_command for a SMP_PASSTHROUGH request.
497 */
498static int
499mpi_pre_smp_passthrough(struct mps_command *cm, struct mps_usr_command *cmd)
500{
501	MPI2_SMP_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req;
502	MPI2_SMP_PASSTHROUGH_REPLY *rpl;
503
504	if (cmd->req_len != sizeof *req)
505		return (EINVAL);
506	if (cmd->rpl_len != sizeof *rpl)
507		return (EINVAL);
508
509	mpi_init_sge(cm, req, &req->SGL);
510	return (0);
511}
512
513/*
514 * Prepare the mps_command for a CONFIG request.
515 */
516static int
517mpi_pre_config(struct mps_command *cm, struct mps_usr_command *cmd)
518{
519	MPI2_CONFIG_REQUEST *req = (void *)cm->cm_req;
520	MPI2_CONFIG_REPLY *rpl;
521
522	if (cmd->req_len != sizeof *req)
523		return (EINVAL);
524	if (cmd->rpl_len != sizeof *rpl)
525		return (EINVAL);
526
527	mpi_init_sge(cm, req, &req->PageBufferSGE);
528	return (0);
529}
530
531/*
532 * Prepare the mps_command for a SAS_IO_UNIT_CONTROL request.
533 */
534static int
535mpi_pre_sas_io_unit_control(struct mps_command *cm,
536			     struct mps_usr_command *cmd)
537{
538
539	cm->cm_sge = NULL;
540	cm->cm_sglsize = 0;
541	return (0);
542}
543
544/*
545 * A set of functions to prepare an mps_command for the various
546 * supported requests.
547 */
548struct mps_user_func {
549	U8		Function;
550	mps_user_f	*f_pre;
551} mps_user_func_list[] = {
552	{ MPI2_FUNCTION_IOC_FACTS,		mpi_pre_ioc_facts },
553	{ MPI2_FUNCTION_PORT_FACTS,		mpi_pre_port_facts },
554	{ MPI2_FUNCTION_FW_DOWNLOAD, 		mpi_pre_fw_download },
555	{ MPI2_FUNCTION_FW_UPLOAD,		mpi_pre_fw_upload },
556	{ MPI2_FUNCTION_SATA_PASSTHROUGH,	mpi_pre_sata_passthrough },
557	{ MPI2_FUNCTION_SMP_PASSTHROUGH,	mpi_pre_smp_passthrough},
558	{ MPI2_FUNCTION_CONFIG,			mpi_pre_config},
559	{ MPI2_FUNCTION_SAS_IO_UNIT_CONTROL,	mpi_pre_sas_io_unit_control },
560	{ 0xFF,					NULL } /* list end */
561};
562
563static int
564mps_user_setup_request(struct mps_command *cm, struct mps_usr_command *cmd)
565{
566	MPI2_REQUEST_HEADER *hdr = (MPI2_REQUEST_HEADER *)cm->cm_req;
567	struct mps_user_func *f;
568
569	for (f = mps_user_func_list; f->f_pre != NULL; f++) {
570		if (hdr->Function == f->Function)
571			return (f->f_pre(cm, cmd));
572	}
573	return (EINVAL);
574}
575
576static int
577mps_user_command(struct mps_softc *sc, struct mps_usr_command *cmd)
578{
579	MPI2_REQUEST_HEADER *hdr;
580	MPI2_DEFAULT_REPLY *rpl;
581	void *buf = NULL;
582	struct mps_command *cm = NULL;
583	int err = 0;
584	int sz;
585
586	mps_lock(sc);
587	cm = mps_alloc_command(sc);
588
589	if (cm == NULL) {
590		mps_printf(sc, "mps_user_command: no mps requests\n");
591		err = ENOMEM;
592		goto Ret;
593	}
594	mps_unlock(sc);
595
596	hdr = (MPI2_REQUEST_HEADER *)cm->cm_req;
597
598	mps_dprint(sc, MPS_INFO, "mps_user_command: req %p %d  rpl %p %d\n",
599		    cmd->req, cmd->req_len, cmd->rpl, cmd->rpl_len );
600
601	if (cmd->req_len > (int)sc->facts->IOCRequestFrameSize * 4) {
602		err = EINVAL;
603		goto RetFreeUnlocked;
604	}
605	err = copyin(cmd->req, hdr, cmd->req_len);
606	if (err != 0)
607		goto RetFreeUnlocked;
608
609	mps_dprint(sc, MPS_INFO, "mps_user_command: Function %02X  "
610	    "MsgFlags %02X\n", hdr->Function, hdr->MsgFlags );
611
612	err = mps_user_setup_request(cm, cmd);
613	if (err != 0) {
614		mps_printf(sc, "mps_user_command: unsupported function 0x%X\n",
615		    hdr->Function );
616		goto RetFreeUnlocked;
617	}
618
619	if (cmd->len > 0) {
620		buf = malloc(cmd->len, M_MPSUSER, M_WAITOK|M_ZERO);
621		cm->cm_data = buf;
622		cm->cm_length = cmd->len;
623	} else {
624		cm->cm_data = NULL;
625		cm->cm_length = 0;
626	}
627
628	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_WAKEUP;
629	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
630
631	mps_lock(sc);
632	err = mps_map_command(sc, cm);
633
634	if (err != 0 && err != EINPROGRESS) {
635		mps_printf(sc, "%s: invalid request: error %d\n",
636		    __func__, err);
637		goto Ret;
638	}
639	msleep(cm, &sc->mps_mtx, 0, "mpsuser", 0);
640
641	rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply;
642	sz = rpl->MsgLength * 4;
643
644	if (sz > cmd->rpl_len) {
645		mps_printf(sc,
646		    "mps_user_command: reply buffer too small %d required %d\n",
647		    cmd->rpl_len, sz );
648		err = EINVAL;
649		sz = cmd->rpl_len;
650	}
651
652	mps_unlock(sc);
653	copyout(rpl, cmd->rpl, sz);
654	if (buf != NULL)
655		copyout(buf, cmd->buf, cmd->len);
656	mps_dprint(sc, MPS_INFO, "mps_user_command: reply size %d\n", sz );
657
658RetFreeUnlocked:
659	mps_lock(sc);
660	if (cm != NULL)
661		mps_free_command(sc, cm);
662Ret:
663	mps_unlock(sc);
664	if (buf != NULL)
665		free(buf, M_MPSUSER);
666	return (err);
667}
668
669static int
670mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag,
671    struct thread *td)
672{
673	struct mps_softc *sc;
674	struct mps_cfg_page_req *page_req;
675	struct mps_ext_cfg_page_req *ext_page_req;
676	void *mps_page;
677	int error;
678
679	mps_page = NULL;
680	sc = dev->si_drv1;
681	page_req = (void *)arg;
682	ext_page_req = (void *)arg;
683
684	switch (cmd) {
685	case MPSIO_READ_CFG_HEADER:
686		mps_lock(sc);
687		error = mps_user_read_cfg_header(sc, page_req);
688		mps_unlock(sc);
689		break;
690	case MPSIO_READ_CFG_PAGE:
691		mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK | M_ZERO);
692		error = copyin(page_req->buf, mps_page,
693		    sizeof(MPI2_CONFIG_PAGE_HEADER));
694		if (error)
695			break;
696		mps_lock(sc);
697		error = mps_user_read_cfg_page(sc, page_req, mps_page);
698		mps_unlock(sc);
699		if (error)
700			break;
701		error = copyout(mps_page, page_req->buf, page_req->len);
702		break;
703	case MPSIO_READ_EXT_CFG_HEADER:
704		mps_lock(sc);
705		error = mps_user_read_extcfg_header(sc, ext_page_req);
706		mps_unlock(sc);
707		break;
708	case MPSIO_READ_EXT_CFG_PAGE:
709		mps_page = malloc(ext_page_req->len, M_MPSUSER, M_WAITOK|M_ZERO);
710		error = copyin(ext_page_req->buf, mps_page,
711		    sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER));
712		if (error)
713			break;
714		mps_lock(sc);
715		error = mps_user_read_extcfg_page(sc, ext_page_req, mps_page);
716		mps_unlock(sc);
717		if (error)
718			break;
719		error = copyout(mps_page, ext_page_req->buf, ext_page_req->len);
720		break;
721	case MPSIO_WRITE_CFG_PAGE:
722		mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK|M_ZERO);
723		error = copyin(page_req->buf, mps_page, page_req->len);
724		if (error)
725			break;
726		mps_lock(sc);
727		error = mps_user_write_cfg_page(sc, page_req, mps_page);
728		mps_unlock(sc);
729		break;
730	case MPSIO_MPS_COMMAND:
731		error = mps_user_command(sc, (struct mps_usr_command *)arg);
732		break;
733	default:
734		error = ENOIOCTL;
735		break;
736	}
737
738	if (mps_page != NULL)
739		free(mps_page, M_MPSUSER);
740
741	return (error);
742}
743
744#ifdef COMPAT_FREEBSD32
745
746/* Macros from compat/freebsd32/freebsd32.h */
747#define	PTRIN(v)	(void *)(uintptr_t)(v)
748#define	PTROUT(v)	(uint32_t)(uintptr_t)(v)
749
750#define	CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0)
751#define	PTRIN_CP(src,dst,fld)				\
752	do { (dst).fld = PTRIN((src).fld); } while (0)
753#define	PTROUT_CP(src,dst,fld) \
754	do { (dst).fld = PTROUT((src).fld); } while (0)
755
756struct mps_cfg_page_req32 {
757	MPI2_CONFIG_PAGE_HEADER header;
758	uint32_t page_address;
759	uint32_t buf;
760	int	len;
761	uint16_t ioc_status;
762};
763
764struct mps_ext_cfg_page_req32 {
765	MPI2_CONFIG_EXTENDED_PAGE_HEADER header;
766	uint32_t page_address;
767	uint32_t buf;
768	int	len;
769	uint16_t ioc_status;
770};
771
772struct mps_raid_action32 {
773	uint8_t action;
774	uint8_t volume_bus;
775	uint8_t volume_id;
776	uint8_t phys_disk_num;
777	uint32_t action_data_word;
778	uint32_t buf;
779	int len;
780	uint32_t volume_status;
781	uint32_t action_data[4];
782	uint16_t action_status;
783	uint16_t ioc_status;
784	uint8_t write;
785};
786
787struct mps_usr_command32 {
788	uint32_t req;
789	uint32_t req_len;
790	uint32_t rpl;
791	uint32_t rpl_len;
792	uint32_t buf;
793	int len;
794	uint32_t flags;
795};
796
797#define	MPSIO_READ_CFG_HEADER32	_IOWR('M', 200, struct mps_cfg_page_req32)
798#define	MPSIO_READ_CFG_PAGE32	_IOWR('M', 201, struct mps_cfg_page_req32)
799#define	MPSIO_READ_EXT_CFG_HEADER32 _IOWR('M', 202, struct mps_ext_cfg_page_req32)
800#define	MPSIO_READ_EXT_CFG_PAGE32 _IOWR('M', 203, struct mps_ext_cfg_page_req32)
801#define	MPSIO_WRITE_CFG_PAGE32	_IOWR('M', 204, struct mps_cfg_page_req32)
802#define	MPSIO_RAID_ACTION32	_IOWR('M', 205, struct mps_raid_action32)
803#define	MPSIO_MPS_COMMAND32	_IOWR('M', 210, struct mps_usr_command32)
804
805static int
806mps_ioctl32(struct cdev *dev, u_long cmd32, void *_arg, int flag,
807    struct thread *td)
808{
809	struct mps_cfg_page_req32 *page32 = _arg;
810	struct mps_ext_cfg_page_req32 *ext32 = _arg;
811	struct mps_raid_action32 *raid32 = _arg;
812	struct mps_usr_command32 *user32 = _arg;
813	union {
814		struct mps_cfg_page_req page;
815		struct mps_ext_cfg_page_req ext;
816		struct mps_raid_action raid;
817		struct mps_usr_command user;
818	} arg;
819	u_long cmd;
820	int error;
821
822	switch (cmd32) {
823	case MPSIO_READ_CFG_HEADER32:
824	case MPSIO_READ_CFG_PAGE32:
825	case MPSIO_WRITE_CFG_PAGE32:
826		if (cmd32 == MPSIO_READ_CFG_HEADER32)
827			cmd = MPSIO_READ_CFG_HEADER;
828		else if (cmd32 == MPSIO_READ_CFG_PAGE32)
829			cmd = MPSIO_READ_CFG_PAGE;
830		else
831			cmd = MPSIO_WRITE_CFG_PAGE;
832		CP(*page32, arg.page, header);
833		CP(*page32, arg.page, page_address);
834		PTRIN_CP(*page32, arg.page, buf);
835		CP(*page32, arg.page, len);
836		CP(*page32, arg.page, ioc_status);
837		break;
838
839	case MPSIO_READ_EXT_CFG_HEADER32:
840	case MPSIO_READ_EXT_CFG_PAGE32:
841		if (cmd32 == MPSIO_READ_EXT_CFG_HEADER32)
842			cmd = MPSIO_READ_EXT_CFG_HEADER;
843		else
844			cmd = MPSIO_READ_EXT_CFG_PAGE;
845		CP(*ext32, arg.ext, header);
846		CP(*ext32, arg.ext, page_address);
847		PTRIN_CP(*ext32, arg.ext, buf);
848		CP(*ext32, arg.ext, len);
849		CP(*ext32, arg.ext, ioc_status);
850		break;
851
852	case MPSIO_RAID_ACTION32:
853		cmd = MPSIO_RAID_ACTION;
854		CP(*raid32, arg.raid, action);
855		CP(*raid32, arg.raid, volume_bus);
856		CP(*raid32, arg.raid, volume_id);
857		CP(*raid32, arg.raid, phys_disk_num);
858		CP(*raid32, arg.raid, action_data_word);
859		PTRIN_CP(*raid32, arg.raid, buf);
860		CP(*raid32, arg.raid, len);
861		CP(*raid32, arg.raid, volume_status);
862		bcopy(raid32->action_data, arg.raid.action_data,
863		    sizeof arg.raid.action_data);
864		CP(*raid32, arg.raid, ioc_status);
865		CP(*raid32, arg.raid, write);
866		break;
867
868	case MPSIO_MPS_COMMAND32:
869		cmd = MPSIO_MPS_COMMAND;
870		PTRIN_CP(*user32, arg.user, req);
871		CP(*user32, arg.user, req_len);
872		PTRIN_CP(*user32, arg.user, rpl);
873		CP(*user32, arg.user, rpl_len);
874		PTRIN_CP(*user32, arg.user, buf);
875		CP(*user32, arg.user, len);
876		CP(*user32, arg.user, flags);
877		break;
878	default:
879		return (ENOIOCTL);
880	}
881
882	error = mps_ioctl(dev, cmd, &arg, flag, td);
883	if (error == 0 && (cmd32 & IOC_OUT) != 0) {
884		switch (cmd32) {
885		case MPSIO_READ_CFG_HEADER32:
886		case MPSIO_READ_CFG_PAGE32:
887		case MPSIO_WRITE_CFG_PAGE32:
888			CP(arg.page, *page32, header);
889			CP(arg.page, *page32, page_address);
890			PTROUT_CP(arg.page, *page32, buf);
891			CP(arg.page, *page32, len);
892			CP(arg.page, *page32, ioc_status);
893			break;
894
895		case MPSIO_READ_EXT_CFG_HEADER32:
896		case MPSIO_READ_EXT_CFG_PAGE32:
897			CP(arg.ext, *ext32, header);
898			CP(arg.ext, *ext32, page_address);
899			PTROUT_CP(arg.ext, *ext32, buf);
900			CP(arg.ext, *ext32, len);
901			CP(arg.ext, *ext32, ioc_status);
902			break;
903
904		case MPSIO_RAID_ACTION32:
905			CP(arg.raid, *raid32, action);
906			CP(arg.raid, *raid32, volume_bus);
907			CP(arg.raid, *raid32, volume_id);
908			CP(arg.raid, *raid32, phys_disk_num);
909			CP(arg.raid, *raid32, action_data_word);
910			PTROUT_CP(arg.raid, *raid32, buf);
911			CP(arg.raid, *raid32, len);
912			CP(arg.raid, *raid32, volume_status);
913			bcopy(arg.raid.action_data, raid32->action_data,
914			    sizeof arg.raid.action_data);
915			CP(arg.raid, *raid32, ioc_status);
916			CP(arg.raid, *raid32, write);
917			break;
918
919		case MPSIO_MPS_COMMAND32:
920			PTROUT_CP(arg.user, *user32, req);
921			CP(arg.user, *user32, req_len);
922			PTROUT_CP(arg.user, *user32, rpl);
923			CP(arg.user, *user32, rpl_len);
924			PTROUT_CP(arg.user, *user32, buf);
925			CP(arg.user, *user32, len);
926			CP(arg.user, *user32, flags);
927			break;
928		}
929	}
930
931	return (error);
932}
933#endif /* COMPAT_FREEBSD32 */
934
935static int
936mps_ioctl_devsw(struct cdev *dev, u_long com, caddr_t arg, int flag,
937    struct thread *td)
938{
939#ifdef COMPAT_FREEBSD32
940	if (SV_CURPROC_FLAG(SV_ILP32))
941		return (mps_ioctl32(dev, com, arg, flag, td));
942#endif
943	return (mps_ioctl(dev, com, arg, flag, td));
944}
945