xref: /dragonfly/sys/dev/raid/ips/ips_commands.c (revision 1bf4b486)
1 /*-
2  * Written by: David Jeffery
3  * Copyright (c) 2002 Adaptec Inc.
4  * All rights reserved.
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  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/dev/ips/ips_commands.c,v 1.10 2004/05/30 04:01:29 scottl Exp $
28  * $DragonFly: src/sys/dev/raid/ips/ips_commands.c,v 1.8 2004/12/10 04:09:46 y0netan1 Exp $
29  */
30 
31 #include <dev/raid/ips/ips.h>
32 
33 /*
34  * This is an interrupt callback.  It is called from
35  * interrupt context when the adapter has completed the
36  * command.  This very generic callback simply stores
37  * the command's return value in command->arg and wake's
38  * up anyone waiting on the command.
39  */
40 static void
41 ips_wakeup_callback(ips_command_t *command)
42 {
43 	ips_cmd_status_t *status;
44 
45 	status = command->arg;
46 	status->value = command->status.value;
47 	bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap,
48 			BUS_DMASYNC_POSTWRITE);
49 	wakeup(status);
50 }
51 
52 /*
53  * Below are a series of functions for sending an IO request
54  * to the adapter.  The flow order is: start, send, callback, finish.
55  * The caller must have already assembled an iorequest struct to hold
56  * the details of the IO request.
57  */
58 static void
59 ips_io_request_finish(ips_command_t *command)
60 {
61 	struct bio *iobuf = command->arg;
62 
63 	if (ips_read_request(iobuf)) {
64 		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
65 				BUS_DMASYNC_POSTREAD);
66 	} else {
67 		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
68 				BUS_DMASYNC_POSTWRITE);
69 	}
70 	bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
71 	bus_dmamap_destroy(command->data_dmatag, command->data_dmamap);
72 	if (COMMAND_ERROR(&command->status)) {
73 		iobuf->bio_flags |=BIO_ERROR;
74 		iobuf->bio_error = EIO;
75 	}
76 	ips_insert_free_cmd(command->sc, command);
77 	ipsd_finish(iobuf);
78 }
79 
80 static void
81 ips_io_request_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
82 			int error)
83 {
84 	ips_softc_t *sc;
85 	ips_command_t *command = cmdptr;
86 	ips_sg_element_t *sg_list;
87 	ips_io_cmd *command_struct;
88 	struct bio *iobuf = command->arg;
89 	int i, length = 0;
90 	u_int8_t cmdtype;
91 
92 	sc = command->sc;
93 	if (error) {
94 		printf("ips: error = %d in ips_sg_request_callback\n", error);
95 		bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
96 		bus_dmamap_destroy(command->data_dmatag, command->data_dmamap);
97 		iobuf->bio_flags |= BIO_ERROR;
98 		iobuf->bio_error = ENOMEM;
99 		ips_insert_free_cmd(sc, command);
100 		ipsd_finish(iobuf);
101 		return;
102 	}
103 	command_struct = (ips_io_cmd *)command->command_buffer;
104 	command_struct->id = command->id;
105 	command_struct->drivenum = (uintptr_t)iobuf->bio_driver1;
106 	if (segnum != 1) {
107 		if (ips_read_request(iobuf))
108 			cmdtype = IPS_SG_READ_CMD;
109 		else
110 			cmdtype = IPS_SG_WRITE_CMD;
111 		command_struct->segnum = segnum;
112 		sg_list = (ips_sg_element_t *)((u_int8_t *)
113 			   command->command_buffer + IPS_COMMAND_LEN);
114 		for (i = 0; i < segnum; i++) {
115 			sg_list[i].addr = segments[i].ds_addr;
116 			sg_list[i].len = segments[i].ds_len;
117 			length += segments[i].ds_len;
118 		}
119 		command_struct->buffaddr =
120 		    (u_int32_t)command->command_phys_addr + IPS_COMMAND_LEN;
121 	} else {
122 		if (ips_read_request(iobuf))
123 			cmdtype = IPS_READ_CMD;
124 		else
125 			cmdtype = IPS_WRITE_CMD;
126 		command_struct->buffaddr = segments[0].ds_addr;
127 		length = segments[0].ds_len;
128 	}
129 	command_struct->command = cmdtype;
130 	command_struct->lba = iobuf->bio_pblkno;
131 	length = (length + IPS_BLKSIZE - 1)/IPS_BLKSIZE;
132 	command_struct->length = length;
133 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
134 			BUS_DMASYNC_PREWRITE);
135 	if (ips_read_request(iobuf)) {
136 		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
137 				BUS_DMASYNC_PREREAD);
138 	} else {
139 		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
140 				BUS_DMASYNC_PREWRITE);
141 	}
142 	/*
143 	 * the cast to long long below is necessary because our b_pblkno
144 	 * is 32bit wide whereas it's 64bit on FreeBSD-CURRENT.
145 	 */
146 	PRINTF(10, "ips test: command id: %d segments: %d "
147 		"pblkno: %lld length: %d, ds_len: %d\n", command->id, segnum,
148 		(long long)iobuf->bio_pblkno,
149 		length, segments[0].ds_len);
150 
151 	sc->ips_issue_cmd(command);
152 	return;
153 }
154 
155 static int
156 ips_send_io_request(ips_command_t *command)
157 {
158 	ips_softc_t *sc = command->sc;
159 	struct bio *iobuf = command->arg;
160 	command->data_dmatag = sc->sg_dmatag;
161 
162 	if (bus_dmamap_create(command->data_dmatag, 0, &command->data_dmamap)) {
163 		device_printf(sc->dev, "dmamap failed\n");
164 		iobuf->bio_flags |= BIO_ERROR;
165 		iobuf->bio_error = ENOMEM;
166 		ips_insert_free_cmd(sc, command);
167 		ipsd_finish(iobuf);
168 		return 0;
169 	}
170 	command->callback = ips_io_request_finish;
171 	PRINTF(10, "ips test: : bcount %ld\n", iobuf->bio_bcount);
172 	bus_dmamap_load(command->data_dmatag, command->data_dmamap,
173 			iobuf->bio_data, iobuf->bio_bcount,
174 			ips_io_request_callback, command, 0);
175 	return 0;
176 }
177 
178 void
179 ips_start_io_request(ips_softc_t *sc, struct bio *iobuf)
180 {
181 	if (ips_get_free_cmd(sc, ips_send_io_request, iobuf, 0)) {
182 		device_printf(sc->dev, "no mem for command slots!\n");
183 		iobuf->bio_flags |= BIO_ERROR;
184 		iobuf->bio_error = ENOMEM;
185 		ipsd_finish(iobuf);
186 		return;
187 	}
188 	return;
189 }
190 
191 /*
192  * Below are a series of functions for sending an adapter info request
193  * to the adapter.  The flow order is: get, send, callback. It uses
194  * the generic finish callback at the top of this file.
195  * This can be used to get configuration/status info from the card
196  */
197 static void
198 ips_adapter_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum,
199 			  int error)
200 {
201 	ips_softc_t *sc;
202 	ips_command_t *command = cmdptr;
203 	ips_adapter_info_cmd *command_struct;
204 	sc = command->sc;
205 	if (error) {
206 		ips_cmd_status_t * status = command->arg;
207 		status->value = IPS_ERROR_STATUS; /* a lovely error value */
208 		ips_insert_free_cmd(sc, command);
209 		printf("ips: error = %d in ips_get_adapter_info\n", error);
210 		return;
211 	}
212 	command_struct = (ips_adapter_info_cmd *)command->command_buffer;
213 	command_struct->command = IPS_ADAPTER_INFO_CMD;
214 	command_struct->id = command->id;
215 	command_struct->buffaddr = segments[0].ds_addr;
216 
217 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
218 			BUS_DMASYNC_PREWRITE);
219 	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
220 			BUS_DMASYNC_PREREAD);
221 	sc->ips_issue_cmd(command);
222 }
223 
224 static int
225 ips_send_adapter_info_cmd(ips_command_t *command)
226 {
227 	ips_softc_t *sc = command->sc;
228 	ips_cmd_status_t *status = command->arg;
229 	int error = 0;
230 
231 	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
232 				/* alignemnt */	1,
233 				/* boundary  */	0,
234 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
235 				/* highaddr  */	BUS_SPACE_MAXADDR,
236 				/* filter    */	NULL,
237 				/* filterarg */	NULL,
238 				/* maxsize   */	IPS_ADAPTER_INFO_LEN,
239 				/* numsegs   */	1,
240 				/* maxsegsize*/	IPS_ADAPTER_INFO_LEN,
241 				/* flags     */	0,
242 				&command->data_dmatag) != 0) {
243 		printf("ips: can't alloc dma tag for adapter status\n");
244 		error = ENOMEM;
245 		goto exit;
246 	}
247 	if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
248 	   BUS_DMA_NOWAIT, &command->data_dmamap)) {
249 		error = ENOMEM;
250 		goto exit;
251 	}
252 	command->callback = ips_wakeup_callback;
253 	bus_dmamap_load(command->data_dmatag, command->data_dmamap,
254 	    command->data_buffer, IPS_ADAPTER_INFO_LEN,
255 	    ips_adapter_info_callback, command, BUS_DMA_NOWAIT);
256 	if ((status->value == IPS_ERROR_STATUS) ||
257 	    tsleep(status, 0, "ips", 30 * hz) == EWOULDBLOCK)
258 		error = ETIMEDOUT;
259 	if (error == 0) {
260 		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
261 		    BUS_DMASYNC_POSTREAD);
262 		memcpy(&(sc->adapter_info), command->data_buffer,
263 			IPS_ADAPTER_INFO_LEN);
264 	}
265 	bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
266 exit:
267 	/* I suppose I should clean up my memory allocations */
268 	bus_dmamem_free(command->data_dmatag, command->data_buffer,
269 	    command->data_dmamap);
270 	bus_dma_tag_destroy(command->data_dmatag);
271 	ips_insert_free_cmd(sc, command);
272 	return error;
273 }
274 
275 int
276 ips_get_adapter_info(ips_softc_t *sc)
277 {
278 	int error = 0;
279 	ips_cmd_status_t *status;
280 
281 	status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
282 	if (ips_get_free_cmd(sc, ips_send_adapter_info_cmd, status,
283 	    IPS_NOWAIT_FLAG) > 0) {
284 		device_printf(sc->dev, "unable to get adapter configuration\n");
285 		free(status, M_IPSBUF);
286 		return ENXIO;
287 	}
288 	if (COMMAND_ERROR(status))
289 		error = ENXIO;
290 	free(status, M_IPSBUF);
291 	return error;
292 }
293 
294 /*
295  * Below are a series of functions for sending a drive info request
296  * to the adapter.  The flow order is: get, send, callback. It uses
297  * the generic finish callback at the top of this file.
298  * This can be used to get drive status info from the card
299  */
300 static void
301 ips_drive_info_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
302 			int error)
303 {
304 	ips_softc_t *sc;
305 	ips_command_t *command = cmdptr;
306 	ips_drive_cmd *command_struct;
307 
308 	sc = command->sc;
309 	if (error) {
310 		ips_cmd_status_t *status = command->arg;
311 
312 		status->value = IPS_ERROR_STATUS;
313 		ips_insert_free_cmd(sc, command);
314 		printf("ips: error = %d in ips_get_drive_info\n", error);
315 		return;
316 	}
317 	command_struct = (ips_drive_cmd *)command->command_buffer;
318 	command_struct->command = IPS_DRIVE_INFO_CMD;
319 	command_struct->id = command->id;
320 	command_struct->buffaddr = segments[0].ds_addr;
321 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
322 	    BUS_DMASYNC_PREWRITE);
323 	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
324 	    BUS_DMASYNC_PREREAD);
325 	sc->ips_issue_cmd(command);
326 }
327 
328 static int
329 ips_send_drive_info_cmd(ips_command_t *command)
330 {
331 	int error = 0;
332 	ips_softc_t *sc = command->sc;
333 	ips_cmd_status_t *status = command->arg;
334 	ips_drive_info_t *driveinfo;
335 
336 	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
337 				/* alignemnt */	1,
338 				/* boundary  */	0,
339 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
340 				/* highaddr  */	BUS_SPACE_MAXADDR,
341 				/* filter    */	NULL,
342 				/* filterarg */	NULL,
343 				/* maxsize   */	IPS_DRIVE_INFO_LEN,
344 				/* numsegs   */	1,
345 				/* maxsegsize*/	IPS_DRIVE_INFO_LEN,
346 				/* flags     */	0,
347 				&command->data_dmatag) != 0) {
348 		printf("ips: can't alloc dma tag for drive status\n");
349 		error = ENOMEM;
350 		goto exit;
351 	}
352 	if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
353 	    BUS_DMA_NOWAIT, &command->data_dmamap)) {
354 		error = ENOMEM;
355 		goto exit;
356 	}
357 	command->callback = ips_wakeup_callback;
358 	bus_dmamap_load(command->data_dmatag, command->data_dmamap,
359 	    command->data_buffer,IPS_DRIVE_INFO_LEN,
360 	    ips_drive_info_callback, command, BUS_DMA_NOWAIT);
361 	if ((status->value == IPS_ERROR_STATUS) ||
362 	    tsleep(status, 0, "ips", 10 * hz) == EWOULDBLOCK)
363 		error = ETIMEDOUT;
364 
365 	if (error == 0) {
366 		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
367 		    BUS_DMASYNC_POSTREAD);
368 		driveinfo = command->data_buffer;
369 		memcpy(sc->drives, driveinfo->drives, sizeof(ips_drive_t) * 8);
370 		sc->drivecount = driveinfo->drivecount;
371 		device_printf(sc->dev, "logical drives: %d\n", sc->drivecount);
372 	}
373 	bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
374 exit:
375 	/* I suppose I should clean up my memory allocations */
376 	bus_dmamem_free(command->data_dmatag, command->data_buffer,
377 			command->data_dmamap);
378 	bus_dma_tag_destroy(command->data_dmatag);
379 	ips_insert_free_cmd(sc, command);
380 	return error;
381 
382 }
383 int
384 ips_get_drive_info(ips_softc_t *sc)
385 {
386 	int error = 0;
387 	ips_cmd_status_t *status;
388 
389 	status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
390 	if (ips_get_free_cmd(sc, ips_send_drive_info_cmd, status,
391 	    IPS_NOWAIT_FLAG) > 0) {
392 		free(status, M_IPSBUF);
393 		device_printf(sc->dev, "unable to get drive configuration\n");
394 		return ENXIO;
395 	}
396 	if (COMMAND_ERROR(status))
397 		error = ENXIO;
398 	free(status, M_IPSBUF);
399 	return error;
400 }
401 
402 /*
403  * Below is a pair of functions for making sure data is safely
404  * on disk by flushing the adapter's cache.
405  */
406 static int
407 ips_send_flush_cache_cmd(ips_command_t *command)
408 {
409 	ips_softc_t *sc = command->sc;
410 	ips_cmd_status_t *status = command->arg;
411 	ips_generic_cmd *command_struct;
412 
413 	PRINTF(10,"ips test: got a command, building flush command\n");
414 	command->callback = ips_wakeup_callback;
415 	command_struct = (ips_generic_cmd *)command->command_buffer;
416 	command_struct->command = IPS_CACHE_FLUSH_CMD;
417 	command_struct->id = command->id;
418 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
419 	    BUS_DMASYNC_PREWRITE);
420 	sc->ips_issue_cmd(command);
421 	if (status->value != IPS_ERROR_STATUS)
422 		tsleep(status, 0, "flush2", 0);
423 	ips_insert_free_cmd(sc, command);
424 	return 0;
425 }
426 
427 int
428 ips_flush_cache(ips_softc_t *sc)
429 {
430 	ips_cmd_status_t *status;
431 
432 	status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
433 	device_printf(sc->dev, "flushing cache\n");
434 	if (ips_get_free_cmd(sc, ips_send_flush_cache_cmd, status,
435 	    IPS_NOWAIT_FLAG)) {
436 		free(status, M_IPSBUF);
437 		device_printf(sc->dev, "ERROR: unable to get a command! "
438 		    "can't flush cache!\n");
439 		return 1;
440 	}
441 	if (COMMAND_ERROR(status)) {
442 		free(status, M_IPSBUF);
443 		device_printf(sc->dev, "ERROR: cache flush command failed!\n");
444 		return 1;
445 	}
446 	free(status, M_IPSBUF);
447 	return 0;
448 }
449 
450 /*
451  * Simplified localtime to provide timevalues for ffdc.
452  * Taken from libc/stdtime/localtime.c
453  */
454 static void
455 ips_ffdc_settime(ips_adapter_ffdc_cmd *command, time_t sctime)
456 {
457 	long	days, rem, y;
458 	int	yleap, *ip, month;
459 	int	year_lengths[2] = { IPS_DAYSPERNYEAR, IPS_DAYSPERLYEAR };
460 	int	mon_lengths[2][IPS_MONSPERYEAR] = {
461 		{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
462 		{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
463 	};
464 
465 	days = sctime / IPS_SECSPERDAY;
466 	rem  = sctime % IPS_SECSPERDAY;
467 
468 	command->hour = rem / IPS_SECSPERHOUR;
469 	rem	      = rem % IPS_SECSPERHOUR;
470 
471 	command->minute = rem / IPS_SECSPERMIN;
472 	command->second = rem % IPS_SECSPERMIN;
473 
474 	y = IPS_EPOCH_YEAR;
475 	while (days < 0 || days >= (long)year_lengths[yleap = ips_isleap(y)]) {
476 		long    newy;
477 
478 		newy = y + days / IPS_DAYSPERNYEAR;
479 		if (days < 0)
480 			--newy;
481 		days -= (newy - y) * IPS_DAYSPERNYEAR +
482 		    IPS_LEAPS_THRU_END_OF(newy - 1) -
483 		    IPS_LEAPS_THRU_END_OF(y - 1);
484 		y = newy;
485 	}
486 	command->yearH = y / 100;
487 	command->yearL = y % 100;
488 	ip = mon_lengths[yleap];
489 	for (month = 0; days >= (long)ip[month]; ++month)
490 		days = days - (long)ip[month];
491 	command->month = month + 1;
492 	command->day = days + 1;
493 }
494 
495 static int
496 ips_send_ffdc_reset_cmd(ips_command_t *command)
497 {
498 	ips_softc_t *sc = command->sc;
499 	ips_cmd_status_t *status = command->arg;
500 	ips_adapter_ffdc_cmd *command_struct;
501 
502 	PRINTF(10, "ips test: got a command, building ffdc reset command\n");
503 	command->callback = ips_wakeup_callback;
504 	command_struct = (ips_adapter_ffdc_cmd *)command->command_buffer;
505 	command_struct->command = IPS_FFDC_CMD;
506 	command_struct->id = command->id;
507 	command_struct->reset_count = sc->ffdc_resetcount;
508 	command_struct->reset_type  = 0x0;
509 	ips_ffdc_settime(command_struct, sc->ffdc_resettime.tv_sec);
510 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
511 	    BUS_DMASYNC_PREWRITE);
512 	sc->ips_issue_cmd(command);
513 	if (status->value != IPS_ERROR_STATUS)
514 		tsleep(status, 0, "ffdc", 0);
515 	ips_insert_free_cmd(sc, command);
516 	return 0;
517 }
518 
519 int
520 ips_ffdc_reset(ips_softc_t *sc)
521 {
522 	ips_cmd_status_t *status;
523 
524 	status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
525 	if (ips_get_free_cmd(sc, ips_send_ffdc_reset_cmd, status,
526 	    IPS_NOWAIT_FLAG)) {
527 		free(status, M_IPSBUF);
528 		device_printf(sc->dev, "ERROR: unable to get a command! "
529 		    "can't send ffdc reset!\n");
530 		return 1;
531 	}
532 	if (COMMAND_ERROR(status)) {
533 		free(status, M_IPSBUF);
534 		device_printf(sc->dev, "ERROR: ffdc reset command failed!\n");
535 		return 1;
536 	}
537 	free(status, M_IPSBUF);
538 	return 0;
539 }
540 
541 static void
542 ips_write_nvram(ips_command_t *command)
543 {
544 	ips_softc_t *sc = command->sc;
545 	ips_rw_nvram_cmd *command_struct;
546 	ips_nvram_page5 *nvram;
547 
548 	/*FIXME check for error */
549 	command->callback = ips_wakeup_callback;
550 	command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
551 	command_struct->command = IPS_RW_NVRAM_CMD;
552 	command_struct->id = command->id;
553 	command_struct->pagenum = 5;
554 	command_struct->rw	= 1;	/* write */
555 	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
556 	    BUS_DMASYNC_POSTREAD);
557 	nvram = command->data_buffer;
558 	/* retrieve adapter info and save in sc */
559 	sc->adapter_type = nvram->adapter_type;
560 	strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4);
561 	strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4);
562 	nvram->operating_system = IPS_OS_FREEBSD;
563 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
564 	    BUS_DMASYNC_PREWRITE);
565 	sc->ips_issue_cmd(command);
566 }
567 
568 static void
569 ips_read_nvram_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
570 			int error)
571 {
572 	ips_softc_t *sc;
573 	ips_command_t *command = cmdptr;
574 	ips_rw_nvram_cmd *command_struct;
575 
576 	sc = command->sc;
577 	if (error) {
578 		ips_cmd_status_t *status = command->arg;
579 
580 		status->value = IPS_ERROR_STATUS;
581 		ips_insert_free_cmd(sc, command);
582 		printf("ips: error = %d in ips_read_nvram_callback\n", error);
583 		return;
584 	}
585 	command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
586 	command_struct->command = IPS_RW_NVRAM_CMD;
587 	command_struct->id = command->id;
588 	command_struct->pagenum = 5;
589 	command_struct->rw = 0;
590 	command_struct->buffaddr = segments[0].ds_addr;
591 
592 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
593 	    BUS_DMASYNC_PREWRITE);
594 	bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
595 	    BUS_DMASYNC_PREREAD);
596 	sc->ips_issue_cmd(command);
597 }
598 
599 static int
600 ips_read_nvram(ips_command_t *command)
601 {
602 	int error = 0;
603 	ips_softc_t *sc = command->sc;
604 	ips_cmd_status_t *status = command->arg;
605 
606 	if (bus_dma_tag_create(	/* parent    */	sc->adapter_dmatag,
607 				/* alignemnt */	1,
608 				/* boundary  */	0,
609 				/* lowaddr   */	BUS_SPACE_MAXADDR_32BIT,
610 				/* highaddr  */	BUS_SPACE_MAXADDR,
611 				/* filter    */	NULL,
612 				/* filterarg */	NULL,
613 				/* maxsize   */	IPS_NVRAM_PAGE_SIZE,
614 				/* numsegs   */	1,
615 				/* maxsegsize*/	IPS_NVRAM_PAGE_SIZE,
616 				/* flags     */	0,
617 				&command->data_dmatag) != 0) {
618 		printf("ips: can't alloc dma tag for nvram\n");
619 		error = ENOMEM;
620 		goto exit;
621 	}
622 	if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
623 	    BUS_DMA_NOWAIT, &command->data_dmamap)) {
624 		error = ENOMEM;
625 		goto exit;
626 	}
627 	command->callback = ips_write_nvram;
628 	bus_dmamap_load(command->data_dmatag, command->data_dmamap,
629 	    command->data_buffer, IPS_NVRAM_PAGE_SIZE, ips_read_nvram_callback,
630 	    command, BUS_DMA_NOWAIT);
631 	if ((status->value == IPS_ERROR_STATUS) ||
632 	    tsleep(status, 0, "ips", 0) == EWOULDBLOCK)
633 		error = ETIMEDOUT;
634 	if (error == 0) {
635 		bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
636 				BUS_DMASYNC_POSTWRITE);
637 	}
638 	bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
639 exit:
640 	bus_dmamem_free(command->data_dmatag, command->data_buffer,
641 			command->data_dmamap);
642 	bus_dma_tag_destroy(command->data_dmatag);
643 	ips_insert_free_cmd(sc, command);
644 	return error;
645 }
646 
647 int
648 ips_update_nvram(ips_softc_t *sc)
649 {
650 	ips_cmd_status_t *status;
651 
652 	status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
653 	if (ips_get_free_cmd(sc, ips_read_nvram, status, IPS_NOWAIT_FLAG)) {
654 		free(status, M_IPSBUF);
655 		device_printf(sc->dev, "ERROR: unable to get a command! "
656 		    "can't update nvram\n");
657 		return 1;
658 	}
659 	if (COMMAND_ERROR(status)) {
660 		free(status, M_IPSBUF);
661 		device_printf(sc->dev, "ERROR: nvram update command failed!\n");
662 		return 1;
663 	}
664 	free(status, M_IPSBUF);
665 	return 0;
666 }
667 
668 static int
669 ips_send_config_sync_cmd(ips_command_t *command)
670 {
671 	ips_softc_t *sc = command->sc;
672 	ips_cmd_status_t *status = command->arg;
673 	ips_generic_cmd *command_struct;
674 
675 	PRINTF(10, "ips test: got a command, building flush command\n");
676 	command->callback = ips_wakeup_callback;
677 	command_struct = (ips_generic_cmd *)command->command_buffer;
678 	command_struct->command = IPS_CONFIG_SYNC_CMD;
679 	command_struct->id = command->id;
680 	command_struct->reserve2 = IPS_POCL;
681 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
682 	    BUS_DMASYNC_PREWRITE);
683 	sc->ips_issue_cmd(command);
684 	if (status->value != IPS_ERROR_STATUS)
685 		tsleep(status, 0, "ipssyn", 0);
686 	ips_insert_free_cmd(sc, command);
687 	return 0;
688 }
689 
690 static int
691 ips_send_error_table_cmd(ips_command_t *command)
692 {
693 	ips_softc_t *sc = command->sc;
694 	ips_cmd_status_t *status = command->arg;
695 	ips_generic_cmd *command_struct;
696 
697 	PRINTF(10, "ips test: got a command, building errortable command\n");
698 	command->callback = ips_wakeup_callback;
699 	command_struct = (ips_generic_cmd *)command->command_buffer;
700 	command_struct->command = IPS_ERROR_TABLE_CMD;
701 	command_struct->id = command->id;
702 	command_struct->reserve2 = IPS_CSL;
703 	bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
704 	    BUS_DMASYNC_PREWRITE);
705 	sc->ips_issue_cmd(command);
706 	if (status->value != IPS_ERROR_STATUS)
707 		tsleep(status, 0, "ipsetc", 0);
708 	ips_insert_free_cmd(sc, command);
709 	return 0;
710 }
711 
712 int
713 ips_clear_adapter(ips_softc_t *sc)
714 {
715 	ips_cmd_status_t *status;
716 
717 	status = malloc(sizeof(ips_cmd_status_t), M_IPSBUF, M_INTWAIT | M_ZERO);
718 	device_printf(sc->dev, "syncing config\n");
719 	if (ips_get_free_cmd(sc, ips_send_config_sync_cmd, status,
720 	    IPS_NOWAIT_FLAG)) {
721 		free(status, M_IPSBUF);
722 		device_printf(sc->dev, "ERROR: unable to get a command! "
723 		    "can't sync cache!\n");
724 		return 1;
725 	}
726 	if (COMMAND_ERROR(status)) {
727 		free(status, M_IPSBUF);
728 		device_printf(sc->dev, "ERROR: cache sync command failed!\n");
729 		return 1;
730 	}
731 	device_printf(sc->dev, "clearing error table\n");
732 	if (ips_get_free_cmd(sc, ips_send_error_table_cmd, status,
733 	    IPS_NOWAIT_FLAG)) {
734 		free(status, M_IPSBUF);
735 		device_printf(sc->dev, "ERROR: unable to get a command! "
736 		    "can't sync cache!\n");
737 		return 1;
738 	}
739 	if (COMMAND_ERROR(status)) {
740 		device_printf(sc->dev, "ERROR: etable command failed!\n");
741 		free(status, M_IPSBUF);
742 		return 1;
743 	}
744 	free(status, M_IPSBUF);
745 	return 0;
746 }
747