diff options
Diffstat (limited to 'drivers/staging/csr/os.c')
-rw-r--r-- | drivers/staging/csr/os.c | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/drivers/staging/csr/os.c b/drivers/staging/csr/os.c new file mode 100644 index 00000000000..35dc9087d79 --- /dev/null +++ b/drivers/staging/csr/os.c @@ -0,0 +1,483 @@ +/* + * --------------------------------------------------------------------------- + * 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 - + * ------------------------------------------------------------------------- */ |