xref: /freebsd/sys/dev/ocs_fc/ocs_utils.c (revision f7c32ed617858bcd22f8d1b03199099d50125721)
1 /*-
2  * Copyright (c) 2017 Broadcom. All rights reserved.
3  * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written 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  * $FreeBSD$
32  */
33 
34 /**
35  * @file
36  *
37  */
38 
39 #include "ocs.h"
40 #include "ocs_os.h"
41 
42 #define DEFAULT_SLAB_LEN		(64*1024)
43 
44 struct ocs_array_s {
45 	ocs_os_handle_t os;
46 
47 	uint32_t size;
48 	uint32_t count;
49 
50 	uint32_t n_rows;
51 	uint32_t elems_per_row;
52 	uint32_t bytes_per_row;
53 
54 	void **array_rows;
55 	uint32_t array_rows_len;
56 };
57 
58 static uint32_t slab_len = DEFAULT_SLAB_LEN;
59 
60 /**
61  * @brief Set array slab allocation length
62  *
63  * The slab length is the maximum allocation length that the array uses.
64  * The default 64k slab length may be overridden using this function.
65  *
66  * @param len new slab length.
67  *
68  * @return none
69  */
70 void
71 ocs_array_set_slablen(uint32_t len)
72 {
73 	slab_len = len;
74 }
75 
76 /**
77  * @brief Allocate an array object
78  *
79  * An array object of size and number of elements is allocated
80  *
81  * @param os OS handle
82  * @param size size of array elements in bytes
83  * @param count number of elements in array
84  *
85  * @return pointer to array object or NULL
86  */
87 ocs_array_t *
88 ocs_array_alloc(ocs_os_handle_t os, uint32_t size, uint32_t count)
89 {
90 	ocs_array_t *array = NULL;
91 	uint32_t i;
92 
93 	/* Fail if the item size exceeds slab_len - caller should increase slab_size,
94 	 * or not use this API.
95 	 */
96 	if (size > slab_len) {
97 		ocs_log_err(NULL, "Error: size exceeds slab length\n");
98 		return NULL;
99 	}
100 
101 	array = ocs_malloc(os, sizeof(*array), OCS_M_ZERO | OCS_M_NOWAIT);
102 	if (array == NULL) {
103 		return NULL;
104 	}
105 
106 	array->os = os;
107 	array->size = size;
108 	array->count = count;
109 	array->elems_per_row = slab_len / size;
110 	array->n_rows = (count + array->elems_per_row - 1) / array->elems_per_row;
111 	array->bytes_per_row = array->elems_per_row * array->size;
112 
113 	array->array_rows_len = array->n_rows * sizeof(*array->array_rows);
114 	array->array_rows = ocs_malloc(os, array->array_rows_len, OCS_M_ZERO | OCS_M_NOWAIT);
115 	if (array->array_rows == NULL) {
116 		ocs_array_free(array);
117 		return NULL;
118 	}
119 	for (i = 0; i < array->n_rows; i++) {
120 		array->array_rows[i] = ocs_malloc(os, array->bytes_per_row, OCS_M_ZERO | OCS_M_NOWAIT);
121 		if (array->array_rows[i] == NULL) {
122 			ocs_array_free(array);
123 			return NULL;
124 		}
125 	}
126 
127 	return array;
128 }
129 
130 /**
131  * @brief Free an array object
132  *
133  * Frees a prevously allocated array object
134  *
135  * @param array pointer to array object
136  *
137  * @return none
138  */
139 void
140 ocs_array_free(ocs_array_t *array)
141 {
142 	uint32_t i;
143 
144 	if (array != NULL) {
145 		if (array->array_rows != NULL) {
146 			for (i = 0; i < array->n_rows; i++) {
147 				if (array->array_rows[i] != NULL) {
148 					ocs_free(array->os, array->array_rows[i], array->bytes_per_row);
149 				}
150 			}
151 			ocs_free(array->os, array->array_rows, array->array_rows_len);
152 		}
153 		ocs_free(array->os, array, sizeof(*array));
154 	}
155 }
156 
157 /**
158  * @brief Return reference to an element of an array object
159  *
160  * Return the address of an array element given an index
161  *
162  * @param array pointer to array object
163  * @param idx array element index
164  *
165  * @return rointer to array element, or NULL if index out of range
166  */
167 void *ocs_array_get(ocs_array_t *array, uint32_t idx)
168 {
169 	void *entry = NULL;
170 
171 	if (idx < array->count) {
172 		uint32_t row = idx / array->elems_per_row;
173 		uint32_t offset = idx % array->elems_per_row;
174 		entry = ((uint8_t*)array->array_rows[row]) + (offset * array->size);
175 	}
176 	return entry;
177 }
178 
179 /**
180  * @brief Return number of elements in an array
181  *
182  * Return the number of elements in an array
183  *
184  * @param array pointer to array object
185  *
186  * @return returns count of elements in an array
187  */
188 uint32_t
189 ocs_array_get_count(ocs_array_t *array)
190 {
191 	return array->count;
192 }
193 
194 /**
195  * @brief Return size of array elements in bytes
196  *
197  * Returns the size in bytes of each array element
198  *
199  * @param array pointer to array object
200  *
201  * @return size of array element
202  */
203 uint32_t
204 ocs_array_get_size(ocs_array_t *array)
205 {
206 	return array->size;
207 }
208 
209 /**
210  * @brief Void pointer array structure
211  *
212  * This structure describes an object consisting of an array of void
213  * pointers.   The object is allocated with a maximum array size, entries
214  * are then added to the array with while maintaining an entry count.   A set of
215  * iterator APIs are included to allow facilitate cycling through the array
216  * entries in a circular fashion.
217  *
218  */
219 struct ocs_varray_s {
220 	ocs_os_handle_t os;
221 	uint32_t array_count;			/*>> maximum entry count in array */
222 	void **array;				/*>> pointer to allocated array memory */
223 	uint32_t entry_count;			/*>> number of entries added to the array */
224 	uint32_t next_index;			/*>> iterator next index */
225 	ocs_lock_t lock;			/*>> iterator lock */
226 };
227 
228 /**
229  * @brief Allocate a void pointer array
230  *
231  * A void pointer array of given length is allocated.
232  *
233  * @param os OS handle
234  * @param array_count Array size
235  *
236  * @return returns a pointer to the ocs_varray_t object, other NULL on error
237  */
238 ocs_varray_t *
239 ocs_varray_alloc(ocs_os_handle_t os, uint32_t array_count)
240 {
241 	ocs_varray_t *va;
242 
243 	va = ocs_malloc(os, sizeof(*va), OCS_M_ZERO | OCS_M_NOWAIT);
244 	if (va != NULL) {
245 		va->os = os;
246 		va->array_count = array_count;
247 		va->array = ocs_malloc(os, sizeof(*va->array) * va->array_count, OCS_M_ZERO | OCS_M_NOWAIT);
248 		if (va->array != NULL) {
249 			va->next_index = 0;
250 			ocs_lock_init(os, &va->lock, "varray:%p", va);
251 		} else {
252 			ocs_free(os, va, sizeof(*va));
253 			va = NULL;
254 		}
255 	}
256 	return va;
257 }
258 
259 /**
260  * @brief Free a void pointer array
261  *
262  * The void pointer array object is free'd
263  *
264  * @param va Pointer to void pointer array
265  *
266  * @return none
267  */
268 void
269 ocs_varray_free(ocs_varray_t *va)
270 {
271 	if (va != NULL) {
272 		ocs_lock_free(&va->lock);
273 		if (va->array != NULL) {
274 			ocs_free(va->os, va->array, sizeof(*va->array) * va->array_count);
275 		}
276 		ocs_free(va->os, va, sizeof(*va));
277 	}
278 }
279 
280 /**
281  * @brief Add an entry to a void pointer array
282  *
283  * An entry is added to the void pointer array
284  *
285  * @param va Pointer to void pointer array
286  * @param entry Pointer to entry to add
287  *
288  * @return returns 0 if entry was added, -1 if there is no more space in the array
289  */
290 int32_t
291 ocs_varray_add(ocs_varray_t *va, void *entry)
292 {
293 	uint32_t rc = -1;
294 
295 	ocs_lock(&va->lock);
296 		if (va->entry_count < va->array_count) {
297 			va->array[va->entry_count++] = entry;
298 			rc = 0;
299 		}
300 	ocs_unlock(&va->lock);
301 
302 	return rc;
303 }
304 
305 /**
306  * @brief Reset the void pointer array iterator
307  *
308  * The next index value of the void pointer array iterator is cleared.
309  *
310  * @param va Pointer to void pointer array
311  *
312  * @return none
313  */
314 void
315 ocs_varray_iter_reset(ocs_varray_t *va)
316 {
317 	ocs_lock(&va->lock);
318 		va->next_index = 0;
319 	ocs_unlock(&va->lock);
320 }
321 
322 /**
323  * @brief Return next entry from a void pointer array
324  *
325  * The next entry in the void pointer array is returned.
326  *
327  * @param va Pointer to void point array
328  *
329  * Note: takes the void pointer array lock
330  *
331  * @return returns next void pointer entry
332  */
333 void *
334 ocs_varray_iter_next(ocs_varray_t *va)
335 {
336 	void *rval = NULL;
337 
338 	if (va != NULL) {
339 		ocs_lock(&va->lock);
340 			rval = _ocs_varray_iter_next(va);
341 		ocs_unlock(&va->lock);
342 	}
343 	return rval;
344 }
345 
346 /**
347  * @brief Return next entry from a void pointer array
348  *
349  * The next entry in the void pointer array is returned.
350  *
351  * @param va Pointer to void point array
352  *
353  * Note: doesn't take the void pointer array lock
354  *
355  * @return returns next void pointer entry
356  */
357 void *
358 _ocs_varray_iter_next(ocs_varray_t *va)
359 {
360 	void *rval;
361 
362 	rval = va->array[va->next_index];
363 	if (++va->next_index >= va->entry_count) {
364 		va->next_index = 0;
365 	}
366 	return rval;
367 }
368 
369 /**
370  * @brief Take void pointer array lock
371  *
372  * Takes the lock for the given void pointer array
373  *
374  * @param va Pointer to void pointer array
375  *
376  * @return none
377  */
378 void
379 ocs_varray_lock(ocs_varray_t *va)
380 {
381 	ocs_lock(&va->lock);
382 }
383 
384 /**
385  * @brief Release void pointer array lock
386  *
387  * Releases the lock for the given void pointer array
388  *
389  * @param va Pointer to void pointer array
390  *
391  * @return none
392  */
393 void
394 ocs_varray_unlock(ocs_varray_t *va)
395 {
396 	ocs_unlock(&va->lock);
397 }
398 
399 /**
400  * @brief Return entry count for a void pointer array
401  *
402  * The entry count for a void pointer array is returned
403  *
404  * @param va Pointer to void pointer array
405  *
406  * @return returns entry count
407  */
408 uint32_t
409 ocs_varray_get_count(ocs_varray_t *va)
410 {
411 	uint32_t rc;
412 
413 	ocs_lock(&va->lock);
414 		rc = va->entry_count;
415 	ocs_unlock(&va->lock);
416 	return rc;
417 }
418 
419 struct ocs_cbuf_s {
420 	ocs_os_handle_t os;		/*<< OS handle */
421 	uint32_t entry_count;		/*<< entry count */
422 	void **array;			/*<< pointer to array of cbuf pointers */
423 	uint32_t pidx;			/*<< producer index */
424 	uint32_t cidx;			/*<< consumer index */
425 	ocs_lock_t cbuf_plock;		/*<< idx lock */
426 	ocs_lock_t cbuf_clock;		/*<< idx lock */
427 	ocs_sem_t cbuf_psem;		/*<< cbuf producer counting semaphore */
428 	ocs_sem_t cbuf_csem;		/*<< cbuf consumer counting semaphore */
429 };
430 
431 /**
432  * @brief Initialize a circular buffer queue
433  *
434  * A circular buffer with producer/consumer API is allocated
435  *
436  * @param os OS handle
437  * @param entry_count count of entries
438  *
439  * @return returns pointer to circular buffer, or NULL
440  */
441 ocs_cbuf_t*
442 ocs_cbuf_alloc(ocs_os_handle_t os, uint32_t entry_count)
443 {
444 	ocs_cbuf_t *cbuf;
445 
446 	cbuf = ocs_malloc(os, sizeof(*cbuf), OCS_M_NOWAIT | OCS_M_ZERO);
447 	if (cbuf == NULL) {
448 		return NULL;
449 	}
450 
451 	cbuf->os = os;
452 	cbuf->entry_count = entry_count;
453 	cbuf->pidx = 0;
454 	cbuf->cidx = 0;
455 
456 	ocs_lock_init(NULL, &cbuf->cbuf_clock, "cbuf_c:%p", cbuf);
457 	ocs_lock_init(NULL, &cbuf->cbuf_plock, "cbuf_p:%p", cbuf);
458 	ocs_sem_init(&cbuf->cbuf_csem, 0, "cbuf:%p", cbuf);
459 	ocs_sem_init(&cbuf->cbuf_psem, cbuf->entry_count, "cbuf:%p", cbuf);
460 
461 	cbuf->array = ocs_malloc(os, entry_count * sizeof(*cbuf->array), OCS_M_NOWAIT | OCS_M_ZERO);
462 	if (cbuf->array == NULL) {
463 		ocs_cbuf_free(cbuf);
464 		return NULL;
465 	}
466 
467 	return cbuf;
468 }
469 
470 /**
471  * @brief Free a circular buffer
472  *
473  * The memory resources of a circular buffer are free'd
474  *
475  * @param cbuf pointer to circular buffer
476  *
477  * @return none
478  */
479 void
480 ocs_cbuf_free(ocs_cbuf_t *cbuf)
481 {
482 	if (cbuf != NULL) {
483 		if (cbuf->array != NULL) {
484 			ocs_free(cbuf->os, cbuf->array, sizeof(*cbuf->array) * cbuf->entry_count);
485 		}
486 		ocs_lock_free(&cbuf->cbuf_clock);
487 		ocs_lock_free(&cbuf->cbuf_plock);
488 		ocs_free(cbuf->os, cbuf, sizeof(*cbuf));
489 	}
490 }
491 
492 /**
493  * @brief Get pointer to buffer
494  *
495  * Wait for a buffer to become available, and return a pointer to the buffer.
496  *
497  * @param cbuf pointer to circular buffer
498  * @param timeout_usec timeout in microseconds
499  *
500  * @return pointer to buffer, or NULL if timeout
501  */
502 void*
503 ocs_cbuf_get(ocs_cbuf_t *cbuf, int32_t timeout_usec)
504 {
505 	void *ret = NULL;
506 
507 	if (likely(ocs_sem_p(&cbuf->cbuf_csem, timeout_usec) == 0)) {
508 		ocs_lock(&cbuf->cbuf_clock);
509 			ret = cbuf->array[cbuf->cidx];
510 			if (unlikely(++cbuf->cidx >= cbuf->entry_count)) {
511 				cbuf->cidx = 0;
512 			}
513 		ocs_unlock(&cbuf->cbuf_clock);
514 		ocs_sem_v(&cbuf->cbuf_psem);
515 	}
516 	return ret;
517 }
518 
519 /**
520  * @brief write a buffer
521  *
522  * The buffer is written to the circular buffer.
523  *
524  * @param cbuf pointer to circular buffer
525  * @param elem pointer to entry
526  *
527  * @return returns 0 for success, a negative error code value for failure.
528  */
529 int32_t
530 ocs_cbuf_put(ocs_cbuf_t *cbuf, void *elem)
531 {
532 	int32_t rc = 0;
533 
534 	if (likely(ocs_sem_p(&cbuf->cbuf_psem, -1) == 0)) {
535 		ocs_lock(&cbuf->cbuf_plock);
536 			cbuf->array[cbuf->pidx] = elem;
537 			if (unlikely(++cbuf->pidx >= cbuf->entry_count)) {
538 				cbuf->pidx = 0;
539 			}
540 		ocs_unlock(&cbuf->cbuf_plock);
541 		ocs_sem_v(&cbuf->cbuf_csem);
542 	} else {
543 		rc = -1;
544 	}
545 	return rc;
546 }
547 
548 /**
549  * @brief Prime a circular buffer data
550  *
551  * Post array buffers to a circular buffer
552  *
553  * @param cbuf pointer to circular buffer
554  * @param array pointer to buffer array
555  *
556  * @return returns 0 for success, a negative error code value for failure.
557  */
558 int32_t
559 ocs_cbuf_prime(ocs_cbuf_t *cbuf, ocs_array_t *array)
560 {
561 	uint32_t i;
562 	uint32_t count = MIN(ocs_array_get_count(array), cbuf->entry_count);
563 
564 	for (i = 0; i < count; i++) {
565 		ocs_cbuf_put(cbuf, ocs_array_get(array, i));
566 	}
567 	return 0;
568 }
569 
570 /**
571  * @brief Generate driver dump start of file information
572  *
573  * The start of file information is added to 'textbuf'
574  *
575  * @param textbuf pointer to driver dump text buffer
576  *
577  * @return none
578  */
579 
580 void
581 ocs_ddump_startfile(ocs_textbuf_t *textbuf)
582 {
583 	ocs_textbuf_printf(textbuf, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n");
584 }
585 
586 /**
587  * @brief Generate driver dump end of file information
588  *
589  * The end of file information is added to 'textbuf'
590  *
591  * @param textbuf pointer to driver dump text buffer
592  *
593  * @return none
594  */
595 
596 void
597 ocs_ddump_endfile(ocs_textbuf_t *textbuf)
598 {
599 }
600 
601 /**
602  * @brief Generate driver dump section start data
603  *
604  * The driver section start information is added to textbuf
605  *
606  * @param textbuf pointer to text buffer
607  * @param name name of section
608  * @param instance instance number of this section
609  *
610  * @return none
611  */
612 
613 void
614 ocs_ddump_section(ocs_textbuf_t *textbuf, const char *name, uint32_t instance)
615 {
616 	ocs_textbuf_printf(textbuf, "<%s type=\"section\" instance=\"%d\">\n", name, instance);
617 }
618 
619 /**
620  * @brief Generate driver dump section end data
621  *
622  * The driver section end information is added to textbuf
623  *
624  * @param textbuf pointer to text buffer
625  * @param name name of section
626  * @param instance instance number of this section
627  *
628  * @return none
629  */
630 
631 void
632 ocs_ddump_endsection(ocs_textbuf_t *textbuf, const char *name, uint32_t instance)
633 {
634 	ocs_textbuf_printf(textbuf, "</%s>\n", name);
635 }
636 
637 /**
638  * @brief Generate driver dump data for a given value
639  *
640  * A value is added to textbuf
641  *
642  * @param textbuf pointer to text buffer
643  * @param name name of variable
644  * @param fmt snprintf format specifier
645  *
646  * @return none
647  */
648 
649 void
650 ocs_ddump_value(ocs_textbuf_t *textbuf, const char *name, const char *fmt, ...)
651 {
652 	va_list ap;
653 	char valuebuf[64];
654 
655 	va_start(ap, fmt);
656 	vsnprintf(valuebuf, sizeof(valuebuf), fmt, ap);
657 	va_end(ap);
658 
659 	ocs_textbuf_printf(textbuf, "<%s>%s</%s>\n", name, valuebuf, name);
660 }
661 
662 /**
663  * @brief Generate driver dump data for an arbitrary buffer of DWORDS
664  *
665  * A status value is added to textbuf
666  *
667  * @param textbuf pointer to text buffer
668  * @param name name of status variable
669  * @param instance instance number of this section
670  * @param buffer buffer to print
671  * @param size size of buffer in bytes
672  *
673  * @return none
674  */
675 
676 void
677 ocs_ddump_buffer(ocs_textbuf_t *textbuf, const char *name, uint32_t instance, void *buffer, uint32_t size)
678 {
679 	uint32_t *dword;
680 	uint32_t i;
681 	uint32_t count;
682 
683 	count = size / sizeof(uint32_t);
684 
685 	if (count == 0) {
686 		return;
687 	}
688 
689 	ocs_textbuf_printf(textbuf, "<%s type=\"buffer\" instance=\"%d\">\n", name, instance);
690 
691 	dword = buffer;
692 	for (i = 0; i < count; i++) {
693 #define OCS_NEWLINE_MOD	8
694 		ocs_textbuf_printf(textbuf, "%08x ", *dword++);
695 		if ((i % OCS_NEWLINE_MOD) == (OCS_NEWLINE_MOD - 1)) {
696 			ocs_textbuf_printf(textbuf, "\n");
697 		}
698 	}
699 
700 	ocs_textbuf_printf(textbuf, "</%s>\n", name);
701 }
702 
703 /**
704  * @brief Generate driver dump for queue
705  *
706  * Add queue elements to text buffer
707  *
708  * @param textbuf pointer to driver dump text buffer
709  * @param q_addr address of start of queue
710  * @param size size of each queue entry
711  * @param length number of queue entries in the queue
712  * @param index current index of queue
713  * @param qentries number of most recent queue entries to dump
714  *
715  * @return none
716  */
717 
718 void
719 ocs_ddump_queue_entries(ocs_textbuf_t *textbuf, void *q_addr, uint32_t size,
720 			uint32_t length, int32_t index, uint32_t qentries)
721 {
722 	uint32_t i;
723 	uint32_t j;
724 	uint8_t *entry;
725 	uint32_t *dword;
726 	uint32_t entry_count = 0;
727 	uint32_t entry_words = size / sizeof(uint32_t);
728 
729 	if ((qentries == (uint32_t)-1) || (qentries > length)) {
730 		/* if qentries is -1 or larger than queue size, dump entire queue */
731 		entry_count = length;
732 		index = 0;
733 	} else {
734 		entry_count = qentries;
735 
736 		index -= (qentries - 1);
737 		if (index < 0) {
738 			index += length;
739 		}
740 	}
741 #define OCS_NEWLINE_MOD	8
742 	ocs_textbuf_printf(textbuf, "<qentries>\n");
743 	for (i = 0; i < entry_count; i++){
744 		entry = q_addr;
745 		entry += index * size;
746 		dword = (uint32_t *)entry;
747 
748 		ocs_textbuf_printf(textbuf, "[%04x] ", index);
749 		for (j = 0; j < entry_words; j++) {
750 			ocs_textbuf_printf(textbuf, "%08x ", *dword++);
751 			if (((j+1) == entry_words) ||
752 			    ((j % OCS_NEWLINE_MOD) == (OCS_NEWLINE_MOD - 1))) {
753 				ocs_textbuf_printf(textbuf, "\n");
754 				if ((j+1) < entry_words) {
755 					ocs_textbuf_printf(textbuf, "       ");
756 				}
757 			}
758 		}
759 
760 		index++;
761 		if ((uint32_t)index >= length) {
762 			index = 0;
763 		}
764 	}
765 	ocs_textbuf_printf(textbuf, "</qentries>\n");
766 }
767 
768 #define OCS_DEBUG_ENABLE(x)	(x ? ~0 : 0)
769 
770 #define OCS_DEBUG_MASK \
771 	(OCS_DEBUG_ENABLE(1)	& OCS_DEBUG_ALWAYS)  | \
772 	(OCS_DEBUG_ENABLE(0)	& OCS_DEBUG_ENABLE_MQ_DUMP) | \
773 	(OCS_DEBUG_ENABLE(0)	& OCS_DEBUG_ENABLE_CQ_DUMP) | \
774 	(OCS_DEBUG_ENABLE(0)	& OCS_DEBUG_ENABLE_WQ_DUMP) | \
775 	(OCS_DEBUG_ENABLE(0)	& OCS_DEBUG_ENABLE_EQ_DUMP) | \
776 	(OCS_DEBUG_ENABLE(0)	& OCS_DEBUG_ENABLE_SPARAM_DUMP)
777 
778 static uint32_t ocs_debug_mask = OCS_DEBUG_MASK;
779 
780 static int
781 _isprint(int c) {
782 	return ((c > 32) && (c < 127));
783 }
784 
785 /**
786  * @ingroup debug
787  * @brief enable debug options
788  *
789  * Enables debug options by or-ing in <b>mask</b> into the currently enabled
790  * debug mask.
791  *
792  * @param mask mask bits to enable
793  *
794  * @return none
795  */
796 
797 void ocs_debug_enable(uint32_t mask) {
798 	ocs_debug_mask |= mask;
799 }
800 
801 /**
802  * @ingroup debug
803  * @brief disable debug options
804  *
805  * Disables debug options by clearing bits in <b>mask</b> into the currently enabled
806  * debug mask.
807  *
808  * @param mask mask bits to enable
809  *
810  * @return none
811  */
812 
813 void ocs_debug_disable(uint32_t mask) {
814 	ocs_debug_mask &= ~mask;
815 }
816 
817 /**
818  * @ingroup debug
819  * @brief return true if debug bits are enabled
820  *
821  * Returns true if the request debug bits are set.
822  *
823  * @param mask debug bit mask
824  *
825  * @return true if corresponding bits are set
826  *
827  * @note Passing in a mask value of zero always returns true
828  */
829 
830 int ocs_debug_is_enabled(uint32_t mask) {
831 	return (ocs_debug_mask & mask) == mask;
832 }
833 
834 /**
835  * @ingroup debug
836  * @brief Dump 32 bit hex/ascii data
837  *
838  * Dumps using ocs_log a buffer of data as 32 bit hex and ascii
839  *
840  * @param mask debug enable bits
841  * @param os os handle
842  * @param label text label for the display (may be NULL)
843  * @param buf pointer to data buffer
844  * @param buf_length length of data buffer
845  *
846  * @return none
847  *
848  */
849 
850 void
851 ocs_dump32(uint32_t mask, ocs_os_handle_t os, const char *label, void *buf, uint32_t buf_length)
852 {
853 	uint32_t word_count = buf_length / sizeof(uint32_t);
854 	uint32_t i;
855 	uint32_t columns = 8;
856 	uint32_t n;
857 	uint32_t *wbuf;
858 	char *cbuf;
859 	uint32_t addr = 0;
860 	char linebuf[200];
861 	char *pbuf = linebuf;
862 
863 	if (!ocs_debug_is_enabled(mask))
864 		return;
865 
866 	if (label)
867 		ocs_log_debug(os, "%s\n", label);
868 
869 	wbuf = buf;
870 	while (word_count > 0) {
871 		pbuf = linebuf;
872 		pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%08X:  ", addr);
873 
874 		n = word_count;
875 		if (n > columns)
876 			n = columns;
877 
878 		for (i = 0; i < n; i ++)
879 			pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%08X ", wbuf[i]);
880 
881 		for (; i < columns; i ++)
882 			pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%8s ", "");
883 
884 		pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "    ");
885 		cbuf = (char*)wbuf;
886 		for (i = 0; i < n*sizeof(uint32_t); i ++)
887 			pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%c", _isprint(cbuf[i]) ? cbuf[i] : '.');
888 		pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "\n");
889 
890 		ocs_log_debug(os, "%s", linebuf);
891 
892 		wbuf += n;
893 		word_count -= n;
894 		addr += n*sizeof(uint32_t);
895 	}
896 }
897 
898 #if defined(OCS_DEBUG_QUEUE_HISTORY)
899 
900 /* each bit corresponds to word to capture */
901 #define OCS_Q_HIST_WQE_WORD_MASK_DEFAULT	(BIT(4) | BIT(6) | BIT(7) | BIT(9) | BIT(12))
902 #define OCS_Q_HIST_TRECV_CONT_WQE_WORD_MASK	(BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9) | BIT(12))
903 #define OCS_Q_HIST_IWRITE_WQE_WORD_MASK		(BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9))
904 #define OCS_Q_HIST_IREAD_WQE_WORD_MASK		(BIT(4) | BIT(6) | BIT(7) | BIT(9))
905 #define OCS_Q_HIST_ABORT_WQE_WORD_MASK		(BIT(3) | BIT(7) | BIT(8) | BIT(9))
906 #define OCS_Q_HIST_WCQE_WORD_MASK		(BIT(0) | BIT(3))
907 #define OCS_Q_HIST_WCQE_WORD_MASK_ERR		(BIT(0) | BIT(1) | BIT(2) | BIT(3))
908 #define OCS_Q_HIST_CQXABT_WORD_MASK		(BIT(0) | BIT(1) | BIT(2) | BIT(3))
909 
910 /* if set, will provide extra queue information in each entry */
911 #define OCS_Q_HIST_ENABLE_Q_INFO	0
912 uint8_t ocs_queue_history_q_info_enabled(void)
913 {
914 	return OCS_Q_HIST_ENABLE_Q_INFO;
915 }
916 
917 /* if set, will provide timestamps in each entry */
918 #define OCS_Q_HIST_ENABLE_TIMESTAMPS	0
919 uint8_t ocs_queue_history_timestamp_enabled(void)
920 {
921 	return OCS_Q_HIST_ENABLE_TIMESTAMPS;
922 }
923 
924 /* Add WQEs and masks to override default WQE mask */
925 ocs_q_hist_wqe_mask_t ocs_q_hist_wqe_masks[] = {
926 	/* WQE command   Word mask */
927 	{SLI4_WQE_ABORT, OCS_Q_HIST_ABORT_WQE_WORD_MASK},
928 	{SLI4_WQE_FCP_IREAD64, OCS_Q_HIST_IREAD_WQE_WORD_MASK},
929 	{SLI4_WQE_FCP_IWRITE64, OCS_Q_HIST_IWRITE_WQE_WORD_MASK},
930 	{SLI4_WQE_FCP_CONT_TRECEIVE64, OCS_Q_HIST_TRECV_CONT_WQE_WORD_MASK},
931 };
932 
933 /* CQE masks */
934 ocs_q_hist_cqe_mask_t ocs_q_hist_cqe_masks[] = {
935 	/* CQE type     Q_hist_type		mask (success) 	mask (non-success) */
936 	{SLI_QENTRY_WQ, OCS_Q_HIST_TYPE_CWQE, 	OCS_Q_HIST_WCQE_WORD_MASK, OCS_Q_HIST_WCQE_WORD_MASK_ERR},
937 	{SLI_QENTRY_XABT, OCS_Q_HIST_TYPE_CXABT, OCS_Q_HIST_CQXABT_WORD_MASK, OCS_Q_HIST_WCQE_WORD_MASK},
938 };
939 
940 static uint32_t ocs_q_hist_get_wqe_mask(sli4_generic_wqe_t *wqe)
941 {
942 	uint32_t i;
943 	for (i = 0; i < ARRAY_SIZE(ocs_q_hist_wqe_masks); i++) {
944 		if (ocs_q_hist_wqe_masks[i].command == wqe->command) {
945 			return ocs_q_hist_wqe_masks[i].mask;
946 		}
947 	}
948 	/* return default WQE mask */
949 	return OCS_Q_HIST_WQE_WORD_MASK_DEFAULT;
950 }
951 
952 /**
953  * @ingroup debug
954  * @brief Initialize resources for queue history
955  *
956  * @param os os handle
957  * @param q_hist Pointer to the queue history object.
958  *
959  * @return none
960  */
961 void
962 ocs_queue_history_init(ocs_t *ocs, ocs_hw_q_hist_t *q_hist)
963 {
964 	q_hist->ocs = ocs;
965 	if (q_hist->q_hist != NULL) {
966 		/* Setup is already done */
967 		ocs_log_debug(ocs, "q_hist not NULL, skipping init\n");
968 		return;
969 	}
970 
971 	q_hist->q_hist = ocs_malloc(ocs, sizeof(*q_hist->q_hist)*OCS_Q_HIST_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
972 
973 	if (q_hist->q_hist == NULL) {
974 		ocs_log_err(ocs, "Could not allocate queue history buffer\n");
975 	} else {
976 		ocs_lock_init(ocs, &q_hist->q_hist_lock, "queue history lock[%d]", ocs_instance(ocs));
977 	}
978 
979 	q_hist->q_hist_index = 0;
980 }
981 
982 /**
983  * @ingroup debug
984  * @brief Free resources for queue history
985  *
986  * @param q_hist Pointer to the queue history object.
987  *
988  * @return none
989  */
990 void
991 ocs_queue_history_free(ocs_hw_q_hist_t *q_hist)
992 {
993 	ocs_t *ocs = q_hist->ocs;
994 
995 	if (q_hist->q_hist != NULL) {
996 		ocs_free(ocs, q_hist->q_hist, sizeof(*q_hist->q_hist)*OCS_Q_HIST_SIZE);
997 		ocs_lock_free(&q_hist->q_hist_lock);
998 		q_hist->q_hist = NULL;
999 	}
1000 }
1001 
1002 static void
1003 ocs_queue_history_add_q_info(ocs_hw_q_hist_t *q_hist, uint32_t qid, uint32_t qindex)
1004 {
1005 	if (ocs_queue_history_q_info_enabled()) {
1006 		/* write qid, index */
1007 		q_hist->q_hist[q_hist->q_hist_index] = (qid << 16) | qindex;
1008 		q_hist->q_hist_index++;
1009 		q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
1010 	}
1011 }
1012 
1013 static void
1014 ocs_queue_history_add_timestamp(ocs_hw_q_hist_t *q_hist)
1015 {
1016 	if (ocs_queue_history_timestamp_enabled()) {
1017 		/* write tsc */
1018 		uint64_t tsc_value;
1019 		tsc_value = get_cyclecount();
1020 		q_hist->q_hist[q_hist->q_hist_index] = ((tsc_value >> 32 ) & 0xFFFFFFFF);
1021 		q_hist->q_hist_index++;
1022 		q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
1023 		q_hist->q_hist[q_hist->q_hist_index] = (tsc_value & 0xFFFFFFFF);
1024 		q_hist->q_hist_index++;
1025 		q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
1026 	}
1027 }
1028 
1029 /**
1030  * @ingroup debug
1031  * @brief Log work queue entry (WQE) into history array
1032  *
1033  * @param q_hist Pointer to the queue history object.
1034  * @param entryw Work queue entry in words
1035  * @param qid Queue ID
1036  * @param qindex Queue index
1037  *
1038  * @return none
1039  */
1040 void
1041 ocs_queue_history_wq(ocs_hw_q_hist_t *q_hist, uint32_t *entryw, uint32_t qid, uint32_t qindex)
1042 {
1043 	int i;
1044 	ocs_q_hist_ftr_t ftr;
1045 	uint32_t wqe_word_mask = ocs_q_hist_get_wqe_mask((sli4_generic_wqe_t *)entryw);
1046 
1047 	if (q_hist->q_hist == NULL) {
1048 		/* Can't save anything */
1049 		return;
1050 	}
1051 
1052 	ftr.word = 0;
1053 	ftr.s.type = OCS_Q_HIST_TYPE_WQE;
1054 	ocs_lock(&q_hist->q_hist_lock);
1055 		/* Capture words in reverse order since we'll be interpretting them LIFO */
1056 		for (i = ((sizeof(wqe_word_mask)*8) - 1); i >= 0; i--){
1057 			if ((wqe_word_mask >> i) & 1) {
1058 				q_hist->q_hist[q_hist->q_hist_index] = entryw[i];
1059 				q_hist->q_hist_index++;
1060 				q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
1061 			}
1062 		}
1063 
1064 		ocs_queue_history_add_q_info(q_hist, qid, qindex);
1065 		ocs_queue_history_add_timestamp(q_hist);
1066 
1067 		/* write footer */
1068 		if (wqe_word_mask) {
1069 			ftr.s.mask = wqe_word_mask;
1070 			q_hist->q_hist[q_hist->q_hist_index] = ftr.word;
1071 			q_hist->q_hist_index++;
1072 			q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
1073 		}
1074 
1075 	ocs_unlock(&q_hist->q_hist_lock);
1076 }
1077 
1078 /**
1079  * @ingroup debug
1080  * @brief Log misc words
1081  *
1082  * @param q_hist Pointer to the queue history object.
1083  * @param entryw array of words
1084  * @param num_words number of words in entryw
1085  *
1086  * @return none
1087  */
1088 void
1089 ocs_queue_history_misc(ocs_hw_q_hist_t *q_hist, uint32_t *entryw, uint32_t num_words)
1090 {
1091 	int i;
1092 	ocs_q_hist_ftr_t ftr;
1093 	uint32_t mask = 0;
1094 
1095 	if (q_hist->q_hist == NULL) {
1096 		/* Can't save anything */
1097 		return;
1098 	}
1099 
1100 	ftr.word = 0;
1101 	ftr.s.type = OCS_Q_HIST_TYPE_MISC;
1102 	ocs_lock(&q_hist->q_hist_lock);
1103 		/* Capture words in reverse order since we'll be interpretting them LIFO */
1104 		for (i = num_words-1; i >= 0; i--) {
1105 			q_hist->q_hist[q_hist->q_hist_index] = entryw[i];
1106 			q_hist->q_hist_index++;
1107 			q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
1108 			mask |= BIT(i);
1109 		}
1110 
1111 		ocs_queue_history_add_timestamp(q_hist);
1112 
1113 		/* write footer */
1114 		if (num_words) {
1115 			ftr.s.mask = mask;
1116 			q_hist->q_hist[q_hist->q_hist_index] = ftr.word;
1117 			q_hist->q_hist_index++;
1118 			q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
1119 		}
1120 
1121 	ocs_unlock(&q_hist->q_hist_lock);
1122 }
1123 
1124 /**
1125  * @ingroup debug
1126  * @brief Log work queue completion (CQE) entry into history
1127  *        array
1128  *
1129  * @param q_hist Pointer to the queue history object.
1130  * @param ctype Type of completion entry
1131  * @param entryw Completion queue entry in words
1132  * @param status Completion queue status
1133  * @param qid Queue ID
1134  * @param qindex Queue index
1135  *
1136  * @return none
1137  */
1138 void
1139 ocs_queue_history_cqe(ocs_hw_q_hist_t *q_hist, uint8_t ctype, uint32_t *entryw, uint8_t status, uint32_t qid, uint32_t qindex)
1140 {
1141 	int i;
1142 	unsigned j;
1143 	uint32_t cqe_word_mask = 0;
1144 	ocs_q_hist_ftr_t ftr;
1145 
1146 	if (q_hist->q_hist == NULL) {
1147 		/* Can't save anything */
1148 		return;
1149 	}
1150 
1151 	ftr.word = 0;
1152 	for (j = 0; j < ARRAY_SIZE(ocs_q_hist_cqe_masks); j++) {
1153 		if (ocs_q_hist_cqe_masks[j].ctype == ctype) {
1154 			ftr.s.type = ocs_q_hist_cqe_masks[j].type;
1155 			if (status != 0) {
1156 				cqe_word_mask = ocs_q_hist_cqe_masks[j].mask_err;
1157 			} else {
1158 				cqe_word_mask = ocs_q_hist_cqe_masks[j].mask;
1159 			}
1160 		}
1161 	}
1162 	ocs_lock(&q_hist->q_hist_lock);
1163 		/* Capture words in reverse order since we'll be interpretting them LIFO */
1164 		for (i = ((sizeof(cqe_word_mask)*8) - 1); i >= 0; i--){
1165 			if ((cqe_word_mask >> i) & 1) {
1166 				q_hist->q_hist[q_hist->q_hist_index] = entryw[i];
1167 				q_hist->q_hist_index++;
1168 				q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
1169 			}
1170 		}
1171 		ocs_queue_history_add_q_info(q_hist, qid, qindex);
1172 		ocs_queue_history_add_timestamp(q_hist);
1173 
1174 		/* write footer */
1175 		if (cqe_word_mask) {
1176 			ftr.s.mask = cqe_word_mask;
1177 			q_hist->q_hist[q_hist->q_hist_index] = ftr.word;
1178 			q_hist->q_hist_index++;
1179 			q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
1180 		}
1181 
1182 	ocs_unlock(&q_hist->q_hist_lock);
1183 }
1184 
1185 /**
1186  * @brief Get previous index
1187  *
1188  * @param index Index from which previous index is derived.
1189  */
1190 uint32_t
1191 ocs_queue_history_prev_index(uint32_t index)
1192 {
1193 	if (index == 0) {
1194 		return OCS_Q_HIST_SIZE - 1;
1195 	} else {
1196 		return index - 1;
1197 	}
1198 }
1199 
1200 #endif /* OCS_DEBUG_QUEUE_HISTORY */
1201 
1202 /**
1203  * @brief Display service parameters
1204  *
1205  * <description>
1206  *
1207  * @param prelabel leading display label
1208  * @param reqlabel display label
1209  * @param dest destination 0=ocs_log, 1=textbuf
1210  * @param textbuf text buffer destination (if dest==1)
1211  * @param sparams pointer to service parameter
1212  *
1213  * @return none
1214  */
1215 
1216 void
1217 ocs_display_sparams(const char *prelabel, const char *reqlabel, int dest, void *textbuf, void *sparams)
1218 {
1219 	char label[64];
1220 
1221 	if (sparams == NULL) {
1222 		return;
1223 	}
1224 
1225 	switch(dest) {
1226 	case 0:
1227 		if (prelabel != NULL) {
1228 			ocs_snprintf(label, sizeof(label), "[%s] sparam: %s", prelabel, reqlabel);
1229 		} else {
1230 			ocs_snprintf(label, sizeof(label), "sparam: %s", reqlabel);
1231 		}
1232 
1233 		ocs_dump32(OCS_DEBUG_ENABLE_SPARAM_DUMP, NULL, label, sparams, sizeof(fc_plogi_payload_t));
1234 		break;
1235 	case 1:
1236 		ocs_ddump_buffer((ocs_textbuf_t*) textbuf, reqlabel, 0, sparams, sizeof(fc_plogi_payload_t));
1237 		break;
1238 	}
1239 }
1240 
1241 /**
1242  * @brief Calculate the T10 PI CRC guard value for a block.
1243  *
1244  * @param buffer Pointer to the data buffer.
1245  * @param size Number of bytes.
1246  * @param crc Previously-calculated CRC, or 0 for a new block.
1247  *
1248  * @return Returns the calculated CRC, which may be passed back in for partial blocks.
1249  *
1250  */
1251 
1252 uint16_t
1253 ocs_scsi_dif_calc_crc(const uint8_t *buffer, uint32_t size, uint16_t crc)
1254 {
1255 	return t10crc16(buffer, size, crc);
1256 }
1257 
1258 /**
1259  * @brief Calculate the IP-checksum guard value for a block.
1260  *
1261  * @param addrlen array of address length pairs
1262  * @param addrlen_count number of entries in the addrlen[] array
1263  *
1264  * Algorithm:
1265  *    Sum all all the 16-byte words in the block
1266  *    Add in the "carry", which is everything in excess of 16-bits
1267  *    Flip all the bits
1268  *
1269  * @return Returns the calculated checksum
1270  */
1271 
1272 uint16_t
1273 ocs_scsi_dif_calc_checksum(ocs_scsi_vaddr_len_t addrlen[], uint32_t addrlen_count)
1274 {
1275 	uint32_t i, j;
1276 	uint16_t checksum;
1277 	uint32_t intermediate; /* Use an intermediate to hold more than 16 bits during calculations */
1278 	uint32_t count;
1279 	uint16_t *buffer;
1280 
1281 	intermediate = 0;
1282 	for (j = 0; j < addrlen_count; j++) {
1283 		buffer = addrlen[j].vaddr;
1284 		count = addrlen[j].length / 2;
1285 		for (i=0; i < count; i++) {
1286 			intermediate += buffer[i];
1287 		}
1288 	}
1289 
1290 	/* Carry is everything over 16 bits */
1291 	intermediate += ((intermediate & 0xffff0000) >> 16);
1292 
1293 	/* Flip all the bits */
1294 	intermediate = ~intermediate;
1295 
1296 	checksum = intermediate;
1297 
1298 	return checksum;
1299 }
1300 
1301 /**
1302  * @brief Return blocksize given SCSI API DIF block size
1303  *
1304  * Given the DIF block size enumerated value, return the block size value. (e.g.
1305  * OCS_SCSI_DIF_BLK_SIZE_512 returns 512)
1306  *
1307  * @param dif_info Pointer to SCSI API DIF info block
1308  *
1309  * @return returns block size, or 0 if SCSI API DIF blocksize is invalid
1310  */
1311 
1312 uint32_t
1313 ocs_scsi_dif_blocksize(ocs_scsi_dif_info_t *dif_info)
1314 {
1315 	uint32_t blocksize = 0;
1316 
1317 	switch(dif_info->blk_size) {
1318 	case OCS_SCSI_DIF_BK_SIZE_512:	blocksize = 512; break;
1319 	case OCS_SCSI_DIF_BK_SIZE_1024:	blocksize = 1024; break;
1320 	case OCS_SCSI_DIF_BK_SIZE_2048:	blocksize = 2048; break;
1321 	case OCS_SCSI_DIF_BK_SIZE_4096:	blocksize = 4096; break;
1322 	case OCS_SCSI_DIF_BK_SIZE_520:	blocksize = 520; break;
1323 	case OCS_SCSI_DIF_BK_SIZE_4104:	blocksize = 4104; break;
1324 	default:
1325 		break;
1326 	}
1327 
1328 	return blocksize;
1329 }
1330 
1331 /**
1332  * @brief Set SCSI API DIF blocksize
1333  *
1334  * Given a blocksize value (512, 1024, etc.), set the SCSI API DIF blocksize
1335  * in the DIF info block
1336  *
1337  * @param dif_info Pointer to the SCSI API DIF info block
1338  * @param blocksize Block size
1339  *
1340  * @return returns 0 for success, a negative error code value for failure.
1341  */
1342 
1343 int32_t
1344 ocs_scsi_dif_set_blocksize(ocs_scsi_dif_info_t *dif_info, uint32_t blocksize)
1345 {
1346 	int32_t rc = 0;
1347 
1348 	switch(blocksize) {
1349 	case 512:	dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_512; break;
1350 	case 1024:	dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_1024; break;
1351 	case 2048:	dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_2048; break;
1352 	case 4096:	dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_4096; break;
1353 	case 520:	dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_520; break;
1354 	case 4104:	dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_4104; break;
1355 	default:
1356 		rc = -1;
1357 		break;
1358 	}
1359 	return rc;
1360 
1361 }
1362 
1363 /**
1364  * @brief Return memory block size given SCSI DIF API
1365  *
1366  * The blocksize in memory for the DIF transfer is returned, given the SCSI DIF info
1367  * block and the direction of transfer.
1368  *
1369  * @param dif_info Pointer to DIF info block
1370  * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire
1371  *
1372  * @return Memory blocksize, or negative error value
1373  *
1374  * WARNING: the order of initialization of the adj[] arrays MUST match the declarations
1375  * of OCS_SCSI_DIF_OPER_*
1376  */
1377 
1378 int32_t
1379 ocs_scsi_dif_mem_blocksize(ocs_scsi_dif_info_t *dif_info, int wiretomem)
1380 {
1381 	uint32_t blocksize;
1382 	uint8_t wiretomem_adj[] = {
1383 		0,		/* OCS_SCSI_DIF_OPER_DISABLED, */
1384 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */
1385 		0,		/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */
1386 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
1387 		0,		/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
1388 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */
1389 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
1390 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */
1391 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */
1392 		DIF_SIZE};	/* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */
1393 	uint8_t memtowire_adj[] = {
1394 		0,		/* OCS_SCSI_DIF_OPER_DISABLED, */
1395 		0,		/* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */
1396 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */
1397 		0,		/* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
1398 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
1399 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */
1400 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
1401 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */
1402 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */
1403 		DIF_SIZE};	/* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */
1404 
1405 	blocksize = ocs_scsi_dif_blocksize(dif_info);
1406 	if (blocksize == 0) {
1407 		return -1;
1408 	}
1409 
1410 	if (wiretomem) {
1411 		ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0);
1412 		blocksize += wiretomem_adj[dif_info->dif_oper];
1413 	} else {	/* mem to wire */
1414 		ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0);
1415 		blocksize += memtowire_adj[dif_info->dif_oper];
1416 	}
1417 	return blocksize;
1418 }
1419 
1420 /**
1421  * @brief Return wire block size given SCSI DIF API
1422  *
1423  * The blocksize on the wire for the DIF transfer is returned, given the SCSI DIF info
1424  * block and the direction of transfer.
1425  *
1426  * @param dif_info Pointer to DIF info block
1427  * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire
1428  *
1429  * @return Wire blocksize or negative error value
1430  *
1431  * WARNING: the order of initialization of the adj[] arrays MUST match the declarations
1432  * of OCS_SCSI_DIF_OPER_*
1433  */
1434 
1435 int32_t
1436 ocs_scsi_dif_wire_blocksize(ocs_scsi_dif_info_t *dif_info, int wiretomem)
1437 {
1438 	uint32_t blocksize;
1439 	uint8_t wiretomem_adj[] = {
1440 		0,		/* OCS_SCSI_DIF_OPER_DISABLED, */
1441 		0,		/* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */
1442 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */
1443 		0,		/* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
1444 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
1445 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */
1446 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
1447 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */
1448 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */
1449 		DIF_SIZE};	/* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */
1450 	uint8_t memtowire_adj[] = {
1451 		0,		/* OCS_SCSI_DIF_OPER_DISABLED, */
1452 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */
1453 		0,		/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */
1454 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
1455 		0,		/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
1456 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */
1457 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
1458 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */
1459 		DIF_SIZE,	/* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */
1460 		DIF_SIZE};	/* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */
1461 
1462 	blocksize = ocs_scsi_dif_blocksize(dif_info);
1463 	if (blocksize == 0) {
1464 		return -1;
1465 	}
1466 
1467 	if (wiretomem) {
1468 		ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0);
1469 		blocksize += wiretomem_adj[dif_info->dif_oper];
1470 	} else {	/* mem to wire */
1471 		ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0);
1472 		blocksize += memtowire_adj[dif_info->dif_oper];
1473 	}
1474 
1475 	return blocksize;
1476 }
1477 /**
1478  * @brief Return blocksize given HW API DIF block size
1479  *
1480  * Given the DIF block size enumerated value, return the block size value. (e.g.
1481  * OCS_SCSI_DIF_BLK_SIZE_512 returns 512)
1482  *
1483  * @param dif_info Pointer to HW API DIF info block
1484  *
1485  * @return returns block size, or 0 if HW API DIF blocksize is invalid
1486  */
1487 
1488 uint32_t
1489 ocs_hw_dif_blocksize(ocs_hw_dif_info_t *dif_info)
1490 {
1491 	uint32_t blocksize = 0;
1492 
1493 	switch(dif_info->blk_size) {
1494 	case OCS_HW_DIF_BK_SIZE_512:	blocksize = 512; break;
1495 	case OCS_HW_DIF_BK_SIZE_1024:	blocksize = 1024; break;
1496 	case OCS_HW_DIF_BK_SIZE_2048:	blocksize = 2048; break;
1497 	case OCS_HW_DIF_BK_SIZE_4096:	blocksize = 4096; break;
1498 	case OCS_HW_DIF_BK_SIZE_520:	blocksize = 520; break;
1499 	case OCS_HW_DIF_BK_SIZE_4104:	blocksize = 4104; break;
1500 	default:
1501 		break;
1502 	}
1503 
1504 	return blocksize;
1505 }
1506 
1507 /**
1508  * @brief Return memory block size given HW DIF API
1509  *
1510  * The blocksize in memory for the DIF transfer is returned, given the HW DIF info
1511  * block and the direction of transfer.
1512  *
1513  * @param dif_info Pointer to DIF info block
1514  * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire
1515  *
1516  * @return Memory blocksize, or negative error value
1517  *
1518  * WARNING: the order of initialization of the adj[] arrays MUST match the declarations
1519  * of OCS_HW_DIF_OPER_*
1520  */
1521 
1522 int32_t
1523 ocs_hw_dif_mem_blocksize(ocs_hw_dif_info_t *dif_info, int wiretomem)
1524 {
1525 	uint32_t blocksize;
1526 	uint8_t wiretomem_adj[] = {
1527 		0,		/* OCS_HW_DIF_OPER_DISABLED, */
1528 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */
1529 		0,		/* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */
1530 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
1531 		0,		/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
1532 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */
1533 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
1534 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */
1535 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */
1536 		DIF_SIZE};	/* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */
1537 	uint8_t memtowire_adj[] = {
1538 		0,		/* OCS_HW_DIF_OPER_DISABLED, */
1539 		0,		/* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */
1540 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */
1541 		0,		/* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
1542 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
1543 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */
1544 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
1545 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */
1546 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */
1547 		DIF_SIZE};	/* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */
1548 
1549 	blocksize = ocs_hw_dif_blocksize(dif_info);
1550 	if (blocksize == 0) {
1551 		return -1;
1552 	}
1553 
1554 	if (wiretomem) {
1555 		ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0);
1556 		blocksize += wiretomem_adj[dif_info->dif_oper];
1557 	} else {	/* mem to wire */
1558 		ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0);
1559 		blocksize += memtowire_adj[dif_info->dif_oper];
1560 	}
1561 	return blocksize;
1562 }
1563 
1564 /**
1565  * @brief Return wire block size given HW DIF API
1566  *
1567  * The blocksize on the wire for the DIF transfer is returned, given the HW DIF info
1568  * block and the direction of transfer.
1569  *
1570  * @param dif_info Pointer to DIF info block
1571  * @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire
1572  *
1573  * @return Wire blocksize or negative error value
1574  *
1575  * WARNING: the order of initialization of the adj[] arrays MUST match the declarations
1576  * of OCS_HW_DIF_OPER_*
1577  */
1578 
1579 int32_t
1580 ocs_hw_dif_wire_blocksize(ocs_hw_dif_info_t *dif_info, int wiretomem)
1581 {
1582 	uint32_t blocksize;
1583 	uint8_t wiretomem_adj[] = {
1584 		0,		/* OCS_HW_DIF_OPER_DISABLED, */
1585 		0,		/* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */
1586 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */
1587 		0,		/* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
1588 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
1589 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */
1590 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
1591 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */
1592 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */
1593 		DIF_SIZE};	/* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */
1594 	uint8_t memtowire_adj[] = {
1595 		0,		/* OCS_HW_DIF_OPER_DISABLED, */
1596 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */
1597 		0,		/* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */
1598 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
1599 		0,		/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
1600 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */
1601 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
1602 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */
1603 		DIF_SIZE,	/* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */
1604 		DIF_SIZE};	/* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */
1605 
1606 	blocksize = ocs_hw_dif_blocksize(dif_info);
1607 	if (blocksize == 0) {
1608 		return -1;
1609 	}
1610 
1611 	if (wiretomem) {
1612 		ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0);
1613 		blocksize += wiretomem_adj[dif_info->dif_oper];
1614 	} else {	/* mem to wire */
1615 		ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0);
1616 		blocksize += memtowire_adj[dif_info->dif_oper];
1617 	}
1618 
1619 	return blocksize;
1620 }
1621 
1622 static int32_t ocs_segment_remaining(ocs_textbuf_segment_t *segment);
1623 static ocs_textbuf_segment_t *ocs_textbuf_segment_alloc(ocs_textbuf_t *textbuf);
1624 static void ocs_textbuf_segment_free(ocs_t *ocs, ocs_textbuf_segment_t *segment);
1625 static ocs_textbuf_segment_t *ocs_textbuf_get_segment(ocs_textbuf_t *textbuf, uint32_t idx);
1626 
1627 uint8_t *
1628 ocs_textbuf_get_buffer(ocs_textbuf_t *textbuf)
1629 {
1630 	return ocs_textbuf_ext_get_buffer(textbuf, 0);
1631 }
1632 
1633 int32_t
1634 ocs_textbuf_get_length(ocs_textbuf_t *textbuf)
1635 {
1636 	return ocs_textbuf_ext_get_length(textbuf, 0);
1637 }
1638 
1639 int32_t
1640 ocs_textbuf_get_written(ocs_textbuf_t *textbuf)
1641 {
1642 	uint32_t idx;
1643 	int32_t n;
1644 	int32_t total = 0;
1645 
1646 	for (idx = 0; (n = ocs_textbuf_ext_get_written(textbuf, idx)) >= 0; idx++) {
1647 		total += n;
1648 	}
1649 	return total;
1650 }
1651 
1652 uint8_t *ocs_textbuf_ext_get_buffer(ocs_textbuf_t *textbuf, uint32_t idx)
1653 {
1654 	ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx);
1655 	if (segment == NULL) {
1656 		return NULL;
1657 	}
1658 	return segment->buffer;
1659 }
1660 
1661 int32_t ocs_textbuf_ext_get_length(ocs_textbuf_t *textbuf, uint32_t idx)
1662 {
1663 	ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx);
1664 	if (segment == NULL) {
1665 		return -1;
1666 	}
1667 	return segment->buffer_length;
1668 }
1669 
1670 int32_t ocs_textbuf_ext_get_written(ocs_textbuf_t *textbuf, uint32_t idx)
1671 {
1672 	ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx);
1673 	if (segment == NULL) {
1674 		return -1;
1675 	}
1676 	return segment->buffer_written;
1677 }
1678 
1679 uint32_t
1680 ocs_textbuf_initialized(ocs_textbuf_t *textbuf)
1681 {
1682 	return (textbuf->ocs != NULL);
1683 }
1684 
1685 int32_t
1686 ocs_textbuf_alloc(ocs_t *ocs, ocs_textbuf_t *textbuf, uint32_t length)
1687 {
1688 	ocs_memset(textbuf, 0, sizeof(*textbuf));
1689 
1690 	textbuf->ocs = ocs;
1691 	ocs_list_init(&textbuf->segment_list, ocs_textbuf_segment_t, link);
1692 
1693 	if (length > OCS_TEXTBUF_MAX_ALLOC_LEN) {
1694 		textbuf->allocation_length = OCS_TEXTBUF_MAX_ALLOC_LEN;
1695 	} else {
1696 		textbuf->allocation_length = length;
1697 	}
1698 
1699 	/* mark as extendable */
1700 	textbuf->extendable = TRUE;
1701 
1702 	/* save maximum allocation length */
1703 	textbuf->max_allocation_length = length;
1704 
1705 	/* Add first segment */
1706 	return (ocs_textbuf_segment_alloc(textbuf) == NULL) ? -1 : 0;
1707 }
1708 
1709 static ocs_textbuf_segment_t *
1710 ocs_textbuf_segment_alloc(ocs_textbuf_t *textbuf)
1711 {
1712 	ocs_textbuf_segment_t *segment = NULL;
1713 
1714 	if (textbuf->extendable) {
1715 		segment = ocs_malloc(textbuf->ocs, sizeof(*segment), OCS_M_ZERO | OCS_M_NOWAIT);
1716 		if (segment != NULL) {
1717 			segment->buffer = ocs_malloc(textbuf->ocs, textbuf->allocation_length, OCS_M_ZERO | OCS_M_NOWAIT);
1718 			if (segment->buffer != NULL) {
1719 				segment->buffer_length = textbuf->allocation_length;
1720 				segment->buffer_written = 0;
1721 				ocs_list_add_tail(&textbuf->segment_list, segment);
1722 				textbuf->total_allocation_length += textbuf->allocation_length;
1723 
1724 				/* If we've allocated our limit, then mark as not extendable */
1725 				if (textbuf->total_allocation_length >= textbuf->max_allocation_length) {
1726 					textbuf->extendable = 0;
1727 				}
1728 
1729 			} else {
1730 				ocs_textbuf_segment_free(textbuf->ocs, segment);
1731 				segment = NULL;
1732 			}
1733 		}
1734 	}
1735 	return segment;
1736 }
1737 
1738 static void
1739 ocs_textbuf_segment_free(ocs_t *ocs, ocs_textbuf_segment_t *segment)
1740 {
1741 	if (segment) {
1742 		if (segment->buffer && !segment->user_allocated) {
1743 			ocs_free(ocs, segment->buffer, segment->buffer_length);
1744 		}
1745 		ocs_free(ocs, segment, sizeof(*segment));
1746 	}
1747 }
1748 
1749 static ocs_textbuf_segment_t *
1750 ocs_textbuf_get_segment(ocs_textbuf_t *textbuf, uint32_t idx)
1751 {
1752 	uint32_t i;
1753 	ocs_textbuf_segment_t *segment;
1754 
1755 	if (ocs_textbuf_initialized(textbuf)) {
1756 		i = 0;
1757 		ocs_list_foreach(&textbuf->segment_list, segment) {
1758 			if (i == idx) {
1759 				return segment;
1760 			}
1761 			i++;
1762 		}
1763 	}
1764 	return NULL;
1765 }
1766 
1767 int32_t
1768 ocs_textbuf_init(ocs_t *ocs, ocs_textbuf_t *textbuf, void *buffer, uint32_t length)
1769 {
1770 	int32_t rc = -1;
1771 	ocs_textbuf_segment_t *segment;
1772 
1773 	ocs_memset(textbuf, 0, sizeof(*textbuf));
1774 
1775 	textbuf->ocs = ocs;
1776 	ocs_list_init(&textbuf->segment_list, ocs_textbuf_segment_t, link);
1777 	segment = ocs_malloc(ocs, sizeof(*segment), OCS_M_ZERO | OCS_M_NOWAIT);
1778 	if (segment) {
1779 		segment->buffer = buffer;
1780 		segment->buffer_length = length;
1781 		segment->buffer_written = 0;
1782 		segment->user_allocated = 1;
1783 		ocs_list_add_tail(&textbuf->segment_list, segment);
1784 		rc = 0;
1785 	}
1786 
1787 	return rc;
1788 }
1789 
1790 void
1791 ocs_textbuf_free(ocs_t *ocs, ocs_textbuf_t *textbuf)
1792 {
1793 	ocs_textbuf_segment_t *segment;
1794 	ocs_textbuf_segment_t *n;
1795 
1796 	if (ocs_textbuf_initialized(textbuf)) {
1797 		ocs_list_foreach_safe(&textbuf->segment_list, segment, n) {
1798 			ocs_list_remove(&textbuf->segment_list, segment);
1799 			ocs_textbuf_segment_free(ocs, segment);
1800 		}
1801 
1802 		ocs_memset(textbuf, 0, sizeof(*textbuf));
1803 	}
1804 }
1805 
1806 void
1807 ocs_textbuf_printf(ocs_textbuf_t *textbuf, const char *fmt, ...)
1808 {
1809 	va_list ap;
1810 
1811 	if (ocs_textbuf_initialized(textbuf)) {
1812 		va_start(ap, fmt);
1813 		ocs_textbuf_vprintf(textbuf, fmt, ap);
1814 		va_end(ap);
1815 	}
1816 }
1817 
1818 void
1819 ocs_textbuf_vprintf(ocs_textbuf_t *textbuf, const char *fmt, va_list ap)
1820 {
1821 	int avail;
1822 	int written;
1823 	ocs_textbuf_segment_t *segment;
1824 	va_list save_ap;
1825 
1826 	if (!ocs_textbuf_initialized(textbuf)) {
1827 		return;
1828 	}
1829 
1830 	va_copy(save_ap, ap);
1831 
1832 	/* fetch last segment */
1833 	segment = ocs_list_get_tail(&textbuf->segment_list);
1834 
1835 	avail = ocs_segment_remaining(segment);
1836 	if (avail == 0) {
1837 		if ((segment = ocs_textbuf_segment_alloc(textbuf)) == NULL) {
1838 			goto out;
1839 		}
1840 		avail = ocs_segment_remaining(segment);
1841 	}
1842 
1843 	written = ocs_vsnprintf(segment->buffer + segment->buffer_written, avail, fmt, ap);
1844 
1845 	/* See if data was truncated */
1846 	if (written >= avail) {
1847 		written = avail;
1848 
1849 		if (textbuf->extendable) {
1850 			/* revert the partially written data */
1851 			*(segment->buffer + segment->buffer_written) = 0;
1852 
1853 			/* Allocate a new segment */
1854 			if ((segment = ocs_textbuf_segment_alloc(textbuf)) == NULL) {
1855 				ocs_log_err(textbuf->ocs, "alloc segment failed\n");
1856 				goto out;
1857 			}
1858 			avail = ocs_segment_remaining(segment);
1859 
1860 			/* Retry the write */
1861 			written = ocs_vsnprintf(segment->buffer + segment->buffer_written, avail, fmt, save_ap);
1862 		}
1863 	}
1864 	segment->buffer_written += written;
1865 
1866 out:
1867 	va_end(save_ap);
1868 }
1869 
1870 void
1871 ocs_textbuf_putc(ocs_textbuf_t *textbuf, uint8_t c)
1872 {
1873 	ocs_textbuf_segment_t *segment;
1874 
1875 	if (ocs_textbuf_initialized(textbuf)) {
1876 		segment = ocs_list_get_tail(&textbuf->segment_list);
1877 
1878 		if (ocs_segment_remaining(segment)) {
1879 			*(segment->buffer + segment->buffer_written++) = c;
1880 		}
1881 		if (ocs_segment_remaining(segment) == 0) {
1882 			ocs_textbuf_segment_alloc(textbuf);
1883 		}
1884 	}
1885 }
1886 
1887 void
1888 ocs_textbuf_puts(ocs_textbuf_t *textbuf, char *s)
1889 {
1890 	if (ocs_textbuf_initialized(textbuf)) {
1891 		while(*s) {
1892 			ocs_textbuf_putc(textbuf, *s++);
1893 		}
1894 	}
1895 }
1896 
1897 void
1898 ocs_textbuf_buffer(ocs_textbuf_t *textbuf, uint8_t *buffer, uint32_t buffer_length)
1899 {
1900 	char *s;
1901 
1902 	if (!ocs_textbuf_initialized(textbuf)) {
1903 		return;
1904 	}
1905 
1906 	s = (char*) buffer;
1907 	while(*s) {
1908 		/*
1909 		 * XML escapes
1910 		 *
1911 		 * "   &quot;
1912 		 * '   &apos;
1913 		 * <   &lt;
1914 		 * >   &gt;
1915 		 * &   &amp;
1916 		 */
1917 
1918 		switch(*s) {
1919 		case '"':	ocs_textbuf_puts(textbuf, "&quot;"); break;
1920 		case '\'':	ocs_textbuf_puts(textbuf, "&apos;"); break;
1921 		case '<':	ocs_textbuf_puts(textbuf, "&lt;"); break;
1922 		case '>':	ocs_textbuf_puts(textbuf, "&gt;"); break;
1923 		case '&':	ocs_textbuf_puts(textbuf, "&amp;"); break;
1924 		default:	ocs_textbuf_putc(textbuf, *s); break;
1925 		}
1926 		s++;
1927 	}
1928 
1929 }
1930 
1931 void
1932 ocs_textbuf_copy(ocs_textbuf_t *textbuf, uint8_t *buffer, uint32_t buffer_length)
1933 {
1934 	char *s;
1935 
1936 	if (!ocs_textbuf_initialized(textbuf)) {
1937 		return;
1938 	}
1939 
1940 	s = (char*) buffer;
1941 	while(*s) {
1942 		ocs_textbuf_putc(textbuf, *s++);
1943 	}
1944 
1945 }
1946 
1947 int32_t
1948 ocs_textbuf_remaining(ocs_textbuf_t *textbuf)
1949 {
1950 	if (ocs_textbuf_initialized(textbuf)) {
1951 		return ocs_segment_remaining(ocs_list_get_head(&textbuf->segment_list));
1952 	} else {
1953 		return 0;
1954 	}
1955 }
1956 
1957 static int32_t
1958 ocs_segment_remaining(ocs_textbuf_segment_t *segment)
1959 {
1960 	return segment->buffer_length - segment->buffer_written;
1961 }
1962 
1963 void
1964 ocs_textbuf_reset(ocs_textbuf_t *textbuf)
1965 {
1966 	uint32_t i = 0;
1967 	ocs_textbuf_segment_t *segment;
1968 	ocs_textbuf_segment_t *n;
1969 
1970 	if (ocs_textbuf_initialized(textbuf)) {
1971 		/* zero written on the first segment, free the rest */
1972 		ocs_list_foreach_safe(&textbuf->segment_list, segment, n) {
1973 			if (i++ == 0) {
1974 				segment->buffer_written = 0;
1975 			} else {
1976 				ocs_list_remove(&textbuf->segment_list, segment);
1977 				ocs_textbuf_segment_free(textbuf->ocs, segment);
1978 			}
1979 		}
1980 	}
1981 }
1982 
1983 /**
1984  * @brief Sparse Vector API.
1985  *
1986  * This is a trimmed down sparse vector implementation tuned to the problem of
1987  * 24-bit FC_IDs. In this case, the 24-bit index value is broken down in three
1988  * 8-bit values. These values are used to index up to three 256 element arrays.
1989  * Arrays are allocated, only when needed. @n @n
1990  * The lookup can complete in constant time (3 indexed array references). @n @n
1991  * A typical use case would be that the fabric/directory FC_IDs would cause two rows to be
1992  * allocated, and the fabric assigned remote nodes would cause two rows to be allocated, with
1993  * the root row always allocated. This gives five rows of 256 x sizeof(void*),
1994  * resulting in 10k.
1995  */
1996 
1997 /**
1998  * @ingroup spv
1999  * @brief Allocate a new sparse vector row.
2000  *
2001  * @param os OS handle
2002  * @param rowcount Count of rows.
2003  *
2004  * @par Description
2005  * A new sparse vector row is allocated.
2006  *
2007  * @param rowcount Number of elements in a row.
2008  *
2009  * @return Returns the pointer to a row.
2010  */
2011 static void
2012 **spv_new_row(ocs_os_handle_t os, uint32_t rowcount)
2013 {
2014 	return ocs_malloc(os, sizeof(void*) * rowcount, OCS_M_ZERO | OCS_M_NOWAIT);
2015 }
2016 
2017 /**
2018  * @ingroup spv
2019  * @brief Delete row recursively.
2020  *
2021  * @par Description
2022  * This function recursively deletes the rows in this sparse vector
2023  *
2024  * @param os OS handle
2025  * @param a Pointer to the row.
2026  * @param n Number of elements in the row.
2027  * @param depth Depth of deleting.
2028  *
2029  * @return None.
2030  */
2031 static void
2032 _spv_del(ocs_os_handle_t os, void **a, uint32_t n, uint32_t depth)
2033 {
2034 	if (a) {
2035 		if (depth) {
2036 			uint32_t i;
2037 
2038 			for (i = 0; i < n; i ++) {
2039 				_spv_del(os, a[i], n, depth-1);
2040 			}
2041 
2042 			ocs_free(os, a, SPV_ROWLEN*sizeof(*a));
2043 		}
2044 	}
2045 }
2046 
2047 /**
2048  * @ingroup spv
2049  * @brief Delete a sparse vector.
2050  *
2051  * @par Description
2052  * The sparse vector is freed.
2053  *
2054  * @param spv Pointer to the sparse vector object.
2055  */
2056 void
2057 spv_del(sparse_vector_t spv)
2058 {
2059 	if (spv) {
2060 		_spv_del(spv->os, spv->array, SPV_ROWLEN, SPV_DIM);
2061 		ocs_free(spv->os, spv, sizeof(*spv));
2062 	}
2063 }
2064 
2065 /**
2066  * @ingroup spv
2067  * @brief Instantiate a new sparse vector object.
2068  *
2069  * @par Description
2070  * A new sparse vector is allocated.
2071  *
2072  * @param os OS handle
2073  *
2074  * @return Returns the pointer to the sparse vector, or NULL.
2075  */
2076 sparse_vector_t
2077 spv_new(ocs_os_handle_t os)
2078 {
2079 	sparse_vector_t spv;
2080 	uint32_t i;
2081 
2082 	spv = ocs_malloc(os, sizeof(*spv), OCS_M_ZERO | OCS_M_NOWAIT);
2083 	if (!spv) {
2084 		return NULL;
2085 	}
2086 
2087 	spv->os = os;
2088 	spv->max_idx = 1;
2089 	for (i = 0; i < SPV_DIM; i ++) {
2090 		spv->max_idx *= SPV_ROWLEN;
2091 	}
2092 
2093 	return spv;
2094 }
2095 
2096 /**
2097  * @ingroup spv
2098  * @brief Return the address of a cell.
2099  *
2100  * @par Description
2101  * Returns the address of a cell, allocates sparse rows as needed if the
2102  *         alloc_new_rows parameter is set.
2103  *
2104  * @param sv Pointer to the sparse vector.
2105  * @param idx Index of which to return the address.
2106  * @param alloc_new_rows If TRUE, then new rows may be allocated to set values,
2107  *                       Set to FALSE for retrieving values.
2108  *
2109  * @return Returns the pointer to the cell, or NULL.
2110  */
2111 static void
2112 *spv_new_cell(sparse_vector_t sv, uint32_t idx, uint8_t alloc_new_rows)
2113 {
2114 	uint32_t a = (idx >> 16) & 0xff;
2115 	uint32_t b = (idx >>  8) & 0xff;
2116 	uint32_t c = (idx >>  0) & 0xff;
2117 	void **p;
2118 
2119 	if (idx >= sv->max_idx) {
2120 		return NULL;
2121 	}
2122 
2123 	if (sv->array == NULL) {
2124 		sv->array = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL);
2125 		if (sv->array == NULL) {
2126 			return NULL;
2127 		}
2128 	}
2129 	p = sv->array;
2130 	if (p[a] == NULL) {
2131 		p[a] = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL);
2132 		if (p[a] == NULL) {
2133 			return NULL;
2134 		}
2135 	}
2136 	p = p[a];
2137 	if (p[b] == NULL) {
2138 		p[b] = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL);
2139 		if (p[b] == NULL) {
2140 			return NULL;
2141 		}
2142 	}
2143 	p = p[b];
2144 
2145 	return &p[c];
2146 }
2147 
2148 /**
2149  * @ingroup spv
2150  * @brief Set the sparse vector cell value.
2151  *
2152  * @par Description
2153  * Sets the sparse vector at @c idx to @c value.
2154  *
2155  * @param sv Pointer to the sparse vector.
2156  * @param idx Index of which to store.
2157  * @param value Value to store.
2158  *
2159  * @return None.
2160  */
2161 void
2162 spv_set(sparse_vector_t sv, uint32_t idx, void *value)
2163 {
2164 	void **ref = spv_new_cell(sv, idx, TRUE);
2165 	if (ref) {
2166 		*ref = value;
2167 	}
2168 }
2169 
2170 /**
2171  * @ingroup spv
2172  * @brief Return the sparse vector cell value.
2173  *
2174  * @par Description
2175  * Returns the value at @c idx.
2176  *
2177  * @param sv Pointer to the sparse vector.
2178  * @param idx Index of which to return the value.
2179  *
2180  * @return Returns the cell value, or NULL.
2181  */
2182 void
2183 *spv_get(sparse_vector_t sv, uint32_t idx)
2184 {
2185 	void **ref = spv_new_cell(sv, idx, FALSE);
2186 	if (ref) {
2187 		return *ref;
2188 	}
2189 	return NULL;
2190 }
2191 
2192 /*****************************************************************/
2193 /*                                                               */
2194 /* CRC LOOKUP TABLE                                              */
2195 /* ================                                              */
2196 /* The following CRC lookup table was generated automagically    */
2197 /* by the Rocksoft^tm Model CRC Algorithm Table Generation       */
2198 /* Program V1.0 using the following model parameters:            */
2199 /*                                                               */
2200 /*    Width   : 2 bytes.                                         */
2201 /*    Poly    : 0x8BB7                                           */
2202 /*    Reverse : FALSE.                                           */
2203 /*                                                               */
2204 /* For more information on the Rocksoft^tm Model CRC Algorithm,  */
2205 /* see the document titled "A Painless Guide to CRC Error        */
2206 /* Detection Algorithms" by Ross Williams                        */
2207 /* (ross@guest.adelaide.edu.au.). This document is likely to be  */
2208 /* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".        */
2209 /*                                                               */
2210 /*****************************************************************/
2211 /*
2212  * Emulex Inc, changes:
2213  * - minor syntax changes for successful compilation with contemporary
2214  *   C compilers, and OCS SDK API
2215  * - crctable[] generated using Rocksoft public domain code
2216  *
2217  * Used in the Emulex SDK, the generated file crctable.out is cut and pasted into
2218  * applicable SDK sources.
2219  */
2220 
2221 static unsigned short crctable[256] =
2222 {
2223  0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B,
2224  0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6,
2225  0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6,
2226  0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B,
2227  0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1,
2228  0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C,
2229  0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C,
2230  0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781,
2231  0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8,
2232  0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255,
2233  0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925,
2234  0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698,
2235  0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472,
2236  0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF,
2237  0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF,
2238  0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02,
2239  0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA,
2240  0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067,
2241  0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17,
2242  0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA,
2243  0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640,
2244  0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD,
2245  0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D,
2246  0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30,
2247  0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759,
2248  0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4,
2249  0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394,
2250  0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29,
2251  0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3,
2252  0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E,
2253  0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E,
2254  0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
2255 };
2256 
2257 /*****************************************************************/
2258 /*                   End of CRC Lookup Table                     */
2259 /*****************************************************************/
2260 
2261 /**
2262  * @brief Calculate the T10 PI CRC guard value for a block.
2263  *
2264  * Code based on Rocksoft's public domain CRC code, refer to
2265  * http://www.ross.net/crc/download/crc_v3.txt.  Minimally altered
2266  * to work with the ocs_dif API.
2267  *
2268  * @param blk_adr Pointer to the data buffer.
2269  * @param blk_len Number of bytes.
2270  * @param crc Previously-calculated CRC, or crcseed for a new block.
2271  *
2272  * @return Returns the calculated CRC, which may be passed back in for partial blocks.
2273  *
2274  */
2275 
2276 unsigned short
2277 t10crc16(const unsigned char *blk_adr, unsigned long blk_len, unsigned short crc)
2278 {
2279 	if (blk_len > 0) {
2280 		while (blk_len--) {
2281 			crc = crctable[((crc>>8) ^ *blk_adr++) & 0xFFL] ^ (crc << 8);
2282 		}
2283 	}
2284 	return crc;
2285 }
2286 
2287 struct ocs_ramlog_s {
2288 	uint32_t initialized;
2289 	uint32_t textbuf_count;
2290 	uint32_t textbuf_base;
2291 	ocs_textbuf_t *textbufs;
2292 	uint32_t cur_textbuf_idx;
2293 	ocs_textbuf_t *cur_textbuf;
2294 	ocs_lock_t lock;
2295 };
2296 
2297 static uint32_t ocs_ramlog_next_idx(ocs_ramlog_t *ramlog, uint32_t idx);
2298 
2299 /**
2300  * @brief Allocate a ramlog buffer.
2301  *
2302  * Initialize a RAM logging buffer with text buffers totalling buffer_len.
2303  *
2304  * @param ocs Pointer to driver structure.
2305  * @param buffer_len Total length of RAM log buffers.
2306  * @param buffer_count Number of text buffers to allocate (totalling buffer-len).
2307  *
2308  * @return Returns pointer to ocs_ramlog_t instance, or NULL.
2309  */
2310 ocs_ramlog_t *
2311 ocs_ramlog_init(ocs_t *ocs, uint32_t buffer_len, uint32_t buffer_count)
2312 {
2313 	uint32_t i;
2314 	uint32_t rc;
2315 	ocs_ramlog_t *ramlog;
2316 
2317 	ramlog = ocs_malloc(ocs, sizeof(*ramlog), OCS_M_ZERO | OCS_M_NOWAIT);
2318 	if (ramlog == NULL) {
2319 		ocs_log_err(ocs, "ocs_malloc ramlog failed\n");
2320 		return NULL;
2321 	}
2322 
2323 	ramlog->textbuf_count = buffer_count;
2324 
2325 	ramlog->textbufs = ocs_malloc(ocs, sizeof(*ramlog->textbufs)*buffer_count, OCS_M_ZERO | OCS_M_NOWAIT);
2326 	if (ramlog->textbufs == NULL) {
2327 		ocs_log_err(ocs, "ocs_malloc textbufs failed\n");
2328 		ocs_ramlog_free(ocs, ramlog);
2329 		return NULL;
2330 	}
2331 
2332 	for (i = 0; i < buffer_count; i ++) {
2333 		rc = ocs_textbuf_alloc(ocs, &ramlog->textbufs[i], buffer_len);
2334 		if (rc) {
2335 			ocs_log_err(ocs, "ocs_textbuf_alloc failed\n");
2336 			ocs_ramlog_free(ocs, ramlog);
2337 			return NULL;
2338 		}
2339 	}
2340 
2341 	ramlog->cur_textbuf_idx = 0;
2342 	ramlog->textbuf_base = 1;
2343 	ramlog->cur_textbuf = &ramlog->textbufs[0];
2344 	ramlog->initialized = TRUE;
2345 	ocs_lock_init(ocs, &ramlog->lock, "ramlog_lock[%d]", ocs_instance(ocs));
2346 	return ramlog;
2347 }
2348 
2349 /**
2350  * @brief Free a ramlog buffer.
2351  *
2352  * A previously allocated RAM logging buffer is freed.
2353  *
2354  * @param ocs Pointer to driver structure.
2355  * @param ramlog Pointer to RAM logging buffer structure.
2356  *
2357  * @return None.
2358  */
2359 
2360 void
2361 ocs_ramlog_free(ocs_t *ocs, ocs_ramlog_t *ramlog)
2362 {
2363 	uint32_t i;
2364 
2365 	if (ramlog != NULL) {
2366 		ocs_lock_free(&ramlog->lock);
2367 		if (ramlog->textbufs) {
2368 			for (i = 0; i < ramlog->textbuf_count; i ++) {
2369 				ocs_textbuf_free(ocs, &ramlog->textbufs[i]);
2370 			}
2371 
2372 			ocs_free(ocs, ramlog->textbufs, ramlog->textbuf_count*sizeof(*ramlog->textbufs));
2373 			ramlog->textbufs = NULL;
2374 		}
2375 		ocs_free(ocs, ramlog, sizeof(*ramlog));
2376 	}
2377 }
2378 
2379 /**
2380  * @brief Clear a ramlog buffer.
2381  *
2382  * The text in the start of day and/or recent ramlog text buffers is cleared.
2383  *
2384  * @param ocs Pointer to driver structure.
2385  * @param ramlog Pointer to RAM logging buffer structure.
2386  * @param clear_start_of_day Clear the start of day (driver init) portion of the ramlog.
2387  * @param clear_recent Clear the recent messages portion of the ramlog.
2388  *
2389  * @return None.
2390  */
2391 
2392 void
2393 ocs_ramlog_clear(ocs_t *ocs, ocs_ramlog_t *ramlog, int clear_start_of_day, int clear_recent)
2394 {
2395 	uint32_t i;
2396 
2397 	if (clear_recent) {
2398 		for (i = ramlog->textbuf_base; i < ramlog->textbuf_count; i ++) {
2399 			ocs_textbuf_reset(&ramlog->textbufs[i]);
2400 		}
2401 		ramlog->cur_textbuf_idx = 1;
2402 	}
2403 	if (clear_start_of_day && ramlog->textbuf_base) {
2404 		ocs_textbuf_reset(&ramlog->textbufs[0]);
2405 		/* Set textbuf_base to 0, so that all buffers are available for
2406 		 * recent logs
2407 		 */
2408 		ramlog->textbuf_base = 0;
2409 	}
2410 }
2411 
2412 /**
2413  * @brief Append formatted printf data to a ramlog buffer.
2414  *
2415  * Formatted data is appended to a RAM logging buffer.
2416  *
2417  * @param os Pointer to driver structure.
2418  * @param fmt Pointer to printf style format specifier.
2419  *
2420  * @return Returns 0 on success, or a negative error code value on failure.
2421  */
2422 
2423 int32_t
2424 ocs_ramlog_printf(void *os, const char *fmt, ...)
2425 {
2426 	ocs_t *ocs = os;
2427 	va_list ap;
2428 	int32_t res;
2429 
2430 	if (ocs == NULL || ocs->ramlog == NULL) {
2431 		return -1;
2432 	}
2433 
2434 	va_start(ap, fmt);
2435 	res = ocs_ramlog_vprintf(ocs->ramlog, fmt, ap);
2436 	va_end(ap);
2437 
2438 	return res;
2439 }
2440 
2441 /**
2442  * @brief Append formatted text to a ramlog using variable arguments.
2443  *
2444  * Formatted data is appended to the RAM logging buffer, using variable arguments.
2445  *
2446  * @param ramlog Pointer to RAM logging buffer.
2447  * @param fmt Pointer to printf style formatting string.
2448  * @param ap Variable argument pointer.
2449  *
2450  * @return Returns 0 on success, or a negative error code value on failure.
2451  */
2452 
2453 int32_t
2454 ocs_ramlog_vprintf(ocs_ramlog_t *ramlog, const char *fmt, va_list ap)
2455 {
2456 	if (ramlog == NULL || !ramlog->initialized) {
2457 		return -1;
2458 	}
2459 
2460 	/* check the current text buffer, if it is almost full (less than 120 characaters), then
2461 	 * roll to the next one.
2462 	 */
2463 	ocs_lock(&ramlog->lock);
2464 	if (ocs_textbuf_remaining(ramlog->cur_textbuf) < 120) {
2465 		ramlog->cur_textbuf_idx = ocs_ramlog_next_idx(ramlog, ramlog->cur_textbuf_idx);
2466 		ramlog->cur_textbuf = &ramlog->textbufs[ramlog->cur_textbuf_idx];
2467 		ocs_textbuf_reset(ramlog->cur_textbuf);
2468 	}
2469 
2470 	ocs_textbuf_vprintf(ramlog->cur_textbuf, fmt, ap);
2471 	ocs_unlock(&ramlog->lock);
2472 
2473 	return 0;
2474 }
2475 
2476 /**
2477  * @brief Return next ramlog buffer index.
2478  *
2479  * Given a RAM logging buffer index, return the next index.
2480  *
2481  * @param ramlog Pointer to RAM logging buffer.
2482  * @param idx Index value.
2483  *
2484  * @return Returns next index value.
2485  */
2486 
2487 static uint32_t
2488 ocs_ramlog_next_idx(ocs_ramlog_t *ramlog, uint32_t idx)
2489 {
2490 	idx = idx + 1;
2491 
2492 	if (idx >= ramlog->textbuf_count) {
2493 		idx = ramlog->textbuf_base;
2494 	}
2495 
2496 	return idx;
2497 }
2498 
2499 /**
2500  * @brief Perform ramlog buffer driver dump.
2501  *
2502  * The RAM logging buffer is appended to the driver dump data.
2503  *
2504  * @param textbuf Pointer to the driver dump text buffer.
2505  * @param ramlog Pointer to the RAM logging buffer.
2506  *
2507  * @return Returns 0 on success, or a negative error code value on failure.
2508  */
2509 
2510 int32_t
2511 ocs_ddump_ramlog(ocs_textbuf_t *textbuf, ocs_ramlog_t *ramlog)
2512 {
2513 	uint32_t i;
2514 	ocs_textbuf_t *rltextbuf;
2515 	int idx;
2516 
2517 	if ((ramlog == NULL) || (ramlog->textbufs == NULL)) {
2518 		return -1;
2519 	}
2520 
2521 	ocs_ddump_section(textbuf, "driver-log", 0);
2522 
2523 	/* Dump the start of day buffer */
2524 	ocs_ddump_section(textbuf, "startofday", 0);
2525 	/* If textbuf_base is 0, then all buffers are used for recent */
2526 	if (ramlog->textbuf_base) {
2527 		rltextbuf = &ramlog->textbufs[0];
2528 		ocs_textbuf_buffer(textbuf, ocs_textbuf_get_buffer(rltextbuf), ocs_textbuf_get_written(rltextbuf));
2529 	}
2530 	ocs_ddump_endsection(textbuf, "startofday", 0);
2531 
2532 	/* Dump the most recent buffers */
2533 	ocs_ddump_section(textbuf, "recent", 0);
2534 
2535 	/* start with the next textbuf */
2536 	idx = ocs_ramlog_next_idx(ramlog, ramlog->textbuf_count);
2537 
2538 	for (i = ramlog->textbuf_base; i < ramlog->textbuf_count; i ++) {
2539 		rltextbuf = &ramlog->textbufs[idx];
2540 		ocs_textbuf_buffer(textbuf, ocs_textbuf_get_buffer(rltextbuf), ocs_textbuf_get_written(rltextbuf));
2541 		idx = ocs_ramlog_next_idx(ramlog, idx);
2542 	}
2543 	ocs_ddump_endsection(textbuf, "recent", 0);
2544 	ocs_ddump_endsection(textbuf, "driver-log", 0);
2545 
2546 	return 0;
2547 }
2548 
2549 struct ocs_pool_s {
2550 	ocs_os_handle_t os;
2551 	ocs_array_t *a;
2552 	ocs_list_t freelist;
2553 	uint32_t use_lock:1;
2554 	ocs_lock_t lock;
2555 };
2556 
2557 typedef struct {
2558 	ocs_list_link_t link;
2559 } pool_hdr_t;
2560 
2561 /**
2562  * @brief Allocate a memory pool.
2563  *
2564  * A memory pool of given size and item count is allocated.
2565  *
2566  * @param os OS handle.
2567  * @param size Size in bytes of item.
2568  * @param count Number of items in a memory pool.
2569  * @param use_lock TRUE to enable locking of pool.
2570  *
2571  * @return Returns pointer to allocated memory pool, or NULL.
2572  */
2573 ocs_pool_t *
2574 ocs_pool_alloc(ocs_os_handle_t os, uint32_t size, uint32_t count, uint32_t use_lock)
2575 {
2576 	ocs_pool_t *pool;
2577 	uint32_t i;
2578 
2579 	pool = ocs_malloc(os, sizeof(*pool), OCS_M_ZERO | OCS_M_NOWAIT);
2580 	if (pool == NULL) {
2581 		return NULL;
2582 	}
2583 
2584 	pool->os = os;
2585 	pool->use_lock = use_lock;
2586 
2587 	/* Allocate an array where each array item is the size of a pool_hdr_t plus
2588 	 * the requested memory item size (size)
2589 	 */
2590 	pool->a = ocs_array_alloc(os, size + sizeof(pool_hdr_t), count);
2591 	if (pool->a == NULL) {
2592 		ocs_pool_free(pool);
2593 		return NULL;
2594 	}
2595 
2596 	ocs_list_init(&pool->freelist, pool_hdr_t, link);
2597 	for (i = 0; i < count; i++) {
2598 		ocs_list_add_tail(&pool->freelist, ocs_array_get(pool->a, i));
2599 	}
2600 
2601 	if (pool->use_lock) {
2602 		ocs_lock_init(os, &pool->lock, "ocs_pool:%p", pool);
2603 	}
2604 
2605 	return pool;
2606 }
2607 
2608 /**
2609  * @brief Reset a memory pool.
2610  *
2611  * Place all pool elements on the free list, and zero them.
2612  *
2613  * @param pool Pointer to the pool object.
2614  *
2615  * @return None.
2616  */
2617 void
2618 ocs_pool_reset(ocs_pool_t *pool)
2619 {
2620 	uint32_t i;
2621 	uint32_t count = ocs_array_get_count(pool->a);
2622 	uint32_t size = ocs_array_get_size(pool->a);
2623 
2624 	if (pool->use_lock) {
2625 		ocs_lock(&pool->lock);
2626 	}
2627 
2628 	/*
2629 	 * Remove all the entries from the free list, otherwise we will
2630 	 * encountered linked list asserts when they are re-added.
2631 	 */
2632 	while (!ocs_list_empty(&pool->freelist)) {
2633 		ocs_list_remove_head(&pool->freelist);
2634 	}
2635 
2636 	/* Reset the free list */
2637 	ocs_list_init(&pool->freelist, pool_hdr_t, link);
2638 
2639 	/* Return all elements to the free list and zero the elements */
2640 	for (i = 0; i < count; i++) {
2641 		ocs_memset(ocs_pool_get_instance(pool, i), 0, size - sizeof(pool_hdr_t));
2642 		ocs_list_add_tail(&pool->freelist, ocs_array_get(pool->a, i));
2643 	}
2644 	if (pool->use_lock) {
2645 		ocs_unlock(&pool->lock);
2646 	}
2647 
2648 }
2649 
2650 /**
2651  * @brief Free a previously allocated memory pool.
2652  *
2653  * The memory pool is freed.
2654  *
2655  * @param pool Pointer to memory pool.
2656  *
2657  * @return None.
2658  */
2659 void
2660 ocs_pool_free(ocs_pool_t *pool)
2661 {
2662 	if (pool != NULL) {
2663 		if (pool->a != NULL) {
2664 			ocs_array_free(pool->a);
2665 		}
2666 		if (pool->use_lock) {
2667 			ocs_lock_free(&pool->lock);
2668 		}
2669 		ocs_free(pool->os, pool, sizeof(*pool));
2670 	}
2671 }
2672 
2673 /**
2674  * @brief Allocate a memory pool item
2675  *
2676  * A memory pool item is taken from the free list and returned.
2677  *
2678  * @param pool Pointer to memory pool.
2679  *
2680  * @return Pointer to allocated item, otherwise NULL if there are no unallocated
2681  *	   items.
2682  */
2683 void *
2684 ocs_pool_get(ocs_pool_t *pool)
2685 {
2686 	pool_hdr_t *h;
2687 	void *item = NULL;
2688 
2689 	if (pool->use_lock) {
2690 		ocs_lock(&pool->lock);
2691 	}
2692 
2693 	h = ocs_list_remove_head(&pool->freelist);
2694 
2695 	if (h != NULL) {
2696 		/* Return the array item address offset by the size of pool_hdr_t */
2697 		item = &h[1];
2698 	}
2699 
2700 	if (pool->use_lock) {
2701 		ocs_unlock(&pool->lock);
2702 	}
2703 	return item;
2704 }
2705 
2706 /**
2707  * @brief free memory pool item
2708  *
2709  * A memory pool item is freed.
2710  *
2711  * @param pool Pointer to memory pool.
2712  * @param item Pointer to item to free.
2713  *
2714  * @return None.
2715  */
2716 void
2717 ocs_pool_put(ocs_pool_t *pool, void *item)
2718 {
2719 	pool_hdr_t *h;
2720 
2721 	if (pool->use_lock) {
2722 		ocs_lock(&pool->lock);
2723 	}
2724 
2725 	/* Fetch the address of the array item, which is the item address negatively offset
2726 	 * by size of pool_hdr_t (note the index of [-1]
2727 	 */
2728 	h = &((pool_hdr_t*)item)[-1];
2729 
2730 	ocs_list_add_tail(&pool->freelist, h);
2731 
2732 	if (pool->use_lock) {
2733 		ocs_unlock(&pool->lock);
2734 	}
2735 
2736 }
2737 
2738 /**
2739  * @brief Return memory pool item count.
2740  *
2741  * Returns the allocated number of items.
2742  *
2743  * @param pool Pointer to memory pool.
2744  *
2745  * @return Returns count of allocated items.
2746  */
2747 uint32_t
2748 ocs_pool_get_count(ocs_pool_t *pool)
2749 {
2750 	uint32_t count;
2751 	if (pool->use_lock) {
2752 		ocs_lock(&pool->lock);
2753 	}
2754 	count = ocs_array_get_count(pool->a);
2755 	if (pool->use_lock) {
2756 		ocs_unlock(&pool->lock);
2757 	}
2758 	return count;
2759 }
2760 
2761 /**
2762  * @brief Return item given an index.
2763  *
2764  * A pointer to a memory pool item is returned given an index.
2765  *
2766  * @param pool Pointer to memory pool.
2767  * @param idx Index.
2768  *
2769  * @return Returns pointer to item, or NULL if index is invalid.
2770  */
2771 void *
2772 ocs_pool_get_instance(ocs_pool_t *pool, uint32_t idx)
2773 {
2774 	pool_hdr_t *h = ocs_array_get(pool->a, idx);
2775 
2776 	if (h == NULL) {
2777 		return NULL;
2778 	}
2779 	return &h[1];
2780 }
2781 
2782 /**
2783  * @brief Return count of free objects in a pool.
2784  *
2785  * The number of objects on a pool's free list.
2786  *
2787  * @param pool Pointer to memory pool.
2788  *
2789  * @return Returns count of objects on free list.
2790  */
2791 uint32_t
2792 ocs_pool_get_freelist_count(ocs_pool_t *pool)
2793 {
2794 	uint32_t count = 0;
2795 	void *item;
2796 
2797 	if (pool->use_lock) {
2798 		ocs_lock(&pool->lock);
2799 	}
2800 
2801 	ocs_list_foreach(&pool->freelist, item) {
2802 		count++;
2803 	}
2804 
2805 	if (pool->use_lock) {
2806 		ocs_unlock(&pool->lock);
2807 	}
2808 	return count;
2809 }
2810