text: /*
   * Copyright (c) 2018 The Linux Foundation. All rights reserved.
   *
   * Permission to use, copy, modify, and/or distribute this software for
   * any purpose with or without fee is hereby granted, provided that the
   * above copyright notice and this permission notice appear in all
   * copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
   * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
   * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
   * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
   * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
   * PERFORMANCE OF THIS SOFTWARE.
   */
  
  /**
   * DOC: wbuff.c
   * wbuff buffer management APIs
   */
  
  #include <wbuff.h>
  #include "i_wbuff.h"
  
  /**
   * Allocation holder array for all wbuff registered modules
   */
  struct wbuff_holder wbuff;
  
  /**
   * wbuff_get_pool_slot_from_len() - get pool_slot from length
   * @len: length of the buffer
   *
   * Return: pool slot
   */
  static uint8_t wbuff_get_pool_slot_from_len(uint16_t len)
  {
  	if ((len > 0) && (len <= WBUFF_LEN_POOL0))
  		return WBUFF_POOL_0;
  	else if ((len > WBUFF_LEN_POOL0) && (len <= WBUFF_LEN_POOL1))
  		return WBUFF_POOL_1;
  	else if ((len > WBUFF_LEN_POOL1) && (len <= WBUFF_LEN_POOL2))
  		return WBUFF_POOL_2;
  	else
  		return WBUFF_POOL_3;
  }
  
  /**
   * wbuff_get_len_from_pool_slot() - get len from pool slot
   * @pool_slot: wbuff pool_slot
   *
   * Return: nbuf length from pool slot
   */
  static uint32_t wbuff_get_len_from_pool_slot(uint16_t pool_slot)
  {
  	uint32_t len = 0;
  
  	switch (pool_slot) {
  	case 0:
  		len = WBUFF_LEN_POOL0;
  		break;
  	case 1:
  		len = WBUFF_LEN_POOL1;
  		break;
  	case 2:
  		len = WBUFF_LEN_POOL2;
  		break;
  	case 3:
  		len = WBUFF_LEN_POOL3;
  		break;
  	default:
  		len = 0;
  	}
  
  	return len;
  }
  
  /**
   * wbuff_get_free_mod_slot() - get free module slot
   *
   * Return: module slot
   */
  static uint8_t wbuff_get_free_mod_slot(void)
  {
  	uint8_t mslot = 0;
  
  	for (mslot = 0; mslot < WBUFF_MAX_MODULES; mslot++) {
  		qdf_spin_lock_bh(&wbuff.mod[mslot].lock);
  		if (!wbuff.mod[mslot].registered) {
  			wbuff.mod[mslot].registered = true;
  			qdf_spin_unlock_bh(&wbuff.mod[mslot].lock);
  			break;
  		}
  		qdf_spin_unlock_bh(&wbuff.mod[mslot].lock);
  	}
  
  	return mslot;
  }
  
  /**
   * wbuff_is_valid_alloc_req() - validate alloc  request
   * @req: allocation request from registered module
   * @num: number of pools required
   *
   * Return: true if valid wbuff_alloc_request
   *         false if invalid wbuff_alloc_request
   */
  static bool wbuff_is_valid_alloc_req(struct wbuff_alloc_request *req,
  				     uint8_t num)
  {
  	uint16_t psize = 0;
  	uint8_t alloc = 0, pslot = 0;
  
  	for (alloc = 0; alloc < num; alloc++) {
  		pslot = req[alloc].slot;
  		psize = req[alloc].size;
  		if ((pslot > WBUFF_MAX_POOLS - 1) ||
  		    (psize > wbuff_alloc_max[pslot]))
  			return false;
  	}
  
  	return true;
  }
  
  /**
   * wbuff_prepare_nbuf() - allocate nbuf
   * @mslot: module slot
   * @pslot: pool slot
   * @len: length of the buffer
   * @reserve: nbuf headroom to start with
   * @align: alignment for the nbuf
   *
   * Return: nbuf if success
   *         NULL if failure
   */
  static qdf_nbuf_t wbuff_prepare_nbuf(uint8_t mslot, uint8_t pslot,
  				     uint32_t len, int reserve, int align)
  {
  	qdf_nbuf_t buf;
  	unsigned long dev_scratch = 0;
  
  	buf = qdf_nbuf_alloc(NULL, roundup(len + reserve, align), reserve,
  			     align, false);
  	if (!buf)
  		return NULL;
  	dev_scratch = mslot;
  	dev_scratch <<= WBUFF_MSLOT_SHIFT;
  	dev_scratch |= ((pslot << WBUFF_PSLOT_SHIFT) | 1);
  	qdf_nbuf_set_dev_scratch(buf, dev_scratch);
  
  	return buf;
  }
  
  /**
   * wbuff_is_valid_handle() - validate wbuff handle
   * @handle: wbuff handle passed by module
   *
   * Return: true - valid wbuff_handle
   *         false - invalid wbuff_handle
   */
  static bool wbuff_is_valid_handle(struct wbuff_handle *handle)
  {
  	if ((handle) && (handle->id < WBUFF_MAX_MODULES) &&
  	    (wbuff.mod[handle->id].registered))
  		return true;
  
  	return false;
  }
  
  QDF_STATUS wbuff_module_init(void)
  {
  	struct wbuff_module *mod = NULL;
  	uint8_t mslot = 0, pslot = 0;
  
  	if (!qdf_nbuf_is_dev_scratch_supported()) {
  		wbuff.initialized = false;
  		return QDF_STATUS_E_NOSUPPORT;
  	}
  
  	for (mslot = 0; mslot < WBUFF_MAX_MODULES; mslot++) {
  		mod = &wbuff.mod[mslot];
  		qdf_spinlock_create(&mod->lock);
  		for (pslot = 0; pslot < WBUFF_MAX_POOLS; pslot++)
  			mod->pool[pslot] = NULL;
  		mod->registered = false;
  	}
  	wbuff.initialized = true;
  
  	return QDF_STATUS_SUCCESS;
  }
  
  QDF_STATUS wbuff_module_deinit(void)
  {
  	struct wbuff_module *mod = NULL;
  	uint8_t mslot = 0;
  
  	if (!wbuff.initialized)
  		return QDF_STATUS_E_INVAL;
  
  	wbuff.initialized = false;
  	for (mslot = 0; mslot < WBUFF_MAX_MODULES; mslot++) {
  		mod = &wbuff.mod[mslot];
  		if (mod->registered)
  			wbuff_module_deregister((struct wbuff_mod_handle *)
  						&mod->handle);
  		qdf_spinlock_destroy(&mod->lock);
  	}
  
  	return QDF_STATUS_SUCCESS;
  }
  
  struct wbuff_mod_handle *
  wbuff_module_register(struct wbuff_alloc_request *req, uint8_t num,
  		      int reserve, int align)
  {
  	struct wbuff_module *mod = NULL;
  	qdf_nbuf_t buf = NULL;
  	uint32_t len = 0;
  	uint16_t idx = 0, psize = 0;
  	uint8_t alloc = 0, mslot = 0, pslot = 0;
  
  	if (!wbuff.initialized)
  		return NULL;
  
  	if ((num == 0) || (num > WBUFF_MAX_POOLS))
  		return NULL;
  
  	if (!wbuff_is_valid_alloc_req(req, num))
  		return NULL;
  
  	mslot = wbuff_get_free_mod_slot();
  	if (mslot == WBUFF_MAX_MODULES)
  		return NULL;
  
  	mod = &wbuff.mod[mslot];
  
  	mod->handle.id = mslot;
  
  	for (alloc = 0; alloc < num; alloc++) {
  		pslot = req[alloc].slot;
  		psize = req[alloc].size;
  		len = wbuff_get_len_from_pool_slot(pslot);
  		/**
  		 * Allocate pool_cnt number of buffers for
  		 * the pool given by pslot
  		 */
  		for (idx = 0; idx < psize; idx++) {
  			buf = wbuff_prepare_nbuf(mslot, pslot, len, reserve,
  						 align);
  			if (!buf)
  				continue;
  			if (!mod->pool[pslot]) {
  				qdf_nbuf_set_next(buf, NULL);
  				mod->pool[pslot] = buf;
  			} else {
  				qdf_nbuf_set_next(buf, mod->pool[pslot]);
  				mod->pool[pslot] = buf;
  			}
  		}
  	}
  	mod->reserve = reserve;
  	mod->align = align;
  
  	return (struct wbuff_mod_handle *)&mod->handle;
  }
  
  QDF_STATUS wbuff_module_deregister(struct wbuff_mod_handle *hdl)
  {
  	struct wbuff_handle *handle;
  	struct wbuff_module *mod = NULL;
  	uint8_t mslot = 0, pslot = 0;
  	qdf_nbuf_t first = NULL, buf = NULL;
  
  	handle = (struct wbuff_handle *)hdl;
  
  	if ((!wbuff.initialized) || (!wbuff_is_valid_handle(handle)))
  		return QDF_STATUS_E_INVAL;
  
  	mslot = handle->id;
  	mod = &wbuff.mod[mslot];
  
  	qdf_spin_lock_bh(&mod->lock);
  	for (pslot = 0; pslot < WBUFF_MAX_POOLS; pslot++) {
  		first = mod->pool[pslot];
  		while (first) {
  			buf = first;
  			first = qdf_nbuf_next(buf);
  			qdf_nbuf_free(buf);
  		}
  	}
  	mod->registered = false;
  	qdf_spin_unlock_bh(&mod->lock);
  
  	return QDF_STATUS_SUCCESS;
  }
  
  qdf_nbuf_t wbuff_buff_get(struct wbuff_mod_handle *hdl, uint32_t len,
  			  uint8_t *file_name, uint32_t line_num)
  {
  	struct wbuff_handle *handle;
  	struct wbuff_module *mod = NULL;
  	uint8_t mslot = 0;
  	uint8_t pslot = 0;
  	qdf_nbuf_t buf = NULL;
  
  	handle = (struct wbuff_handle *)hdl;
  
  	if ((!wbuff.initialized) || (!wbuff_is_valid_handle(handle)) || !len ||
  	    (len > WBUFF_MAX_BUFFER_SIZE))
  		return NULL;
  
  	mslot = handle->id;
  	pslot = wbuff_get_pool_slot_from_len(len);
  	mod = &wbuff.mod[mslot];
  
  	qdf_spin_lock_bh(&mod->lock);
  	if (mod->pool[pslot]) {
  		buf = mod->pool[pslot];
  		mod->pool[pslot] = qdf_nbuf_next(buf);
  		mod->pending_returns++;
  	}
  	qdf_spin_unlock_bh(&mod->lock);
  	if (buf) {
  		qdf_nbuf_set_next(buf, NULL);
  		qdf_net_buf_debug_update_node(buf, file_name, line_num);
  	}
  
  	return buf;
  }
  
  qdf_nbuf_t wbuff_buff_put(qdf_nbuf_t buf)
  {
  	qdf_nbuf_t buffer = buf;
  	unsigned long slot_info = 0;
  	uint8_t mslot = 0, pslot = 0;
  
  	if (!wbuff.initialized)
  		return buffer;
  
  	slot_info = qdf_nbuf_get_dev_scratch(buf);
  	if (!slot_info)
  		return buffer;
  
  	mslot = (slot_info & WBUFF_MSLOT_BITMASK) >> WBUFF_MSLOT_SHIFT;
  	pslot = (slot_info & WBUFF_PSLOT_BITMASK) >> WBUFF_PSLOT_SHIFT;
  	qdf_nbuf_reset(buffer, wbuff.mod[mslot].reserve, wbuff.mod[mslot].
  		       align);
  	qdf_spin_lock_bh(&wbuff.mod[mslot].lock);
  	if (wbuff.mod[mslot].registered) {
  		qdf_nbuf_set_next(buffer, wbuff.mod[mslot].pool[pslot]);
  		wbuff.mod[mslot].pool[pslot] = buffer;
  		wbuff.mod[mslot].pending_returns--;
  		buffer = NULL;
  	}
  	qdf_spin_unlock_bh(&wbuff.mod[mslot].lock);
  
  	return buffer;
  }
  