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