blob: c4e0a79968f1c2d4c48e53c89c04595d74eca79f [file] [log] [blame]
/******************************************************************************
*
* Copyright (c) 2014 - 2017 Samsung Electronics Co., Ltd. All rights reserved
*
*****************************************************************************/
#ifndef __MBULK_H__
#define __MBULK_H__
/**
* mbulk(bulk memory) API
*
* This header file describes APIs of the bulk memory management.
* The diagram below is an example of a mbulk buffer with one
* segment (i.e. not a chained mbulk).
*
* sig_bufsz
* |<-------->|
* | |<--------- dat_bufsz ---------->|
* +--------------------------------------------------+
* | mbulk| signal | bulk buffer |
* +-------------------------+---------------+--------+
* | | | valid data | |
* | | |<--------+---->| |
* | | | mbulk_tlen(m) | |
* | |<----->| | |<------>|
* | mbulk_headroom(m)| | mbulk_tailroom(m)
* | | |
* | |-- off -->|
* v v |
* mbulk_get_sig(m) mbulk_dat(m) |
* v
* mbulk_dat_at(m,off)
*
* In general, all clients are supposed to use only mbulk_xxx() APIs (but not
* mbulk_seg_xxx() APIs), as they can handle S/G chained mbulk as well.
* But as of now, specially in Condor, S/G chained mbulk is not supported,
* which means the most of mbulk_xxx() would be wrappers of mbulk_seg_xxx().
*
* An in-lined signal buffer can be allocated along with a mbulk buffer.
* There is no direct life-cycle relationship between the signal and the
* associated mbulk in this case, which means that the signal buffer should be
* de-allocated independently of the mbulk buffer.
*
*/
/**
* bulk buffer descriptor
*/
struct mbulk;
/**
* mbulk pool ID
*/
#define MBULK_POOL_ID_MIN (0)
#define MBULK_POOL_ID_MAX_FW (4) /* 0 ~ 3 used for firmware pools */
#ifdef JAVA_VIRT_HOST
#define MBULK_POOL_ID_MIN_HOST (MBULK_POOL_ID_MAX_FW)
#define MBULK_POOL_ID_MAX_HOST (MBULK_POOL_ID_MIN_HOST + 2)
#define MBULK_POOL_ID_MAX (MBULK_POOL_ID_MAX_HOST)
#else
#define MBULK_POOL_ID_MAX (MBULK_POOL_ID_MAX_FW)
#endif
/* Max number of mbulk to be freed by the Host */
#define MBULK_MAX_HOST_TO_FREE 2
/**
* mbulk buffer classification
*
* Note that PACKED attribute is added to enum definition so that
* compiler assigns the smallest integral type (u8).
*/
enum mbulk_class {
MBULK_CLASS_FROM_HOST_DAT = 0,
MBULK_CLASS_FROM_HOST_CTL = 1,
MBULK_CLASS_FROM_RADIO = 2,
MBULK_CLASS_CONTRL = 3,
MBULK_CLASS_DEBUG = 4,
MBULK_CLASS_OTHERS = 5,
MBULK_CLASS_MAX
} __packed;
/**
* The private definition of mbulk structure is included here
* so that its members can be directly accessed, and the access
* codes can be in-lined by the compiler.
* But client codes are not supposed to directly refer to mbulk
* members, nor use mbulk_seg_xxx() functions. Only modules handling
* mbulk scatter/gather chain would directly use mulk_seg_xxx() APIs.
*/
#include "mbulk_def.h"
/**
* Get the bulk data reference counter
*
* After a bulk buffer with non-zero data buffer size is created,
* the reference counter is set to one. Each time it is duplicated,
* its reference counter would be increased.
*
* Note that the reference counter is initialized to zero if a signal
* is created from mbulk pool but with zero data buffer size, as there
* is no data buffer.
*/
static inline int mbulk_refcnt(const struct mbulk *m)
{
return MBULK_SEG_REFCNT(m);
}
/**
* Get the bulk data buffer size
*
*/
static inline int mbulk_buffer_size(const struct mbulk *m)
{
return MBULK_SEG_DAT_BUFSIZE(m);
}
/**
* Check if mbulk has an in-lined signal buffer
*
*/
static inline bool mbulk_has_signal(const struct mbulk *m)
{
return MBULK_SEG_HAS_SIGNAL(m);
}
/**
* Set mbulk to be read-only
*/
static inline void mbulk_set_readonly(struct mbulk *m)
{
MBULK_SEG_SET_READONLY(m);
}
/**
* is mbulk read-only
*/
static inline bool mbulk_is_readonly(const struct mbulk *m)
{
return MBULK_SEG_IS_READONLY(m);
}
/**
* check if mbulk is a scatter/gather chained buffer
*
*/
static inline bool mbulk_is_sg(const struct mbulk *m)
{
return MBULK_SEG_IS_CHAIN_HEAD(m);
}
/**
* check if mbulk is a part of scatter/gather chained buffer
*
*/
static inline bool mbulk_is_chained(const struct mbulk *m)
{
return MBULK_SEG_IS_CHAINED(m);
}
/**
* Allocate a bulk buffer with an in-lined signal buffer
*
* Only one mbulk segment is used for allocation starting from the
* mbulk pool with the smallest segment size. If no segment fitting
* the requested size, then return NULL without trying to create
* a chained buffer.
*
*/
struct mbulk *mbulk_with_signal_alloc(enum mbulk_class clas, size_t sig_bufsz,
size_t dat_bufsz);
/**
* Allocate a bulk buffer with an in-lined signal buffer
*
* A mbulk segment is allocated from the given the pool, if its size
* meeting the requested size.
*
*/
struct mbulk *mbulk_with_signal_alloc_by_pool(u8 pool_id, u16 colour,
enum mbulk_class clas, size_t sig_bufsz, size_t dat_bufsz);
/**
* Get the number of free mbulk slots in a pool
*
* Returns the number of mbulk slots available in a given pool.
*/
int mbulk_pool_get_free_count(u8 pool_id);
/**
* Get a signal buffer address
*
* Given a mbulk buffer, returns a signal buffer address.
*
* @param m mbulk
* @return in-lined signal buffer
*/
static inline void *mbulk_get_seg(const struct mbulk *m)
{
return (void *)MBULK_SEG_B(m);
}
/**
* Get a signal buffer address
*
* Given a mbulk buffer, returns a signal buffer address if any in-lined
* signal buffer.
*
*/
static inline void *mbulk_get_signal(const struct mbulk *m)
{
bool ret = false;
ret = mbulk_has_signal(m);
return ret ? mbulk_get_seg(m) : NULL;
}
/**
* Allocate a bulk buffer
*
* Only one mbulk segment is used for allocation starting from the
* mbulk pool with the smallest segment size. If no segment fitting
* the requested size, then return NULL without trying to create
* a chained buffer.
*
*/
static inline struct mbulk *mbulk_alloc(enum mbulk_class clas, size_t dat_bufsz)
{
return mbulk_with_signal_alloc(clas, 0, dat_bufsz);
}
/**
* free mbulk buffer
*
* After checking the bulk reference counter, this function return the buffer
* to the mbulk pool if it is zero. Note that this doesn't free the in-lined
* signal buffer.
*/
static inline void mbulk_free(struct mbulk *m)
{
mbulk_seg_free(m);
}
/**
* get bulk buffer address for read or write access
*
* The address is the buffer address after the headroom in the mbulk segment.
* Note that this function can only be used to access the data in the same
* segment, including a segment in the mbulk chain (for example, to access
* the 802.11 header of A-MSDU).
*
*/
static inline void *mbulk_dat_rw(const struct mbulk *m)
{
WARN_ON(MBULK_SEG_IS_READONLY(m));
return MBULK_SEG_DAT(m);
}
/**
* get bulk buffer address for read-only
*
* The address is the buffer address after the headroom in the mbulk segment.
* Note that this function can only be used to access the data in the same
* segment, including a segment in the mbulk chain (for example, to access
* the 802.11 header of A-MSDU).
*
*/
static inline const void *mbulk_dat_r(const struct mbulk *m)
{
return (const void *)MBULK_SEG_DAT(m);
}
/**
* get bulk buffer address at the offset for read or write access
*
*/
static inline void *mbulk_dat_at_rw(const struct mbulk *m, size_t off)
{
WARN_ON(MBULK_SEG_IS_READONLY(m));
return MBULK_SEG_DAT_AT(m, off);
}
/**
* get bulk buffer address at the offset for read access
*
*/
static inline /*const*/ void *mbulk_dat_at_r(const struct mbulk *m, size_t off)
{
return (/*const */ void *)MBULK_SEG_DAT_AT(m, off);
}
/**
* get valid data length
*
*/
static inline size_t mbulk_tlen(const struct mbulk *m)
{
return MBULK_SEG_LEN(m);
}
/**
* get headroom
*
*/
static inline size_t mbulk_headroom(const struct mbulk *m)
{
return MBULK_SEG_HEADROOM(m);
}
static inline size_t mbulk_tailroom(const struct mbulk *m)
{
return MBULK_SEG_TAILROOM(m);
}
/**
* reserve headroom
*
* Note this API should be called right after mbulk is created or the valid
* data length is zero.
*
*/
static inline bool mbulk_reserve_head(struct mbulk *m, size_t headroom)
{
return mbulk_seg_reserve_head(m, headroom);
}
/**
* adjust the valid data range
*
* headroom would be placed after the signal buffer (or mbuf descriptor if
* no in-lined signal), and the valid data length is set to \len.
*
*/
static inline bool mbulk_adjust_range(struct mbulk *m, size_t headroom, size_t len)
{
return mbulk_seg_adjust_range(m, headroom, len);
}
/**
* extend the data range at the head
*
* The headroom would be reduced, and the data range is extended.
* To prepend data in the head, the headroom should have been reserved before.
*
*/
static inline bool mbulk_prepend_head(struct mbulk *m, size_t more)
{
return mbulk_seg_prepend_head(m, more);
}
/**
* extend the data at the tail
*
* Data range is expanded towards the tail.
*
*/
static inline bool mbulk_append_tail(struct mbulk *m, size_t more)
{
return mbulk_seg_append_tail(m, more);
}
/**
* trim data at the head
*
* The headroom would be increased, and the valid data range is reduced
* accordingly.
*
*/
static inline bool mbulk_trim_head(struct mbulk *m, size_t less)
{
return mbulk_seg_trim_head(m, less);
}
/**
* trim data at the tail
*
* The data length would be reduced.
*
*/
static inline bool mbulk_trim_tail(struct mbulk *m, size_t less)
{
return mbulk_seg_trim_tail(m, less);
}
/**
* duplicate a mbulk
*
* There is no data copy. but the referece counter of the orignal mbulk is
* increased by one.
*
*/
static inline struct mbulk *mbulk_duplicate(struct mbulk *m)
{
return mbulk_seg_duplicate(m);
}
/**
* clone a mbulk
*
* New mbulk buffer is created, and contents are copied. The signal is copied
* only when \copy_sig is TRUE.
*
*/
static inline struct mbulk *mbulk_clone(const struct mbulk *m, enum mbulk_class clas,
bool copy_sig)
{
return mbulk_seg_clone(m, clas, copy_sig);
}
/**
* allocate a signal buffer from mbulk pool
*
*/
void *msignal_alloc(size_t sig_sz);
/**
* free a signal buffer created from mbulk pool
*
*/
void msignal_free(void *sig);
/**
* get mbulk descriptor given a signal buffer address
*
*/
struct mbulk *msignal_to_mbulk(void *sig);
/**
* get next chained mbulk in a scatter/gathered list
*/
static inline scsc_mifram_ref mbulk_chain_next(struct mbulk *m)
{
return MBULK_SEG_CHAIN_NEXT(m);
}
#ifdef MBULK_SUPPORT_SG_CHAIN
/**
* Scatter/Gather Chained Mbulk APIs
* =================================
*/
/**
* allocate a chained mbulk buffer from a specific mbulk pool
*
*/
struct mbulk *mbulk_chain_with_signal_alloc_by_pool(u8 pool_id,
enum mbulk_class clas, size_t sig_bufsz, size_t dat_bufsz);
/**
* free a chained mbulk
*/
void mbulk_chain_free(struct mbulk *sg);
/**
* get a tail mbulk in the chain
*
*/
struct mbulk *mbulk_chain_tail(struct mbulk *m);
/**
* total buffer size in a chanied mbulk
*
*/
size_t mbulk_chain_bufsz(struct mbulk *m);
/**
* total data length in a chanied mbulk
*
*/
size_t mbulk_chain_tlen(struct mbulk *m);
/**
* get a number of mbulk segments in a chained mbulk
*/
static inline int mbulk_chain_num(const struct mbulk *m)
{
if (mbulk_is_sg(m)) {
int n = 0;
while (m != NULL) {
n++;
m = m->chain_next;
}
return n;
}
return 1;
}
/* NOT IMPLEMENTED YET. */
void *mbulk_chain_access(struct mbulk *m, size_t off, char *local_buf, size_t local_bufsz);
void *mbulk_chain_writeback(struct mbulk *m, size_t off, char *local_buf, size_t local_bufsz);
void *mbulk_chain_copy_from(struct mbulk *m, size_t off, char *buf, int len);
void *mbulk_chain_copy_to(struct mbulk *m, size_t off, char *buf, int len);
#endif /*MBULK_SUPPORT_SG_CHAIN*/
/**
* init mbulk library
*/
/*extern void init_mbulk(void);*/
void init_mbulk(void *mem, size_t pool_size);
/**
* add a memory zone to a mbulk pool list
*
*/
#ifdef CONFIG_SCSC_WLAN_DEBUG
int mbulk_pool_add(u8 pool_id, char *base, char *end, size_t seg_size, u8 guard, int minor);
#else
int mbulk_pool_add(u8 pool_id, char *base, char *end, size_t buf_size, u8 guard);
#endif
/**
* check sanity of a mbulk pool
*/
void mbulk_pool_check_sanity(u8 pool_id);
/**
* configure the handler which returning the buffer to the host
*/
void mbulk_set_handler_return_host_mbulk(void (*free_host_buf)(struct mbulk *m));
/**
* free a mbulk in the virtual host
*/
void mbulk_free_virt_host(struct mbulk *m);
void mbulk_pool_dump(u8 pool_id, int max_cnt);
#endif /*__MBULK_H__*/