diff options
Diffstat (limited to 'drivers/staging')
30 files changed, 1451 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 38f1fb08eaef..d3934d79524a 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -132,4 +132,6 @@ source "drivers/staging/ipack/Kconfig" source "drivers/staging/gdm72xx/Kconfig" +source "drivers/staging/csr/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index e0c4c2444fed..5b2219ac5207 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_RAMSTER) += ramster/ obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/ obj-$(CONFIG_USB_G_CCG) += ccg/ obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/ +obj-$(CONFIG_CSR_WIFI) += csr/ diff --git a/drivers/staging/csr/Kconfig b/drivers/staging/csr/Kconfig new file mode 100644 index 000000000000..5d7b800058a3 --- /dev/null +++ b/drivers/staging/csr/Kconfig @@ -0,0 +1,7 @@ +config CSR_WIFI + tristate "CSR wireless driver" + depends on PCI + help + Driver for the CSR wireless SDIO device. + + If unsure, select N. diff --git a/drivers/staging/csr/Makefile b/drivers/staging/csr/Makefile new file mode 100644 index 000000000000..b9ba3b378e65 --- /dev/null +++ b/drivers/staging/csr/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_CSR_WIFI) += oska/ diff --git a/drivers/staging/csr/oska/Makefile b/drivers/staging/csr/oska/Makefile new file mode 100644 index 000000000000..3a0b648ddebc --- /dev/null +++ b/drivers/staging/csr/oska/Makefile @@ -0,0 +1,12 @@ +obj-$(CONFIG_CSR_WIFI) := csr_oska.o + +csr_oska-y := \ + list.o \ + refcount.o \ + compat.o \ + event.o \ + oska_module.o \ + print.o \ + thread.o \ + timer.o + diff --git a/drivers/staging/csr/oska/all.h b/drivers/staging/csr/oska/all.h new file mode 100644 index 000000000000..5fe85834894b --- /dev/null +++ b/drivers/staging/csr/oska/all.h @@ -0,0 +1,61 @@ +/* + * Operating system kernel abstraction -- all functions + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_ALL_H +#define __OSKA_ALL_H + +/** + * @mainpage Operating System Kernel Abstraction + * + * @section intro Introduction + * + * The Operating System Kernel Abstraction (oska) is a software + * package providing an abstraction for various operating system + * kernel facilities for use by device drivers and other OS kernel + * software (e.g., SDIO stacks). Oska is modularized and intended to + * be a lightweight wrapper around an OSes interfaces. + * + * @section modules Modules + * + * Oska is organized into the modules, each of which has it's own + * header file providing the interface. + * + * - \ref alloc "Memory allocation" <oska/alloc.h> + * - \ref event "Events" <oska/event.h> + * - \ref mutex "Mutexes" <oska/mutex.h> + * - \ref print "Console output" <oska/print.h> + * - \ref spinlock "Spinlocks" <oska/spinlock.h> + * - \ref thread "Threading" <oska/thread.h> + * - \ref time "Timing and delays" <oska/time.h> + * - \ref timer "Timers" <oska/timer.h> + * - \ref types "Standard Types" <oska/types.h> + * - \ref util "Miscellaneous utilities" <oska/util.h> + * + * An <oska/all.h> header is provided which includes all the above + * modules. + * + * There are additional modules that are not included in <oska/all.h>. + * + * - \ref io "Memory mapped I/O" <oska/io.h> + * - \ref refcount "Reference Counting" <oska/refcount.h> + * - \ref list "Linked lists" <oska/list.h> + * - \ref trace "Tracing messages" <oska/trace.h> + */ + +#include "alloc.h" +#include "event.h" +#include "mutex.h" +#include "print.h" +#include "spinlock.h" +#include "thread.h" +#include "time.h" +#include "timer.h" +#include "types.h" +#include "util.h" + +#endif /* __OSKA_ALL_H */ diff --git a/drivers/staging/csr/oska/alloc.h b/drivers/staging/csr/oska/alloc.h new file mode 100644 index 000000000000..0f106016e1f7 --- /dev/null +++ b/drivers/staging/csr/oska/alloc.h @@ -0,0 +1,41 @@ +/* + * OSKA Linux implementation -- memory allocation + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_ALLOC_H +#define __OSKA_LINUX_ALLOC_H + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> + +static inline void *os_alloc(size_t size) +{ + return kzalloc(size, GFP_ATOMIC); +} + +static inline void *os_alloc_nonzeroed(size_t size) +{ + return kmalloc(size, GFP_KERNEL); +} + +static inline void os_free(void *ptr) +{ + kfree(ptr); +} + +static inline void *os_alloc_big(size_t size) +{ + return vmalloc(size); +} + +static inline void os_free_big(void *ptr) +{ + vfree(ptr); +} + +#endif /* #ifndef __OSKA_LINUX_ALLOC_H */ diff --git a/drivers/staging/csr/oska/compat.c b/drivers/staging/csr/oska/compat.c new file mode 100644 index 000000000000..790b97a7d5f9 --- /dev/null +++ b/drivers/staging/csr/oska/compat.c @@ -0,0 +1,54 @@ +/* + * Linux version compatibility functions. + * + * Copyright (C) 2008 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#include "kernel-compat.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) + +int dev_set_name(struct device *dev, const char *fmt, ...) +{ + va_list vargs; + + va_start(vargs, fmt); + vsnprintf(dev->bus_id, sizeof(dev->bus_id), fmt, vargs); + va_end(vargs); + return 0; +} +EXPORT_SYMBOL_GPL(dev_set_name); + +#endif /* Linux kernel < 2.6.26 */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + +struct device *class_find_device(struct class *class, struct device *start, + void *data, int (*match)(struct device *, void *)) +{ + struct device *dev; + + list_for_each_entry(dev, &class->devices, node) { + if (match(dev, data)) { + get_device(dev); + return dev; + } + } + return NULL; +} +EXPORT_SYMBOL_GPL(class_find_device); + +#endif /* Linux kernel < 2.6.25 */ diff --git a/drivers/staging/csr/oska/event.c b/drivers/staging/csr/oska/event.c new file mode 100644 index 000000000000..4aedaaa0d9e4 --- /dev/null +++ b/drivers/staging/csr/oska/event.c @@ -0,0 +1,82 @@ +/* + * Linux event functions. + * + * Copyright (C) 2009 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#include <linux/module.h> +#include <linux/sched.h> + +#include "event.h" + +void os_event_init(os_event_t *evt) +{ + init_waitqueue_head(&evt->wq); + spin_lock_init(&evt->lock); + evt->events = 0; +} +EXPORT_SYMBOL(os_event_init); + +uint16_t os_event_wait(os_event_t *evt) +{ + uint16_t e; + unsigned long flags; + + wait_event(evt->wq, evt->events != 0); + + spin_lock_irqsave(&evt->lock, flags); + e = evt->events; + evt->events &= ~e; + spin_unlock_irqrestore(&evt->lock, flags); + + return e; +} +EXPORT_SYMBOL(os_event_wait); + +uint16_t os_event_wait_interruptible(os_event_t *evt) +{ + uint16_t e; + unsigned long flags; + + wait_event_interruptible(evt->wq, evt->events != 0); + + spin_lock_irqsave(&evt->lock, flags); + e = evt->events; + evt->events &= ~e; + spin_unlock_irqrestore(&evt->lock, flags); + + return e; +} +EXPORT_SYMBOL(os_event_wait_interruptible); + +uint16_t os_event_wait_timed(os_event_t *evt, unsigned timeout_ms) +{ + uint16_t e; + unsigned long flags; + + wait_event_interruptible_timeout(evt->wq, + evt->events != 0, + msecs_to_jiffies(timeout_ms)); + + spin_lock_irqsave(&evt->lock, flags); + e = evt->events; + evt->events &= ~e; + spin_unlock_irqrestore(&evt->lock, flags); + + return e; +} +EXPORT_SYMBOL(os_event_wait_timed); + +void os_event_raise(os_event_t *evt, uint16_t events) +{ + unsigned long flags; + + spin_lock_irqsave(&evt->lock, flags); + evt->events |= events; + spin_unlock_irqrestore(&evt->lock, flags); + + wake_up(&evt->wq); +} +EXPORT_SYMBOL(os_event_raise); diff --git a/drivers/staging/csr/oska/event.h b/drivers/staging/csr/oska/event.h new file mode 100644 index 000000000000..be52e42c37dc --- /dev/null +++ b/drivers/staging/csr/oska/event.h @@ -0,0 +1,33 @@ +/* + * OSKA Linux implementation -- events + * + * Copyright (C) 2009 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_EVENT_H +#define __OSKA_LINUX_EVENT_H + +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/spinlock.h> + +typedef struct { + wait_queue_head_t wq; + spinlock_t lock; + uint16_t events; +} os_event_t; + +void os_event_init(os_event_t *evt); + +static inline void os_event_destroy(os_event_t *evt) +{ +} + +uint16_t os_event_wait(os_event_t *evt); +uint16_t os_event_wait_interruptible(os_event_t *evt); +uint16_t os_event_wait_timed(os_event_t *evt, unsigned timeout_ms); +void os_event_raise(os_event_t *evt, uint16_t events); + +#endif /* #ifndef __OSKA_LINUX_EVENT_H */ diff --git a/drivers/staging/csr/oska/io.h b/drivers/staging/csr/oska/io.h new file mode 100644 index 000000000000..c6c406c364b9 --- /dev/null +++ b/drivers/staging/csr/oska/io.h @@ -0,0 +1,63 @@ +/* + * OSKA Linux implementation -- memory mapped I/O. + * + * Copyright (C) 2009 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_IO_H +#define __OSKA_LINUX_IO_H + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/kernel-compat.h> + +typedef void __iomem *os_io_mem_t; + +static inline uint8_t os_io_read8(os_io_mem_t base, unsigned offset) +{ + return readb(base + offset); +} + +static inline uint16_t os_io_read16(os_io_mem_t base, unsigned offset) +{ + return readw(base + offset); +} + +static inline uint32_t os_io_read32(os_io_mem_t base, unsigned offset) +{ + return readl(base + offset); +} + +static inline uint64_t os_io_read64(os_io_mem_t base, unsigned offset) +{ + return readq(base + offset); +} + +static inline void os_io_write8(os_io_mem_t base, unsigned offset, uint8_t val) +{ + writeb(val, base + offset); +} + +static inline void os_io_write16(os_io_mem_t base, unsigned offset, uint16_t val) +{ + writew(val, base + offset); +} + +static inline void os_io_write32(os_io_mem_t base, unsigned offset, uint32_t val) +{ + writel(val, base + offset); +} + +static inline void os_io_write64(os_io_mem_t base, unsigned offset, uint64_t val) +{ + writeq(val, base + offset); +} + +static inline void os_io_memory_barrier(void) +{ + mb(); +} + +#endif /* #ifndef __OSKA_LINUX_IO_H */ diff --git a/drivers/staging/csr/oska/kernel-compat.h b/drivers/staging/csr/oska/kernel-compat.h new file mode 100644 index 000000000000..b6d27d39c33b --- /dev/null +++ b/drivers/staging/csr/oska/kernel-compat.h @@ -0,0 +1,199 @@ +/* + * Kernel version compatibility. + * + * Copyright (C) 2007-2008 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + * + * Wherever possible compatible implementations of newer APIs are + * provided for older kernel versions. + */ +#ifndef __LINUX_KERNEL_COMPAT_H +#define __LINUX_KERNEL_COMPAT_H + +#include <linux/version.h> +#include <linux/device.h> +#include <linux/workqueue.h> + +#include <asm/io.h> + +/* + * linux/semaphore.h replaces asm/semaphore.h in 2.6.27. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) +# include <asm/semaphore.h> +#else +# include <linux/semaphore.h> +#endif + +/* + * Workqueue API changes in 2.6.20 + * + * See http://lwn.net/Articles/211279/ for details. + * + * We deliberately don't provide the non-automatic release (NAR) + * variants as a simple compatible implementation is not possible. + * This shouldn't be a problem as all usage so far is to embed the + * struct work_struct into another struct and the NAR variants aren't + * useful in this case (see http://lwn.net/Articles/213149/). + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + +#include <linux/workqueue.h> + +#undef INIT_WORK +#define INIT_WORK(_work, _func) \ + do { \ + INIT_LIST_HEAD(&(_work)->entry); \ + (_work)->pending = 0; \ + PREPARE_WORK((_work), (_func), (_work)); \ + init_timer(&(_work)->timer); \ + } while(0) + +#undef DECLARE_WORK +#define DECLARE_WORK(n, f) \ + struct work_struct n = __WORK_INITIALIZER((n), (f), &(n)) + +struct delayed_work { + struct work_struct work; +}; + +#define INIT_DELAYED_WORK(dw, fn) \ + INIT_WORK(&(dw)->work, (fn)) + +#define queue_delayed_work(wq, dw, delay) \ + queue_delayed_work((wq), &(dw)->work, (delay)) + +#define schedule_delayed_work(dw, delay) \ + schedule_delayed_work(&(dw)->work, (delay)) + +#define cancel_delayed_work(dw) \ + cancel_delayed_work(&(dw)->work) + +#endif /* Linux kernel < 2.6.20 */ + +/* + * device_create()/class_device_create() + * + * device_create() gains a drvdata parameter in 2.6.27. Since all + * users of device_create() in CSR code don't use drvdata just ignore + * it. + * + * device_create() replaces class_device_create() in 2.6.21. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) + +#define device_create(class, parent, devt, drvdata, fmt, args...) \ + class_device_create((class), (parent), (devt), NULL, (fmt), ## args) +#define device_destroy(class, devt) \ + class_device_destroy(class, devt) + +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + +#define device_create(class, parent, devt, drvdata, fmt, args...) \ + device_create((class), (parent), (devt), (fmt), ## args) + +#endif /* Linux kernel < 2.6.26 */ + +/* + * dev_name() and dev_set_name() added in 2.6.26. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) + +static inline char *dev_name(struct device *dev) +{ + return dev->bus_id; +} + +int dev_set_name(struct device *dev, const char *fmt, ...); + +#endif /* Linux kernel < 2.6.26 */ + +/* + * class_find_device() in 2.6.25 + */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + +struct device *class_find_device(struct class *class, struct device *start, + void *data, int (*match)(struct device *, void *)); + +#endif /* Linux kernel < 2.6.25 */ + +/* + * list_first_entry in 2.6.22. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +#endif /* Linux kernel < 2.6.22 */ + +/* + * 2.6.19 adds a bool type. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) + +typedef _Bool bool; +enum { + false = 0, + true = 1 +}; + +#endif /* Linux kernel < 2.6.19 */ + +/* + * Provide readq() and writeq() if unavailable. + */ +#ifndef readq +static inline __u64 readq(const volatile void __iomem *addr) +{ + const volatile u32 __iomem *p = addr; + u32 low, high; + + low = readl(p); + high = readl(p + 1); + + return low + ((u64)high << 32); +} +#endif + +#ifndef writeq +static inline void writeq(__u64 val, volatile void __iomem *addr) +{ + writel(val, addr); + writel(val >> 32, addr+4); +} +#endif + +/* + * get_unaligned_le16() and friends added in 2.6.26. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) +#include <asm/unaligned.h> + +static inline __u16 get_unaligned_le16(const void *p) +{ + return le16_to_cpu(get_unaligned((__le16 *)p)); +} + +static inline void put_unaligned_le16(__u16 val, const void *p) +{ + put_unaligned(cpu_to_le16(val), (__le16 *)p); +} +#endif /* Linux kernel < 2.6.26 */ + +/* + * Various device or vendor IDs may not exist. + */ +#ifndef PCI_VENDOR_ID_CSR +# define PCI_VENDOR_ID_CSR 0x18e5 +#endif + +#ifndef PCI_DEVICE_ID_JMICRON_JMB38X_SD +# define PCI_DEVICE_ID_JMICRON_JMB38X_SD 0x2381 +#endif + +#endif /* #ifndef __LINUX_KERNEL_COMPAT_H */ diff --git a/drivers/staging/csr/oska/list.c b/drivers/staging/csr/oska/list.c new file mode 100644 index 000000000000..b5e884e1f201 --- /dev/null +++ b/drivers/staging/csr/oska/list.c @@ -0,0 +1,103 @@ +/* + * Operating system kernel abstraction -- linked lists. + * + * Copyright (C) 2009-2010 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ + +#include <stddef.h> + +#include "list.h" +#include "util.h" + +/** + * Initialize an empty list. + * + * @ingroup list + */ +void os_list_init(struct os_list *list) +{ + list->head.next = list->head.prev = &list->head; +} + +/** + * Is the list empty? + * + * @return true iff the list contains no nodes. + * + * @ingroup list + */ +int os_list_empty(struct os_list *list) +{ + return list->head.next == &list->head; +} + +static void os_list_add(struct os_list_node *prev, struct os_list_node *new, + struct os_list_node *next) +{ + OS_ASSERT(new->next == NULL && new->prev == NULL); + + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * Add a node to the tail of the list. + * + * @param list the list. + * @param node the list node to add. + * + * @ingroup list + */ +void os_list_add_tail(struct os_list *list, struct os_list_node *node) +{ + os_list_add(list->head.prev, node, &list->head); +} + +/** + * Remove a node from a list. + * + * @param node the node to remove. + * + * @ingroup list + */ +void os_list_del(struct os_list_node *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; + + node->prev = node->next = NULL; +} + +/** + * The node at the head of the list. + * + * @param list the list. + * + * @return the node at the head of the list; or os_list_end() if the + * list is empty. + * + * @ingroup list + */ +struct os_list_node *os_list_head(struct os_list *list) +{ + return list->head.next; +} + +/** + * The node marking the end of a list. + * + * @param list the list. + * + * @return the node that marks the end of the list. + * + * @ingroup list + */ +struct os_list_node *os_list_end(struct os_list *list) +{ + return &list->head; +} diff --git a/drivers/staging/csr/oska/list.h b/drivers/staging/csr/oska/list.h new file mode 100644 index 000000000000..a69b3b7d96b4 --- /dev/null +++ b/drivers/staging/csr/oska/list.h @@ -0,0 +1,115 @@ +/* + * Operating system kernel abstraction -- linked lists. + * + * Copyright (C) 2009-2010 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LIST_H +#define __OSKA_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup list Linked Lists + * + * Generic linked list implementations suitable for all platforms. + * + * - Circular, doubly-linked list (struct os_list). + */ + +/** + * A list node. + * + * This list node structure should be the first field within any + * structure that is to be stored in a list. + * + * @see struct os_list + * @ingroup list + */ +struct os_list_node { + /** + * The pointer to the previous node in the list, or os_list_end() + * if the end of the list has been reached. + */ + struct os_list_node *prev; + /** + * The pointer to the next node in the list, or os_list_end() if + * the end of the list has been reached. + */ + struct os_list_node *next; +}; + +/** + * A circular, doubly-linked list of nodes. + * + * Structures to be stored in a list should contains a struct + * os_list_node as the \e first field. + * \code + * struct foo { + * struct os_list_node node; + * int bar; + * ... + * }; + * \endcode + * Going to/from a struct foo to a list node is then simple. + * \code + * struct os_list_node *node; + * struct foo *foo; + * [...] + * node = &foo->node; + * foo = (struct foo *)node + * \endcode + * Lists must be initialized with os_list_init() before adding nodes + * with os_list_add_tail(). The node at the head of the list is + * obtained with os_list_head(). Nodes are removed from the list with + * os_list_del(). + * + * A list can be interated from the head to the tail using: + * \code + * struct os_list_node *node; + * for (node = os_list_head(list); node != os_list_end(list); node = node->next) { + * struct foo *foo = (struct foo *)node; + * ... + * } + * \endcode + * + * In the above loop, the current list node cannot be removed (with + * os_list_del()). If this is required use this form of loop: + * \code + * struct os_list_node *node, *next; + * for (node = os_list_head(list), next = node->next; + * node != os_list_end(list); + * node = next, next = node->next) { + * struct foo *foo = (struct foo *)node; + * ... + * os_list_del(node); + * ... + * } + * \endcode + * + * @ingroup list + */ +struct os_list { + /** + * @internal + * The dummy node marking the end of the list. + */ + struct os_list_node head; +}; + +void os_list_init(struct os_list *list); +int os_list_empty(struct os_list *list); +void os_list_add_tail(struct os_list *list, struct os_list_node *node); +void os_list_del(struct os_list_node *node); +struct os_list_node *os_list_head(struct os_list *list); +struct os_list_node *os_list_end(struct os_list *list); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* #ifndef __OSKA_LIST_H */ diff --git a/drivers/staging/csr/oska/mutex.h b/drivers/staging/csr/oska/mutex.h new file mode 100644 index 000000000000..9138b2881832 --- /dev/null +++ b/drivers/staging/csr/oska/mutex.h @@ -0,0 +1,42 @@ +/* + * OSKA Linux implementation -- mutexes + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_MUTEX_H +#define __OSKA_LINUX_MUTEX_H + +#include <linux/kernel.h> +#include <linux/mutex.h> + +#include "kernel-compat.h" + +/* Real mutexes were only added to 2.6.16 so use semaphores + instead. */ +typedef struct semaphore os_mutex_t; + +static inline void os_mutex_init(os_mutex_t *mutex) +{ + //init_MUTEX(mutex); + sema_init(mutex, 1); +} + +static inline void os_mutex_destroy(os_mutex_t *mutex) +{ + /* no op */ +} + +static inline void os_mutex_lock(os_mutex_t *mutex) +{ + down(mutex); +} + +static inline void os_mutex_unlock(os_mutex_t *mutex) +{ + up(mutex); +} + +#endif /* __OSKA_LINUX_MUTEX_H */ diff --git a/drivers/staging/csr/oska/oska_module.c b/drivers/staging/csr/oska/oska_module.c new file mode 100644 index 000000000000..da125643d2d6 --- /dev/null +++ b/drivers/staging/csr/oska/oska_module.c @@ -0,0 +1,21 @@ +/* + * Linux kernel module support. + * + * Copyright (C) 2010 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#include <linux/module.h> + +#include "all.h" +#include "refcount.h" + +EXPORT_SYMBOL(os_refcount_init); +EXPORT_SYMBOL(os_refcount_destroy); +EXPORT_SYMBOL(os_refcount_get); +EXPORT_SYMBOL(os_refcount_put); + +MODULE_DESCRIPTION("Operating System Kernel Abstraction"); +MODULE_AUTHOR("Cambridge Silicon Radio Ltd."); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/staging/csr/oska/print.c b/drivers/staging/csr/oska/print.c new file mode 100644 index 000000000000..5f5b26310663 --- /dev/null +++ b/drivers/staging/csr/oska/print.c @@ -0,0 +1,44 @@ +/* + * Linux console printing functions. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#include <linux/module.h> + +#include "print.h" + +void os_print(enum os_print_level level, const char *prefix, const char *name, + const char *format, ...) +{ + va_list va_args; + + va_start(va_args, format); + os_vprint(level, prefix, name, format, va_args); + va_end(va_args); +} +EXPORT_SYMBOL(os_print); + +void os_vprint(enum os_print_level level, const char *prefix, const char *name, + const char *format, va_list args) +{ + const char *level_str[] = { + [OS_PRINT_ERROR] = KERN_ERR, + [OS_PRINT_WARNING] = KERN_WARNING, + [OS_PRINT_INFO] = KERN_INFO, + [OS_PRINT_DEBUG] = KERN_DEBUG, + }; + char buf[80]; + int w = 0; + + if (name) { + w += snprintf(buf + w, sizeof(buf) - w, "%s%s%s: ", level_str[level], prefix, name); + } else { + w += snprintf(buf + w, sizeof(buf) - w, "%s%s", level_str[level], prefix); + } + w += vsnprintf(buf + w, sizeof(buf) - w, format, args); + printk("%s\n", buf); +} +EXPORT_SYMBOL(os_vprint); diff --git a/drivers/staging/csr/oska/print.h b/drivers/staging/csr/oska/print.h new file mode 100644 index 000000000000..f48bb836a587 --- /dev/null +++ b/drivers/staging/csr/oska/print.h @@ -0,0 +1,32 @@ +/* + * OSKA Linux implementation -- console printing + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_PRINT_H +#define __OSKA_LINUX_PRINT_H + +#include <linux/kernel.h> + +/** + * Severity of a console or log message. + * + * @ingroup print + */ +enum os_print_level { + OS_PRINT_ERROR, + OS_PRINT_WARNING, + OS_PRINT_INFO, + OS_PRINT_DEBUG, +}; + +void os_print(enum os_print_level level, const char *prefix, const char *name, + const char *format, ...); +void os_vprint(enum os_print_level level, const char *prefix, const char *name, + const char *format, va_list args); + + +#endif /* #ifndef __OSKA_LINUX_PRINT_H */ diff --git a/drivers/staging/csr/oska/refcount.c b/drivers/staging/csr/oska/refcount.c new file mode 100644 index 000000000000..28abb64a9d20 --- /dev/null +++ b/drivers/staging/csr/oska/refcount.c @@ -0,0 +1,47 @@ +/* + * OSKA generic implementation -- reference counting. + * + * Copyright (C) 2010 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#include "refcount.h" +#include "types.h" + +void os_refcount_init(os_refcount_t *refcount, os_refcount_callback_f func, void *arg) +{ + os_spinlock_init(&refcount->lock); + refcount->count = 1; + refcount->func = func; + refcount->arg = arg; +} + +void os_refcount_destroy(os_refcount_t *refcount) +{ + os_spinlock_destroy(&refcount->lock); +} + +void os_refcount_get(os_refcount_t *refcount) +{ + os_int_status_t istate; + + os_spinlock_lock_intsave(&refcount->lock, &istate); + refcount->count++; + os_spinlock_unlock_intrestore(&refcount->lock, &istate); +} + +void os_refcount_put(os_refcount_t *refcount) +{ + bool is_zero; + os_int_status_t istate; + + os_spinlock_lock_intsave(&refcount->lock, &istate); + refcount->count--; + is_zero = refcount->count == 0; + os_spinlock_unlock_intrestore(&refcount->lock, &istate); + + if (is_zero) { + refcount->func(refcount->arg); + } +} diff --git a/drivers/staging/csr/oska/refcount.h b/drivers/staging/csr/oska/refcount.h new file mode 100644 index 000000000000..741b00afcdf0 --- /dev/null +++ b/drivers/staging/csr/oska/refcount.h @@ -0,0 +1,86 @@ +/* + * Operating system kernel abstraction -- reference counting. + * + * Copyright (C) 2010 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_REFCOUNT_H +#define __OSKA_REFCOUNT_H + +#include "spinlock.h" + +/** + * @defgroup refcount Reference Counting + * + * A reference count is an atomic counter. A callback function is + * called whenever the count reaches zero. + * + * A generic implementation is provided that is suitable for all + * platforms that support the spinlock API in <oska/spinlock.h> (see + * \ref spinlock). + */ + +typedef void (*os_refcount_callback_f)(void *arg); + +struct __os_refcount_impl { + unsigned count; + os_spinlock_t lock; + os_refcount_callback_f func; + void *arg; +}; + +/** + * A reference count object. + * + * @ingroup refcount + */ +typedef struct __os_refcount_impl os_refcount_t; + +/** + * Initialize a reference count to 1. + * + * Initialized reference counts must be destroyed by calling + * os_refcount_destroy(). + * + * @param refcount the reference count. + * @param func the function which will be called when the + * reference count reaches 0. + * @param arg an argument to pass to func. + * + * @ingroup refcount + */ +void os_refcount_init(os_refcount_t *refcount, os_refcount_callback_f func, void *arg); + +/** + * Destroy a reference count object. + * + * @param refcount the reference count. + * + * @ingroup refcount + */ +void os_refcount_destroy(os_refcount_t *refcount); + +/** + * Atomically increase the reference count by 1. + * + * @param refcount the reference count. + * + * @ingroup refcount + */ +void os_refcount_get(os_refcount_t *refcount); + +/** + * Atomically decrease the reference count by 1. + * + * The callback function passed to the call to os_refcount_init() is + * called if the count was decreased to zero. + * + * @param refcount the reference count. + * + * @ingroup refcount + */ +void os_refcount_put(os_refcount_t *refcount); + +#endif /* #ifndef __OSKA_REFCOUNT_H */ diff --git a/drivers/staging/csr/oska/semaphore.h b/drivers/staging/csr/oska/semaphore.h new file mode 100644 index 000000000000..965bfe8f52cf --- /dev/null +++ b/drivers/staging/csr/oska/semaphore.h @@ -0,0 +1,70 @@ +/* + * OSKA Linux implementation -- semaphores + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_SEMAPHORE_H +#define __OSKA_LINUX_SEMAPHORE_H + +#include <linux/kernel.h> + +#include <linux/kernel-compat.h> + +typedef struct semaphore os_semaphore_t; + +static inline void os_semaphore_init(os_semaphore_t *sem) +{ + sema_init(sem, 0); +} + +static inline void os_semaphore_destroy(os_semaphore_t *sem) +{ +} + +static inline void os_semaphore_wait(os_semaphore_t *sem) +{ + down(sem); +} + +/* + * down_timeout() was added in 2.6.26 with the generic semaphore + * implementation. For now, only support it on recent kernels as + * semaphores may be replaced by an event API that would be + * implemented with wait_event(), and wait_event_timeout(). + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + +static inline int os_semaphore_wait_timed(os_semaphore_t *sem, + int time_ms) +{ + if (down_timeout(sem, msecs_to_jiffies(time_ms)) < 0) { + return -ETIMEDOUT; + } + return 0; +} + +#else + +static inline int os_semaphore_wait_timed(os_semaphore_t *sem, int time_ms) +{ + unsigned long now = jiffies; + do{ + if(!down_trylock(sem)) + return 0; + msleep(1); + } while(time_before(jiffies, now + msecs_to_jiffies(time_ms))); + + return -ETIMEDOUT; +} + +#endif + +static inline void os_semaphore_post(os_semaphore_t *sem) +{ + up(sem); +} + +#endif /* __OSKA_LINUX_SEMAPHORE_H */ diff --git a/drivers/staging/csr/oska/spinlock.h b/drivers/staging/csr/oska/spinlock.h new file mode 100644 index 000000000000..157b350107ae --- /dev/null +++ b/drivers/staging/csr/oska/spinlock.h @@ -0,0 +1,43 @@ +/* + * OSKA Linux implementation -- spinlocks + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_SPINLOCK_H +#define __OSKA_LINUX_SPINLOCK_H + +#include <linux/kernel.h> +#include <linux/spinlock.h> + +typedef spinlock_t os_spinlock_t; +typedef unsigned long os_int_status_t; + +static inline void os_spinlock_init(os_spinlock_t *lock) +{ + spinlock_t *l = (spinlock_t *)lock; + spin_lock_init(l); +} + +static inline void os_spinlock_destroy(os_spinlock_t *lock) +{ + /* no op */ +} + +static inline void os_spinlock_lock_intsave(os_spinlock_t *lock, + os_int_status_t *int_state) +{ + spinlock_t *l = (spinlock_t *)lock; + spin_lock_irqsave(l, *int_state); +} + +static inline void os_spinlock_unlock_intrestore(os_spinlock_t *lock, + os_int_status_t *int_state) +{ + spinlock_t *l = (spinlock_t *)lock; + spin_unlock_irqrestore(l, *int_state); +} + +#endif /* #ifndef __OSKA_LINUX_SPINLOCK_H */ diff --git a/drivers/staging/csr/oska/thread.c b/drivers/staging/csr/oska/thread.c new file mode 100644 index 000000000000..f680cef709e7 --- /dev/null +++ b/drivers/staging/csr/oska/thread.c @@ -0,0 +1,66 @@ +/* + * Linux thread functions. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#include <linux/module.h> + +#include "thread.h" + +static int thread_func(void *data) +{ + os_thread_t *thread = data; + + thread->func(thread->arg); + + /* + * kthread_stop() cannot handle the thread exiting while + * kthread_should_stop() is false, so sleep until kthread_stop() + * wakes us up. + */ + set_current_state(TASK_INTERRUPTIBLE); + if (!kthread_should_stop()) + schedule(); + + return 0; +} + +int os_thread_create(os_thread_t *thread, const char *name, void (*func)(void *), void *arg) +{ + thread->func = func; + thread->arg = arg; + + thread->stop = 0; + + thread->task = kthread_run(thread_func, thread, name); + if (IS_ERR(thread->task)) { + return PTR_ERR(thread->task); + } + return 0; +} +EXPORT_SYMBOL(os_thread_create); + +void os_thread_stop(os_thread_t *thread, os_event_t *evt) +{ + /* + * Stop flag must be set before the event is raised so + * kthread_should_stop() cannot be used. + */ + thread->stop = 1; + + if (evt) { + os_event_raise(evt, ~0); + } + + kthread_stop(thread->task); +} +EXPORT_SYMBOL(os_thread_stop); + +int os_thread_should_stop(os_thread_t *thread) +{ + return thread->stop; +} +EXPORT_SYMBOL(os_thread_should_stop); diff --git a/drivers/staging/csr/oska/thread.h b/drivers/staging/csr/oska/thread.h new file mode 100644 index 000000000000..8816dc853e26 --- /dev/null +++ b/drivers/staging/csr/oska/thread.h @@ -0,0 +1,39 @@ +/* + * OSKA Linux implementation -- threading + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_THREAD_H +#define __OSKA_LINUX_THREAD_H + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19) +#include <linux/freezer.h> +#endif +#include "event.h" + +struct os_thread_lx { + void (*func)(void *); + void *arg; + struct task_struct *task; + int stop; +}; + +typedef struct os_thread_lx os_thread_t; + +int os_thread_create(os_thread_t *thread, const char *name, + void (*func)(void *), void *arg); +void os_thread_stop(os_thread_t *thread, os_event_t *evt); +int os_thread_should_stop(os_thread_t *thread); + +static inline void os_try_suspend_thread(os_thread_t *thread) +{ + try_to_freeze(); +} + +#endif /* __OSKA_LINUX_THREAD_H */ diff --git a/drivers/staging/csr/oska/time.h b/drivers/staging/csr/oska/time.h new file mode 100644 index 000000000000..d246ce937309 --- /dev/null +++ b/drivers/staging/csr/oska/time.h @@ -0,0 +1,34 @@ +/* + * OSKA Linux implementation -- timing + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_TIME_H +#define __OSKA_LINUX_TIME_H + +#include <linux/kernel.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/jiffies.h> + +static inline unsigned long os_current_time_ms(void) +{ + return jiffies_to_msecs(jiffies); +} + +static inline void os_sleep_ms(unsigned ms) +{ + msleep_interruptible(ms); +} + +static inline void os_delay_us(unsigned us) +{ + udelay(us); +} + +#define os_time_after(a, b) time_after((a), (b)) + +#endif /* __OSKA_LINUX_TIME_H */ diff --git a/drivers/staging/csr/oska/timer.c b/drivers/staging/csr/oska/timer.c new file mode 100644 index 000000000000..67d3423315f5 --- /dev/null +++ b/drivers/staging/csr/oska/timer.c @@ -0,0 +1,28 @@ +/* + * OSKA Linux implementation -- timers. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#include <linux/module.h> + +#include "timer.h" + +static void timer_func(unsigned long data) +{ + os_timer_t *timer = (os_timer_t *)data; + + timer->func(timer->arg); +} + +void os_timer_init(os_timer_t *timer, os_timer_func_t func, void *arg) +{ + timer->func = func; + timer->arg = arg; + timer->timer.function = timer_func; + timer->timer.data = (unsigned long)timer; + init_timer(&timer->timer); +} +EXPORT_SYMBOL(os_timer_init); diff --git a/drivers/staging/csr/oska/timer.h b/drivers/staging/csr/oska/timer.h new file mode 100644 index 000000000000..3045fc3b98b7 --- /dev/null +++ b/drivers/staging/csr/oska/timer.h @@ -0,0 +1,40 @@ +/* + * OSKA Linux implementation -- timers. + * + * Copyright (C) 2009 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_TIMER_H +#define __OSKA_LINUX_TIMER_H + +#include <linux/kernel.h> +#include <linux/timer.h> + +typedef void (*os_timer_func_t)(void *arg); + +typedef struct { + os_timer_func_t func; + void *arg; + struct timer_list timer; +} os_timer_t; + +void os_timer_init(os_timer_t *timer, os_timer_func_t func, void *arg); + +static inline void os_timer_destroy(os_timer_t *timer) +{ + del_timer_sync(&timer->timer); +} + +static inline void os_timer_set(os_timer_t *timer, unsigned long expires_ms) +{ + mod_timer(&timer->timer, jiffies + msecs_to_jiffies(expires_ms)); +} + +static inline void os_timer_cancel(os_timer_t *timer) +{ + del_timer(&timer->timer); +} + +#endif /* #ifndef __OSKA_LINUX_TIMER_H */ diff --git a/drivers/staging/csr/oska/trace.h b/drivers/staging/csr/oska/trace.h new file mode 100644 index 000000000000..b28f37da4fbb --- /dev/null +++ b/drivers/staging/csr/oska/trace.h @@ -0,0 +1,23 @@ +/* + * OSKA Linux implementation -- tracing messages. + * + * Copyright (C) 2009 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_TRACE_H +#define __OSKA_LINUX_TRACE_H + +#include <linux/kernel.h> + +#ifndef OS_TRACE_PREFIX +# define OS_TRACE_PREFIX "" +#endif + +#define os_trace_err(format, ...) printk(KERN_ERR OS_TRACE_PREFIX format "\n", ## __VA_ARGS__) +#define os_trace_warn(format, ...) printk(KERN_WARNING OS_TRACE_PREFIX format "\n", ## __VA_ARGS__) +#define os_trace_info(format, ...) printk(KERN_INFO OS_TRACE_PREFIX format "\n", ## __VA_ARGS__) +#define os_trace_dbg(format, ...) printk(KERN_DEBUG OS_TRACE_PREFIX format "\n", ## __VA_ARGS__) + +#endif /* #ifndef __OSKA_LINUX_TRACE_H */ diff --git a/drivers/staging/csr/oska/types.h b/drivers/staging/csr/oska/types.h new file mode 100644 index 000000000000..18d7c111e74a --- /dev/null +++ b/drivers/staging/csr/oska/types.h @@ -0,0 +1,14 @@ +/* + * OSKA Linux implementation -- types + * + * Copyright (C) 2009 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_TYPES_H +#define __OSKA_LINUX_TYPES_H + +#include <linux/types.h> + +#endif /* #ifndef __OSKA_LINUX_TYPES_H */ diff --git a/drivers/staging/csr/oska/util.h b/drivers/staging/csr/oska/util.h new file mode 100644 index 000000000000..bf29e2d906ed --- /dev/null +++ b/drivers/staging/csr/oska/util.h @@ -0,0 +1,48 @@ +/* + * OSKA Linux implementation -- misc. utility functions + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * Refer to LICENSE.txt included with this source code for details on + * the license terms. + */ +#ifndef __OSKA_LINUX_UTILS_H +#define __OSKA_LINUX_UTILS_H + +#include <linux/kernel.h> +#include <linux/bug.h> +#include <asm/byteorder.h> + +#define OS_ASSERT(expr) BUG_ON(!(expr)) + +static inline uint16_t os_le16_to_cpu(uint16_t x) +{ + return le16_to_cpu(x); +} + +static inline uint16_t os_cpu_to_le16(uint16_t x) +{ + return cpu_to_le16(x); +} + +static inline uint32_t os_le32_to_cpu(uint32_t x) +{ + return le32_to_cpu(x); +} + +static inline uint32_t os_cpu_to_le32(uint32_t x) +{ + return cpu_to_le32(x); +} + +static inline uint64_t os_le64_to_cpu(uint64_t x) +{ + return le64_to_cpu(x); +} + +static inline uint64_t os_cpu_to_le64(uint64_t x) +{ + return cpu_to_le64(x); +} + +#endif /* __OSKA_LINUX_UTILS_H */ |