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