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
42struct 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
56static 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 */
68void
69ocs_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 */
85ocs_array_t *
86ocs_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 */
137void
138ocs_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 */
165void *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 */
186uint32_t
187ocs_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 */
201uint32_t
202ocs_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 */
217struct 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 */
236ocs_varray_t *
237ocs_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 */
266void
267ocs_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 */
288int32_t
289ocs_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 */
312void
313ocs_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 */
331void *
332ocs_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 */
355void *
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 */
376void
377ocs_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 */
391void
392ocs_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 */
406uint32_t
407ocs_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
417struct 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 */
439ocs_cbuf_t*
440ocs_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 */
477void
478ocs_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 */
500void*
501ocs_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 */
527int32_t
528ocs_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 */
556int32_t
557ocs_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
578void
579ocs_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
594void
595ocs_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
611void
612ocs_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
629void
630ocs_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
647void
648ocs_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
674void
675ocs_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
716void
717ocs_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
776static uint32_t ocs_debug_mask = OCS_DEBUG_MASK;
777
778static 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
795void 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
811void 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
828int 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
848void
849ocs_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
910uint8_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
917uint8_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 */
923ocs_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 */
932ocs_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
938static 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 */
959void
960ocs_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 */
988void
989ocs_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
1000static void
1001ocs_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
1011static void
1012ocs_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 */
1038void
1039ocs_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 */
1086void
1087ocs_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 */
1136void
1137ocs_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 */
1188uint32_t
1189ocs_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
1214void
1215ocs_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
1250uint16_t
1251ocs_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
1270uint16_t
1271ocs_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
1310uint32_t
1311ocs_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
1341int32_t
1342ocs_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
1376int32_t
1377ocs_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
1433int32_t
1434ocs_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
1486uint32_t
1487ocs_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
1520int32_t
1521ocs_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
1577int32_t
1578ocs_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
1620static int32_t ocs_segment_remaining(ocs_textbuf_segment_t *segment);
1621static ocs_textbuf_segment_t *ocs_textbuf_segment_alloc(ocs_textbuf_t *textbuf);
1622static void ocs_textbuf_segment_free(ocs_t *ocs, ocs_textbuf_segment_t *segment);
1623static ocs_textbuf_segment_t *ocs_textbuf_get_segment(ocs_textbuf_t *textbuf, uint32_t idx);
1624
1625uint8_t *
1626ocs_textbuf_get_buffer(ocs_textbuf_t *textbuf)
1627{
1628	return ocs_textbuf_ext_get_buffer(textbuf, 0);
1629}
1630
1631int32_t
1632ocs_textbuf_get_length(ocs_textbuf_t *textbuf)
1633{
1634	return ocs_textbuf_ext_get_length(textbuf, 0);
1635}
1636
1637int32_t
1638ocs_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
1650uint8_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
1659int32_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
1668int32_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
1677uint32_t
1678ocs_textbuf_initialized(ocs_textbuf_t *textbuf)
1679{
1680	return (textbuf->ocs != NULL);
1681}
1682
1683int32_t
1684ocs_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
1707static ocs_textbuf_segment_t *
1708ocs_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
1736static void
1737ocs_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
1747static ocs_textbuf_segment_t *
1748ocs_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
1765int32_t
1766ocs_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
1788void
1789ocs_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
1804void
1805ocs_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
1816void
1817ocs_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
1864out:
1865	va_end(save_ap);
1866}
1867
1868void
1869ocs_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
1885void
1886ocs_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
1895void
1896ocs_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
1929void
1930ocs_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
1945int32_t
1946ocs_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
1955static int32_t
1956ocs_segment_remaining(ocs_textbuf_segment_t *segment)
1957{
1958	return segment->buffer_length - segment->buffer_written;
1959}
1960
1961void
1962ocs_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 */
2009static 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 */
2029static 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 */
2054void
2055spv_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 */
2074sparse_vector_t
2075spv_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 */
2109static 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 */
2159void
2160spv_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 */
2180void
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
2219static 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
2274unsigned short
2275t10crc16(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
2285struct 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
2295static 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 */
2308ocs_ramlog_t *
2309ocs_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
2358void
2359ocs_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
2390void
2391ocs_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
2421int32_t
2422ocs_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
2451int32_t
2452ocs_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
2485static uint32_t
2486ocs_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
2508int32_t
2509ocs_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
2547struct 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
2555typedef 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 */
2571ocs_pool_t *
2572ocs_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 */
2615void
2616ocs_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 */
2657void
2658ocs_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 */
2681void *
2682ocs_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 */
2714void
2715ocs_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 */
2745uint32_t
2746ocs_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 */
2769void *
2770ocs_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 */
2789uint32_t
2790ocs_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