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