1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2008 Yahoo!, Inc.
5 * All rights reserved.
6 * Written by: John Baldwin <jhb@FreeBSD.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * LSI MPT-Fusion Host Adapter FreeBSD userland interface
33 */
34
35#include <sys/param.h>
36#ifdef __amd64__
37#include <sys/abi_compat.h>
38#endif
39#include <sys/conf.h>
40#include <sys/errno.h>
41#include <sys/ioccom.h>
42#include <sys/mpt_ioctl.h>
43
44#include <dev/mpt/mpt.h>
45
46struct mpt_user_raid_action_result {
47	uint32_t	volume_status;
48	uint32_t	action_data[4];
49	uint16_t	action_status;
50};
51
52struct mpt_page_memory {
53	bus_dma_tag_t	tag;
54	bus_dmamap_t	map;
55	bus_addr_t	paddr;
56	void		*vaddr;
57};
58
59static mpt_probe_handler_t	mpt_user_probe;
60static mpt_attach_handler_t	mpt_user_attach;
61static mpt_enable_handler_t	mpt_user_enable;
62static mpt_ready_handler_t	mpt_user_ready;
63static mpt_event_handler_t	mpt_user_event;
64static mpt_reset_handler_t	mpt_user_reset;
65static mpt_detach_handler_t	mpt_user_detach;
66
67static struct mpt_personality mpt_user_personality = {
68	.name		= "mpt_user",
69	.probe		= mpt_user_probe,
70	.attach		= mpt_user_attach,
71	.enable		= mpt_user_enable,
72	.ready		= mpt_user_ready,
73	.event		= mpt_user_event,
74	.reset		= mpt_user_reset,
75	.detach		= mpt_user_detach,
76};
77
78DECLARE_MPT_PERSONALITY(mpt_user, SI_ORDER_SECOND);
79
80static mpt_reply_handler_t	mpt_user_reply_handler;
81
82static d_open_t		mpt_open;
83static d_close_t	mpt_close;
84static d_ioctl_t	mpt_ioctl;
85
86static struct cdevsw mpt_cdevsw = {
87	.d_version =	D_VERSION,
88	.d_flags =	0,
89	.d_open =	mpt_open,
90	.d_close =	mpt_close,
91	.d_ioctl =	mpt_ioctl,
92	.d_name =	"mpt",
93};
94
95static MALLOC_DEFINE(M_MPTUSER, "mpt_user", "Buffers for mpt(4) ioctls");
96
97static uint32_t user_handler_id = MPT_HANDLER_ID_NONE;
98
99static int
100mpt_user_probe(struct mpt_softc *mpt)
101{
102
103	/* Attach to every controller. */
104	return (0);
105}
106
107static int
108mpt_user_attach(struct mpt_softc *mpt)
109{
110	mpt_handler_t handler;
111	int error, unit;
112
113	MPT_LOCK(mpt);
114	handler.reply_handler = mpt_user_reply_handler;
115	error = mpt_register_handler(mpt, MPT_HANDLER_REPLY, handler,
116				     &user_handler_id);
117	MPT_UNLOCK(mpt);
118	if (error != 0) {
119		mpt_prt(mpt, "Unable to register user handler!\n");
120		return (error);
121	}
122	unit = device_get_unit(mpt->dev);
123	mpt->cdev = make_dev(&mpt_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640,
124	    "mpt%d", unit);
125	if (mpt->cdev == NULL) {
126		MPT_LOCK(mpt);
127		mpt_deregister_handler(mpt, MPT_HANDLER_REPLY, handler,
128		    user_handler_id);
129		MPT_UNLOCK(mpt);
130		return (ENOMEM);
131	}
132	mpt->cdev->si_drv1 = mpt;
133	return (0);
134}
135
136static int
137mpt_user_enable(struct mpt_softc *mpt)
138{
139
140	return (0);
141}
142
143static void
144mpt_user_ready(struct mpt_softc *mpt)
145{
146
147}
148
149static int
150mpt_user_event(struct mpt_softc *mpt, request_t *req,
151    MSG_EVENT_NOTIFY_REPLY *msg)
152{
153
154	/* Someday we may want to let a user daemon listen for events? */
155	return (0);
156}
157
158static void
159mpt_user_reset(struct mpt_softc *mpt, int type)
160{
161
162}
163
164static void
165mpt_user_detach(struct mpt_softc *mpt)
166{
167	mpt_handler_t handler;
168
169	/* XXX: do a purge of pending requests? */
170	destroy_dev(mpt->cdev);
171
172	MPT_LOCK(mpt);
173	handler.reply_handler = mpt_user_reply_handler;
174	mpt_deregister_handler(mpt, MPT_HANDLER_REPLY, handler,
175	    user_handler_id);
176	MPT_UNLOCK(mpt);
177}
178
179static int
180mpt_open(struct cdev *dev, int flags, int fmt, struct thread *td)
181{
182
183	return (0);
184}
185
186static int
187mpt_close(struct cdev *dev, int flags, int fmt, struct thread *td)
188{
189
190	return (0);
191}
192
193static int
194mpt_alloc_buffer(struct mpt_softc *mpt, struct mpt_page_memory *page_mem,
195    size_t len)
196{
197	struct mpt_map_info mi;
198	int error;
199
200	page_mem->vaddr = NULL;
201
202	/* Limit requests to 16M. */
203	if (len > 16 * 1024 * 1024)
204		return (ENOSPC);
205	error = mpt_dma_tag_create(mpt, mpt->parent_dmat, 1, 0,
206	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
207	    len, 1, len, 0, &page_mem->tag);
208	if (error)
209		return (error);
210	error = bus_dmamem_alloc(page_mem->tag, &page_mem->vaddr,
211	    BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &page_mem->map);
212	if (error) {
213		bus_dma_tag_destroy(page_mem->tag);
214		return (error);
215	}
216	mi.mpt = mpt;
217	error = bus_dmamap_load(page_mem->tag, page_mem->map, page_mem->vaddr,
218	    len, mpt_map_rquest, &mi, BUS_DMA_NOWAIT);
219	if (error == 0)
220		error = mi.error;
221	if (error) {
222		bus_dmamem_free(page_mem->tag, page_mem->vaddr, page_mem->map);
223		bus_dma_tag_destroy(page_mem->tag);
224		page_mem->vaddr = NULL;
225		return (error);
226	}
227	page_mem->paddr = mi.phys;
228	return (0);
229}
230
231static void
232mpt_free_buffer(struct mpt_page_memory *page_mem)
233{
234
235	if (page_mem->vaddr == NULL)
236		return;
237	bus_dmamap_unload(page_mem->tag, page_mem->map);
238	bus_dmamem_free(page_mem->tag, page_mem->vaddr, page_mem->map);
239	bus_dma_tag_destroy(page_mem->tag);
240	page_mem->vaddr = NULL;
241}
242
243static int
244mpt_user_read_cfg_header(struct mpt_softc *mpt,
245    struct mpt_cfg_page_req *page_req)
246{
247	request_t  *req;
248	cfgparms_t params;
249	MSG_CONFIG *cfgp;
250	int	    error;
251
252	req = mpt_get_request(mpt, TRUE);
253	if (req == NULL) {
254		mpt_prt(mpt, "mpt_user_read_cfg_header: Get request failed!\n");
255		return (ENOMEM);
256	}
257
258	params.Action = MPI_CONFIG_ACTION_PAGE_HEADER;
259	params.PageVersion = 0;
260	params.PageLength = 0;
261	params.PageNumber = page_req->header.PageNumber;
262	params.PageType = page_req->header.PageType;
263	params.PageAddress = le32toh(page_req->page_address);
264	error = mpt_issue_cfg_req(mpt, req, &params, /*addr*/0, /*len*/0,
265				  TRUE, 5000);
266	if (error != 0) {
267		/*
268		 * Leave the request. Without resetting the chip, it's
269		 * still owned by it and we'll just get into trouble
270		 * freeing it now. Mark it as abandoned so that if it
271		 * shows up later it can be freed.
272		 */
273		mpt_prt(mpt, "read_cfg_header timed out\n");
274		return (ETIMEDOUT);
275	}
276
277	page_req->ioc_status = htole16(req->IOCStatus);
278	if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS) {
279		cfgp = req->req_vbuf;
280		bcopy(&cfgp->Header, &page_req->header,
281		    sizeof(page_req->header));
282	}
283	mpt_free_request(mpt, req);
284	return (0);
285}
286
287static int
288mpt_user_read_cfg_page(struct mpt_softc *mpt, struct mpt_cfg_page_req *page_req,
289    struct mpt_page_memory *mpt_page)
290{
291	CONFIG_PAGE_HEADER *hdr;
292	request_t    *req;
293	cfgparms_t    params;
294	int	      error;
295
296	req = mpt_get_request(mpt, TRUE);
297	if (req == NULL) {
298		mpt_prt(mpt, "mpt_user_read_cfg_page: Get request failed!\n");
299		return (ENOMEM);
300	}
301
302	hdr = mpt_page->vaddr;
303	params.Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
304	params.PageVersion = hdr->PageVersion;
305	params.PageLength = hdr->PageLength;
306	params.PageNumber = hdr->PageNumber;
307	params.PageType = hdr->PageType & MPI_CONFIG_PAGETYPE_MASK;
308	params.PageAddress = le32toh(page_req->page_address);
309	bus_dmamap_sync(mpt_page->tag, mpt_page->map,
310	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
311	error = mpt_issue_cfg_req(mpt, req, &params, mpt_page->paddr,
312	    le32toh(page_req->len), TRUE, 5000);
313	if (error != 0) {
314		mpt_prt(mpt, "mpt_user_read_cfg_page timed out\n");
315		return (ETIMEDOUT);
316	}
317
318	page_req->ioc_status = htole16(req->IOCStatus);
319	if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS)
320		bus_dmamap_sync(mpt_page->tag, mpt_page->map,
321		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
322	mpt_free_request(mpt, req);
323	return (0);
324}
325
326static int
327mpt_user_read_extcfg_header(struct mpt_softc *mpt,
328    struct mpt_ext_cfg_page_req *ext_page_req)
329{
330	request_t  *req;
331	cfgparms_t params;
332	MSG_CONFIG_REPLY *cfgp;
333	int	    error;
334
335	req = mpt_get_request(mpt, TRUE);
336	if (req == NULL) {
337		mpt_prt(mpt, "mpt_user_read_extcfg_header: Get request failed!\n");
338		return (ENOMEM);
339	}
340
341	params.Action = MPI_CONFIG_ACTION_PAGE_HEADER;
342	params.PageVersion = ext_page_req->header.PageVersion;
343	params.PageLength = 0;
344	params.PageNumber = ext_page_req->header.PageNumber;
345	params.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
346	params.PageAddress = le32toh(ext_page_req->page_address);
347	params.ExtPageType = ext_page_req->header.ExtPageType;
348	params.ExtPageLength = 0;
349	error = mpt_issue_cfg_req(mpt, req, &params, /*addr*/0, /*len*/0,
350				  TRUE, 5000);
351	if (error != 0) {
352		/*
353		 * Leave the request. Without resetting the chip, it's
354		 * still owned by it and we'll just get into trouble
355		 * freeing it now. Mark it as abandoned so that if it
356		 * shows up later it can be freed.
357		 */
358		mpt_prt(mpt, "mpt_user_read_extcfg_header timed out\n");
359		return (ETIMEDOUT);
360	}
361
362	ext_page_req->ioc_status = htole16(req->IOCStatus);
363	if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS) {
364		cfgp = req->req_vbuf;
365		ext_page_req->header.PageVersion = cfgp->Header.PageVersion;
366		ext_page_req->header.PageNumber = cfgp->Header.PageNumber;
367		ext_page_req->header.PageType = cfgp->Header.PageType;
368		ext_page_req->header.ExtPageLength = cfgp->ExtPageLength;
369		ext_page_req->header.ExtPageType = cfgp->ExtPageType;
370	}
371	mpt_free_request(mpt, req);
372	return (0);
373}
374
375static int
376mpt_user_read_extcfg_page(struct mpt_softc *mpt,
377    struct mpt_ext_cfg_page_req *ext_page_req, struct mpt_page_memory *mpt_page)
378{
379	CONFIG_EXTENDED_PAGE_HEADER *hdr;
380	request_t    *req;
381	cfgparms_t    params;
382	int	      error;
383
384	req = mpt_get_request(mpt, TRUE);
385	if (req == NULL) {
386		mpt_prt(mpt, "mpt_user_read_extcfg_page: Get request failed!\n");
387		return (ENOMEM);
388	}
389
390	hdr = mpt_page->vaddr;
391	params.Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
392	params.PageVersion = hdr->PageVersion;
393	params.PageLength = 0;
394	params.PageNumber = hdr->PageNumber;
395	params.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
396	params.PageAddress = le32toh(ext_page_req->page_address);
397	params.ExtPageType = hdr->ExtPageType;
398	params.ExtPageLength = hdr->ExtPageLength;
399	bus_dmamap_sync(mpt_page->tag, mpt_page->map,
400	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
401	error = mpt_issue_cfg_req(mpt, req, &params, mpt_page->paddr,
402	    le32toh(ext_page_req->len), TRUE, 5000);
403	if (error != 0) {
404		mpt_prt(mpt, "mpt_user_read_extcfg_page timed out\n");
405		return (ETIMEDOUT);
406	}
407
408	ext_page_req->ioc_status = htole16(req->IOCStatus);
409	if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS)
410		bus_dmamap_sync(mpt_page->tag, mpt_page->map,
411		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
412	mpt_free_request(mpt, req);
413	return (0);
414}
415
416static int
417mpt_user_write_cfg_page(struct mpt_softc *mpt,
418    struct mpt_cfg_page_req *page_req, struct mpt_page_memory *mpt_page)
419{
420	CONFIG_PAGE_HEADER *hdr;
421	request_t    *req;
422	cfgparms_t    params;
423	u_int	      hdr_attr;
424	int	      error;
425
426	hdr = mpt_page->vaddr;
427	hdr_attr = hdr->PageType & MPI_CONFIG_PAGEATTR_MASK;
428	if (hdr_attr != MPI_CONFIG_PAGEATTR_CHANGEABLE &&
429	    hdr_attr != MPI_CONFIG_PAGEATTR_PERSISTENT) {
430		mpt_prt(mpt, "page type 0x%x not changeable\n",
431			hdr->PageType & MPI_CONFIG_PAGETYPE_MASK);
432		return (EINVAL);
433	}
434
435#if	0
436	/*
437	 * We shouldn't mask off other bits here.
438	 */
439	hdr->PageType &= ~MPI_CONFIG_PAGETYPE_MASK;
440#endif
441
442	req = mpt_get_request(mpt, TRUE);
443	if (req == NULL)
444		return (ENOMEM);
445
446	bus_dmamap_sync(mpt_page->tag, mpt_page->map, BUS_DMASYNC_PREREAD |
447	    BUS_DMASYNC_PREWRITE);
448
449	/*
450	 * There isn't any point in restoring stripped out attributes
451	 * if you then mask them going down to issue the request.
452	 */
453
454	params.Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
455	params.PageVersion = hdr->PageVersion;
456	params.PageLength = hdr->PageLength;
457	params.PageNumber = hdr->PageNumber;
458	params.PageAddress = le32toh(page_req->page_address);
459#if	0
460	/* Restore stripped out attributes */
461	hdr->PageType |= hdr_attr;
462	params.PageType = hdr->PageType & MPI_CONFIG_PAGETYPE_MASK;
463#else
464	params.PageType = hdr->PageType;
465#endif
466	error = mpt_issue_cfg_req(mpt, req, &params, mpt_page->paddr,
467	    le32toh(page_req->len), TRUE, 5000);
468	if (error != 0) {
469		mpt_prt(mpt, "mpt_write_cfg_page timed out\n");
470		return (ETIMEDOUT);
471	}
472
473	page_req->ioc_status = htole16(req->IOCStatus);
474	bus_dmamap_sync(mpt_page->tag, mpt_page->map, BUS_DMASYNC_POSTREAD |
475	    BUS_DMASYNC_POSTWRITE);
476	mpt_free_request(mpt, req);
477	return (0);
478}
479
480static int
481mpt_user_reply_handler(struct mpt_softc *mpt, request_t *req,
482    uint32_t reply_desc, MSG_DEFAULT_REPLY *reply_frame)
483{
484	MSG_RAID_ACTION_REPLY *reply;
485	struct mpt_user_raid_action_result *res;
486
487	if (req == NULL)
488		return (TRUE);
489
490	if (reply_frame != NULL) {
491		reply = (MSG_RAID_ACTION_REPLY *)reply_frame;
492		req->IOCStatus = le16toh(reply->IOCStatus);
493		res = (struct mpt_user_raid_action_result *)
494		    (((uint8_t *)req->req_vbuf) + MPT_RQSL(mpt));
495		res->action_status = reply->ActionStatus;
496		res->volume_status = reply->VolumeStatus;
497		bcopy(&reply->ActionData, res->action_data,
498		    sizeof(res->action_data));
499	}
500
501	req->state &= ~REQ_STATE_QUEUED;
502	req->state |= REQ_STATE_DONE;
503	TAILQ_REMOVE(&mpt->request_pending_list, req, links);
504
505	if ((req->state & REQ_STATE_NEED_WAKEUP) != 0) {
506		wakeup(req);
507	} else if ((req->state & REQ_STATE_TIMEDOUT) != 0) {
508		/*
509		 * Whew- we can free this request (late completion)
510		 */
511		mpt_free_request(mpt, req);
512	}
513
514	return (TRUE);
515}
516
517/*
518 * We use the first part of the request buffer after the request frame
519 * to hold the action data and action status from the RAID reply.  The
520 * rest of the request buffer is used to hold the buffer for the
521 * action SGE.
522 */
523static int
524mpt_user_raid_action(struct mpt_softc *mpt, struct mpt_raid_action *raid_act,
525	struct mpt_page_memory *mpt_page)
526{
527	request_t *req;
528	struct mpt_user_raid_action_result *res;
529	MSG_RAID_ACTION_REQUEST *rap;
530	SGE_SIMPLE32 *se;
531	int error;
532
533	req = mpt_get_request(mpt, TRUE);
534	if (req == NULL)
535		return (ENOMEM);
536	rap = req->req_vbuf;
537	memset(rap, 0, sizeof *rap);
538	rap->Action = raid_act->action;
539	rap->ActionDataWord = raid_act->action_data_word;
540	rap->Function = MPI_FUNCTION_RAID_ACTION;
541	rap->VolumeID = raid_act->volume_id;
542	rap->VolumeBus = raid_act->volume_bus;
543	rap->PhysDiskNum = raid_act->phys_disk_num;
544	se = (SGE_SIMPLE32 *)&rap->ActionDataSGE;
545	if (mpt_page->vaddr != NULL && raid_act->len != 0) {
546		bus_dmamap_sync(mpt_page->tag, mpt_page->map,
547		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
548		se->Address = htole32(mpt_page->paddr);
549		MPI_pSGE_SET_LENGTH(se, le32toh(raid_act->len));
550		MPI_pSGE_SET_FLAGS(se, (MPI_SGE_FLAGS_SIMPLE_ELEMENT |
551		    MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER |
552		    MPI_SGE_FLAGS_END_OF_LIST |
553		    (raid_act->write ? MPI_SGE_FLAGS_HOST_TO_IOC :
554		    MPI_SGE_FLAGS_IOC_TO_HOST)));
555	}
556	se->FlagsLength = htole32(se->FlagsLength);
557	rap->MsgContext = htole32(req->index | user_handler_id);
558
559	mpt_check_doorbell(mpt);
560	mpt_send_cmd(mpt, req);
561
562	error = mpt_wait_req(mpt, req, REQ_STATE_DONE, REQ_STATE_DONE, TRUE,
563	    2000);
564	if (error != 0) {
565		/*
566		 * Leave request so it can be cleaned up later.
567		 */
568		mpt_prt(mpt, "mpt_user_raid_action timed out\n");
569		return (error);
570	}
571
572	raid_act->ioc_status = htole16(req->IOCStatus);
573	if ((req->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) {
574		mpt_free_request(mpt, req);
575		return (0);
576	}
577
578	res = (struct mpt_user_raid_action_result *)
579	    (((uint8_t *)req->req_vbuf) + MPT_RQSL(mpt));
580	raid_act->volume_status = res->volume_status;
581	raid_act->action_status = res->action_status;
582	bcopy(res->action_data, raid_act->action_data,
583	    sizeof(res->action_data));
584	if (mpt_page->vaddr != NULL)
585		bus_dmamap_sync(mpt_page->tag, mpt_page->map,
586		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
587	mpt_free_request(mpt, req);
588	return (0);
589}
590
591static int
592mpt_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
593{
594	struct mpt_softc *mpt;
595	struct mpt_cfg_page_req *page_req;
596	struct mpt_ext_cfg_page_req *ext_page_req;
597	struct mpt_raid_action *raid_act;
598	struct mpt_page_memory mpt_page;
599#ifdef __amd64__
600	struct mpt_cfg_page_req32 *page_req32;
601	struct mpt_cfg_page_req page_req_swab;
602	struct mpt_ext_cfg_page_req32 *ext_page_req32;
603	struct mpt_ext_cfg_page_req ext_page_req_swab;
604	struct mpt_raid_action32 *raid_act32;
605	struct mpt_raid_action raid_act_swab;
606#endif
607	int error;
608
609	mpt = dev->si_drv1;
610	page_req = (void *)arg;
611	ext_page_req = (void *)arg;
612	raid_act = (void *)arg;
613	mpt_page.vaddr = NULL;
614
615#ifdef __amd64__
616	/* Convert 32-bit structs to native ones. */
617	page_req32 = (void *)arg;
618	ext_page_req32 = (void *)arg;
619	raid_act32 = (void *)arg;
620	switch (cmd) {
621	case MPTIO_READ_CFG_HEADER32:
622	case MPTIO_READ_CFG_PAGE32:
623	case MPTIO_WRITE_CFG_PAGE32:
624		page_req = &page_req_swab;
625		page_req->header = page_req32->header;
626		page_req->page_address = page_req32->page_address;
627		page_req->buf = PTRIN(page_req32->buf);
628		page_req->len = page_req32->len;
629		page_req->ioc_status = page_req32->ioc_status;
630		break;
631	case MPTIO_READ_EXT_CFG_HEADER32:
632	case MPTIO_READ_EXT_CFG_PAGE32:
633		ext_page_req = &ext_page_req_swab;
634		ext_page_req->header = ext_page_req32->header;
635		ext_page_req->page_address = ext_page_req32->page_address;
636		ext_page_req->buf = PTRIN(ext_page_req32->buf);
637		ext_page_req->len = ext_page_req32->len;
638		ext_page_req->ioc_status = ext_page_req32->ioc_status;
639		break;
640	case MPTIO_RAID_ACTION32:
641		raid_act = &raid_act_swab;
642		raid_act->action = raid_act32->action;
643		raid_act->volume_bus = raid_act32->volume_bus;
644		raid_act->volume_id = raid_act32->volume_id;
645		raid_act->phys_disk_num = raid_act32->phys_disk_num;
646		raid_act->action_data_word = raid_act32->action_data_word;
647		raid_act->buf = PTRIN(raid_act32->buf);
648		raid_act->len = raid_act32->len;
649		raid_act->volume_status = raid_act32->volume_status;
650		bcopy(raid_act32->action_data, raid_act->action_data,
651		    sizeof(raid_act->action_data));
652		raid_act->action_status = raid_act32->action_status;
653		raid_act->ioc_status = raid_act32->ioc_status;
654		raid_act->write = raid_act32->write;
655		break;
656	}
657#endif
658
659	switch (cmd) {
660#ifdef __amd64__
661	case MPTIO_READ_CFG_HEADER32:
662#endif
663	case MPTIO_READ_CFG_HEADER:
664		MPT_LOCK(mpt);
665		error = mpt_user_read_cfg_header(mpt, page_req);
666		MPT_UNLOCK(mpt);
667		break;
668#ifdef __amd64__
669	case MPTIO_READ_CFG_PAGE32:
670#endif
671	case MPTIO_READ_CFG_PAGE:
672		if (page_req->len < (int)sizeof(CONFIG_PAGE_HEADER)) {
673			error = EINVAL;
674			break;
675		}
676		error = mpt_alloc_buffer(mpt, &mpt_page, page_req->len);
677		if (error)
678			break;
679		error = copyin(page_req->buf, mpt_page.vaddr,
680		    sizeof(CONFIG_PAGE_HEADER));
681		if (error)
682			break;
683		MPT_LOCK(mpt);
684		error = mpt_user_read_cfg_page(mpt, page_req, &mpt_page);
685		MPT_UNLOCK(mpt);
686		if (error)
687			break;
688		error = copyout(mpt_page.vaddr, page_req->buf, page_req->len);
689		break;
690#ifdef __amd64__
691	case MPTIO_READ_EXT_CFG_HEADER32:
692#endif
693	case MPTIO_READ_EXT_CFG_HEADER:
694		MPT_LOCK(mpt);
695		error = mpt_user_read_extcfg_header(mpt, ext_page_req);
696		MPT_UNLOCK(mpt);
697		break;
698#ifdef __amd64__
699	case MPTIO_READ_EXT_CFG_PAGE32:
700#endif
701	case MPTIO_READ_EXT_CFG_PAGE:
702		if (ext_page_req->len <
703		    (int)sizeof(CONFIG_EXTENDED_PAGE_HEADER)) {
704			error = EINVAL;
705			break;
706		}
707		error = mpt_alloc_buffer(mpt, &mpt_page, ext_page_req->len);
708		if (error)
709			break;
710		error = copyin(ext_page_req->buf, mpt_page.vaddr,
711		    sizeof(CONFIG_EXTENDED_PAGE_HEADER));
712		if (error)
713			break;
714		MPT_LOCK(mpt);
715		error = mpt_user_read_extcfg_page(mpt, ext_page_req, &mpt_page);
716		MPT_UNLOCK(mpt);
717		if (error)
718			break;
719		error = copyout(mpt_page.vaddr, ext_page_req->buf,
720		    ext_page_req->len);
721		break;
722#ifdef __amd64__
723	case MPTIO_WRITE_CFG_PAGE32:
724#endif
725	case MPTIO_WRITE_CFG_PAGE:
726		if (page_req->len < (int)sizeof(CONFIG_PAGE_HEADER)) {
727			error = EINVAL;
728			break;
729		}
730		error = mpt_alloc_buffer(mpt, &mpt_page, page_req->len);
731		if (error)
732			break;
733		error = copyin(page_req->buf, mpt_page.vaddr, page_req->len);
734		if (error)
735			break;
736		MPT_LOCK(mpt);
737		error = mpt_user_write_cfg_page(mpt, page_req, &mpt_page);
738		MPT_UNLOCK(mpt);
739		break;
740#ifdef __amd64__
741	case MPTIO_RAID_ACTION32:
742#endif
743	case MPTIO_RAID_ACTION:
744		if (raid_act->buf != NULL) {
745			error = mpt_alloc_buffer(mpt, &mpt_page, raid_act->len);
746			if (error)
747				break;
748			error = copyin(raid_act->buf, mpt_page.vaddr,
749			    raid_act->len);
750			if (error)
751				break;
752		}
753		MPT_LOCK(mpt);
754		error = mpt_user_raid_action(mpt, raid_act, &mpt_page);
755		MPT_UNLOCK(mpt);
756		if (error)
757			break;
758		if (raid_act->buf != NULL)
759			error = copyout(mpt_page.vaddr, raid_act->buf,
760			    raid_act->len);
761		break;
762	default:
763		error = ENOIOCTL;
764		break;
765	}
766
767	mpt_free_buffer(&mpt_page);
768
769	if (error)
770		return (error);
771
772#ifdef __amd64__
773	/* Convert native structs to 32-bit ones. */
774	switch (cmd) {
775	case MPTIO_READ_CFG_HEADER32:
776	case MPTIO_READ_CFG_PAGE32:
777	case MPTIO_WRITE_CFG_PAGE32:
778		page_req32->header = page_req->header;
779		page_req32->page_address = page_req->page_address;
780		page_req32->buf = PTROUT(page_req->buf);
781		page_req32->len = page_req->len;
782		page_req32->ioc_status = page_req->ioc_status;
783		break;
784	case MPTIO_READ_EXT_CFG_HEADER32:
785	case MPTIO_READ_EXT_CFG_PAGE32:
786		ext_page_req32->header = ext_page_req->header;
787		ext_page_req32->page_address = ext_page_req->page_address;
788		ext_page_req32->buf = PTROUT(ext_page_req->buf);
789		ext_page_req32->len = ext_page_req->len;
790		ext_page_req32->ioc_status = ext_page_req->ioc_status;
791		break;
792	case MPTIO_RAID_ACTION32:
793		raid_act32->action = raid_act->action;
794		raid_act32->volume_bus = raid_act->volume_bus;
795		raid_act32->volume_id = raid_act->volume_id;
796		raid_act32->phys_disk_num = raid_act->phys_disk_num;
797		raid_act32->action_data_word = raid_act->action_data_word;
798		raid_act32->buf = PTROUT(raid_act->buf);
799		raid_act32->len = raid_act->len;
800		raid_act32->volume_status = raid_act->volume_status;
801		bcopy(raid_act->action_data, raid_act32->action_data,
802		    sizeof(raid_act->action_data));
803		raid_act32->action_status = raid_act->action_status;
804		raid_act32->ioc_status = raid_act->ioc_status;
805		raid_act32->write = raid_act->write;
806		break;
807	}
808#endif
809
810	return (0);
811}
812