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