summaryrefslogtreecommitdiffstats
path: root/drivers/staging/csr/os.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/csr/os.c')
-rw-r--r--drivers/staging/csr/os.c483
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 000000000000..35dc9087d79f
--- /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 -
+ * ------------------------------------------------------------------------- */