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