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