2810 lines
70 KiB
C
2810 lines
70 KiB
C
/*-
|
|
* Copyright (c) 2017 Broadcom. All rights reserved.
|
|
* The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
*/
|
|
|
|
#include "ocs.h"
|
|
#include "ocs_os.h"
|
|
|
|
#define DEFAULT_SLAB_LEN (64*1024)
|
|
|
|
struct ocs_array_s {
|
|
ocs_os_handle_t os;
|
|
|
|
uint32_t size;
|
|
uint32_t count;
|
|
|
|
uint32_t n_rows;
|
|
uint32_t elems_per_row;
|
|
uint32_t bytes_per_row;
|
|
|
|
void **array_rows;
|
|
uint32_t array_rows_len;
|
|
};
|
|
|
|
static uint32_t slab_len = DEFAULT_SLAB_LEN;
|
|
|
|
/**
|
|
* @brief Set array slab allocation length
|
|
*
|
|
* The slab length is the maximum allocation length that the array uses.
|
|
* The default 64k slab length may be overridden using this function.
|
|
*
|
|
* @param len new slab length.
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_array_set_slablen(uint32_t len)
|
|
{
|
|
slab_len = len;
|
|
}
|
|
|
|
/**
|
|
* @brief Allocate an array object
|
|
*
|
|
* An array object of size and number of elements is allocated
|
|
*
|
|
* @param os OS handle
|
|
* @param size size of array elements in bytes
|
|
* @param count number of elements in array
|
|
*
|
|
* @return pointer to array object or NULL
|
|
*/
|
|
ocs_array_t *
|
|
ocs_array_alloc(ocs_os_handle_t os, uint32_t size, uint32_t count)
|
|
{
|
|
ocs_array_t *array = NULL;
|
|
uint32_t i;
|
|
|
|
/* Fail if the item size exceeds slab_len - caller should increase slab_size,
|
|
* or not use this API.
|
|
*/
|
|
if (size > slab_len) {
|
|
ocs_log_err(NULL, "Error: size exceeds slab length\n");
|
|
return NULL;
|
|
}
|
|
|
|
array = ocs_malloc(os, sizeof(*array), OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (array == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
array->os = os;
|
|
array->size = size;
|
|
array->count = count;
|
|
array->elems_per_row = slab_len / size;
|
|
array->n_rows = (count + array->elems_per_row - 1) / array->elems_per_row;
|
|
array->bytes_per_row = array->elems_per_row * array->size;
|
|
|
|
array->array_rows_len = array->n_rows * sizeof(*array->array_rows);
|
|
array->array_rows = ocs_malloc(os, array->array_rows_len, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (array->array_rows == NULL) {
|
|
ocs_array_free(array);
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < array->n_rows; i++) {
|
|
array->array_rows[i] = ocs_malloc(os, array->bytes_per_row, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (array->array_rows[i] == NULL) {
|
|
ocs_array_free(array);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
/**
|
|
* @brief Free an array object
|
|
*
|
|
* Frees a prevously allocated array object
|
|
*
|
|
* @param array pointer to array object
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_array_free(ocs_array_t *array)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (array != NULL) {
|
|
if (array->array_rows != NULL) {
|
|
for (i = 0; i < array->n_rows; i++) {
|
|
if (array->array_rows[i] != NULL) {
|
|
ocs_free(array->os, array->array_rows[i], array->bytes_per_row);
|
|
}
|
|
}
|
|
ocs_free(array->os, array->array_rows, array->array_rows_len);
|
|
}
|
|
ocs_free(array->os, array, sizeof(*array));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Return reference to an element of an array object
|
|
*
|
|
* Return the address of an array element given an index
|
|
*
|
|
* @param array pointer to array object
|
|
* @param idx array element index
|
|
*
|
|
* @return rointer to array element, or NULL if index out of range
|
|
*/
|
|
void *ocs_array_get(ocs_array_t *array, uint32_t idx)
|
|
{
|
|
void *entry = NULL;
|
|
|
|
if (idx < array->count) {
|
|
uint32_t row = idx / array->elems_per_row;
|
|
uint32_t offset = idx % array->elems_per_row;
|
|
entry = ((uint8_t*)array->array_rows[row]) + (offset * array->size);
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
/**
|
|
* @brief Return number of elements in an array
|
|
*
|
|
* Return the number of elements in an array
|
|
*
|
|
* @param array pointer to array object
|
|
*
|
|
* @return returns count of elements in an array
|
|
*/
|
|
uint32_t
|
|
ocs_array_get_count(ocs_array_t *array)
|
|
{
|
|
return array->count;
|
|
}
|
|
|
|
/**
|
|
* @brief Return size of array elements in bytes
|
|
*
|
|
* Returns the size in bytes of each array element
|
|
*
|
|
* @param array pointer to array object
|
|
*
|
|
* @return size of array element
|
|
*/
|
|
uint32_t
|
|
ocs_array_get_size(ocs_array_t *array)
|
|
{
|
|
return array->size;
|
|
}
|
|
|
|
/**
|
|
* @brief Void pointer array structure
|
|
*
|
|
* This structure describes an object consisting of an array of void
|
|
* pointers. The object is allocated with a maximum array size, entries
|
|
* are then added to the array with while maintaining an entry count. A set of
|
|
* iterator APIs are included to allow facilitate cycling through the array
|
|
* entries in a circular fashion.
|
|
*
|
|
*/
|
|
struct ocs_varray_s {
|
|
ocs_os_handle_t os;
|
|
uint32_t array_count; /*>> maximum entry count in array */
|
|
void **array; /*>> pointer to allocated array memory */
|
|
uint32_t entry_count; /*>> number of entries added to the array */
|
|
uint32_t next_index; /*>> iterator next index */
|
|
ocs_lock_t lock; /*>> iterator lock */
|
|
};
|
|
|
|
/**
|
|
* @brief Allocate a void pointer array
|
|
*
|
|
* A void pointer array of given length is allocated.
|
|
*
|
|
* @param os OS handle
|
|
* @param array_count Array size
|
|
*
|
|
* @return returns a pointer to the ocs_varray_t object, other NULL on error
|
|
*/
|
|
ocs_varray_t *
|
|
ocs_varray_alloc(ocs_os_handle_t os, uint32_t array_count)
|
|
{
|
|
ocs_varray_t *va;
|
|
|
|
va = ocs_malloc(os, sizeof(*va), OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (va != NULL) {
|
|
va->os = os;
|
|
va->array_count = array_count;
|
|
va->array = ocs_malloc(os, sizeof(*va->array) * va->array_count, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (va->array != NULL) {
|
|
va->next_index = 0;
|
|
ocs_lock_init(os, &va->lock, "varray:%p", va);
|
|
} else {
|
|
ocs_free(os, va, sizeof(*va));
|
|
va = NULL;
|
|
}
|
|
}
|
|
return va;
|
|
}
|
|
|
|
/**
|
|
* @brief Free a void pointer array
|
|
*
|
|
* The void pointer array object is free'd
|
|
*
|
|
* @param va Pointer to void pointer array
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_varray_free(ocs_varray_t *va)
|
|
{
|
|
if (va != NULL) {
|
|
ocs_lock_free(&va->lock);
|
|
if (va->array != NULL) {
|
|
ocs_free(va->os, va->array, sizeof(*va->array) * va->array_count);
|
|
}
|
|
ocs_free(va->os, va, sizeof(*va));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Add an entry to a void pointer array
|
|
*
|
|
* An entry is added to the void pointer array
|
|
*
|
|
* @param va Pointer to void pointer array
|
|
* @param entry Pointer to entry to add
|
|
*
|
|
* @return returns 0 if entry was added, -1 if there is no more space in the array
|
|
*/
|
|
int32_t
|
|
ocs_varray_add(ocs_varray_t *va, void *entry)
|
|
{
|
|
uint32_t rc = -1;
|
|
|
|
ocs_lock(&va->lock);
|
|
if (va->entry_count < va->array_count) {
|
|
va->array[va->entry_count++] = entry;
|
|
rc = 0;
|
|
}
|
|
ocs_unlock(&va->lock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* @brief Reset the void pointer array iterator
|
|
*
|
|
* The next index value of the void pointer array iterator is cleared.
|
|
*
|
|
* @param va Pointer to void pointer array
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_varray_iter_reset(ocs_varray_t *va)
|
|
{
|
|
ocs_lock(&va->lock);
|
|
va->next_index = 0;
|
|
ocs_unlock(&va->lock);
|
|
}
|
|
|
|
/**
|
|
* @brief Return next entry from a void pointer array
|
|
*
|
|
* The next entry in the void pointer array is returned.
|
|
*
|
|
* @param va Pointer to void point array
|
|
*
|
|
* Note: takes the void pointer array lock
|
|
*
|
|
* @return returns next void pointer entry
|
|
*/
|
|
void *
|
|
ocs_varray_iter_next(ocs_varray_t *va)
|
|
{
|
|
void *rval = NULL;
|
|
|
|
if (va != NULL) {
|
|
ocs_lock(&va->lock);
|
|
rval = _ocs_varray_iter_next(va);
|
|
ocs_unlock(&va->lock);
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
/**
|
|
* @brief Return next entry from a void pointer array
|
|
*
|
|
* The next entry in the void pointer array is returned.
|
|
*
|
|
* @param va Pointer to void point array
|
|
*
|
|
* Note: doesn't take the void pointer array lock
|
|
*
|
|
* @return returns next void pointer entry
|
|
*/
|
|
void *
|
|
_ocs_varray_iter_next(ocs_varray_t *va)
|
|
{
|
|
void *rval;
|
|
|
|
rval = va->array[va->next_index];
|
|
if (++va->next_index >= va->entry_count) {
|
|
va->next_index = 0;
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
/**
|
|
* @brief Take void pointer array lock
|
|
*
|
|
* Takes the lock for the given void pointer array
|
|
*
|
|
* @param va Pointer to void pointer array
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_varray_lock(ocs_varray_t *va)
|
|
{
|
|
ocs_lock(&va->lock);
|
|
}
|
|
|
|
/**
|
|
* @brief Release void pointer array lock
|
|
*
|
|
* Releases the lock for the given void pointer array
|
|
*
|
|
* @param va Pointer to void pointer array
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_varray_unlock(ocs_varray_t *va)
|
|
{
|
|
ocs_unlock(&va->lock);
|
|
}
|
|
|
|
/**
|
|
* @brief Return entry count for a void pointer array
|
|
*
|
|
* The entry count for a void pointer array is returned
|
|
*
|
|
* @param va Pointer to void pointer array
|
|
*
|
|
* @return returns entry count
|
|
*/
|
|
uint32_t
|
|
ocs_varray_get_count(ocs_varray_t *va)
|
|
{
|
|
uint32_t rc;
|
|
|
|
ocs_lock(&va->lock);
|
|
rc = va->entry_count;
|
|
ocs_unlock(&va->lock);
|
|
return rc;
|
|
}
|
|
|
|
struct ocs_cbuf_s {
|
|
ocs_os_handle_t os; /*<< OS handle */
|
|
uint32_t entry_count; /*<< entry count */
|
|
void **array; /*<< pointer to array of cbuf pointers */
|
|
uint32_t pidx; /*<< producer index */
|
|
uint32_t cidx; /*<< consumer index */
|
|
ocs_lock_t cbuf_plock; /*<< idx lock */
|
|
ocs_lock_t cbuf_clock; /*<< idx lock */
|
|
ocs_sem_t cbuf_psem; /*<< cbuf producer counting semaphore */
|
|
ocs_sem_t cbuf_csem; /*<< cbuf consumer counting semaphore */
|
|
};
|
|
|
|
/**
|
|
* @brief Initialize a circular buffer queue
|
|
*
|
|
* A circular buffer with producer/consumer API is allocated
|
|
*
|
|
* @param os OS handle
|
|
* @param entry_count count of entries
|
|
*
|
|
* @return returns pointer to circular buffer, or NULL
|
|
*/
|
|
ocs_cbuf_t*
|
|
ocs_cbuf_alloc(ocs_os_handle_t os, uint32_t entry_count)
|
|
{
|
|
ocs_cbuf_t *cbuf;
|
|
|
|
cbuf = ocs_malloc(os, sizeof(*cbuf), OCS_M_NOWAIT | OCS_M_ZERO);
|
|
if (cbuf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
cbuf->os = os;
|
|
cbuf->entry_count = entry_count;
|
|
cbuf->pidx = 0;
|
|
cbuf->cidx = 0;
|
|
|
|
ocs_lock_init(NULL, &cbuf->cbuf_clock, "cbuf_c:%p", cbuf);
|
|
ocs_lock_init(NULL, &cbuf->cbuf_plock, "cbuf_p:%p", cbuf);
|
|
ocs_sem_init(&cbuf->cbuf_csem, 0, "cbuf:%p", cbuf);
|
|
ocs_sem_init(&cbuf->cbuf_psem, cbuf->entry_count, "cbuf:%p", cbuf);
|
|
|
|
cbuf->array = ocs_malloc(os, entry_count * sizeof(*cbuf->array), OCS_M_NOWAIT | OCS_M_ZERO);
|
|
if (cbuf->array == NULL) {
|
|
ocs_cbuf_free(cbuf);
|
|
return NULL;
|
|
}
|
|
|
|
return cbuf;
|
|
}
|
|
|
|
/**
|
|
* @brief Free a circular buffer
|
|
*
|
|
* The memory resources of a circular buffer are free'd
|
|
*
|
|
* @param cbuf pointer to circular buffer
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_cbuf_free(ocs_cbuf_t *cbuf)
|
|
{
|
|
if (cbuf != NULL) {
|
|
if (cbuf->array != NULL) {
|
|
ocs_free(cbuf->os, cbuf->array, sizeof(*cbuf->array) * cbuf->entry_count);
|
|
}
|
|
ocs_lock_free(&cbuf->cbuf_clock);
|
|
ocs_lock_free(&cbuf->cbuf_plock);
|
|
ocs_free(cbuf->os, cbuf, sizeof(*cbuf));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get pointer to buffer
|
|
*
|
|
* Wait for a buffer to become available, and return a pointer to the buffer.
|
|
*
|
|
* @param cbuf pointer to circular buffer
|
|
* @param timeout_usec timeout in microseconds
|
|
*
|
|
* @return pointer to buffer, or NULL if timeout
|
|
*/
|
|
void*
|
|
ocs_cbuf_get(ocs_cbuf_t *cbuf, int32_t timeout_usec)
|
|
{
|
|
void *ret = NULL;
|
|
|
|
if (likely(ocs_sem_p(&cbuf->cbuf_csem, timeout_usec) == 0)) {
|
|
ocs_lock(&cbuf->cbuf_clock);
|
|
ret = cbuf->array[cbuf->cidx];
|
|
if (unlikely(++cbuf->cidx >= cbuf->entry_count)) {
|
|
cbuf->cidx = 0;
|
|
}
|
|
ocs_unlock(&cbuf->cbuf_clock);
|
|
ocs_sem_v(&cbuf->cbuf_psem);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief write a buffer
|
|
*
|
|
* The buffer is written to the circular buffer.
|
|
*
|
|
* @param cbuf pointer to circular buffer
|
|
* @param elem pointer to entry
|
|
*
|
|
* @return returns 0 for success, a negative error code value for failure.
|
|
*/
|
|
int32_t
|
|
ocs_cbuf_put(ocs_cbuf_t *cbuf, void *elem)
|
|
{
|
|
int32_t rc = 0;
|
|
|
|
if (likely(ocs_sem_p(&cbuf->cbuf_psem, -1) == 0)) {
|
|
ocs_lock(&cbuf->cbuf_plock);
|
|
cbuf->array[cbuf->pidx] = elem;
|
|
if (unlikely(++cbuf->pidx >= cbuf->entry_count)) {
|
|
cbuf->pidx = 0;
|
|
}
|
|
ocs_unlock(&cbuf->cbuf_plock);
|
|
ocs_sem_v(&cbuf->cbuf_csem);
|
|
} else {
|
|
rc = -1;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* @brief Prime a circular buffer data
|
|
*
|
|
* Post array buffers to a circular buffer
|
|
*
|
|
* @param cbuf pointer to circular buffer
|
|
* @param array pointer to buffer array
|
|
*
|
|
* @return returns 0 for success, a negative error code value for failure.
|
|
*/
|
|
int32_t
|
|
ocs_cbuf_prime(ocs_cbuf_t *cbuf, ocs_array_t *array)
|
|
{
|
|
uint32_t i;
|
|
uint32_t count = MIN(ocs_array_get_count(array), cbuf->entry_count);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
ocs_cbuf_put(cbuf, ocs_array_get(array, i));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Generate driver dump start of file information
|
|
*
|
|
* The start of file information is added to 'textbuf'
|
|
*
|
|
* @param textbuf pointer to driver dump text buffer
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void
|
|
ocs_ddump_startfile(ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_textbuf_printf(textbuf, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n");
|
|
}
|
|
|
|
/**
|
|
* @brief Generate driver dump end of file information
|
|
*
|
|
* The end of file information is added to 'textbuf'
|
|
*
|
|
* @param textbuf pointer to driver dump text buffer
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void
|
|
ocs_ddump_endfile(ocs_textbuf_t *textbuf)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @brief Generate driver dump section start data
|
|
*
|
|
* The driver section start information is added to textbuf
|
|
*
|
|
* @param textbuf pointer to text buffer
|
|
* @param name name of section
|
|
* @param instance instance number of this section
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void
|
|
ocs_ddump_section(ocs_textbuf_t *textbuf, const char *name, uint32_t instance)
|
|
{
|
|
ocs_textbuf_printf(textbuf, "<%s type=\"section\" instance=\"%d\">\n", name, instance);
|
|
}
|
|
|
|
/**
|
|
* @brief Generate driver dump section end data
|
|
*
|
|
* The driver section end information is added to textbuf
|
|
*
|
|
* @param textbuf pointer to text buffer
|
|
* @param name name of section
|
|
* @param instance instance number of this section
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void
|
|
ocs_ddump_endsection(ocs_textbuf_t *textbuf, const char *name, uint32_t instance)
|
|
{
|
|
ocs_textbuf_printf(textbuf, "</%s>\n", name);
|
|
}
|
|
|
|
/**
|
|
* @brief Generate driver dump data for a given value
|
|
*
|
|
* A value is added to textbuf
|
|
*
|
|
* @param textbuf pointer to text buffer
|
|
* @param name name of variable
|
|
* @param fmt snprintf format specifier
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void
|
|
ocs_ddump_value(ocs_textbuf_t *textbuf, const char *name, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char valuebuf[64];
|
|
|
|
va_start(ap, fmt);
|
|
vsnprintf(valuebuf, sizeof(valuebuf), fmt, ap);
|
|
va_end(ap);
|
|
|
|
ocs_textbuf_printf(textbuf, "<%s>%s</%s>\n", name, valuebuf, name);
|
|
}
|
|
|
|
/**
|
|
* @brief Generate driver dump data for an arbitrary buffer of DWORDS
|
|
*
|
|
* A status value is added to textbuf
|
|
*
|
|
* @param textbuf pointer to text buffer
|
|
* @param name name of status variable
|
|
* @param instance instance number of this section
|
|
* @param buffer buffer to print
|
|
* @param size size of buffer in bytes
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void
|
|
ocs_ddump_buffer(ocs_textbuf_t *textbuf, const char *name, uint32_t instance, void *buffer, uint32_t size)
|
|
{
|
|
uint32_t *dword;
|
|
uint32_t i;
|
|
uint32_t count;
|
|
|
|
count = size / sizeof(uint32_t);
|
|
|
|
if (count == 0) {
|
|
return;
|
|
}
|
|
|
|
ocs_textbuf_printf(textbuf, "<%s type=\"buffer\" instance=\"%d\">\n", name, instance);
|
|
|
|
dword = buffer;
|
|
for (i = 0; i < count; i++) {
|
|
#define OCS_NEWLINE_MOD 8
|
|
ocs_textbuf_printf(textbuf, "%08x ", *dword++);
|
|
if ((i % OCS_NEWLINE_MOD) == (OCS_NEWLINE_MOD - 1)) {
|
|
ocs_textbuf_printf(textbuf, "\n");
|
|
}
|
|
}
|
|
|
|
ocs_textbuf_printf(textbuf, "</%s>\n", name);
|
|
}
|
|
|
|
/**
|
|
* @brief Generate driver dump for queue
|
|
*
|
|
* Add queue elements to text buffer
|
|
*
|
|
* @param textbuf pointer to driver dump text buffer
|
|
* @param q_addr address of start of queue
|
|
* @param size size of each queue entry
|
|
* @param length number of queue entries in the queue
|
|
* @param index current index of queue
|
|
* @param qentries number of most recent queue entries to dump
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void
|
|
ocs_ddump_queue_entries(ocs_textbuf_t *textbuf, void *q_addr, uint32_t size,
|
|
uint32_t length, int32_t index, uint32_t qentries)
|
|
{
|
|
uint32_t i;
|
|
uint32_t j;
|
|
uint8_t *entry;
|
|
uint32_t *dword;
|
|
uint32_t entry_count = 0;
|
|
uint32_t entry_words = size / sizeof(uint32_t);
|
|
|
|
if ((qentries == (uint32_t)-1) || (qentries > length)) {
|
|
/* if qentries is -1 or larger than queue size, dump entire queue */
|
|
entry_count = length;
|
|
index = 0;
|
|
} else {
|
|
entry_count = qentries;
|
|
|
|
index -= (qentries - 1);
|
|
if (index < 0) {
|
|
index += length;
|
|
}
|
|
}
|
|
#define OCS_NEWLINE_MOD 8
|
|
ocs_textbuf_printf(textbuf, "<qentries>\n");
|
|
for (i = 0; i < entry_count; i++){
|
|
entry = q_addr;
|
|
entry += index * size;
|
|
dword = (uint32_t *)entry;
|
|
|
|
ocs_textbuf_printf(textbuf, "[%04x] ", index);
|
|
for (j = 0; j < entry_words; j++) {
|
|
ocs_textbuf_printf(textbuf, "%08x ", *dword++);
|
|
if (((j+1) == entry_words) ||
|
|
((j % OCS_NEWLINE_MOD) == (OCS_NEWLINE_MOD - 1))) {
|
|
ocs_textbuf_printf(textbuf, "\n");
|
|
if ((j+1) < entry_words) {
|
|
ocs_textbuf_printf(textbuf, " ");
|
|
}
|
|
}
|
|
}
|
|
|
|
index++;
|
|
if ((uint32_t)index >= length) {
|
|
index = 0;
|
|
}
|
|
}
|
|
ocs_textbuf_printf(textbuf, "</qentries>\n");
|
|
}
|
|
|
|
#define OCS_DEBUG_ENABLE(x) (x ? ~0 : 0)
|
|
|
|
#define OCS_DEBUG_MASK \
|
|
(OCS_DEBUG_ENABLE(1) & OCS_DEBUG_ALWAYS) | \
|
|
(OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_MQ_DUMP) | \
|
|
(OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_CQ_DUMP) | \
|
|
(OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_WQ_DUMP) | \
|
|
(OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_EQ_DUMP) | \
|
|
(OCS_DEBUG_ENABLE(0) & OCS_DEBUG_ENABLE_SPARAM_DUMP)
|
|
|
|
static uint32_t ocs_debug_mask = OCS_DEBUG_MASK;
|
|
|
|
static int
|
|
_isprint(int c) {
|
|
return ((c > 32) && (c < 127));
|
|
}
|
|
|
|
/**
|
|
* @ingroup debug
|
|
* @brief enable debug options
|
|
*
|
|
* Enables debug options by or-ing in <b>mask</b> into the currently enabled
|
|
* debug mask.
|
|
*
|
|
* @param mask mask bits to enable
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void ocs_debug_enable(uint32_t mask) {
|
|
ocs_debug_mask |= mask;
|
|
}
|
|
|
|
/**
|
|
* @ingroup debug
|
|
* @brief disable debug options
|
|
*
|
|
* Disables debug options by clearing bits in <b>mask</b> into the currently enabled
|
|
* debug mask.
|
|
*
|
|
* @param mask mask bits to enable
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void ocs_debug_disable(uint32_t mask) {
|
|
ocs_debug_mask &= ~mask;
|
|
}
|
|
|
|
/**
|
|
* @ingroup debug
|
|
* @brief return true if debug bits are enabled
|
|
*
|
|
* Returns true if the request debug bits are set.
|
|
*
|
|
* @param mask debug bit mask
|
|
*
|
|
* @return true if corresponding bits are set
|
|
*
|
|
* @note Passing in a mask value of zero always returns true
|
|
*/
|
|
|
|
int ocs_debug_is_enabled(uint32_t mask) {
|
|
return (ocs_debug_mask & mask) == mask;
|
|
}
|
|
|
|
/**
|
|
* @ingroup debug
|
|
* @brief Dump 32 bit hex/ascii data
|
|
*
|
|
* Dumps using ocs_log a buffer of data as 32 bit hex and ascii
|
|
*
|
|
* @param mask debug enable bits
|
|
* @param os os handle
|
|
* @param label text label for the display (may be NULL)
|
|
* @param buf pointer to data buffer
|
|
* @param buf_length length of data buffer
|
|
*
|
|
* @return none
|
|
*
|
|
*/
|
|
|
|
void
|
|
ocs_dump32(uint32_t mask, ocs_os_handle_t os, const char *label, void *buf, uint32_t buf_length)
|
|
{
|
|
uint32_t word_count = buf_length / sizeof(uint32_t);
|
|
uint32_t i;
|
|
uint32_t columns = 8;
|
|
uint32_t n;
|
|
uint32_t *wbuf;
|
|
char *cbuf;
|
|
uint32_t addr = 0;
|
|
char linebuf[200];
|
|
char *pbuf = linebuf;
|
|
|
|
if (!ocs_debug_is_enabled(mask))
|
|
return;
|
|
|
|
if (label)
|
|
ocs_log_debug(os, "%s\n", label);
|
|
|
|
wbuf = buf;
|
|
while (word_count > 0) {
|
|
pbuf = linebuf;
|
|
pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%08X: ", addr);
|
|
|
|
n = word_count;
|
|
if (n > columns)
|
|
n = columns;
|
|
|
|
for (i = 0; i < n; i ++)
|
|
pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%08X ", wbuf[i]);
|
|
|
|
for (; i < columns; i ++)
|
|
pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%8s ", "");
|
|
|
|
pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), " ");
|
|
cbuf = (char*)wbuf;
|
|
for (i = 0; i < n*sizeof(uint32_t); i ++)
|
|
pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "%c", _isprint(cbuf[i]) ? cbuf[i] : '.');
|
|
pbuf += ocs_snprintf(pbuf, sizeof(linebuf) - (pbuf-linebuf), "\n");
|
|
|
|
ocs_log_debug(os, "%s", linebuf);
|
|
|
|
wbuf += n;
|
|
word_count -= n;
|
|
addr += n*sizeof(uint32_t);
|
|
}
|
|
}
|
|
|
|
#if defined(OCS_DEBUG_QUEUE_HISTORY)
|
|
|
|
/* each bit corresponds to word to capture */
|
|
#define OCS_Q_HIST_WQE_WORD_MASK_DEFAULT (BIT(4) | BIT(6) | BIT(7) | BIT(9) | BIT(12))
|
|
#define OCS_Q_HIST_TRECV_CONT_WQE_WORD_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9) | BIT(12))
|
|
#define OCS_Q_HIST_IWRITE_WQE_WORD_MASK (BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(9))
|
|
#define OCS_Q_HIST_IREAD_WQE_WORD_MASK (BIT(4) | BIT(6) | BIT(7) | BIT(9))
|
|
#define OCS_Q_HIST_ABORT_WQE_WORD_MASK (BIT(3) | BIT(7) | BIT(8) | BIT(9))
|
|
#define OCS_Q_HIST_WCQE_WORD_MASK (BIT(0) | BIT(3))
|
|
#define OCS_Q_HIST_WCQE_WORD_MASK_ERR (BIT(0) | BIT(1) | BIT(2) | BIT(3))
|
|
#define OCS_Q_HIST_CQXABT_WORD_MASK (BIT(0) | BIT(1) | BIT(2) | BIT(3))
|
|
|
|
/* if set, will provide extra queue information in each entry */
|
|
#define OCS_Q_HIST_ENABLE_Q_INFO 0
|
|
uint8_t ocs_queue_history_q_info_enabled(void)
|
|
{
|
|
return OCS_Q_HIST_ENABLE_Q_INFO;
|
|
}
|
|
|
|
/* if set, will provide timestamps in each entry */
|
|
#define OCS_Q_HIST_ENABLE_TIMESTAMPS 0
|
|
uint8_t ocs_queue_history_timestamp_enabled(void)
|
|
{
|
|
return OCS_Q_HIST_ENABLE_TIMESTAMPS;
|
|
}
|
|
|
|
/* Add WQEs and masks to override default WQE mask */
|
|
ocs_q_hist_wqe_mask_t ocs_q_hist_wqe_masks[] = {
|
|
/* WQE command Word mask */
|
|
{SLI4_WQE_ABORT, OCS_Q_HIST_ABORT_WQE_WORD_MASK},
|
|
{SLI4_WQE_FCP_IREAD64, OCS_Q_HIST_IREAD_WQE_WORD_MASK},
|
|
{SLI4_WQE_FCP_IWRITE64, OCS_Q_HIST_IWRITE_WQE_WORD_MASK},
|
|
{SLI4_WQE_FCP_CONT_TRECEIVE64, OCS_Q_HIST_TRECV_CONT_WQE_WORD_MASK},
|
|
};
|
|
|
|
/* CQE masks */
|
|
ocs_q_hist_cqe_mask_t ocs_q_hist_cqe_masks[] = {
|
|
/* CQE type Q_hist_type mask (success) mask (non-success) */
|
|
{SLI_QENTRY_WQ, OCS_Q_HIST_TYPE_CWQE, OCS_Q_HIST_WCQE_WORD_MASK, OCS_Q_HIST_WCQE_WORD_MASK_ERR},
|
|
{SLI_QENTRY_XABT, OCS_Q_HIST_TYPE_CXABT, OCS_Q_HIST_CQXABT_WORD_MASK, OCS_Q_HIST_WCQE_WORD_MASK},
|
|
};
|
|
|
|
static uint32_t ocs_q_hist_get_wqe_mask(sli4_generic_wqe_t *wqe)
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < ARRAY_SIZE(ocs_q_hist_wqe_masks); i++) {
|
|
if (ocs_q_hist_wqe_masks[i].command == wqe->command) {
|
|
return ocs_q_hist_wqe_masks[i].mask;
|
|
}
|
|
}
|
|
/* return default WQE mask */
|
|
return OCS_Q_HIST_WQE_WORD_MASK_DEFAULT;
|
|
}
|
|
|
|
/**
|
|
* @ingroup debug
|
|
* @brief Initialize resources for queue history
|
|
*
|
|
* @param os os handle
|
|
* @param q_hist Pointer to the queue history object.
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_queue_history_init(ocs_t *ocs, ocs_hw_q_hist_t *q_hist)
|
|
{
|
|
q_hist->ocs = ocs;
|
|
if (q_hist->q_hist != NULL) {
|
|
/* Setup is already done */
|
|
ocs_log_debug(ocs, "q_hist not NULL, skipping init\n");
|
|
return;
|
|
}
|
|
|
|
q_hist->q_hist = ocs_malloc(ocs, sizeof(*q_hist->q_hist)*OCS_Q_HIST_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
|
|
if (q_hist->q_hist == NULL) {
|
|
ocs_log_err(ocs, "Could not allocate queue history buffer\n");
|
|
} else {
|
|
ocs_lock_init(ocs, &q_hist->q_hist_lock, "queue history lock[%d]", ocs_instance(ocs));
|
|
}
|
|
|
|
q_hist->q_hist_index = 0;
|
|
}
|
|
|
|
/**
|
|
* @ingroup debug
|
|
* @brief Free resources for queue history
|
|
*
|
|
* @param q_hist Pointer to the queue history object.
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_queue_history_free(ocs_hw_q_hist_t *q_hist)
|
|
{
|
|
ocs_t *ocs = q_hist->ocs;
|
|
|
|
if (q_hist->q_hist != NULL) {
|
|
ocs_free(ocs, q_hist->q_hist, sizeof(*q_hist->q_hist)*OCS_Q_HIST_SIZE);
|
|
ocs_lock_free(&q_hist->q_hist_lock);
|
|
q_hist->q_hist = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ocs_queue_history_add_q_info(ocs_hw_q_hist_t *q_hist, uint32_t qid, uint32_t qindex)
|
|
{
|
|
if (ocs_queue_history_q_info_enabled()) {
|
|
/* write qid, index */
|
|
q_hist->q_hist[q_hist->q_hist_index] = (qid << 16) | qindex;
|
|
q_hist->q_hist_index++;
|
|
q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ocs_queue_history_add_timestamp(ocs_hw_q_hist_t *q_hist)
|
|
{
|
|
if (ocs_queue_history_timestamp_enabled()) {
|
|
/* write tsc */
|
|
uint64_t tsc_value;
|
|
tsc_value = get_cyclecount();
|
|
q_hist->q_hist[q_hist->q_hist_index] = ((tsc_value >> 32 ) & 0xFFFFFFFF);
|
|
q_hist->q_hist_index++;
|
|
q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
|
|
q_hist->q_hist[q_hist->q_hist_index] = (tsc_value & 0xFFFFFFFF);
|
|
q_hist->q_hist_index++;
|
|
q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ingroup debug
|
|
* @brief Log work queue entry (WQE) into history array
|
|
*
|
|
* @param q_hist Pointer to the queue history object.
|
|
* @param entryw Work queue entry in words
|
|
* @param qid Queue ID
|
|
* @param qindex Queue index
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_queue_history_wq(ocs_hw_q_hist_t *q_hist, uint32_t *entryw, uint32_t qid, uint32_t qindex)
|
|
{
|
|
int i;
|
|
ocs_q_hist_ftr_t ftr;
|
|
uint32_t wqe_word_mask = ocs_q_hist_get_wqe_mask((sli4_generic_wqe_t *)entryw);
|
|
|
|
if (q_hist->q_hist == NULL) {
|
|
/* Can't save anything */
|
|
return;
|
|
}
|
|
|
|
ftr.word = 0;
|
|
ftr.s.type = OCS_Q_HIST_TYPE_WQE;
|
|
ocs_lock(&q_hist->q_hist_lock);
|
|
/* Capture words in reverse order since we'll be interpretting them LIFO */
|
|
for (i = ((sizeof(wqe_word_mask)*8) - 1); i >= 0; i--){
|
|
if ((wqe_word_mask >> i) & 1) {
|
|
q_hist->q_hist[q_hist->q_hist_index] = entryw[i];
|
|
q_hist->q_hist_index++;
|
|
q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
|
|
}
|
|
}
|
|
|
|
ocs_queue_history_add_q_info(q_hist, qid, qindex);
|
|
ocs_queue_history_add_timestamp(q_hist);
|
|
|
|
/* write footer */
|
|
if (wqe_word_mask) {
|
|
ftr.s.mask = wqe_word_mask;
|
|
q_hist->q_hist[q_hist->q_hist_index] = ftr.word;
|
|
q_hist->q_hist_index++;
|
|
q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
|
|
}
|
|
|
|
ocs_unlock(&q_hist->q_hist_lock);
|
|
}
|
|
|
|
/**
|
|
* @ingroup debug
|
|
* @brief Log misc words
|
|
*
|
|
* @param q_hist Pointer to the queue history object.
|
|
* @param entryw array of words
|
|
* @param num_words number of words in entryw
|
|
*
|
|
* @return none
|
|
*/
|
|
void
|
|
ocs_queue_history_misc(ocs_hw_q_hist_t *q_hist, uint32_t *entryw, uint32_t num_words)
|
|
{
|
|
int i;
|
|
ocs_q_hist_ftr_t ftr;
|
|
uint32_t mask = 0;
|
|
|
|
if (q_hist->q_hist == NULL) {
|
|
/* Can't save anything */
|
|
return;
|
|
}
|
|
|
|
ftr.word = 0;
|
|
ftr.s.type = OCS_Q_HIST_TYPE_MISC;
|
|
ocs_lock(&q_hist->q_hist_lock);
|
|
/* Capture words in reverse order since we'll be interpretting them LIFO */
|
|
for (i = num_words-1; i >= 0; i--) {
|
|
q_hist->q_hist[q_hist->q_hist_index] = entryw[i];
|
|
q_hist->q_hist_index++;
|
|
q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
|
|
mask |= BIT(i);
|
|
}
|
|
|
|
ocs_queue_history_add_timestamp(q_hist);
|
|
|
|
/* write footer */
|
|
if (num_words) {
|
|
ftr.s.mask = mask;
|
|
q_hist->q_hist[q_hist->q_hist_index] = ftr.word;
|
|
q_hist->q_hist_index++;
|
|
q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
|
|
}
|
|
|
|
ocs_unlock(&q_hist->q_hist_lock);
|
|
}
|
|
|
|
/**
|
|
* @ingroup debug
|
|
* @brief Log work queue completion (CQE) entry into history
|
|
* array
|
|
*
|
|
* @param q_hist Pointer to the queue history object.
|
|
* @param ctype Type of completion entry
|
|
* @param entryw Completion queue entry in words
|
|
* @param status Completion queue status
|
|
* @param qid Queue ID
|
|
* @param qindex Queue index
|
|
*
|
|
* @return none
|
|
*/
|
|
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)
|
|
{
|
|
int i;
|
|
unsigned j;
|
|
uint32_t cqe_word_mask = 0;
|
|
ocs_q_hist_ftr_t ftr;
|
|
|
|
if (q_hist->q_hist == NULL) {
|
|
/* Can't save anything */
|
|
return;
|
|
}
|
|
|
|
ftr.word = 0;
|
|
for (j = 0; j < ARRAY_SIZE(ocs_q_hist_cqe_masks); j++) {
|
|
if (ocs_q_hist_cqe_masks[j].ctype == ctype) {
|
|
ftr.s.type = ocs_q_hist_cqe_masks[j].type;
|
|
if (status != 0) {
|
|
cqe_word_mask = ocs_q_hist_cqe_masks[j].mask_err;
|
|
} else {
|
|
cqe_word_mask = ocs_q_hist_cqe_masks[j].mask;
|
|
}
|
|
}
|
|
}
|
|
ocs_lock(&q_hist->q_hist_lock);
|
|
/* Capture words in reverse order since we'll be interpretting them LIFO */
|
|
for (i = ((sizeof(cqe_word_mask)*8) - 1); i >= 0; i--){
|
|
if ((cqe_word_mask >> i) & 1) {
|
|
q_hist->q_hist[q_hist->q_hist_index] = entryw[i];
|
|
q_hist->q_hist_index++;
|
|
q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
|
|
}
|
|
}
|
|
ocs_queue_history_add_q_info(q_hist, qid, qindex);
|
|
ocs_queue_history_add_timestamp(q_hist);
|
|
|
|
/* write footer */
|
|
if (cqe_word_mask) {
|
|
ftr.s.mask = cqe_word_mask;
|
|
q_hist->q_hist[q_hist->q_hist_index] = ftr.word;
|
|
q_hist->q_hist_index++;
|
|
q_hist->q_hist_index = q_hist->q_hist_index % OCS_Q_HIST_SIZE;
|
|
}
|
|
|
|
ocs_unlock(&q_hist->q_hist_lock);
|
|
}
|
|
|
|
/**
|
|
* @brief Get previous index
|
|
*
|
|
* @param index Index from which previous index is derived.
|
|
*/
|
|
uint32_t
|
|
ocs_queue_history_prev_index(uint32_t index)
|
|
{
|
|
if (index == 0) {
|
|
return OCS_Q_HIST_SIZE - 1;
|
|
} else {
|
|
return index - 1;
|
|
}
|
|
}
|
|
|
|
#endif /* OCS_DEBUG_QUEUE_HISTORY */
|
|
|
|
/**
|
|
* @brief Display service parameters
|
|
*
|
|
* <description>
|
|
*
|
|
* @param prelabel leading display label
|
|
* @param reqlabel display label
|
|
* @param dest destination 0=ocs_log, 1=textbuf
|
|
* @param textbuf text buffer destination (if dest==1)
|
|
* @param sparams pointer to service parameter
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void
|
|
ocs_display_sparams(const char *prelabel, const char *reqlabel, int dest, void *textbuf, void *sparams)
|
|
{
|
|
char label[64];
|
|
|
|
if (sparams == NULL) {
|
|
return;
|
|
}
|
|
|
|
switch(dest) {
|
|
case 0:
|
|
if (prelabel != NULL) {
|
|
ocs_snprintf(label, sizeof(label), "[%s] sparam: %s", prelabel, reqlabel);
|
|
} else {
|
|
ocs_snprintf(label, sizeof(label), "sparam: %s", reqlabel);
|
|
}
|
|
|
|
ocs_dump32(OCS_DEBUG_ENABLE_SPARAM_DUMP, NULL, label, sparams, sizeof(fc_plogi_payload_t));
|
|
break;
|
|
case 1:
|
|
ocs_ddump_buffer((ocs_textbuf_t*) textbuf, reqlabel, 0, sparams, sizeof(fc_plogi_payload_t));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate the T10 PI CRC guard value for a block.
|
|
*
|
|
* @param buffer Pointer to the data buffer.
|
|
* @param size Number of bytes.
|
|
* @param crc Previously-calculated CRC, or 0 for a new block.
|
|
*
|
|
* @return Returns the calculated CRC, which may be passed back in for partial blocks.
|
|
*
|
|
*/
|
|
|
|
uint16_t
|
|
ocs_scsi_dif_calc_crc(const uint8_t *buffer, uint32_t size, uint16_t crc)
|
|
{
|
|
return t10crc16(buffer, size, crc);
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate the IP-checksum guard value for a block.
|
|
*
|
|
* @param addrlen array of address length pairs
|
|
* @param addrlen_count number of entries in the addrlen[] array
|
|
*
|
|
* Algorithm:
|
|
* Sum all all the 16-byte words in the block
|
|
* Add in the "carry", which is everything in excess of 16-bits
|
|
* Flip all the bits
|
|
*
|
|
* @return Returns the calculated checksum
|
|
*/
|
|
|
|
uint16_t
|
|
ocs_scsi_dif_calc_checksum(ocs_scsi_vaddr_len_t addrlen[], uint32_t addrlen_count)
|
|
{
|
|
uint32_t i, j;
|
|
uint16_t checksum;
|
|
uint32_t intermediate; /* Use an intermediate to hold more than 16 bits during calculations */
|
|
uint32_t count;
|
|
uint16_t *buffer;
|
|
|
|
intermediate = 0;
|
|
for (j = 0; j < addrlen_count; j++) {
|
|
buffer = addrlen[j].vaddr;
|
|
count = addrlen[j].length / 2;
|
|
for (i=0; i < count; i++) {
|
|
intermediate += buffer[i];
|
|
}
|
|
}
|
|
|
|
/* Carry is everything over 16 bits */
|
|
intermediate += ((intermediate & 0xffff0000) >> 16);
|
|
|
|
/* Flip all the bits */
|
|
intermediate = ~intermediate;
|
|
|
|
checksum = intermediate;
|
|
|
|
return checksum;
|
|
}
|
|
|
|
/**
|
|
* @brief Return blocksize given SCSI API DIF block size
|
|
*
|
|
* Given the DIF block size enumerated value, return the block size value. (e.g.
|
|
* OCS_SCSI_DIF_BLK_SIZE_512 returns 512)
|
|
*
|
|
* @param dif_info Pointer to SCSI API DIF info block
|
|
*
|
|
* @return returns block size, or 0 if SCSI API DIF blocksize is invalid
|
|
*/
|
|
|
|
uint32_t
|
|
ocs_scsi_dif_blocksize(ocs_scsi_dif_info_t *dif_info)
|
|
{
|
|
uint32_t blocksize = 0;
|
|
|
|
switch(dif_info->blk_size) {
|
|
case OCS_SCSI_DIF_BK_SIZE_512: blocksize = 512; break;
|
|
case OCS_SCSI_DIF_BK_SIZE_1024: blocksize = 1024; break;
|
|
case OCS_SCSI_DIF_BK_SIZE_2048: blocksize = 2048; break;
|
|
case OCS_SCSI_DIF_BK_SIZE_4096: blocksize = 4096; break;
|
|
case OCS_SCSI_DIF_BK_SIZE_520: blocksize = 520; break;
|
|
case OCS_SCSI_DIF_BK_SIZE_4104: blocksize = 4104; break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return blocksize;
|
|
}
|
|
|
|
/**
|
|
* @brief Set SCSI API DIF blocksize
|
|
*
|
|
* Given a blocksize value (512, 1024, etc.), set the SCSI API DIF blocksize
|
|
* in the DIF info block
|
|
*
|
|
* @param dif_info Pointer to the SCSI API DIF info block
|
|
* @param blocksize Block size
|
|
*
|
|
* @return returns 0 for success, a negative error code value for failure.
|
|
*/
|
|
|
|
int32_t
|
|
ocs_scsi_dif_set_blocksize(ocs_scsi_dif_info_t *dif_info, uint32_t blocksize)
|
|
{
|
|
int32_t rc = 0;
|
|
|
|
switch(blocksize) {
|
|
case 512: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_512; break;
|
|
case 1024: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_1024; break;
|
|
case 2048: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_2048; break;
|
|
case 4096: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_4096; break;
|
|
case 520: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_520; break;
|
|
case 4104: dif_info->blk_size = OCS_SCSI_DIF_BK_SIZE_4104; break;
|
|
default:
|
|
rc = -1;
|
|
break;
|
|
}
|
|
return rc;
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief Return memory block size given SCSI DIF API
|
|
*
|
|
* The blocksize in memory for the DIF transfer is returned, given the SCSI DIF info
|
|
* block and the direction of transfer.
|
|
*
|
|
* @param dif_info Pointer to DIF info block
|
|
* @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire
|
|
*
|
|
* @return Memory blocksize, or negative error value
|
|
*
|
|
* WARNING: the order of initialization of the adj[] arrays MUST match the declarations
|
|
* of OCS_SCSI_DIF_OPER_*
|
|
*/
|
|
|
|
int32_t
|
|
ocs_scsi_dif_mem_blocksize(ocs_scsi_dif_info_t *dif_info, int wiretomem)
|
|
{
|
|
uint32_t blocksize;
|
|
uint8_t wiretomem_adj[] = {
|
|
0, /* OCS_SCSI_DIF_OPER_DISABLED, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */
|
|
0, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
|
|
0, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */
|
|
DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */
|
|
uint8_t memtowire_adj[] = {
|
|
0, /* OCS_SCSI_DIF_OPER_DISABLED, */
|
|
0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */
|
|
0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */
|
|
DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */
|
|
|
|
blocksize = ocs_scsi_dif_blocksize(dif_info);
|
|
if (blocksize == 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (wiretomem) {
|
|
ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0);
|
|
blocksize += wiretomem_adj[dif_info->dif_oper];
|
|
} else { /* mem to wire */
|
|
ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0);
|
|
blocksize += memtowire_adj[dif_info->dif_oper];
|
|
}
|
|
return blocksize;
|
|
}
|
|
|
|
/**
|
|
* @brief Return wire block size given SCSI DIF API
|
|
*
|
|
* The blocksize on the wire for the DIF transfer is returned, given the SCSI DIF info
|
|
* block and the direction of transfer.
|
|
*
|
|
* @param dif_info Pointer to DIF info block
|
|
* @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire
|
|
*
|
|
* @return Wire blocksize or negative error value
|
|
*
|
|
* WARNING: the order of initialization of the adj[] arrays MUST match the declarations
|
|
* of OCS_SCSI_DIF_OPER_*
|
|
*/
|
|
|
|
int32_t
|
|
ocs_scsi_dif_wire_blocksize(ocs_scsi_dif_info_t *dif_info, int wiretomem)
|
|
{
|
|
uint32_t blocksize;
|
|
uint8_t wiretomem_adj[] = {
|
|
0, /* OCS_SCSI_DIF_OPER_DISABLED, */
|
|
0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */
|
|
0, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */
|
|
DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */
|
|
uint8_t memtowire_adj[] = {
|
|
0, /* OCS_SCSI_DIF_OPER_DISABLED, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CRC, */
|
|
0, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
|
|
0, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CRC_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_SCSI_DIF_OPER_IN_CHKSUM_OUT_CRC, */
|
|
DIF_SIZE}; /* OCS_SCSI_DIF_OPER_IN_RAW_OUT_RAW, */
|
|
|
|
blocksize = ocs_scsi_dif_blocksize(dif_info);
|
|
if (blocksize == 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (wiretomem) {
|
|
ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0);
|
|
blocksize += wiretomem_adj[dif_info->dif_oper];
|
|
} else { /* mem to wire */
|
|
ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0);
|
|
blocksize += memtowire_adj[dif_info->dif_oper];
|
|
}
|
|
|
|
return blocksize;
|
|
}
|
|
/**
|
|
* @brief Return blocksize given HW API DIF block size
|
|
*
|
|
* Given the DIF block size enumerated value, return the block size value. (e.g.
|
|
* OCS_SCSI_DIF_BLK_SIZE_512 returns 512)
|
|
*
|
|
* @param dif_info Pointer to HW API DIF info block
|
|
*
|
|
* @return returns block size, or 0 if HW API DIF blocksize is invalid
|
|
*/
|
|
|
|
uint32_t
|
|
ocs_hw_dif_blocksize(ocs_hw_dif_info_t *dif_info)
|
|
{
|
|
uint32_t blocksize = 0;
|
|
|
|
switch(dif_info->blk_size) {
|
|
case OCS_HW_DIF_BK_SIZE_512: blocksize = 512; break;
|
|
case OCS_HW_DIF_BK_SIZE_1024: blocksize = 1024; break;
|
|
case OCS_HW_DIF_BK_SIZE_2048: blocksize = 2048; break;
|
|
case OCS_HW_DIF_BK_SIZE_4096: blocksize = 4096; break;
|
|
case OCS_HW_DIF_BK_SIZE_520: blocksize = 520; break;
|
|
case OCS_HW_DIF_BK_SIZE_4104: blocksize = 4104; break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return blocksize;
|
|
}
|
|
|
|
/**
|
|
* @brief Return memory block size given HW DIF API
|
|
*
|
|
* The blocksize in memory for the DIF transfer is returned, given the HW DIF info
|
|
* block and the direction of transfer.
|
|
*
|
|
* @param dif_info Pointer to DIF info block
|
|
* @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire
|
|
*
|
|
* @return Memory blocksize, or negative error value
|
|
*
|
|
* WARNING: the order of initialization of the adj[] arrays MUST match the declarations
|
|
* of OCS_HW_DIF_OPER_*
|
|
*/
|
|
|
|
int32_t
|
|
ocs_hw_dif_mem_blocksize(ocs_hw_dif_info_t *dif_info, int wiretomem)
|
|
{
|
|
uint32_t blocksize;
|
|
uint8_t wiretomem_adj[] = {
|
|
0, /* OCS_HW_DIF_OPER_DISABLED, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */
|
|
0, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
|
|
0, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */
|
|
DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */
|
|
uint8_t memtowire_adj[] = {
|
|
0, /* OCS_HW_DIF_OPER_DISABLED, */
|
|
0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */
|
|
0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */
|
|
DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */
|
|
|
|
blocksize = ocs_hw_dif_blocksize(dif_info);
|
|
if (blocksize == 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (wiretomem) {
|
|
ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0);
|
|
blocksize += wiretomem_adj[dif_info->dif_oper];
|
|
} else { /* mem to wire */
|
|
ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0);
|
|
blocksize += memtowire_adj[dif_info->dif_oper];
|
|
}
|
|
return blocksize;
|
|
}
|
|
|
|
/**
|
|
* @brief Return wire block size given HW DIF API
|
|
*
|
|
* The blocksize on the wire for the DIF transfer is returned, given the HW DIF info
|
|
* block and the direction of transfer.
|
|
*
|
|
* @param dif_info Pointer to DIF info block
|
|
* @param wiretomem Transfer direction, 1 is wire to memory, 0 is memory to wire
|
|
*
|
|
* @return Wire blocksize or negative error value
|
|
*
|
|
* WARNING: the order of initialization of the adj[] arrays MUST match the declarations
|
|
* of OCS_HW_DIF_OPER_*
|
|
*/
|
|
|
|
int32_t
|
|
ocs_hw_dif_wire_blocksize(ocs_hw_dif_info_t *dif_info, int wiretomem)
|
|
{
|
|
uint32_t blocksize;
|
|
uint8_t wiretomem_adj[] = {
|
|
0, /* OCS_HW_DIF_OPER_DISABLED, */
|
|
0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */
|
|
0, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */
|
|
DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */
|
|
uint8_t memtowire_adj[] = {
|
|
0, /* OCS_HW_DIF_OPER_DISABLED, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CRC, */
|
|
0, /* OCS_HW_DIF_OPER_IN_CRC_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_NODIF_OUT_CHKSUM, */
|
|
0, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_NODIF, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CRC, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CRC_OUT_CHKSUM, */
|
|
DIF_SIZE, /* OCS_HW_DIF_OPER_IN_CHKSUM_OUT_CRC, */
|
|
DIF_SIZE}; /* OCS_HW_DIF_OPER_IN_RAW_OUT_RAW, */
|
|
|
|
blocksize = ocs_hw_dif_blocksize(dif_info);
|
|
if (blocksize == 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (wiretomem) {
|
|
ocs_assert(dif_info->dif_oper < ARRAY_SIZE(wiretomem_adj), 0);
|
|
blocksize += wiretomem_adj[dif_info->dif_oper];
|
|
} else { /* mem to wire */
|
|
ocs_assert(dif_info->dif_oper < ARRAY_SIZE(memtowire_adj), 0);
|
|
blocksize += memtowire_adj[dif_info->dif_oper];
|
|
}
|
|
|
|
return blocksize;
|
|
}
|
|
|
|
static int32_t ocs_segment_remaining(ocs_textbuf_segment_t *segment);
|
|
static ocs_textbuf_segment_t *ocs_textbuf_segment_alloc(ocs_textbuf_t *textbuf);
|
|
static void ocs_textbuf_segment_free(ocs_t *ocs, ocs_textbuf_segment_t *segment);
|
|
static ocs_textbuf_segment_t *ocs_textbuf_get_segment(ocs_textbuf_t *textbuf, uint32_t idx);
|
|
|
|
uint8_t *
|
|
ocs_textbuf_get_buffer(ocs_textbuf_t *textbuf)
|
|
{
|
|
return ocs_textbuf_ext_get_buffer(textbuf, 0);
|
|
}
|
|
|
|
int32_t
|
|
ocs_textbuf_get_length(ocs_textbuf_t *textbuf)
|
|
{
|
|
return ocs_textbuf_ext_get_length(textbuf, 0);
|
|
}
|
|
|
|
int32_t
|
|
ocs_textbuf_get_written(ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t idx;
|
|
int32_t n;
|
|
int32_t total = 0;
|
|
|
|
for (idx = 0; (n = ocs_textbuf_ext_get_written(textbuf, idx)) >= 0; idx++) {
|
|
total += n;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
uint8_t *ocs_textbuf_ext_get_buffer(ocs_textbuf_t *textbuf, uint32_t idx)
|
|
{
|
|
ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx);
|
|
if (segment == NULL) {
|
|
return NULL;
|
|
}
|
|
return segment->buffer;
|
|
}
|
|
|
|
int32_t ocs_textbuf_ext_get_length(ocs_textbuf_t *textbuf, uint32_t idx)
|
|
{
|
|
ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx);
|
|
if (segment == NULL) {
|
|
return -1;
|
|
}
|
|
return segment->buffer_length;
|
|
}
|
|
|
|
int32_t ocs_textbuf_ext_get_written(ocs_textbuf_t *textbuf, uint32_t idx)
|
|
{
|
|
ocs_textbuf_segment_t *segment = ocs_textbuf_get_segment(textbuf, idx);
|
|
if (segment == NULL) {
|
|
return -1;
|
|
}
|
|
return segment->buffer_written;
|
|
}
|
|
|
|
uint32_t
|
|
ocs_textbuf_initialized(ocs_textbuf_t *textbuf)
|
|
{
|
|
return (textbuf->ocs != NULL);
|
|
}
|
|
|
|
int32_t
|
|
ocs_textbuf_alloc(ocs_t *ocs, ocs_textbuf_t *textbuf, uint32_t length)
|
|
{
|
|
ocs_memset(textbuf, 0, sizeof(*textbuf));
|
|
|
|
textbuf->ocs = ocs;
|
|
ocs_list_init(&textbuf->segment_list, ocs_textbuf_segment_t, link);
|
|
|
|
if (length > OCS_TEXTBUF_MAX_ALLOC_LEN) {
|
|
textbuf->allocation_length = OCS_TEXTBUF_MAX_ALLOC_LEN;
|
|
} else {
|
|
textbuf->allocation_length = length;
|
|
}
|
|
|
|
/* mark as extendable */
|
|
textbuf->extendable = TRUE;
|
|
|
|
/* save maximum allocation length */
|
|
textbuf->max_allocation_length = length;
|
|
|
|
/* Add first segment */
|
|
return (ocs_textbuf_segment_alloc(textbuf) == NULL) ? -1 : 0;
|
|
}
|
|
|
|
static ocs_textbuf_segment_t *
|
|
ocs_textbuf_segment_alloc(ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_textbuf_segment_t *segment = NULL;
|
|
|
|
if (textbuf->extendable) {
|
|
segment = ocs_malloc(textbuf->ocs, sizeof(*segment), OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (segment != NULL) {
|
|
segment->buffer = ocs_malloc(textbuf->ocs, textbuf->allocation_length, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (segment->buffer != NULL) {
|
|
segment->buffer_length = textbuf->allocation_length;
|
|
segment->buffer_written = 0;
|
|
ocs_list_add_tail(&textbuf->segment_list, segment);
|
|
textbuf->total_allocation_length += textbuf->allocation_length;
|
|
|
|
/* If we've allocated our limit, then mark as not extendable */
|
|
if (textbuf->total_allocation_length >= textbuf->max_allocation_length) {
|
|
textbuf->extendable = 0;
|
|
}
|
|
|
|
} else {
|
|
ocs_textbuf_segment_free(textbuf->ocs, segment);
|
|
segment = NULL;
|
|
}
|
|
}
|
|
}
|
|
return segment;
|
|
}
|
|
|
|
static void
|
|
ocs_textbuf_segment_free(ocs_t *ocs, ocs_textbuf_segment_t *segment)
|
|
{
|
|
if (segment) {
|
|
if (segment->buffer && !segment->user_allocated) {
|
|
ocs_free(ocs, segment->buffer, segment->buffer_length);
|
|
}
|
|
ocs_free(ocs, segment, sizeof(*segment));
|
|
}
|
|
}
|
|
|
|
static ocs_textbuf_segment_t *
|
|
ocs_textbuf_get_segment(ocs_textbuf_t *textbuf, uint32_t idx)
|
|
{
|
|
uint32_t i;
|
|
ocs_textbuf_segment_t *segment;
|
|
|
|
if (ocs_textbuf_initialized(textbuf)) {
|
|
i = 0;
|
|
ocs_list_foreach(&textbuf->segment_list, segment) {
|
|
if (i == idx) {
|
|
return segment;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int32_t
|
|
ocs_textbuf_init(ocs_t *ocs, ocs_textbuf_t *textbuf, void *buffer, uint32_t length)
|
|
{
|
|
int32_t rc = -1;
|
|
ocs_textbuf_segment_t *segment;
|
|
|
|
ocs_memset(textbuf, 0, sizeof(*textbuf));
|
|
|
|
textbuf->ocs = ocs;
|
|
ocs_list_init(&textbuf->segment_list, ocs_textbuf_segment_t, link);
|
|
segment = ocs_malloc(ocs, sizeof(*segment), OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (segment) {
|
|
segment->buffer = buffer;
|
|
segment->buffer_length = length;
|
|
segment->buffer_written = 0;
|
|
segment->user_allocated = 1;
|
|
ocs_list_add_tail(&textbuf->segment_list, segment);
|
|
rc = 0;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void
|
|
ocs_textbuf_free(ocs_t *ocs, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_textbuf_segment_t *segment;
|
|
ocs_textbuf_segment_t *n;
|
|
|
|
if (ocs_textbuf_initialized(textbuf)) {
|
|
ocs_list_foreach_safe(&textbuf->segment_list, segment, n) {
|
|
ocs_list_remove(&textbuf->segment_list, segment);
|
|
ocs_textbuf_segment_free(ocs, segment);
|
|
}
|
|
|
|
ocs_memset(textbuf, 0, sizeof(*textbuf));
|
|
}
|
|
}
|
|
|
|
void
|
|
ocs_textbuf_printf(ocs_textbuf_t *textbuf, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (ocs_textbuf_initialized(textbuf)) {
|
|
va_start(ap, fmt);
|
|
ocs_textbuf_vprintf(textbuf, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
|
|
void
|
|
ocs_textbuf_vprintf(ocs_textbuf_t *textbuf, const char *fmt, va_list ap)
|
|
{
|
|
int avail;
|
|
int written;
|
|
ocs_textbuf_segment_t *segment;
|
|
va_list save_ap;
|
|
|
|
if (!ocs_textbuf_initialized(textbuf)) {
|
|
return;
|
|
}
|
|
|
|
va_copy(save_ap, ap);
|
|
|
|
/* fetch last segment */
|
|
segment = ocs_list_get_tail(&textbuf->segment_list);
|
|
|
|
avail = ocs_segment_remaining(segment);
|
|
if (avail == 0) {
|
|
if ((segment = ocs_textbuf_segment_alloc(textbuf)) == NULL) {
|
|
goto out;
|
|
}
|
|
avail = ocs_segment_remaining(segment);
|
|
}
|
|
|
|
written = ocs_vsnprintf(segment->buffer + segment->buffer_written, avail, fmt, ap);
|
|
|
|
/* See if data was truncated */
|
|
if (written >= avail) {
|
|
written = avail;
|
|
|
|
if (textbuf->extendable) {
|
|
/* revert the partially written data */
|
|
*(segment->buffer + segment->buffer_written) = 0;
|
|
|
|
/* Allocate a new segment */
|
|
if ((segment = ocs_textbuf_segment_alloc(textbuf)) == NULL) {
|
|
ocs_log_err(textbuf->ocs, "alloc segment failed\n");
|
|
goto out;
|
|
}
|
|
avail = ocs_segment_remaining(segment);
|
|
|
|
/* Retry the write */
|
|
written = ocs_vsnprintf(segment->buffer + segment->buffer_written, avail, fmt, save_ap);
|
|
}
|
|
}
|
|
segment->buffer_written += written;
|
|
|
|
out:
|
|
va_end(save_ap);
|
|
}
|
|
|
|
void
|
|
ocs_textbuf_putc(ocs_textbuf_t *textbuf, uint8_t c)
|
|
{
|
|
ocs_textbuf_segment_t *segment;
|
|
|
|
if (ocs_textbuf_initialized(textbuf)) {
|
|
segment = ocs_list_get_tail(&textbuf->segment_list);
|
|
|
|
if (ocs_segment_remaining(segment)) {
|
|
*(segment->buffer + segment->buffer_written++) = c;
|
|
}
|
|
if (ocs_segment_remaining(segment) == 0) {
|
|
ocs_textbuf_segment_alloc(textbuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ocs_textbuf_puts(ocs_textbuf_t *textbuf, char *s)
|
|
{
|
|
if (ocs_textbuf_initialized(textbuf)) {
|
|
while(*s) {
|
|
ocs_textbuf_putc(textbuf, *s++);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ocs_textbuf_buffer(ocs_textbuf_t *textbuf, uint8_t *buffer, uint32_t buffer_length)
|
|
{
|
|
char *s;
|
|
|
|
if (!ocs_textbuf_initialized(textbuf)) {
|
|
return;
|
|
}
|
|
|
|
s = (char*) buffer;
|
|
while(*s) {
|
|
/*
|
|
* XML escapes
|
|
*
|
|
* " "
|
|
* ' '
|
|
* < <
|
|
* > >
|
|
* & &
|
|
*/
|
|
|
|
switch(*s) {
|
|
case '"': ocs_textbuf_puts(textbuf, """); break;
|
|
case '\'': ocs_textbuf_puts(textbuf, "'"); break;
|
|
case '<': ocs_textbuf_puts(textbuf, "<"); break;
|
|
case '>': ocs_textbuf_puts(textbuf, ">"); break;
|
|
case '&': ocs_textbuf_puts(textbuf, "&"); break;
|
|
default: ocs_textbuf_putc(textbuf, *s); break;
|
|
}
|
|
s++;
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
ocs_textbuf_copy(ocs_textbuf_t *textbuf, uint8_t *buffer, uint32_t buffer_length)
|
|
{
|
|
char *s;
|
|
|
|
if (!ocs_textbuf_initialized(textbuf)) {
|
|
return;
|
|
}
|
|
|
|
s = (char*) buffer;
|
|
while(*s) {
|
|
ocs_textbuf_putc(textbuf, *s++);
|
|
}
|
|
|
|
}
|
|
|
|
int32_t
|
|
ocs_textbuf_remaining(ocs_textbuf_t *textbuf)
|
|
{
|
|
if (ocs_textbuf_initialized(textbuf)) {
|
|
return ocs_segment_remaining(ocs_list_get_head(&textbuf->segment_list));
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int32_t
|
|
ocs_segment_remaining(ocs_textbuf_segment_t *segment)
|
|
{
|
|
return segment->buffer_length - segment->buffer_written;
|
|
}
|
|
|
|
void
|
|
ocs_textbuf_reset(ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t i = 0;
|
|
ocs_textbuf_segment_t *segment;
|
|
ocs_textbuf_segment_t *n;
|
|
|
|
if (ocs_textbuf_initialized(textbuf)) {
|
|
/* zero written on the first segment, free the rest */
|
|
ocs_list_foreach_safe(&textbuf->segment_list, segment, n) {
|
|
if (i++ == 0) {
|
|
segment->buffer_written = 0;
|
|
} else {
|
|
ocs_list_remove(&textbuf->segment_list, segment);
|
|
ocs_textbuf_segment_free(textbuf->ocs, segment);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Sparse Vector API.
|
|
*
|
|
* This is a trimmed down sparse vector implementation tuned to the problem of
|
|
* 24-bit FC_IDs. In this case, the 24-bit index value is broken down in three
|
|
* 8-bit values. These values are used to index up to three 256 element arrays.
|
|
* Arrays are allocated, only when needed. @n @n
|
|
* The lookup can complete in constant time (3 indexed array references). @n @n
|
|
* A typical use case would be that the fabric/directory FC_IDs would cause two rows to be
|
|
* allocated, and the fabric assigned remote nodes would cause two rows to be allocated, with
|
|
* the root row always allocated. This gives five rows of 256 x sizeof(void*),
|
|
* resulting in 10k.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup spv
|
|
* @brief Allocate a new sparse vector row.
|
|
*
|
|
* @param os OS handle
|
|
* @param rowcount Count of rows.
|
|
*
|
|
* @par Description
|
|
* A new sparse vector row is allocated.
|
|
*
|
|
* @param rowcount Number of elements in a row.
|
|
*
|
|
* @return Returns the pointer to a row.
|
|
*/
|
|
static void
|
|
**spv_new_row(ocs_os_handle_t os, uint32_t rowcount)
|
|
{
|
|
return ocs_malloc(os, sizeof(void*) * rowcount, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
}
|
|
|
|
/**
|
|
* @ingroup spv
|
|
* @brief Delete row recursively.
|
|
*
|
|
* @par Description
|
|
* This function recursively deletes the rows in this sparse vector
|
|
*
|
|
* @param os OS handle
|
|
* @param a Pointer to the row.
|
|
* @param n Number of elements in the row.
|
|
* @param depth Depth of deleting.
|
|
*
|
|
* @return None.
|
|
*/
|
|
static void
|
|
_spv_del(ocs_os_handle_t os, void **a, uint32_t n, uint32_t depth)
|
|
{
|
|
if (a) {
|
|
if (depth) {
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < n; i ++) {
|
|
_spv_del(os, a[i], n, depth-1);
|
|
}
|
|
|
|
ocs_free(os, a, SPV_ROWLEN*sizeof(*a));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ingroup spv
|
|
* @brief Delete a sparse vector.
|
|
*
|
|
* @par Description
|
|
* The sparse vector is freed.
|
|
*
|
|
* @param spv Pointer to the sparse vector object.
|
|
*/
|
|
void
|
|
spv_del(sparse_vector_t spv)
|
|
{
|
|
if (spv) {
|
|
_spv_del(spv->os, spv->array, SPV_ROWLEN, SPV_DIM);
|
|
ocs_free(spv->os, spv, sizeof(*spv));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ingroup spv
|
|
* @brief Instantiate a new sparse vector object.
|
|
*
|
|
* @par Description
|
|
* A new sparse vector is allocated.
|
|
*
|
|
* @param os OS handle
|
|
*
|
|
* @return Returns the pointer to the sparse vector, or NULL.
|
|
*/
|
|
sparse_vector_t
|
|
spv_new(ocs_os_handle_t os)
|
|
{
|
|
sparse_vector_t spv;
|
|
uint32_t i;
|
|
|
|
spv = ocs_malloc(os, sizeof(*spv), OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (!spv) {
|
|
return NULL;
|
|
}
|
|
|
|
spv->os = os;
|
|
spv->max_idx = 1;
|
|
for (i = 0; i < SPV_DIM; i ++) {
|
|
spv->max_idx *= SPV_ROWLEN;
|
|
}
|
|
|
|
return spv;
|
|
}
|
|
|
|
/**
|
|
* @ingroup spv
|
|
* @brief Return the address of a cell.
|
|
*
|
|
* @par Description
|
|
* Returns the address of a cell, allocates sparse rows as needed if the
|
|
* alloc_new_rows parameter is set.
|
|
*
|
|
* @param sv Pointer to the sparse vector.
|
|
* @param idx Index of which to return the address.
|
|
* @param alloc_new_rows If TRUE, then new rows may be allocated to set values,
|
|
* Set to FALSE for retrieving values.
|
|
*
|
|
* @return Returns the pointer to the cell, or NULL.
|
|
*/
|
|
static void
|
|
*spv_new_cell(sparse_vector_t sv, uint32_t idx, uint8_t alloc_new_rows)
|
|
{
|
|
uint32_t a = (idx >> 16) & 0xff;
|
|
uint32_t b = (idx >> 8) & 0xff;
|
|
uint32_t c = (idx >> 0) & 0xff;
|
|
void **p;
|
|
|
|
if (idx >= sv->max_idx) {
|
|
return NULL;
|
|
}
|
|
|
|
if (sv->array == NULL) {
|
|
sv->array = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL);
|
|
if (sv->array == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
p = sv->array;
|
|
if (p[a] == NULL) {
|
|
p[a] = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL);
|
|
if (p[a] == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
p = p[a];
|
|
if (p[b] == NULL) {
|
|
p[b] = (alloc_new_rows ? spv_new_row(sv->os, SPV_ROWLEN) : NULL);
|
|
if (p[b] == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
p = p[b];
|
|
|
|
return &p[c];
|
|
}
|
|
|
|
/**
|
|
* @ingroup spv
|
|
* @brief Set the sparse vector cell value.
|
|
*
|
|
* @par Description
|
|
* Sets the sparse vector at @c idx to @c value.
|
|
*
|
|
* @param sv Pointer to the sparse vector.
|
|
* @param idx Index of which to store.
|
|
* @param value Value to store.
|
|
*
|
|
* @return None.
|
|
*/
|
|
void
|
|
spv_set(sparse_vector_t sv, uint32_t idx, void *value)
|
|
{
|
|
void **ref = spv_new_cell(sv, idx, TRUE);
|
|
if (ref) {
|
|
*ref = value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ingroup spv
|
|
* @brief Return the sparse vector cell value.
|
|
*
|
|
* @par Description
|
|
* Returns the value at @c idx.
|
|
*
|
|
* @param sv Pointer to the sparse vector.
|
|
* @param idx Index of which to return the value.
|
|
*
|
|
* @return Returns the cell value, or NULL.
|
|
*/
|
|
void
|
|
*spv_get(sparse_vector_t sv, uint32_t idx)
|
|
{
|
|
void **ref = spv_new_cell(sv, idx, FALSE);
|
|
if (ref) {
|
|
return *ref;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************/
|
|
/* */
|
|
/* CRC LOOKUP TABLE */
|
|
/* ================ */
|
|
/* The following CRC lookup table was generated automagically */
|
|
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
|
|
/* Program V1.0 using the following model parameters: */
|
|
/* */
|
|
/* Width : 2 bytes. */
|
|
/* Poly : 0x8BB7 */
|
|
/* Reverse : FALSE. */
|
|
/* */
|
|
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
|
|
/* see the document titled "A Painless Guide to CRC Error */
|
|
/* Detection Algorithms" by Ross Williams */
|
|
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
|
|
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
|
|
/* */
|
|
/*****************************************************************/
|
|
/*
|
|
* Emulex Inc, changes:
|
|
* - minor syntax changes for successful compilation with contemporary
|
|
* C compilers, and OCS SDK API
|
|
* - crctable[] generated using Rocksoft public domain code
|
|
*
|
|
* Used in the Emulex SDK, the generated file crctable.out is cut and pasted into
|
|
* applicable SDK sources.
|
|
*/
|
|
|
|
static unsigned short crctable[256] =
|
|
{
|
|
0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B,
|
|
0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6,
|
|
0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6,
|
|
0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B,
|
|
0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1,
|
|
0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C,
|
|
0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C,
|
|
0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781,
|
|
0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8,
|
|
0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255,
|
|
0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925,
|
|
0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698,
|
|
0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472,
|
|
0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF,
|
|
0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF,
|
|
0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02,
|
|
0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA,
|
|
0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067,
|
|
0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17,
|
|
0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA,
|
|
0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640,
|
|
0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD,
|
|
0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D,
|
|
0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30,
|
|
0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759,
|
|
0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4,
|
|
0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394,
|
|
0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29,
|
|
0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3,
|
|
0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E,
|
|
0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E,
|
|
0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
|
|
};
|
|
|
|
/*****************************************************************/
|
|
/* End of CRC Lookup Table */
|
|
/*****************************************************************/
|
|
|
|
/**
|
|
* @brief Calculate the T10 PI CRC guard value for a block.
|
|
*
|
|
* Code based on Rocksoft's public domain CRC code, refer to
|
|
* http://www.ross.net/crc/download/crc_v3.txt. Minimally altered
|
|
* to work with the ocs_dif API.
|
|
*
|
|
* @param blk_adr Pointer to the data buffer.
|
|
* @param blk_len Number of bytes.
|
|
* @param crc Previously-calculated CRC, or crcseed for a new block.
|
|
*
|
|
* @return Returns the calculated CRC, which may be passed back in for partial blocks.
|
|
*
|
|
*/
|
|
|
|
unsigned short
|
|
t10crc16(const unsigned char *blk_adr, unsigned long blk_len, unsigned short crc)
|
|
{
|
|
if (blk_len > 0) {
|
|
while (blk_len--) {
|
|
crc = crctable[((crc>>8) ^ *blk_adr++) & 0xFFL] ^ (crc << 8);
|
|
}
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
struct ocs_ramlog_s {
|
|
uint32_t initialized;
|
|
uint32_t textbuf_count;
|
|
uint32_t textbuf_base;
|
|
ocs_textbuf_t *textbufs;
|
|
uint32_t cur_textbuf_idx;
|
|
ocs_textbuf_t *cur_textbuf;
|
|
ocs_lock_t lock;
|
|
};
|
|
|
|
static uint32_t ocs_ramlog_next_idx(ocs_ramlog_t *ramlog, uint32_t idx);
|
|
|
|
/**
|
|
* @brief Allocate a ramlog buffer.
|
|
*
|
|
* Initialize a RAM logging buffer with text buffers totalling buffer_len.
|
|
*
|
|
* @param ocs Pointer to driver structure.
|
|
* @param buffer_len Total length of RAM log buffers.
|
|
* @param buffer_count Number of text buffers to allocate (totalling buffer-len).
|
|
*
|
|
* @return Returns pointer to ocs_ramlog_t instance, or NULL.
|
|
*/
|
|
ocs_ramlog_t *
|
|
ocs_ramlog_init(ocs_t *ocs, uint32_t buffer_len, uint32_t buffer_count)
|
|
{
|
|
uint32_t i;
|
|
uint32_t rc;
|
|
ocs_ramlog_t *ramlog;
|
|
|
|
ramlog = ocs_malloc(ocs, sizeof(*ramlog), OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (ramlog == NULL) {
|
|
ocs_log_err(ocs, "ocs_malloc ramlog failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
ramlog->textbuf_count = buffer_count;
|
|
|
|
ramlog->textbufs = ocs_malloc(ocs, sizeof(*ramlog->textbufs)*buffer_count, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (ramlog->textbufs == NULL) {
|
|
ocs_log_err(ocs, "ocs_malloc textbufs failed\n");
|
|
ocs_ramlog_free(ocs, ramlog);
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < buffer_count; i ++) {
|
|
rc = ocs_textbuf_alloc(ocs, &ramlog->textbufs[i], buffer_len);
|
|
if (rc) {
|
|
ocs_log_err(ocs, "ocs_textbuf_alloc failed\n");
|
|
ocs_ramlog_free(ocs, ramlog);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
ramlog->cur_textbuf_idx = 0;
|
|
ramlog->textbuf_base = 1;
|
|
ramlog->cur_textbuf = &ramlog->textbufs[0];
|
|
ramlog->initialized = TRUE;
|
|
ocs_lock_init(ocs, &ramlog->lock, "ramlog_lock[%d]", ocs_instance(ocs));
|
|
return ramlog;
|
|
}
|
|
|
|
/**
|
|
* @brief Free a ramlog buffer.
|
|
*
|
|
* A previously allocated RAM logging buffer is freed.
|
|
*
|
|
* @param ocs Pointer to driver structure.
|
|
* @param ramlog Pointer to RAM logging buffer structure.
|
|
*
|
|
* @return None.
|
|
*/
|
|
|
|
void
|
|
ocs_ramlog_free(ocs_t *ocs, ocs_ramlog_t *ramlog)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (ramlog != NULL) {
|
|
ocs_lock_free(&ramlog->lock);
|
|
if (ramlog->textbufs) {
|
|
for (i = 0; i < ramlog->textbuf_count; i ++) {
|
|
ocs_textbuf_free(ocs, &ramlog->textbufs[i]);
|
|
}
|
|
|
|
ocs_free(ocs, ramlog->textbufs, ramlog->textbuf_count*sizeof(*ramlog->textbufs));
|
|
ramlog->textbufs = NULL;
|
|
}
|
|
ocs_free(ocs, ramlog, sizeof(*ramlog));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Clear a ramlog buffer.
|
|
*
|
|
* The text in the start of day and/or recent ramlog text buffers is cleared.
|
|
*
|
|
* @param ocs Pointer to driver structure.
|
|
* @param ramlog Pointer to RAM logging buffer structure.
|
|
* @param clear_start_of_day Clear the start of day (driver init) portion of the ramlog.
|
|
* @param clear_recent Clear the recent messages portion of the ramlog.
|
|
*
|
|
* @return None.
|
|
*/
|
|
|
|
void
|
|
ocs_ramlog_clear(ocs_t *ocs, ocs_ramlog_t *ramlog, int clear_start_of_day, int clear_recent)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (clear_recent) {
|
|
for (i = ramlog->textbuf_base; i < ramlog->textbuf_count; i ++) {
|
|
ocs_textbuf_reset(&ramlog->textbufs[i]);
|
|
}
|
|
ramlog->cur_textbuf_idx = 1;
|
|
}
|
|
if (clear_start_of_day && ramlog->textbuf_base) {
|
|
ocs_textbuf_reset(&ramlog->textbufs[0]);
|
|
/* Set textbuf_base to 0, so that all buffers are available for
|
|
* recent logs
|
|
*/
|
|
ramlog->textbuf_base = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Append formatted printf data to a ramlog buffer.
|
|
*
|
|
* Formatted data is appended to a RAM logging buffer.
|
|
*
|
|
* @param os Pointer to driver structure.
|
|
* @param fmt Pointer to printf style format specifier.
|
|
*
|
|
* @return Returns 0 on success, or a negative error code value on failure.
|
|
*/
|
|
|
|
int32_t
|
|
ocs_ramlog_printf(void *os, const char *fmt, ...)
|
|
{
|
|
ocs_t *ocs = os;
|
|
va_list ap;
|
|
int32_t res;
|
|
|
|
if (ocs == NULL || ocs->ramlog == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
va_start(ap, fmt);
|
|
res = ocs_ramlog_vprintf(ocs->ramlog, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* @brief Append formatted text to a ramlog using variable arguments.
|
|
*
|
|
* Formatted data is appended to the RAM logging buffer, using variable arguments.
|
|
*
|
|
* @param ramlog Pointer to RAM logging buffer.
|
|
* @param fmt Pointer to printf style formatting string.
|
|
* @param ap Variable argument pointer.
|
|
*
|
|
* @return Returns 0 on success, or a negative error code value on failure.
|
|
*/
|
|
|
|
int32_t
|
|
ocs_ramlog_vprintf(ocs_ramlog_t *ramlog, const char *fmt, va_list ap)
|
|
{
|
|
if (ramlog == NULL || !ramlog->initialized) {
|
|
return -1;
|
|
}
|
|
|
|
/* check the current text buffer, if it is almost full (less than 120 characaters), then
|
|
* roll to the next one.
|
|
*/
|
|
ocs_lock(&ramlog->lock);
|
|
if (ocs_textbuf_remaining(ramlog->cur_textbuf) < 120) {
|
|
ramlog->cur_textbuf_idx = ocs_ramlog_next_idx(ramlog, ramlog->cur_textbuf_idx);
|
|
ramlog->cur_textbuf = &ramlog->textbufs[ramlog->cur_textbuf_idx];
|
|
ocs_textbuf_reset(ramlog->cur_textbuf);
|
|
}
|
|
|
|
ocs_textbuf_vprintf(ramlog->cur_textbuf, fmt, ap);
|
|
ocs_unlock(&ramlog->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Return next ramlog buffer index.
|
|
*
|
|
* Given a RAM logging buffer index, return the next index.
|
|
*
|
|
* @param ramlog Pointer to RAM logging buffer.
|
|
* @param idx Index value.
|
|
*
|
|
* @return Returns next index value.
|
|
*/
|
|
|
|
static uint32_t
|
|
ocs_ramlog_next_idx(ocs_ramlog_t *ramlog, uint32_t idx)
|
|
{
|
|
idx = idx + 1;
|
|
|
|
if (idx >= ramlog->textbuf_count) {
|
|
idx = ramlog->textbuf_base;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
/**
|
|
* @brief Perform ramlog buffer driver dump.
|
|
*
|
|
* The RAM logging buffer is appended to the driver dump data.
|
|
*
|
|
* @param textbuf Pointer to the driver dump text buffer.
|
|
* @param ramlog Pointer to the RAM logging buffer.
|
|
*
|
|
* @return Returns 0 on success, or a negative error code value on failure.
|
|
*/
|
|
|
|
int32_t
|
|
ocs_ddump_ramlog(ocs_textbuf_t *textbuf, ocs_ramlog_t *ramlog)
|
|
{
|
|
uint32_t i;
|
|
ocs_textbuf_t *rltextbuf;
|
|
int idx;
|
|
|
|
if ((ramlog == NULL) || (ramlog->textbufs == NULL)) {
|
|
return -1;
|
|
}
|
|
|
|
ocs_ddump_section(textbuf, "driver-log", 0);
|
|
|
|
/* Dump the start of day buffer */
|
|
ocs_ddump_section(textbuf, "startofday", 0);
|
|
/* If textbuf_base is 0, then all buffers are used for recent */
|
|
if (ramlog->textbuf_base) {
|
|
rltextbuf = &ramlog->textbufs[0];
|
|
ocs_textbuf_buffer(textbuf, ocs_textbuf_get_buffer(rltextbuf), ocs_textbuf_get_written(rltextbuf));
|
|
}
|
|
ocs_ddump_endsection(textbuf, "startofday", 0);
|
|
|
|
/* Dump the most recent buffers */
|
|
ocs_ddump_section(textbuf, "recent", 0);
|
|
|
|
/* start with the next textbuf */
|
|
idx = ocs_ramlog_next_idx(ramlog, ramlog->textbuf_count);
|
|
|
|
for (i = ramlog->textbuf_base; i < ramlog->textbuf_count; i ++) {
|
|
rltextbuf = &ramlog->textbufs[idx];
|
|
ocs_textbuf_buffer(textbuf, ocs_textbuf_get_buffer(rltextbuf), ocs_textbuf_get_written(rltextbuf));
|
|
idx = ocs_ramlog_next_idx(ramlog, idx);
|
|
}
|
|
ocs_ddump_endsection(textbuf, "recent", 0);
|
|
ocs_ddump_endsection(textbuf, "driver-log", 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct ocs_pool_s {
|
|
ocs_os_handle_t os;
|
|
ocs_array_t *a;
|
|
ocs_list_t freelist;
|
|
uint32_t use_lock:1;
|
|
ocs_lock_t lock;
|
|
};
|
|
|
|
typedef struct {
|
|
ocs_list_link_t link;
|
|
} pool_hdr_t;
|
|
|
|
/**
|
|
* @brief Allocate a memory pool.
|
|
*
|
|
* A memory pool of given size and item count is allocated.
|
|
*
|
|
* @param os OS handle.
|
|
* @param size Size in bytes of item.
|
|
* @param count Number of items in a memory pool.
|
|
* @param use_lock TRUE to enable locking of pool.
|
|
*
|
|
* @return Returns pointer to allocated memory pool, or NULL.
|
|
*/
|
|
ocs_pool_t *
|
|
ocs_pool_alloc(ocs_os_handle_t os, uint32_t size, uint32_t count, uint32_t use_lock)
|
|
{
|
|
ocs_pool_t *pool;
|
|
uint32_t i;
|
|
|
|
pool = ocs_malloc(os, sizeof(*pool), OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (pool == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
pool->os = os;
|
|
pool->use_lock = use_lock;
|
|
|
|
/* Allocate an array where each array item is the size of a pool_hdr_t plus
|
|
* the requested memory item size (size)
|
|
*/
|
|
pool->a = ocs_array_alloc(os, size + sizeof(pool_hdr_t), count);
|
|
if (pool->a == NULL) {
|
|
ocs_pool_free(pool);
|
|
return NULL;
|
|
}
|
|
|
|
ocs_list_init(&pool->freelist, pool_hdr_t, link);
|
|
for (i = 0; i < count; i++) {
|
|
ocs_list_add_tail(&pool->freelist, ocs_array_get(pool->a, i));
|
|
}
|
|
|
|
if (pool->use_lock) {
|
|
ocs_lock_init(os, &pool->lock, "ocs_pool:%p", pool);
|
|
}
|
|
|
|
return pool;
|
|
}
|
|
|
|
/**
|
|
* @brief Reset a memory pool.
|
|
*
|
|
* Place all pool elements on the free list, and zero them.
|
|
*
|
|
* @param pool Pointer to the pool object.
|
|
*
|
|
* @return None.
|
|
*/
|
|
void
|
|
ocs_pool_reset(ocs_pool_t *pool)
|
|
{
|
|
uint32_t i;
|
|
uint32_t count = ocs_array_get_count(pool->a);
|
|
uint32_t size = ocs_array_get_size(pool->a);
|
|
|
|
if (pool->use_lock) {
|
|
ocs_lock(&pool->lock);
|
|
}
|
|
|
|
/*
|
|
* Remove all the entries from the free list, otherwise we will
|
|
* encountered linked list asserts when they are re-added.
|
|
*/
|
|
while (!ocs_list_empty(&pool->freelist)) {
|
|
ocs_list_remove_head(&pool->freelist);
|
|
}
|
|
|
|
/* Reset the free list */
|
|
ocs_list_init(&pool->freelist, pool_hdr_t, link);
|
|
|
|
/* Return all elements to the free list and zero the elements */
|
|
for (i = 0; i < count; i++) {
|
|
ocs_memset(ocs_pool_get_instance(pool, i), 0, size - sizeof(pool_hdr_t));
|
|
ocs_list_add_tail(&pool->freelist, ocs_array_get(pool->a, i));
|
|
}
|
|
if (pool->use_lock) {
|
|
ocs_unlock(&pool->lock);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief Free a previously allocated memory pool.
|
|
*
|
|
* The memory pool is freed.
|
|
*
|
|
* @param pool Pointer to memory pool.
|
|
*
|
|
* @return None.
|
|
*/
|
|
void
|
|
ocs_pool_free(ocs_pool_t *pool)
|
|
{
|
|
if (pool != NULL) {
|
|
if (pool->a != NULL) {
|
|
ocs_array_free(pool->a);
|
|
}
|
|
if (pool->use_lock) {
|
|
ocs_lock_free(&pool->lock);
|
|
}
|
|
ocs_free(pool->os, pool, sizeof(*pool));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Allocate a memory pool item
|
|
*
|
|
* A memory pool item is taken from the free list and returned.
|
|
*
|
|
* @param pool Pointer to memory pool.
|
|
*
|
|
* @return Pointer to allocated item, otherwise NULL if there are no unallocated
|
|
* items.
|
|
*/
|
|
void *
|
|
ocs_pool_get(ocs_pool_t *pool)
|
|
{
|
|
pool_hdr_t *h;
|
|
void *item = NULL;
|
|
|
|
if (pool->use_lock) {
|
|
ocs_lock(&pool->lock);
|
|
}
|
|
|
|
h = ocs_list_remove_head(&pool->freelist);
|
|
|
|
if (h != NULL) {
|
|
/* Return the array item address offset by the size of pool_hdr_t */
|
|
item = &h[1];
|
|
}
|
|
|
|
if (pool->use_lock) {
|
|
ocs_unlock(&pool->lock);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* @brief free memory pool item
|
|
*
|
|
* A memory pool item is freed.
|
|
*
|
|
* @param pool Pointer to memory pool.
|
|
* @param item Pointer to item to free.
|
|
*
|
|
* @return None.
|
|
*/
|
|
void
|
|
ocs_pool_put(ocs_pool_t *pool, void *item)
|
|
{
|
|
pool_hdr_t *h;
|
|
|
|
if (pool->use_lock) {
|
|
ocs_lock(&pool->lock);
|
|
}
|
|
|
|
/* Fetch the address of the array item, which is the item address negatively offset
|
|
* by size of pool_hdr_t (note the index of [-1]
|
|
*/
|
|
h = &((pool_hdr_t*)item)[-1];
|
|
|
|
ocs_list_add_tail(&pool->freelist, h);
|
|
|
|
if (pool->use_lock) {
|
|
ocs_unlock(&pool->lock);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief Return memory pool item count.
|
|
*
|
|
* Returns the allocated number of items.
|
|
*
|
|
* @param pool Pointer to memory pool.
|
|
*
|
|
* @return Returns count of allocated items.
|
|
*/
|
|
uint32_t
|
|
ocs_pool_get_count(ocs_pool_t *pool)
|
|
{
|
|
uint32_t count;
|
|
if (pool->use_lock) {
|
|
ocs_lock(&pool->lock);
|
|
}
|
|
count = ocs_array_get_count(pool->a);
|
|
if (pool->use_lock) {
|
|
ocs_unlock(&pool->lock);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* @brief Return item given an index.
|
|
*
|
|
* A pointer to a memory pool item is returned given an index.
|
|
*
|
|
* @param pool Pointer to memory pool.
|
|
* @param idx Index.
|
|
*
|
|
* @return Returns pointer to item, or NULL if index is invalid.
|
|
*/
|
|
void *
|
|
ocs_pool_get_instance(ocs_pool_t *pool, uint32_t idx)
|
|
{
|
|
pool_hdr_t *h = ocs_array_get(pool->a, idx);
|
|
|
|
if (h == NULL) {
|
|
return NULL;
|
|
}
|
|
return &h[1];
|
|
}
|
|
|
|
/**
|
|
* @brief Return count of free objects in a pool.
|
|
*
|
|
* The number of objects on a pool's free list.
|
|
*
|
|
* @param pool Pointer to memory pool.
|
|
*
|
|
* @return Returns count of objects on free list.
|
|
*/
|
|
uint32_t
|
|
ocs_pool_get_freelist_count(ocs_pool_t *pool)
|
|
{
|
|
uint32_t count = 0;
|
|
void *item;
|
|
|
|
if (pool->use_lock) {
|
|
ocs_lock(&pool->lock);
|
|
}
|
|
|
|
ocs_list_foreach(&pool->freelist, item) {
|
|
count++;
|
|
}
|
|
|
|
if (pool->use_lock) {
|
|
ocs_unlock(&pool->lock);
|
|
}
|
|
return count;
|
|
}
|