| /* |
| * --------------------------------------------------------------------------- |
| * 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; |
| 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]); |
| } |
| printk("\n"); |
| |
| } /* dump_str() */ |
| #endif /* CSR_ONLY_NOTES */ |
| |
| |
| #endif /* UNIFI_DEBUG */ |
| |
| |
| /* --------------------------------------------------------------------------- |
| * - End - |
| * ------------------------------------------------------------------------- */ |