xref: /freebsd/sys/dev/mrsas/mrsas_ioctl.c (revision d6b92ffa)
1 /*
2  * Copyright (c) 2015, AVAGO Tech. All rights reserved. Author: Marian Choy
3  * Copyright (c) 2014, LSI Corp. All rights reserved. Author: Marian Choy
4  * Support: freebsdraid@avagotech.com
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer. 2. Redistributions
12  * in binary form must reproduce the above copyright notice, this list of
13  * conditions and the following disclaimer in the documentation and/or other
14  * materials provided with the distribution. 3. Neither the name of the
15  * <ORGANIZATION> nor the names of its contributors may be used to endorse or
16  * promote products derived from this software without specific prior written
17  * permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  * The views and conclusions contained in the software and documentation are
32  * those of the authors and should not be interpreted as representing
33  * official policies,either expressed or implied, of the FreeBSD Project.
34  *
35  * Send feedback to: <megaraidfbsd@avagotech.com> Mail to: AVAGO TECHNOLOGIES, 1621
36  * Barber Lane, Milpitas, CA 95035 ATTN: MegaRaid FreeBSD
37  *
38  */
39 
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
42 
43 #include <dev/mrsas/mrsas.h>
44 #include <dev/mrsas/mrsas_ioctl.h>
45 
46 /*
47  * Function prototypes
48  */
49 int	mrsas_alloc_mfi_cmds(struct mrsas_softc *sc);
50 int	mrsas_passthru(struct mrsas_softc *sc, void *arg, u_long ioctlCmd);
51 void	mrsas_free_ioc_cmd(struct mrsas_softc *sc);
52 void	mrsas_free_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd);
53 void   *mrsas_alloc_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd);
54 static int mrsas_create_frame_pool(struct mrsas_softc *sc);
55 static void
56 mrsas_alloc_cb(void *arg, bus_dma_segment_t *segs,
57     int nsegs, int error);
58 
59 extern struct mrsas_mfi_cmd *mrsas_get_mfi_cmd(struct mrsas_softc *sc);
60 extern void mrsas_release_mfi_cmd(struct mrsas_mfi_cmd *cmd);
61 extern int
62 mrsas_issue_blocked_cmd(struct mrsas_softc *sc,
63     struct mrsas_mfi_cmd *cmd);
64 
65 /*
66  * mrsas_passthru:	Handle pass-through commands
67  * input:			Adapter instance soft state argument pointer
68  *
69  * This function is called from mrsas_ioctl() to handle pass-through and ioctl
70  * commands to Firmware.
71  */
72 int
73 mrsas_passthru(struct mrsas_softc *sc, void *arg, u_long ioctlCmd)
74 {
75 	struct mrsas_iocpacket *user_ioc = (struct mrsas_iocpacket *)arg;
76 
77 #ifdef COMPAT_FREEBSD32
78 	struct mrsas_iocpacket32 *user_ioc32 = (struct mrsas_iocpacket32 *)arg;
79 
80 #endif
81 	union mrsas_frame *in_cmd = (union mrsas_frame *)&(user_ioc->frame.raw);
82 	struct mrsas_mfi_cmd *cmd = NULL;
83 	bus_dma_tag_t ioctl_data_tag[MAX_IOCTL_SGE];
84 	bus_dmamap_t ioctl_data_dmamap[MAX_IOCTL_SGE];
85 	void *ioctl_data_mem[MAX_IOCTL_SGE];
86 	bus_addr_t ioctl_data_phys_addr[MAX_IOCTL_SGE];
87 	bus_dma_tag_t ioctl_sense_tag = 0;
88 	bus_dmamap_t ioctl_sense_dmamap = 0;
89 	void *ioctl_sense_mem = NULL;
90 	bus_addr_t ioctl_sense_phys_addr = 0;
91 	int i, ioctl_data_size = 0, ioctl_sense_size, ret = 0;
92 	struct mrsas_sge32 *kern_sge32;
93 	unsigned long *sense_ptr;
94 	uint8_t *iov_base_ptrin = NULL;
95 	size_t iov_len = 0;
96 
97 	/*
98 	 * Check for NOP from MegaCli... MegaCli can issue a DCMD of 0.  In
99 	 * this case do nothing and return 0 to it as status.
100 	 */
101 	if (in_cmd->dcmd.opcode == 0) {
102 		device_printf(sc->mrsas_dev, "In %s() Got a NOP\n", __func__);
103 		user_ioc->frame.hdr.cmd_status = MFI_STAT_OK;
104 		return (0);
105 	}
106 	/* Validate SGL length */
107 	if (user_ioc->sge_count > MAX_IOCTL_SGE) {
108 		device_printf(sc->mrsas_dev, "In %s() SGL is too long (%d > 8).\n",
109 		    __func__, user_ioc->sge_count);
110 		return (ENOENT);
111 	}
112 	/* Get a command */
113 	cmd = mrsas_get_mfi_cmd(sc);
114 	if (!cmd) {
115 		device_printf(sc->mrsas_dev, "Failed to get a free cmd for IOCTL\n");
116 		return (ENOMEM);
117 	}
118 	/*
119 	 * User's IOCTL packet has 2 frames (maximum). Copy those two frames
120 	 * into our cmd's frames. cmd->frame's context will get overwritten
121 	 * when we copy from user's frames. So set that value alone
122 	 * separately
123 	 */
124 	memcpy(cmd->frame, user_ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE);
125 	cmd->frame->hdr.context = cmd->index;
126 	cmd->frame->hdr.pad_0 = 0;
127 	cmd->frame->hdr.flags &= ~(MFI_FRAME_IEEE | MFI_FRAME_SGL64 |
128 	    MFI_FRAME_SENSE64);
129 
130 	/*
131 	 * The management interface between applications and the fw uses MFI
132 	 * frames. E.g, RAID configuration changes, LD property changes etc
133 	 * are accomplishes through different kinds of MFI frames. The driver
134 	 * needs to care only about substituting user buffers with kernel
135 	 * buffers in SGLs. The location of SGL is embedded in the struct
136 	 * iocpacket itself.
137 	 */
138 	kern_sge32 = (struct mrsas_sge32 *)
139 	    ((unsigned long)cmd->frame + user_ioc->sgl_off);
140 
141 	memset(ioctl_data_tag, 0, (sizeof(bus_dma_tag_t) * MAX_IOCTL_SGE));
142 	memset(ioctl_data_dmamap, 0, (sizeof(bus_dmamap_t) * MAX_IOCTL_SGE));
143 	memset(ioctl_data_mem, 0, (sizeof(void *) * MAX_IOCTL_SGE));
144 	memset(ioctl_data_phys_addr, 0, (sizeof(bus_addr_t) * MAX_IOCTL_SGE));
145 
146 	/*
147 	 * For each user buffer, create a mirror buffer and copy in
148 	 */
149 	for (i = 0; i < user_ioc->sge_count; i++) {
150 		if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
151 			if (!user_ioc->sgl[i].iov_len)
152 				continue;
153 			ioctl_data_size = user_ioc->sgl[i].iov_len;
154 #ifdef COMPAT_FREEBSD32
155 		} else {
156 			if (!user_ioc32->sgl[i].iov_len)
157 				continue;
158 			ioctl_data_size = user_ioc32->sgl[i].iov_len;
159 #endif
160 		}
161 		if (bus_dma_tag_create(sc->mrsas_parent_tag,
162 		    1, 0,
163 		    BUS_SPACE_MAXADDR_32BIT,
164 		    BUS_SPACE_MAXADDR,
165 		    NULL, NULL,
166 		    ioctl_data_size,
167 		    1,
168 		    ioctl_data_size,
169 		    BUS_DMA_ALLOCNOW,
170 		    NULL, NULL,
171 		    &ioctl_data_tag[i])) {
172 			device_printf(sc->mrsas_dev, "Cannot allocate ioctl data tag\n");
173 			ret = ENOMEM;
174 			goto out;
175 		}
176 		if (bus_dmamem_alloc(ioctl_data_tag[i], (void **)&ioctl_data_mem[i],
177 		    (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_data_dmamap[i])) {
178 			device_printf(sc->mrsas_dev, "Cannot allocate ioctl data mem\n");
179 			ret = ENOMEM;
180 			goto out;
181 		}
182 		if (bus_dmamap_load(ioctl_data_tag[i], ioctl_data_dmamap[i],
183 		    ioctl_data_mem[i], ioctl_data_size, mrsas_alloc_cb,
184 		    &ioctl_data_phys_addr[i], BUS_DMA_NOWAIT)) {
185 			device_printf(sc->mrsas_dev, "Cannot load ioctl data mem\n");
186 			ret = ENOMEM;
187 			goto out;
188 		}
189 		/* Save the physical address and length */
190 		kern_sge32[i].phys_addr = (u_int32_t)ioctl_data_phys_addr[i];
191 
192 		if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
193 			kern_sge32[i].length = user_ioc->sgl[i].iov_len;
194 
195 			iov_base_ptrin = user_ioc->sgl[i].iov_base;
196 			iov_len = user_ioc->sgl[i].iov_len;
197 #ifdef COMPAT_FREEBSD32
198 		} else {
199 			kern_sge32[i].length = user_ioc32->sgl[i].iov_len;
200 
201 			iov_base_ptrin = PTRIN(user_ioc32->sgl[i].iov_base);
202 			iov_len = user_ioc32->sgl[i].iov_len;
203 #endif
204 		}
205 
206 		/* Copy in data from user space */
207 		ret = copyin(iov_base_ptrin, ioctl_data_mem[i], iov_len);
208 		if (ret) {
209 			device_printf(sc->mrsas_dev, "IOCTL copyin failed!\n");
210 			goto out;
211 		}
212 	}
213 
214 	ioctl_sense_size = user_ioc->sense_len;
215 
216 	if (user_ioc->sense_len) {
217 		if (bus_dma_tag_create(sc->mrsas_parent_tag,
218 		    1, 0,
219 		    BUS_SPACE_MAXADDR_32BIT,
220 		    BUS_SPACE_MAXADDR,
221 		    NULL, NULL,
222 		    ioctl_sense_size,
223 		    1,
224 		    ioctl_sense_size,
225 		    BUS_DMA_ALLOCNOW,
226 		    NULL, NULL,
227 		    &ioctl_sense_tag)) {
228 			device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense tag\n");
229 			ret = ENOMEM;
230 			goto out;
231 		}
232 		if (bus_dmamem_alloc(ioctl_sense_tag, (void **)&ioctl_sense_mem,
233 		    (BUS_DMA_NOWAIT | BUS_DMA_ZERO), &ioctl_sense_dmamap)) {
234 			device_printf(sc->mrsas_dev, "Cannot allocate ioctl sense mem\n");
235 			ret = ENOMEM;
236 			goto out;
237 		}
238 		if (bus_dmamap_load(ioctl_sense_tag, ioctl_sense_dmamap,
239 		    ioctl_sense_mem, ioctl_sense_size, mrsas_alloc_cb,
240 		    &ioctl_sense_phys_addr, BUS_DMA_NOWAIT)) {
241 			device_printf(sc->mrsas_dev, "Cannot load ioctl sense mem\n");
242 			ret = ENOMEM;
243 			goto out;
244 		}
245 		sense_ptr =
246 		    (unsigned long *)((unsigned long)cmd->frame + user_ioc->sense_off);
247 		*sense_ptr = ioctl_sense_phys_addr;
248 	}
249 	/*
250 	 * Set the sync_cmd flag so that the ISR knows not to complete this
251 	 * cmd to the SCSI mid-layer
252 	 */
253 	cmd->sync_cmd = 1;
254 	ret = mrsas_issue_blocked_cmd(sc, cmd);
255 	if (ret == ETIMEDOUT) {
256 		mrsas_dprint(sc, MRSAS_OCR,
257 		    "IOCTL command is timed out, initiating OCR\n");
258 		sc->do_timedout_reset = MFI_DCMD_TIMEOUT_OCR;
259 		ret = EAGAIN;
260 		goto out;
261 	}
262 	cmd->sync_cmd = 0;
263 
264 	/*
265 	 * copy out the kernel buffers to user buffers
266 	 */
267 	for (i = 0; i < user_ioc->sge_count; i++) {
268 		if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
269 			iov_base_ptrin = user_ioc->sgl[i].iov_base;
270 			iov_len = user_ioc->sgl[i].iov_len;
271 #ifdef COMPAT_FREEBSD32
272 		} else {
273 			iov_base_ptrin = PTRIN(user_ioc32->sgl[i].iov_base);
274 			iov_len = user_ioc32->sgl[i].iov_len;
275 #endif
276 		}
277 
278 		ret = copyout(ioctl_data_mem[i], iov_base_ptrin, iov_len);
279 		if (ret) {
280 			device_printf(sc->mrsas_dev, "IOCTL copyout failed!\n");
281 			goto out;
282 		}
283 	}
284 
285 	/*
286 	 * copy out the sense
287 	 */
288 	if (user_ioc->sense_len) {
289 		/*
290 		 * sense_buff points to the location that has the user sense
291 		 * buffer address
292 		 */
293 		sense_ptr = (unsigned long *)((unsigned long)user_ioc->frame.raw +
294 		    user_ioc->sense_off);
295 		ret = copyout(ioctl_sense_mem, (unsigned long *)*sense_ptr,
296 		    user_ioc->sense_len);
297 		if (ret) {
298 			device_printf(sc->mrsas_dev, "IOCTL sense copyout failed!\n");
299 			goto out;
300 		}
301 	}
302 	/*
303 	 * Return command status to user space
304 	 */
305 	memcpy(&user_ioc->frame.hdr.cmd_status, &cmd->frame->hdr.cmd_status,
306 	    sizeof(u_int8_t));
307 
308 out:
309 	/*
310 	 * Release sense buffer
311 	 */
312 	if (user_ioc->sense_len) {
313 		if (ioctl_sense_phys_addr)
314 			bus_dmamap_unload(ioctl_sense_tag, ioctl_sense_dmamap);
315 		if (ioctl_sense_mem != NULL)
316 			bus_dmamem_free(ioctl_sense_tag, ioctl_sense_mem, ioctl_sense_dmamap);
317 		if (ioctl_sense_tag != NULL)
318 			bus_dma_tag_destroy(ioctl_sense_tag);
319 	}
320 	/*
321 	 * Release data buffers
322 	 */
323 	for (i = 0; i < user_ioc->sge_count; i++) {
324 		if (ioctlCmd == MRSAS_IOC_FIRMWARE_PASS_THROUGH64) {
325 			if (!user_ioc->sgl[i].iov_len)
326 				continue;
327 #ifdef COMPAT_FREEBSD32
328 		} else {
329 			if (!user_ioc32->sgl[i].iov_len)
330 				continue;
331 #endif
332 		}
333 		if (ioctl_data_phys_addr[i])
334 			bus_dmamap_unload(ioctl_data_tag[i], ioctl_data_dmamap[i]);
335 		if (ioctl_data_mem[i] != NULL)
336 			bus_dmamem_free(ioctl_data_tag[i], ioctl_data_mem[i],
337 			    ioctl_data_dmamap[i]);
338 		if (ioctl_data_tag[i] != NULL)
339 			bus_dma_tag_destroy(ioctl_data_tag[i]);
340 	}
341 	/* Free command */
342 	mrsas_release_mfi_cmd(cmd);
343 
344 	return (ret);
345 }
346 
347 /*
348  * mrsas_alloc_mfi_cmds:	Allocates the command packets
349  * input:					Adapter instance soft state
350  *
351  * Each IOCTL or passthru command that is issued to the FW are wrapped in a
352  * local data structure called mrsas_mfi_cmd.  The frame embedded in this
353  * mrsas_mfi is issued to FW. The array is used only to look up the
354  * mrsas_mfi_cmd given the context. The free commands are maintained in a
355  * linked list.
356  */
357 int
358 mrsas_alloc_mfi_cmds(struct mrsas_softc *sc)
359 {
360 	int i, j;
361 	u_int32_t max_cmd;
362 	struct mrsas_mfi_cmd *cmd;
363 
364 	max_cmd = MRSAS_MAX_MFI_CMDS;
365 
366 	/*
367 	 * sc->mfi_cmd_list is an array of struct mrsas_mfi_cmd pointers.
368 	 * Allocate the dynamic array first and then allocate individual
369 	 * commands.
370 	 */
371 	sc->mfi_cmd_list = malloc(sizeof(struct mrsas_mfi_cmd *) * max_cmd, M_MRSAS, M_NOWAIT);
372 	if (!sc->mfi_cmd_list) {
373 		device_printf(sc->mrsas_dev, "Cannot alloc memory for mfi_cmd cmd_list.\n");
374 		return (ENOMEM);
375 	}
376 	memset(sc->mfi_cmd_list, 0, sizeof(struct mrsas_mfi_cmd *) * max_cmd);
377 	for (i = 0; i < max_cmd; i++) {
378 		sc->mfi_cmd_list[i] = malloc(sizeof(struct mrsas_mfi_cmd),
379 		    M_MRSAS, M_NOWAIT);
380 		if (!sc->mfi_cmd_list[i]) {
381 			for (j = 0; j < i; j++)
382 				free(sc->mfi_cmd_list[j], M_MRSAS);
383 			free(sc->mfi_cmd_list, M_MRSAS);
384 			sc->mfi_cmd_list = NULL;
385 			return (ENOMEM);
386 		}
387 	}
388 
389 	for (i = 0; i < max_cmd; i++) {
390 		cmd = sc->mfi_cmd_list[i];
391 		memset(cmd, 0, sizeof(struct mrsas_mfi_cmd));
392 		cmd->index = i;
393 		cmd->ccb_ptr = NULL;
394 		cmd->sc = sc;
395 		TAILQ_INSERT_TAIL(&(sc->mrsas_mfi_cmd_list_head), cmd, next);
396 	}
397 
398 	/* create a frame pool and assign one frame to each command */
399 	if (mrsas_create_frame_pool(sc)) {
400 		device_printf(sc->mrsas_dev, "Cannot allocate DMA frame pool.\n");
401 		/* Free the frames */
402 		for (i = 0; i < MRSAS_MAX_MFI_CMDS; i++) {
403 			cmd = sc->mfi_cmd_list[i];
404 			mrsas_free_frame(sc, cmd);
405 		}
406 		if (sc->mficmd_frame_tag != NULL)
407 			bus_dma_tag_destroy(sc->mficmd_frame_tag);
408 		return (ENOMEM);
409 	}
410 	return (0);
411 }
412 
413 /*
414  * mrsas_create_frame_pool:	Creates DMA pool for cmd frames
415  * input:					Adapter soft state
416  *
417  * Each command packet has an embedded DMA memory buffer that is used for
418  * filling MFI frame and the SG list that immediately follows the frame. This
419  * function creates those DMA memory buffers for each command packet by using
420  * PCI pool facility. pad_0 is initialized to 0 to prevent corrupting value
421  * of context and could cause FW crash.
422  */
423 static int
424 mrsas_create_frame_pool(struct mrsas_softc *sc)
425 {
426 	int i;
427 	struct mrsas_mfi_cmd *cmd;
428 
429 	if (bus_dma_tag_create(sc->mrsas_parent_tag,
430 	    1, 0,
431 	    BUS_SPACE_MAXADDR_32BIT,
432 	    BUS_SPACE_MAXADDR,
433 	    NULL, NULL,
434 	    MRSAS_MFI_FRAME_SIZE,
435 	    1,
436 	    MRSAS_MFI_FRAME_SIZE,
437 	    BUS_DMA_ALLOCNOW,
438 	    NULL, NULL,
439 	    &sc->mficmd_frame_tag)) {
440 		device_printf(sc->mrsas_dev, "Cannot create MFI frame tag\n");
441 		return (ENOMEM);
442 	}
443 	for (i = 0; i < MRSAS_MAX_MFI_CMDS; i++) {
444 		cmd = sc->mfi_cmd_list[i];
445 		cmd->frame = mrsas_alloc_frame(sc, cmd);
446 		if (cmd->frame == NULL) {
447 			device_printf(sc->mrsas_dev, "Cannot alloc MFI frame memory\n");
448 			return (ENOMEM);
449 		}
450 		/*
451 		 * For MFI controllers.
452 		 * max_num_sge = 60
453 		 * max_sge_sz  = 16 byte (sizeof megasas_sge_skinny)
454 		 * Totl 960 byte (15 MFI frame of 64 byte)
455 		 *
456 		 * Fusion adapter require only 3 extra frame.
457 		 * max_num_sge = 16 (defined as MAX_IOCTL_SGE)
458 		 * max_sge_sz  = 12 byte (sizeof  megasas_sge64)
459 		 * Total 192 byte (3 MFI frame of 64 byte)
460 		 */
461 		memset(cmd->frame, 0, MRSAS_MFI_FRAME_SIZE);
462 		cmd->frame->io.context = cmd->index;
463 		cmd->frame->io.pad_0 = 0;
464 	}
465 
466 	return (0);
467 }
468 
469 /*
470  * mrsas_alloc_frame:	Allocates MFI Frames
471  * input:				Adapter soft state
472  *
473  * Create bus DMA memory tag and dmamap and load memory for MFI frames. Returns
474  * virtual memory pointer to allocated region.
475  */
476 void   *
477 mrsas_alloc_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd)
478 {
479 	u_int32_t frame_size = MRSAS_MFI_FRAME_SIZE;
480 
481 	if (bus_dmamem_alloc(sc->mficmd_frame_tag, (void **)&cmd->frame_mem,
482 	    BUS_DMA_NOWAIT, &cmd->frame_dmamap)) {
483 		device_printf(sc->mrsas_dev, "Cannot alloc MFI frame memory\n");
484 		return (NULL);
485 	}
486 	if (bus_dmamap_load(sc->mficmd_frame_tag, cmd->frame_dmamap,
487 	    cmd->frame_mem, frame_size, mrsas_alloc_cb,
488 	    &cmd->frame_phys_addr, BUS_DMA_NOWAIT)) {
489 		device_printf(sc->mrsas_dev, "Cannot load IO request memory\n");
490 		return (NULL);
491 	}
492 	return (cmd->frame_mem);
493 }
494 
495 /*
496  * mrsas_alloc_cb:	Callback function of bus_dmamap_load()
497  * input:			callback argument,
498  * 					machine dependent type that describes DMA segments,
499  * 					number of segments,
500  * 					error code.
501  *
502  * This function is for the driver to receive mapping information resultant of
503  * the bus_dmamap_load(). The information is actually not being used, but the
504  * address is saved anyway.
505  */
506 static void
507 mrsas_alloc_cb(void *arg, bus_dma_segment_t *segs,
508     int nsegs, int error)
509 {
510 	bus_addr_t *addr;
511 
512 	addr = arg;
513 	*addr = segs[0].ds_addr;
514 }
515 
516 /*
517  * mrsas_free_frames:	Frees memory for  MFI frames
518  * input:				Adapter soft state
519  *
520  * Deallocates MFI frames memory.  Called from mrsas_free_mem() during detach
521  * and error case during creation of frame pool.
522  */
523 void
524 mrsas_free_frame(struct mrsas_softc *sc, struct mrsas_mfi_cmd *cmd)
525 {
526 	if (cmd->frame_phys_addr)
527 		bus_dmamap_unload(sc->mficmd_frame_tag, cmd->frame_dmamap);
528 	if (cmd->frame_mem != NULL)
529 		bus_dmamem_free(sc->mficmd_frame_tag, cmd->frame_mem, cmd->frame_dmamap);
530 }
531