| /* | |
| * --------------------------------------------------------------------------- | |
| * 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 - | |
| * ------------------------------------------------------------------------- */ |