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