blob: 35dc9087d79f37daa2f071971a8b2d52448109cc [file] [log] [blame]
/*
* ---------------------------------------------------------------------------
* FILE: os.c
*
* PURPOSE:
* Routines to fulfil the OS-abstraction for the HIP lib.
* It is part of the porting exercise.
*
* Copyright (C) 2005-2009 by Cambridge Silicon Radio Ltd.
*
* Refer to LICENSE.txt included with this source code for details on
* the license terms.
*
* ---------------------------------------------------------------------------
*/
/**
* The HIP lib OS abstraction consists of the implementation
* of the functions in this file. It is part of the porting exercise.
*/
#include "unifi_priv.h"
/*
* ---------------------------------------------------------------------------
* unifi_net_data_malloc
*
* Allocate an OS specific net data buffer of "size" bytes.
* The bulk_data_slot.os_data_ptr must be initialised to point
* to the buffer allocated. The bulk_data_slot.length must be
* initialised to the requested size, zero otherwise.
* The bulk_data_slot.os_net_buf_ptr can be initialised to
* an OS specific pointer to be used in the unifi_net_data_free().
*
*
* Arguments:
* ospriv Pointer to device private context struct.
* bulk_data_slot Pointer to the bulk data structure to initialise.
* size Size of the buffer to be allocated.
*
* Returns:
* CSR_RESULT_SUCCESS on success, CSR_RESULT_FAILURE otherwise.
* ---------------------------------------------------------------------------
*/
CsrResult
unifi_net_data_malloc(void *ospriv, bulk_data_desc_t *bulk_data_slot, unsigned int size)
{
struct sk_buff *skb;
unifi_priv_t *priv = (unifi_priv_t*)ospriv;
int rounded_length;
if (priv->card_info.sdio_block_size == 0) {
unifi_error(priv, "unifi_net_data_malloc: Invalid SDIO block size\n");
return CSR_RESULT_FAILURE;
}
rounded_length = (size + priv->card_info.sdio_block_size - 1) & ~(priv->card_info.sdio_block_size - 1);
/*
* (ETH_HLEN + 2) bytes tailroom for header manipulation
* CSR_WIFI_ALIGN_BYTES bytes headroom for alignment manipulation
*/
skb = dev_alloc_skb(rounded_length + 2 + ETH_HLEN + CSR_WIFI_ALIGN_BYTES);
if (! skb) {
unifi_error(ospriv, "alloc_skb failed.\n");
bulk_data_slot->os_net_buf_ptr = NULL;
bulk_data_slot->net_buf_length = 0;
bulk_data_slot->os_data_ptr = NULL;
bulk_data_slot->data_length = 0;
return CSR_RESULT_FAILURE;
}
bulk_data_slot->os_net_buf_ptr = (const unsigned char*)skb;
bulk_data_slot->net_buf_length = rounded_length + 2 + ETH_HLEN + CSR_WIFI_ALIGN_BYTES;
bulk_data_slot->os_data_ptr = (const void*)skb->data;
bulk_data_slot->data_length = size;
return CSR_RESULT_SUCCESS;
} /* unifi_net_data_malloc() */
/*
* ---------------------------------------------------------------------------
* unifi_net_data_free
*
* Free an OS specific net data buffer.
* The bulk_data_slot.length must be initialised to 0.
*
*
* Arguments:
* ospriv Pointer to device private context struct.
* bulk_data_slot Pointer to the bulk data structure that
* holds the data to be freed.
*
* Returns:
* None.
* ---------------------------------------------------------------------------
*/
void
unifi_net_data_free(void *ospriv, bulk_data_desc_t *bulk_data_slot)
{
struct sk_buff *skb;
CSR_UNUSED(ospriv);
skb = (struct sk_buff *)bulk_data_slot->os_net_buf_ptr;
dev_kfree_skb(skb);
bulk_data_slot->net_buf_length = 0;
bulk_data_slot->data_length = 0;
bulk_data_slot->os_data_ptr = bulk_data_slot->os_net_buf_ptr = NULL;
} /* unifi_net_data_free() */
/*
* ---------------------------------------------------------------------------
* unifi_net_dma_align
*
* DMA align an OS specific net data buffer.
* The buffer must be empty.
*
*
* Arguments:
* ospriv Pointer to device private context struct.
* bulk_data_slot Pointer to the bulk data structure that
* holds the data to be aligned.
*
* Returns:
* None.
* ---------------------------------------------------------------------------
*/
CsrResult
unifi_net_dma_align(void *ospriv, bulk_data_desc_t *bulk_data_slot)
{
struct sk_buff *skb;
unsigned long buf_address;
int offset;
unifi_priv_t *priv = (unifi_priv_t*)ospriv;
if ((bulk_data_slot == NULL) || (CSR_WIFI_ALIGN_BYTES == 0)) {
return CSR_RESULT_SUCCESS;
}
if ((bulk_data_slot->os_data_ptr == NULL) || (bulk_data_slot->data_length == 0)) {
return CSR_RESULT_SUCCESS;
}
buf_address = (unsigned long)(bulk_data_slot->os_data_ptr) & (CSR_WIFI_ALIGN_BYTES - 1);
unifi_trace(priv, UDBG5,
"unifi_net_dma_align: Allign buffer (0x%p) by %d bytes\n",
bulk_data_slot->os_data_ptr, buf_address);
offset = CSR_WIFI_ALIGN_BYTES - buf_address;
if (offset < 0) {
unifi_error(priv, "unifi_net_dma_align: Failed (offset=%d)\n", offset);
return CSR_RESULT_FAILURE;
}
skb = (struct sk_buff*)(bulk_data_slot->os_net_buf_ptr);
skb_reserve(skb, offset);
bulk_data_slot->os_net_buf_ptr = (const unsigned char*)skb;
bulk_data_slot->os_data_ptr = (const void*)(skb->data);
return CSR_RESULT_SUCCESS;
} /* unifi_net_dma_align() */
#ifdef ANDROID_TIMESTAMP
static volatile unsigned int printk_cpu = UINT_MAX;
char tbuf[30];
char* print_time(void )
{
unsigned long long t;
unsigned long nanosec_rem;
t = cpu_clock(printk_cpu);
nanosec_rem = do_div(t, 1000000000);
sprintf(tbuf, "[%5lu.%06lu] ",
(unsigned long) t,
nanosec_rem / 1000);
return tbuf;
}
#endif
/* Module parameters */
extern int unifi_debug;
#ifdef UNIFI_DEBUG
#define DEBUG_BUFFER_SIZE 120
#define FORMAT_TRACE(_s, _len, _args, _fmt) \
do { \
va_start(_args, _fmt); \
_len += vsnprintf(&(_s)[_len], \
(DEBUG_BUFFER_SIZE - _len), \
_fmt, _args); \
va_end(_args); \
if (_len >= DEBUG_BUFFER_SIZE) { \
(_s)[DEBUG_BUFFER_SIZE - 2] = '\n'; \
(_s)[DEBUG_BUFFER_SIZE - 1] = 0; \
} \
} while (0)
void
unifi_error(void* ospriv, const char *fmt, ...)
{
unifi_priv_t *priv = (unifi_priv_t*) ospriv;
char s[DEBUG_BUFFER_SIZE];
va_list args;
unsigned int len;
#ifdef ANDROID_TIMESTAMP
if (priv != NULL) {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_ERR "%s unifi%d: ", print_time(), priv->instance);
} else {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_ERR "%s unifi: ", print_time());
}
#else
if (priv != NULL) {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_ERR "unifi%d: ", priv->instance);
} else {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_ERR "unifi: ");
}
#endif /* ANDROID_TIMESTAMP */
FORMAT_TRACE(s, len, args, fmt);
printk("%s", s);
}
void
unifi_warning(void* ospriv, const char *fmt, ...)
{
unifi_priv_t *priv = (unifi_priv_t*) ospriv;
char s[DEBUG_BUFFER_SIZE];
va_list args;
unsigned int len;
#ifdef ANDROID_TIMESTAMP
if (priv != NULL) {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_WARNING "%s unifi%d: ", print_time(), priv->instance);
} else {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_WARNING "%s unifi: ", print_time());
}
#else
if (priv != NULL) {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_WARNING "unifi%d: ", priv->instance);
} else {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_WARNING "unifi: ");
}
#endif /* ANDROID_TIMESTAMP */
FORMAT_TRACE(s, len, args, fmt);
printk("%s", s);
}
void
unifi_notice(void* ospriv, const char *fmt, ...)
{
unifi_priv_t *priv = (unifi_priv_t*) ospriv;
char s[DEBUG_BUFFER_SIZE];
va_list args;
unsigned int len;
#ifdef ANDROID_TIMESTAMP
if (priv != NULL) {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_NOTICE "%s unifi%d: ", print_time(), priv->instance);
} else {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_NOTICE "%s unifi: ", print_time());
}
#else
if (priv != NULL) {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_NOTICE "unifi%d: ", priv->instance);
} else {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_NOTICE "unifi: ");
}
#endif /* ANDROID_TIMESTAMP */
FORMAT_TRACE(s, len, args, fmt);
printk("%s", s);
}
void
unifi_info(void* ospriv, const char *fmt, ...)
{
unifi_priv_t *priv = (unifi_priv_t*) ospriv;
char s[DEBUG_BUFFER_SIZE];
va_list args;
unsigned int len;
#ifdef ANDROID_TIMESTAMP
if (priv != NULL) {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_INFO "%s unifi%d: ", print_time(), priv->instance);
} else {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_INFO "%s unifi: ", print_time());
}
#else
if (priv != NULL) {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_INFO "unifi%d: ", priv->instance);
} else {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_INFO "unifi: ");
}
#endif /* ANDROID_TIMESTAMP */
FORMAT_TRACE(s, len, args, fmt);
printk("%s", s);
}
/* debugging */
void
unifi_trace(void* ospriv, int level, const char *fmt, ...)
{
unifi_priv_t *priv = (unifi_priv_t*) ospriv;
char s[DEBUG_BUFFER_SIZE];
va_list args;
unsigned int len;
if (unifi_debug >= level) {
#ifdef ANDROID_TIMESTAMP
if (priv != NULL) {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_ERR "%s unifi%d: ", print_time(), priv->instance);
} else {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_ERR "%s unifi: ", print_time());
}
#else
if (priv != NULL) {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_ERR "unifi%d: ", priv->instance);
} else {
len = snprintf(s, DEBUG_BUFFER_SIZE, KERN_ERR "unifi: ");
}
#endif /* ANDROID_TIMESTAMP */
FORMAT_TRACE(s, len, args, fmt);
printk("%s", s);
}
}
#else
void
unifi_error_nop(void* ospriv, const char *fmt, ...)
{
}
void
unifi_trace_nop(void* ospriv, int level, const char *fmt, ...)
{
}
#endif /* UNIFI_DEBUG */
/*
* ---------------------------------------------------------------------------
*
* Debugging support.
*
* ---------------------------------------------------------------------------
*/
#ifdef UNIFI_DEBUG
/* Memory dump with level filter controlled by unifi_debug */
void
unifi_dump(void *ospriv, int level, const char *msg, void *mem, u16 len)
{
unifi_priv_t *priv = (unifi_priv_t*) ospriv;
if (unifi_debug >= level) {
#ifdef ANDROID_TIMESTAMP
if (priv != NULL) {
printk(KERN_ERR "%s unifi%d: --- dump: %s ---\n", print_time(), priv->instance, msg ? msg : "");
} else {
printk(KERN_ERR "%s unifi: --- dump: %s ---\n", print_time(), msg ? msg : "");
}
#else
if (priv != NULL) {
printk(KERN_ERR "unifi%d: --- dump: %s ---\n", priv->instance, msg ? msg : "");
} else {
printk(KERN_ERR "unifi: --- dump: %s ---\n", msg ? msg : "");
}
#endif /* ANDROID_TIMESTAMP */
dump(mem, len);
if (priv != NULL) {
printk(KERN_ERR "unifi%d: --- end of dump ---\n", priv->instance);
} else {
printk(KERN_ERR "unifi: --- end of dump ---\n");
}
}
}
/* Memory dump that appears all the time, use sparingly */
void
dump(void *mem, u16 len)
{
int i, col = 0;
unsigned char *pdata = (unsigned char *)mem;
#ifdef ANDROID_TIMESTAMP
printk("timestamp %s \n", print_time());
#endif /* ANDROID_TIMESTAMP */
if (mem == NULL) {
printk("(null dump)\n");
return;
}
for (i = 0; i < len; i++) {
if (col == 0) {
printk("0x%02X: ", i);
}
printk(" %02X", pdata[i]);
if (++col == 16) {
printk("\n");
col = 0;
}
}
if (col) {
printk("\n");
}
} /* dump() */
void
dump16(void *mem, u16 len)
{
int i, col=0;
unsigned short *p = (unsigned short *)mem;
#ifdef ANDROID_TIMESTAMP
printk("timestamp %s \n", print_time());
#endif /* ANDROID_TIMESTAMP */
for (i = 0; i < len; i+=2) {
if (col == 0) {
printk("0x%02X: ", i);
}
printk(" %04X", *p++);
if (++col == 8) {
printk("\n");
col = 0;
}
}
if (col) {
printk("\n");
}
}
#ifdef CSR_WIFI_HIP_DEBUG_OFFLINE
void
dump_str(void *mem, u16 len)
{
int i, col = 0;
unsigned char *pdata = (unsigned char *)mem;
#ifdef ANDROID_TIMESTAMP
printk("timestamp %s \n", print_time());
#endif /* ANDROID_TIMESTAMP */
for (i = 0; i < len; i++) {
printk("%c", pdata[i]);
}
if (col) {
printk("\n");
}
} /* dump_str() */
#endif /* CSR_ONLY_NOTES */
#endif /* UNIFI_DEBUG */
/* ---------------------------------------------------------------------------
* - End -
* ------------------------------------------------------------------------- */