diff options
201 files changed, 14644 insertions, 5398 deletions
diff --git a/arch/arm/configs/herring_defconfig b/arch/arm/configs/herring_defconfig index e1c2a37..74d6850 100644 --- a/arch/arm/configs/herring_defconfig +++ b/arch/arm/configs/herring_defconfig @@ -14,9 +14,8 @@ CONFIG_KALLSYMS_ALL=y CONFIG_ASHMEM=y # CONFIG_AIO is not set CONFIG_EMBEDDED=y -# CONFIG_PERF_EVENTS is not set +CONFIG_PERF_COUNTERS=y CONFIG_PROFILING=y -CONFIG_OPROFILE=y CONFIG_MODULES=y CONFIG_MODULE_FORCE_LOAD=y CONFIG_MODULE_UNLOAD=y @@ -34,6 +33,7 @@ CONFIG_FIQ_DEBUGGER_CONSOLE=y CONFIG_PREEMPT=y CONFIG_AEABI=y CONFIG_COMPACTION=y +CONFIG_KSM=y CONFIG_CMDLINE="console=ttyFIQ0" CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y @@ -51,6 +51,7 @@ CONFIG_APM_EMULATION=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y +CONFIG_XFRM_USER=y CONFIG_NET_KEY=y CONFIG_INET=y CONFIG_IP_MULTICAST=y @@ -87,6 +88,7 @@ CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_TPROXY=y CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_MARK=y CONFIG_NETFILTER_XT_TARGET_NFLOG=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y @@ -162,6 +164,10 @@ CONFIG_BT_BNEP=y CONFIG_BT_HIDP=y CONFIG_BT_HCIUART=y CONFIG_BT_HCIUART_H4=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_WEXT is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y CONFIG_WIMAX=y CONFIG_RFKILL=y CONFIG_RFKILL_INPUT=y @@ -193,9 +199,9 @@ CONFIG_DM_UEVENT=y CONFIG_NETDEVICES=y CONFIG_IFB=y CONFIG_TUN=y -CONFIG_BCM4329=m -CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" -CONFIG_BCM4329_NVRAM_PATH="/vendor/firmware/nvram_net.txt" +CONFIG_BCMDHD=y +CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/vendor/firmware/nvram_net.txt" CONFIG_WIMAX_CMC7XX=y CONFIG_PPP=y CONFIG_PPP_DEFLATE=y @@ -262,6 +268,7 @@ CONFIG_PVR_SGX=y CONFIG_PVR_NEED_PVR_DPF=y CONFIG_PVR_NEED_PVR_ASSERT=y CONFIG_PVR_USSE_EDM_STATUS_DEBUG=y +CONFIG_PVR_LINUX_MEM_AREA_POOL=y CONFIG_FB=y CONFIG_FB_S3C=y CONFIG_FB_S3C_NR_BUFFERS=7 @@ -387,11 +394,12 @@ CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y CONFIG_PRINTK_TIME=y CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y # CONFIG_DEBUG_PREEMPT is not set CONFIG_DEBUG_INFO=y CONFIG_SYSCTL_SYSCALL_CHECK=y +CONFIG_SCHED_TRACER=y +CONFIG_DYNAMIC_DEBUG=y # CONFIG_ARM_UNWIND is not set CONFIG_DEBUG_USER=y CONFIG_DEBUG_S3C_UART=2 diff --git a/arch/arm/mach-s5pv210/mach-herring.c b/arch/arm/mach-s5pv210/mach-herring.c index b730141..2daf093 100755 --- a/arch/arm/mach-s5pv210/mach-herring.c +++ b/arch/arm/mach-s5pv210/mach-herring.c @@ -366,6 +366,7 @@ static struct s3cfb_lcd r61408 = { }; #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0 (6144 * SZ_1K) +// #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC1 (4 * SZ_1K) #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC2 (6144 * SZ_1K) #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC0 (11264 * SZ_1K) // 11Mb #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC1 (11264 * SZ_1K) // 11Mb diff --git a/arch/arm/mach-s5pv210/power-domain.c b/arch/arm/mach-s5pv210/power-domain.c index 59a1a98..683901c 100644 --- a/arch/arm/mach-s5pv210/power-domain.c +++ b/arch/arm/mach-s5pv210/power-domain.c @@ -410,7 +410,7 @@ static int s5pv210_pd_is_enabled(struct regulator_dev *dev) static int s5pv210_pd_enable(struct regulator_dev *dev) { struct s5pv210_pd_data *data = rdev_get_drvdata(dev); - int ret; + int ret = 0; if (data->clk_run) s5pv210_pd_clk_enable(data->clk_run); @@ -418,13 +418,12 @@ static int s5pv210_pd_enable(struct regulator_dev *dev) ret = s5pv210_pd_ctrl(data->ctrlbit, 1); if (ret < 0) { printk(KERN_ERR "failed to enable power domain\n"); - return ret; } if (data->clk_run) s5pv210_pd_clk_disable(data->clk_run); - return 0; + return ret; } static int s5pv210_pd_disable(struct regulator_dev *dev) diff --git a/arch/arm/plat-samsung/include/plat/regs-otg.h b/arch/arm/plat-samsung/include/plat/regs-otg.h index 214d730..dccdb26 100644 --- a/arch/arm/plat-samsung/include/plat/regs-otg.h +++ b/arch/arm/plat-samsung/include/plat/regs-otg.h @@ -208,7 +208,9 @@ #define DEPCTL_EPENA (0x1<<31) #define DEPCTL_EPDIS (0x1<<30) #define DEPCTL_SETD1PID (0x1<<29) +#define DEPCTL_SET_ODD_FRM (0x1<<29) #define DEPCTL_SETD0PID (0x1<<28) +#define DEPCTL_SET_EVEN_FRM (0x1<<28) #define DEPCTL_SNAK (0x1<<27) #define DEPCTL_CNAK (0x1<<26) #define DEPCTL_STALL (0x1<<21) diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 53cb06f..78829fa 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -419,13 +419,15 @@ static int vfp_pm_suspend(void) /* disable, just in case */ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); } else if (vfp_current_hw_state[ti->cpu]) { +#ifndef CONFIG_SMP fmxr(FPEXC, fpexc | FPEXC_EN); vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc); fmxr(FPEXC, fpexc); +#endif } /* clear any information we had about last context state */ - memset(vfp_current_hw_state, 0, sizeof(vfp_current_hw_state)); + vfp_current_hw_state[ti->cpu] = NULL; return 0; } diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index d57e8d0..13305c8 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -168,4 +168,30 @@ config SYS_HYPERVISOR bool default n +config SYNC + bool "Synchronization framework" + default n + select ANON_INODES + help + This option enables the framework for synchronization between multiple + drivers. Sync implementations can take advantage of hardware + synchronization built into devices like GPUs. + +config SW_SYNC + bool "Software synchronization objects" + default n + depends on SYNC + help + A sync object driver that uses a 32bit counter to coordinate + syncrhronization. Useful when there is no hardware primitive backing + the synchronization. + +config SW_SYNC_USER + bool "Userspace API for SW_SYNC" + default n + depends on SW_SYNC + help + Provides a user space API to the sw sync object. + *WARNING* improper use of this can result in deadlocking kernel + drivers from userspace. endmenu diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 4c5701c..b616882 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -19,5 +19,8 @@ obj-$(CONFIG_MODULES) += module.o endif obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o +obj-$(CONFIG_SYNC) += sync.o +obj-$(CONFIG_SW_SYNC) += sw_sync.o + ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/sw_sync.c b/drivers/base/sw_sync.c new file mode 100644 index 0000000..21ddf4f --- /dev/null +++ b/drivers/base/sw_sync.c @@ -0,0 +1,256 @@ +/* + * drivers/base/sw_sync.c + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/sw_sync.h> +#include <linux/syscalls.h> +#include <linux/uaccess.h> + +static int sw_sync_cmp(u32 a, u32 b) +{ + if (a == b) + return 0; + + return ((s32)a - (s32)b) < 0 ? -1 : 1; +} + +struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) +{ + struct sw_sync_pt *pt; + + pt = (struct sw_sync_pt *) + sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt)); + + pt->value = value; + + return (struct sync_pt *)pt; +} + +static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *) sync_pt; + struct sw_sync_timeline *obj = + (struct sw_sync_timeline *)sync_pt->parent; + + return (struct sync_pt *) sw_sync_pt_create(obj, pt->value); +} + +static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + struct sw_sync_timeline *obj = + (struct sw_sync_timeline *)sync_pt->parent; + + return sw_sync_cmp(obj->value, pt->value) >= 0; +} + +static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b) +{ + struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a; + struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b; + + return sw_sync_cmp(pt_a->value, pt_b->value); +} + +static void sw_sync_print_obj(struct seq_file *s, + struct sync_timeline *sync_timeline) +{ + struct sw_sync_timeline *obj = (struct sw_sync_timeline *)sync_timeline; + + seq_printf(s, "%d", obj->value); +} + +static void sw_sync_print_pt(struct seq_file *s, struct sync_pt *sync_pt) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + struct sw_sync_timeline *obj = + (struct sw_sync_timeline *)sync_pt->parent; + + seq_printf(s, "%d / %d", pt->value, obj->value); +} + +static int sw_sync_fill_driver_data(struct sync_pt *sync_pt, + void *data, int size) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + + if (size < sizeof(pt->value)) + return -ENOMEM; + + memcpy(data, &pt->value, sizeof(pt->value)); + + return sizeof(pt->value); +} + +struct sync_timeline_ops sw_sync_timeline_ops = { + .driver_name = "sw_sync", + .dup = sw_sync_pt_dup, + .has_signaled = sw_sync_pt_has_signaled, + .compare = sw_sync_pt_compare, + .print_obj = sw_sync_print_obj, + .print_pt = sw_sync_print_pt, + .fill_driver_data = sw_sync_fill_driver_data, +}; + + +struct sw_sync_timeline *sw_sync_timeline_create(const char *name) +{ + struct sw_sync_timeline *obj = (struct sw_sync_timeline *) + sync_timeline_create(&sw_sync_timeline_ops, + sizeof(struct sw_sync_timeline), + name); + + return obj; +} + +void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) +{ + obj->value += inc; + + sync_timeline_signal(&obj->obj); +} + + +#ifdef CONFIG_SW_SYNC_USER +/* *WARNING* + * + * improper use of this can result in deadlocking kernel drivers from userspace. + */ + +/* opening sw_sync create a new sync obj */ +int sw_sync_open(struct inode *inode, struct file *file) +{ + struct sw_sync_timeline *obj; + char task_comm[TASK_COMM_LEN]; + + get_task_comm(task_comm, current); + + obj = sw_sync_timeline_create(task_comm); + if (obj == NULL) + return -ENOMEM; + + file->private_data = obj; + + return 0; +} + +int sw_sync_release(struct inode *inode, struct file *file) +{ + struct sw_sync_timeline *obj = file->private_data; + sync_timeline_destroy(&obj->obj); + return 0; +} + +long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, unsigned long arg) +{ + int fd = get_unused_fd(); + int err; + struct sync_pt *pt; + struct sync_fence *fence; + struct sw_sync_create_fence_data data; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; + + pt = sw_sync_pt_create(obj, data.value); + if (pt == NULL) { + err = -ENOMEM; + goto err; + } + + data.name[sizeof(data.name) - 1] = '\0'; + fence = sync_fence_create(data.name, pt); + if (fence == NULL) { + sync_pt_free(pt); + err = -ENOMEM; + goto err; + } + + data.fence = fd; + if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + sync_fence_put(fence); + err = -EFAULT; + goto err; + } + + sync_fence_install(fence, fd); + + return 0; + +err: + put_unused_fd(fd); + return err; +} + +long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg) +{ + u32 value; + + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) + return -EFAULT; + + sw_sync_timeline_inc(obj, value); + + return 0; +} + +long sw_sync_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct sw_sync_timeline *obj = file->private_data; + + switch (cmd) { + case SW_SYNC_IOC_CREATE_FENCE: + return sw_sync_ioctl_create_fence(obj, arg); + + case SW_SYNC_IOC_INC: + return sw_sync_ioctl_inc(obj, arg); + + default: + return -ENOTTY; + } +} + +static const struct file_operations sw_sync_fops = { + .owner = THIS_MODULE, + .open = sw_sync_open, + .release = sw_sync_release, + .unlocked_ioctl = sw_sync_ioctl, +}; + +static struct miscdevice sw_sync_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sw_sync", + .fops = &sw_sync_fops, +}; + +int __init sw_sync_device_init(void) +{ + return misc_register(&sw_sync_dev); +} + +void __exit sw_sync_device_remove(void) +{ + misc_deregister(&sw_sync_dev); +} + +module_init(sw_sync_device_init); +module_exit(sw_sync_device_remove); + +#endif /* CONFIG_SW_SYNC_USER */ diff --git a/drivers/base/sync.c b/drivers/base/sync.c new file mode 100644 index 0000000..d6913f8 --- /dev/null +++ b/drivers/base/sync.c @@ -0,0 +1,801 @@ +/* + * drivers/base/sync.c + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/debugfs.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/sync.h> +#include <linux/uaccess.h> + +#include <linux/anon_inodes.h> + +static void sync_fence_signal_pt(struct sync_pt *pt); +static int _sync_pt_has_signaled(struct sync_pt *pt); + +static LIST_HEAD(sync_timeline_list_head); +static DEFINE_SPINLOCK(sync_timeline_list_lock); + +static LIST_HEAD(sync_fence_list_head); +static DEFINE_SPINLOCK(sync_fence_list_lock); + +struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, + int size, const char *name) +{ + struct sync_timeline *obj; + unsigned long flags; + + if (size < sizeof(struct sync_timeline)) + return NULL; + + obj = kzalloc(size, GFP_KERNEL); + if (obj == NULL) + return NULL; + + obj->ops = ops; + strlcpy(obj->name, name, sizeof(obj->name)); + + INIT_LIST_HEAD(&obj->child_list_head); + spin_lock_init(&obj->child_list_lock); + + INIT_LIST_HEAD(&obj->active_list_head); + spin_lock_init(&obj->active_list_lock); + + spin_lock_irqsave(&sync_timeline_list_lock, flags); + list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head); + spin_unlock_irqrestore(&sync_timeline_list_lock, flags); + + return obj; +} + +static void sync_timeline_free(struct sync_timeline *obj) +{ + unsigned long flags; + + if (obj->ops->release_obj) + obj->ops->release_obj(obj); + + spin_lock_irqsave(&sync_timeline_list_lock, flags); + list_del(&obj->sync_timeline_list); + spin_unlock_irqrestore(&sync_timeline_list_lock, flags); + + kfree(obj); +} + +void sync_timeline_destroy(struct sync_timeline *obj) +{ + unsigned long flags; + bool needs_freeing; + + spin_lock_irqsave(&obj->child_list_lock, flags); + obj->destroyed = true; + needs_freeing = list_empty(&obj->child_list_head); + spin_unlock_irqrestore(&obj->child_list_lock, flags); + + if (needs_freeing) + sync_timeline_free(obj); + else + sync_timeline_signal(obj); +} + +static void sync_timeline_add_pt(struct sync_timeline *obj, struct sync_pt *pt) +{ + unsigned long flags; + + pt->parent = obj; + + spin_lock_irqsave(&obj->child_list_lock, flags); + list_add_tail(&pt->child_list, &obj->child_list_head); + spin_unlock_irqrestore(&obj->child_list_lock, flags); +} + +static void sync_timeline_remove_pt(struct sync_pt *pt) +{ + struct sync_timeline *obj = pt->parent; + unsigned long flags; + bool needs_freeing; + + spin_lock_irqsave(&obj->active_list_lock, flags); + if (!list_empty(&pt->active_list)) + list_del_init(&pt->active_list); + spin_unlock_irqrestore(&obj->active_list_lock, flags); + + spin_lock_irqsave(&obj->child_list_lock, flags); + list_del(&pt->child_list); + needs_freeing = obj->destroyed && list_empty(&obj->child_list_head); + spin_unlock_irqrestore(&obj->child_list_lock, flags); + + if (needs_freeing) + sync_timeline_free(obj); +} + +void sync_timeline_signal(struct sync_timeline *obj) +{ + unsigned long flags; + LIST_HEAD(signaled_pts); + struct list_head *pos, *n; + + spin_lock_irqsave(&obj->active_list_lock, flags); + + list_for_each_safe(pos, n, &obj->active_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, active_list); + + if (_sync_pt_has_signaled(pt)) + list_move(pos, &signaled_pts); + } + + spin_unlock_irqrestore(&obj->active_list_lock, flags); + + list_for_each_safe(pos, n, &signaled_pts) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, active_list); + + list_del_init(pos); + sync_fence_signal_pt(pt); + } +} + +struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size) +{ + struct sync_pt *pt; + + if (size < sizeof(struct sync_pt)) + return NULL; + + pt = kzalloc(size, GFP_KERNEL); + if (pt == NULL) + return NULL; + + INIT_LIST_HEAD(&pt->active_list); + sync_timeline_add_pt(parent, pt); + + return pt; +} + +void sync_pt_free(struct sync_pt *pt) +{ + if (pt->parent->ops->free_pt) + pt->parent->ops->free_pt(pt); + + sync_timeline_remove_pt(pt); + + kfree(pt); +} + +/* call with pt->parent->active_list_lock held */ +static int _sync_pt_has_signaled(struct sync_pt *pt) +{ + int old_status = pt->status; + + if (!pt->status) + pt->status = pt->parent->ops->has_signaled(pt); + + if (!pt->status && pt->parent->destroyed) + pt->status = -ENOENT; + + if (pt->status != old_status) + pt->timestamp = ktime_get(); + + return pt->status; +} + +static struct sync_pt *sync_pt_dup(struct sync_pt *pt) +{ + return pt->parent->ops->dup(pt); +} + +/* Adds a sync pt to the active queue. Called when added to a fence */ +static void sync_pt_activate(struct sync_pt *pt) +{ + struct sync_timeline *obj = pt->parent; + unsigned long flags; + int err; + + spin_lock_irqsave(&obj->active_list_lock, flags); + + err = _sync_pt_has_signaled(pt); + if (err != 0) + goto out; + + list_add_tail(&pt->active_list, &obj->active_list_head); + +out: + spin_unlock_irqrestore(&obj->active_list_lock, flags); +} + +static int sync_fence_release(struct inode *inode, struct file *file); +static unsigned int sync_fence_poll(struct file *file, poll_table *wait); +static long sync_fence_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); + + +static const struct file_operations sync_fence_fops = { + .release = sync_fence_release, + .poll = sync_fence_poll, + .unlocked_ioctl = sync_fence_ioctl, +}; + +static struct sync_fence *sync_fence_alloc(const char *name) +{ + struct sync_fence *fence; + unsigned long flags; + + fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL); + if (fence == NULL) + return NULL; + + fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops, + fence, 0); + if (fence->file == NULL) + goto err; + + strlcpy(fence->name, name, sizeof(fence->name)); + + INIT_LIST_HEAD(&fence->pt_list_head); + INIT_LIST_HEAD(&fence->waiter_list_head); + spin_lock_init(&fence->waiter_list_lock); + + init_waitqueue_head(&fence->wq); + + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_add_tail(&fence->sync_fence_list, &sync_fence_list_head); + spin_unlock_irqrestore(&sync_fence_list_lock, flags); + + return fence; + +err: + kfree(fence); + return NULL; +} + +/* TODO: implement a create which takes more that one sync_pt */ +struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt) +{ + struct sync_fence *fence; + + if (pt->fence) + return NULL; + + fence = sync_fence_alloc(name); + if (fence == NULL) + return NULL; + + pt->fence = fence; + list_add(&pt->pt_list, &fence->pt_list_head); + sync_pt_activate(pt); + + return fence; +} + +static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src) +{ + struct list_head *pos; + + list_for_each(pos, &src->pt_list_head) { + struct sync_pt *orig_pt = + container_of(pos, struct sync_pt, pt_list); + struct sync_pt *new_pt = sync_pt_dup(orig_pt); + + if (new_pt == NULL) + return -ENOMEM; + + new_pt->fence = dst; + list_add(&new_pt->pt_list, &dst->pt_list_head); + sync_pt_activate(new_pt); + } + + return 0; +} + +static void sync_fence_free_pts(struct sync_fence *fence) +{ + struct list_head *pos, *n; + + list_for_each_safe(pos, n, &fence->pt_list_head) { + struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); + sync_pt_free(pt); + } +} + +struct sync_fence *sync_fence_fdget(int fd) +{ + struct file *file = fget(fd); + + if (file == NULL) + return NULL; + + if (file->f_op != &sync_fence_fops) + goto err; + + return file->private_data; + +err: + fput(file); + return NULL; +} + +void sync_fence_put(struct sync_fence *fence) +{ + fput(fence->file); +} + +void sync_fence_install(struct sync_fence *fence, int fd) +{ + fd_install(fd, fence->file); +} + +static int sync_fence_get_status(struct sync_fence *fence) +{ + struct list_head *pos; + int status = 1; + + list_for_each(pos, &fence->pt_list_head) { + struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); + int pt_status = pt->status; + + if (pt_status < 0) { + status = pt_status; + break; + } else if (status == 1) { + status = pt_status; + } + } + + return status; +} + +struct sync_fence *sync_fence_merge(const char *name, + struct sync_fence *a, struct sync_fence *b) +{ + struct sync_fence *fence; + int err; + + fence = sync_fence_alloc(name); + if (fence == NULL) + return NULL; + + err = sync_fence_copy_pts(fence, a); + if (err < 0) + goto err; + + err = sync_fence_copy_pts(fence, b); + if (err < 0) + goto err; + + fence->status = sync_fence_get_status(fence); + + return fence; +err: + sync_fence_free_pts(fence); + kfree(fence); + return NULL; +} + +static void sync_fence_signal_pt(struct sync_pt *pt) +{ + LIST_HEAD(signaled_waiters); + struct sync_fence *fence = pt->fence; + struct list_head *pos; + struct list_head *n; + unsigned long flags; + int status; + + status = sync_fence_get_status(fence); + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + /* + * this should protect against two threads racing on the signaled + * false -> true transition + */ + if (status && !fence->status) { + list_for_each_safe(pos, n, &fence->waiter_list_head) + list_move(pos, &signaled_waiters); + + fence->status = status; + } else { + status = 0; + } + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); + + if (status) { + list_for_each_safe(pos, n, &signaled_waiters) { + struct sync_fence_waiter *waiter = + container_of(pos, struct sync_fence_waiter, + waiter_list); + + waiter->callback(fence, waiter->callback_data); + list_del(pos); + kfree(waiter); + } + wake_up(&fence->wq); + } +} + +int sync_fence_wait_async(struct sync_fence *fence, + void (*callback)(struct sync_fence *, void *data), + void *callback_data) +{ + struct sync_fence_waiter *waiter; + unsigned long flags; + int err = 0; + + waiter = kzalloc(sizeof(struct sync_fence_waiter), GFP_KERNEL); + if (waiter == NULL) + return -ENOMEM; + + waiter->callback = callback; + waiter->callback_data = callback_data; + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + + if (fence->status) { + kfree(waiter); + err = fence->status; + goto out; + } + + list_add_tail(&waiter->waiter_list, &fence->waiter_list_head); +out: + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); + + return err; +} + +int sync_fence_wait(struct sync_fence *fence, long timeout) +{ + int err; + + if (timeout) { + timeout = msecs_to_jiffies(timeout); + err = wait_event_interruptible_timeout(fence->wq, + fence->status != 0, + timeout); + } else { + err = wait_event_interruptible(fence->wq, fence->status != 0); + } + + if (err < 0) + return err; + + if (fence->status < 0) + return fence->status; + + if (fence->status == 0) + return -ETIME; + + return 0; +} + +static int sync_fence_release(struct inode *inode, struct file *file) +{ + struct sync_fence *fence = file->private_data; + unsigned long flags; + + sync_fence_free_pts(fence); + + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_del(&fence->sync_fence_list); + spin_unlock_irqrestore(&sync_fence_list_lock, flags); + + kfree(fence); + + return 0; +} + +static unsigned int sync_fence_poll(struct file *file, poll_table *wait) +{ + struct sync_fence *fence = file->private_data; + + poll_wait(file, &fence->wq, wait); + + if (fence->status == 1) + return POLLIN; + else if (fence->status < 0) + return POLLERR; + else + return 0; +} + +static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg) +{ + __u32 value; + + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) + return -EFAULT; + + return sync_fence_wait(fence, value); +} + +static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) +{ + int fd = get_unused_fd(); + int err; + struct sync_fence *fence2, *fence3; + struct sync_merge_data data; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; + + fence2 = sync_fence_fdget(data.fd2); + if (fence2 == NULL) { + err = -ENOENT; + goto err_put_fd; + } + + data.name[sizeof(data.name) - 1] = '\0'; + fence3 = sync_fence_merge(data.name, fence, fence2); + if (fence3 == NULL) { + err = -ENOMEM; + goto err_put_fence2; + } + + data.fence = fd; + if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + err = -EFAULT; + goto err_put_fence3; + } + + sync_fence_install(fence3, fd); + sync_fence_put(fence2); + return 0; + +err_put_fence3: + sync_fence_put(fence3); + +err_put_fence2: + sync_fence_put(fence2); + +err_put_fd: + put_unused_fd(fd); + return err; +} + +int sync_fill_pt_info(struct sync_pt *pt, void *data, int size) +{ + struct sync_pt_info *info = data; + int ret; + + if (size < sizeof(struct sync_pt_info)) + return -ENOMEM; + + info->len = sizeof(struct sync_pt_info); + + if (pt->parent->ops->fill_driver_data) { + ret = pt->parent->ops->fill_driver_data(pt, info->driver_data, + size - sizeof(*info)); + if (ret < 0) + return ret; + + info->len += ret; + } + + strlcpy(info->obj_name, pt->parent->name, sizeof(info->obj_name)); + strlcpy(info->driver_name, pt->parent->ops->driver_name, + sizeof(info->driver_name)); + info->status = pt->status; + info->timestamp_ns = ktime_to_ns(pt->timestamp); + + return info->len; +} + + +static long sync_fence_ioctl_fence_info(struct sync_fence *fence, + unsigned long arg) +{ + struct sync_fence_info_data *data; + struct list_head *pos; + __u32 size; + __u32 len = 0; + int ret; + + if (copy_from_user(&size, (void __user *)arg, sizeof(size))) + return -EFAULT; + + if (size < sizeof(struct sync_fence_info_data)) + return -EINVAL; + + if (size > 4096) + size = 4096; + + data = kzalloc(size, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + strlcpy(data->name, fence->name, sizeof(data->name)); + data->status = fence->status; + len = sizeof(struct sync_fence_info_data); + + list_for_each(pos, &fence->pt_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, pt_list); + + ret = sync_fill_pt_info(pt, (u8 *)data + len, size - len); + + if (ret < 0) + goto out; + + len += ret; + } + + data->len = len; + + if (copy_to_user((void __user *)arg, data, len)) + ret = -EFAULT; + else + ret = 0; + +out: + kfree(data); + + return ret; +} + +static long sync_fence_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct sync_fence *fence = file->private_data; + switch (cmd) { + case SYNC_IOC_WAIT: + return sync_fence_ioctl_wait(fence, arg); + + case SYNC_IOC_MERGE: + return sync_fence_ioctl_merge(fence, arg); + + case SYNC_IOC_FENCE_INFO: + return sync_fence_ioctl_fence_info(fence, arg); + + default: + return -ENOTTY; + } +} + +#ifdef CONFIG_DEBUG_FS +static const char *sync_status_str(int status) +{ + if (status > 0) + return "signaled"; + else if (status == 0) + return "active"; + else + return "error"; +} + +static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence) +{ + int status = pt->status; + seq_printf(s, " %s%spt %s", + fence ? pt->parent->name : "", + fence ? "_" : "", + sync_status_str(status)); + if (pt->status) { + struct timeval tv = ktime_to_timeval(pt->timestamp); + seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec); + } + + if (pt->parent->ops->print_pt) { + seq_printf(s, ": "); + pt->parent->ops->print_pt(s, pt); + } + + seq_printf(s, "\n"); +} + +static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj) +{ + struct list_head *pos; + unsigned long flags; + + seq_printf(s, "%s %s", obj->name, obj->ops->driver_name); + + if (obj->ops->print_obj) { + seq_printf(s, ": "); + obj->ops->print_obj(s, obj); + } + + seq_printf(s, "\n"); + + spin_lock_irqsave(&obj->child_list_lock, flags); + list_for_each(pos, &obj->child_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, child_list); + sync_print_pt(s, pt, false); + } + spin_unlock_irqrestore(&obj->child_list_lock, flags); +} + +static void sync_print_fence(struct seq_file *s, struct sync_fence *fence) +{ + struct list_head *pos; + unsigned long flags; + + seq_printf(s, "%s: %s\n", fence->name, sync_status_str(fence->status)); + + list_for_each(pos, &fence->pt_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, pt_list); + sync_print_pt(s, pt, true); + } + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + list_for_each(pos, &fence->waiter_list_head) { + struct sync_fence_waiter *waiter = + container_of(pos, struct sync_fence_waiter, + waiter_list); + + seq_printf(s, "waiter %pF %p\n", waiter->callback, + waiter->callback_data); + } + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); +} + +static int sync_debugfs_show(struct seq_file *s, void *unused) +{ + unsigned long flags; + struct list_head *pos; + + seq_printf(s, "objs:\n--------------\n"); + + spin_lock_irqsave(&sync_timeline_list_lock, flags); + list_for_each(pos, &sync_timeline_list_head) { + struct sync_timeline *obj = + container_of(pos, struct sync_timeline, + sync_timeline_list); + + sync_print_obj(s, obj); + seq_printf(s, "\n"); + } + spin_unlock_irqrestore(&sync_timeline_list_lock, flags); + + seq_printf(s, "fences:\n--------------\n"); + + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_for_each(pos, &sync_fence_list_head) { + struct sync_fence *fence = + container_of(pos, struct sync_fence, sync_fence_list); + + sync_print_fence(s, fence); + seq_printf(s, "\n"); + } + spin_unlock_irqrestore(&sync_fence_list_lock, flags); + return 0; +} + +static int sync_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, sync_debugfs_show, inode->i_private); +} + +static const struct file_operations sync_debugfs_fops = { + .open = sync_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static __init int sync_debugfs_init(void) +{ + debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops); + return 0; +} + +late_initcall(sync_debugfs_init); + +#endif diff --git a/drivers/gpu/pvr/Kconfig b/drivers/gpu/pvr/Kconfig index 9bb3556..aa6139e 100644 --- a/drivers/gpu/pvr/Kconfig +++ b/drivers/gpu/pvr/Kconfig @@ -91,3 +91,17 @@ config PVR_PDUMP bool "Support for parameter dumping (Pdump)" depends on PVR_SGX default n + +config PVR_LINUX_MEM_AREA_POOL + bool "Enable uncached allocation pool" + depends on PVR_SGX + default n + +config PVR_LINUX_MEM_AREA_POOL_MAX_PAGES + int "Maximum number of pages in pool" + depends on PVR_LINUX_MEM_AREA_POOL + default 5400 + help + Pool size in pages. + A size of 0 disables the pool. + A size of -1 allows the pool to grow indefinitely. diff --git a/drivers/gpu/pvr/Makefile b/drivers/gpu/pvr/Makefile index 22d626e..d428d50 100644 --- a/drivers/gpu/pvr/Makefile +++ b/drivers/gpu/pvr/Makefile @@ -36,6 +36,8 @@ ccflags-y += \ -DPVRSRV_MODNAME="\"pvrsrvkm\"" \ -Idrivers/gpu/pvr/sgx \ -DSUPPORT_GET_DC_BUFFERS_SYS_PHYADDRS \ + -DPVRSRV_MMU_MAKE_READWRITE_ON_DEMAND \ + -DPVRSRV_AVOID_RANGED_INVALIDATE \ -DPVR_LDM_DRIVER_REGISTRATION_NAME="\"pvrsrvkm\"" ccflags-$(CONFIG_PVR_BUILD_RELEASE) += \ @@ -65,6 +67,10 @@ ccflags-$(CONFIG_PVR_DUMP_MK_TRACE) += -DPVRSRV_DUMP_MK_TRACE ccflags-$(CONFIG_PVR_PDUMP) += \ -DPDUMP -DSUPPORT_PDUMP_MULTI_PROCESS +ccflags-$(CONFIG_PVR_LINUX_MEM_AREA_POOL) += \ + -DPVR_LINUX_MEM_AREA_POOL_MAX_PAGES=CONFIG_PVR_LINUX_MEM_AREA_POOL_MAX_PAGES \ + -DPVR_LINUX_MEM_AREA_USE_VMAP -DPVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK + pvrsrvkm-y := \ osfunc.o \ mutils.o \ diff --git a/drivers/gpu/pvr/bridged_pvr_bridge.c b/drivers/gpu/pvr/bridged_pvr_bridge.c index 7a84ee8..b585b99 100644 --- a/drivers/gpu/pvr/bridged_pvr_bridge.c +++ b/drivers/gpu/pvr/bridged_pvr_bridge.c @@ -3568,40 +3568,6 @@ PVRSRVMapMemInfoMemBW(IMG_UINT32 ui32BridgeID, -static IMG_INT -MMU_GetPDDevPAddrBW(IMG_UINT32 ui32BridgeID, - PVRSRV_BRIDGE_IN_GETMMU_PD_DEVPADDR *psGetMmuPDDevPAddrIN, - PVRSRV_BRIDGE_OUT_GETMMU_PD_DEVPADDR *psGetMmuPDDevPAddrOUT, - PVRSRV_PER_PROCESS_DATA *psPerProc) -{ - IMG_HANDLE hDevMemContextInt; - - PVRSRV_BRIDGE_ASSERT_CMD(ui32BridgeID, PVRSRV_BRIDGE_GETMMU_PD_DEVPADDR); - - psGetMmuPDDevPAddrOUT->eError = - PVRSRVLookupHandle(psPerProc->psHandleBase, &hDevMemContextInt, - psGetMmuPDDevPAddrIN->hDevMemContext, - PVRSRV_HANDLE_TYPE_DEV_MEM_CONTEXT); - if(psGetMmuPDDevPAddrOUT->eError != PVRSRV_OK) - { - return 0; - } - - psGetMmuPDDevPAddrOUT->sPDDevPAddr = - BM_GetDeviceNode(hDevMemContextInt)->pfnMMUGetPDDevPAddr(BM_GetMMUContextFromMemContext(hDevMemContextInt)); - if(psGetMmuPDDevPAddrOUT->sPDDevPAddr.uiAddr) - { - psGetMmuPDDevPAddrOUT->eError = PVRSRV_OK; - } - else - { - psGetMmuPDDevPAddrOUT->eError = PVRSRV_ERROR_INVALID_PHYS_ADDR; - } - return 0; -} - - - IMG_INT DummyBW(IMG_UINT32 ui32BridgeID, IMG_VOID *psBridgeIn, @@ -4062,6 +4028,7 @@ static PVRSRV_ERROR ModifyCompleteSyncOpsCallBack(IMG_PVOID pvParam, OpFlushedComplete: DoModifyCompleteSyncOps(psModSyncOpInfo); + PVRSRVKernelSyncInfoDecRef(psModSyncOpInfo->psKernelSyncInfo, IMG_NULL); } OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(MODIFY_SYNC_OP_INFO), (IMG_VOID *)psModSyncOpInfo, 0); @@ -4146,6 +4113,8 @@ PVRSRVDestroySyncInfoModObjBW(IMG_UINT32 return 0; } + PVRSRVKernelSyncInfoDecRef(psModSyncOpInfo->psKernelSyncInfo, IMG_NULL); + psDestroySyncInfoModObjOUT->eError = PVRSRVReleaseHandle(psPerProc->psHandleBase, psDestroySyncInfoModObjIN->hKernelSyncInfoModObj, PVRSRV_HANDLE_TYPE_SYNC_INFO_MOD_OBJ); @@ -4207,6 +4176,15 @@ PVRSRVModifyPendingSyncOpsBW(IMG_UINT32 ui32BridgeID, } + if (psKernelSyncInfo == IMG_NULL) + { + psModifySyncOpsOUT->eError = PVRSRV_ERROR_INVALID_PARAMS; + PVR_DPF((PVR_DBG_VERBOSE, "PVRSRVModifyPendingSyncOpsBW: SyncInfo bad handle")); + return 0; + } + + PVRSRVKernelSyncInfoIncRef(psKernelSyncInfo, IMG_NULL); + psModSyncOpInfo->psKernelSyncInfo = psKernelSyncInfo; psModSyncOpInfo->ui32ModifyFlags = psModifySyncOpsIN->ui32ModifyFlags; psModSyncOpInfo->ui32ReadOpsPendingSnapShot = psKernelSyncInfo->psSyncData->ui32ReadOpsPending; @@ -4278,6 +4256,7 @@ PVRSRVModifyCompleteSyncOpsBW(IMG_UINT32 ui32BridgeID, return 0; } + PVRSRVKernelSyncInfoDecRef(psModSyncOpInfo->psKernelSyncInfo, IMG_NULL); psModSyncOpInfo->psKernelSyncInfo = IMG_NULL; @@ -4472,18 +4451,13 @@ FreeSyncInfoCallback(IMG_PVOID pvParam, IMG_BOOL bDummy) { PVRSRV_KERNEL_SYNC_INFO *psSyncInfo; - PVRSRV_ERROR eError; PVR_UNREFERENCED_PARAMETER(ui32Param); PVR_UNREFERENCED_PARAMETER(bDummy); psSyncInfo = (PVRSRV_KERNEL_SYNC_INFO *)pvParam; - eError = PVRSRVFreeSyncInfoKM(psSyncInfo); - if (eError != PVRSRV_OK) - { - return eError; - } + PVRSRVKernelSyncInfoDecRef(psSyncInfo, IMG_NULL); return PVRSRV_OK; } @@ -4546,7 +4520,7 @@ PVRSRVAllocSyncInfoBW(IMG_UINT32 ui32Bri allocsyncinfo_errorexit_freesyncinfo: - PVRSRVFreeSyncInfoKM(psSyncInfo); + PVRSRVKernelSyncInfoDecRef(psSyncInfo, IMG_NULL); allocsyncinfo_errorexit: @@ -4729,9 +4703,6 @@ CommonBridgeInit(IMG_VOID) SetDispatchTableEntry(PVRSRV_BRIDGE_FREE_SHARED_SYS_MEM, PVRSRVFreeSharedSysMemoryBW); SetDispatchTableEntry(PVRSRV_BRIDGE_MAP_MEMINFO_MEM, PVRSRVMapMemInfoMemBW); - - SetDispatchTableEntry(PVRSRV_BRIDGE_GETMMU_PD_DEVPADDR, MMU_GetPDDevPAddrBW); - SetDispatchTableEntry(PVRSRV_BRIDGE_INITSRV_CONNECT, &PVRSRVInitSrvConnectBW); SetDispatchTableEntry(PVRSRV_BRIDGE_INITSRV_DISCONNECT, &PVRSRVInitSrvDisconnectBW); diff --git a/drivers/gpu/pvr/buffer_manager.c b/drivers/gpu/pvr/buffer_manager.c index 27ed8a1..ee65df0 100644 --- a/drivers/gpu/pvr/buffer_manager.c +++ b/drivers/gpu/pvr/buffer_manager.c @@ -1764,16 +1764,7 @@ static IMG_UINT32 gXProcWorkaroundShareIndex = XPROC_WORKAROUND_BAD_SHAREINDEX; static IMG_UINT32 gXProcWorkaroundState = XPROC_WORKAROUND_UNKNOWN; -static struct { - IMG_UINT32 ui32RefCount; - IMG_UINT32 ui32AllocFlags; - IMG_UINT32 ui32Size; - IMG_UINT32 ui32PageSize; - RA_ARENA *psArena; - IMG_SYS_PHYADDR sSysPAddr; - IMG_VOID *pvCpuVAddr; - IMG_HANDLE hOSMemHandle; -} gXProcWorkaroundShareData[XPROC_WORKAROUND_NUM_SHAREABLES] = {{0}}; +XPROC_DATA gXProcWorkaroundShareData[XPROC_WORKAROUND_NUM_SHAREABLES] = {{0}}; PVRSRV_ERROR BM_XProcWorkaroundSetShareIndex(IMG_UINT32 ui32Index) { @@ -1888,7 +1879,7 @@ XProcWorkaroundAllocShareable(RA_ARENA *psArena, *ppvCpuVAddr = gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].pvCpuVAddr; *phOSMemHandle = gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].hOSMemHandle; - gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32RefCount++; + BM_XProcIndexAcquire(gXProcWorkaroundShareIndex); return PVRSRV_OK; } @@ -1969,7 +1960,7 @@ XProcWorkaroundAllocShareable(RA_ARENA *psArena, *ppvCpuVAddr = gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].pvCpuVAddr; *phOSMemHandle = gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].hOSMemHandle; - gXProcWorkaroundShareData[gXProcWorkaroundShareIndex].ui32RefCount++; + BM_XProcIndexAcquire(gXProcWorkaroundShareIndex); return PVRSRV_OK; } @@ -2009,52 +2000,78 @@ static PVRSRV_ERROR XProcWorkaroundHandleToSI(IMG_HANDLE hOSMemHandle, IMG_UINT3 return PVRSRV_OK; } -static IMG_VOID XProcWorkaroundFreeShareable(IMG_HANDLE hOSMemHandle) +#if defined(PVRSRV_REFCOUNT_DEBUG) +IMG_VOID _BM_XProcIndexAcquireDebug(const IMG_CHAR *pszFile, IMG_INT iLine, IMG_UINT32 ui32Index) +#else +IMG_VOID _BM_XProcIndexAcquire(IMG_UINT32 ui32Index) +#endif { - IMG_UINT32 ui32SI = (IMG_UINT32)((IMG_UINTPTR_T)hOSMemHandle & 0xffffU); - PVRSRV_ERROR eError; - - eError = XProcWorkaroundHandleToSI(hOSMemHandle, &ui32SI); - if (eError != PVRSRV_OK) - { - PVR_DPF((PVR_DBG_ERROR, "bad handle")); - return; - } +#if defined(PVRSRV_REFCOUNT_DEBUG) + PVRSRVBMXProcIncRef2(pszFile, iLine, ui32Index); +#else + PVRSRVBMXProcIncRef(ui32Index); +#endif +} - gXProcWorkaroundShareData[ui32SI].ui32RefCount--; +#if defined(PVRSRV_REFCOUNT_DEBUG) +IMG_VOID _BM_XProcIndexReleaseDebug(const IMG_CHAR *pszFile, IMG_INT iLine, IMG_UINT32 ui32Index) +#else +IMG_VOID _BM_XProcIndexRelease(IMG_UINT32 ui32Index) +#endif +{ +#if defined(PVRSRV_REFCOUNT_DEBUG) + PVRSRVBMXProcDecRef2(pszFile, iLine, ui32Index); +#else + PVRSRVBMXProcDecRef(ui32Index); +#endif PVR_DPF((PVR_DBG_VERBOSE, "Reduced refcount of SI[%d] from %d to %d", - ui32SI, gXProcWorkaroundShareData[ui32SI].ui32RefCount+1, gXProcWorkaroundShareData[ui32SI].ui32RefCount)); + ui32Index, gXProcWorkaroundShareData[ui32Index].ui32RefCount+1, gXProcWorkaroundShareData[ui32Index].ui32RefCount)); - if (gXProcWorkaroundShareData[ui32SI].ui32RefCount == 0) + if (gXProcWorkaroundShareData[ui32Index].ui32RefCount == 0) { - if (gXProcWorkaroundShareData[ui32SI].psArena != IMG_NULL) + if (gXProcWorkaroundShareData[ui32Index].psArena != IMG_NULL) { IMG_SYS_PHYADDR sSysPAddr; - if (gXProcWorkaroundShareData[ui32SI].pvCpuVAddr != IMG_NULL) + if (gXProcWorkaroundShareData[ui32Index].pvCpuVAddr != IMG_NULL) { - OSUnReservePhys(gXProcWorkaroundShareData[ui32SI].pvCpuVAddr, - gXProcWorkaroundShareData[ui32SI].ui32Size, - gXProcWorkaroundShareData[ui32SI].ui32AllocFlags, - gXProcWorkaroundShareData[ui32SI].hOSMemHandle); + OSUnReservePhys(gXProcWorkaroundShareData[ui32Index].pvCpuVAddr, + gXProcWorkaroundShareData[ui32Index].ui32Size, + gXProcWorkaroundShareData[ui32Index].ui32AllocFlags, + gXProcWorkaroundShareData[ui32Index].hOSMemHandle); } - sSysPAddr = gXProcWorkaroundShareData[ui32SI].sSysPAddr; - RA_Free (gXProcWorkaroundShareData[ui32SI].psArena, + sSysPAddr = gXProcWorkaroundShareData[ui32Index].sSysPAddr; + RA_Free (gXProcWorkaroundShareData[ui32Index].psArena, sSysPAddr.uiAddr, IMG_FALSE); } else { PVR_DPF((PVR_DBG_VERBOSE, "freeing OS memory")); - OSFreePages(gXProcWorkaroundShareData[ui32SI].ui32AllocFlags, - gXProcWorkaroundShareData[ui32SI].ui32PageSize, - gXProcWorkaroundShareData[ui32SI].pvCpuVAddr, - gXProcWorkaroundShareData[ui32SI].hOSMemHandle); + OSFreePages(gXProcWorkaroundShareData[ui32Index].ui32AllocFlags, + gXProcWorkaroundShareData[ui32Index].ui32PageSize, + gXProcWorkaroundShareData[ui32Index].pvCpuVAddr, + gXProcWorkaroundShareData[ui32Index].hOSMemHandle); } } } +static IMG_VOID XProcWorkaroundFreeShareable(IMG_HANDLE hOSMemHandle) +{ + IMG_UINT32 ui32SI = (IMG_UINT32)((IMG_UINTPTR_T)hOSMemHandle & 0xffffU); + PVRSRV_ERROR eError; + + eError = XProcWorkaroundHandleToSI(hOSMemHandle, &ui32SI); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "bad handle")); + return; + } + + BM_XProcIndexRelease(ui32SI); +} + static IMG_BOOL BM_ImportMemory (IMG_VOID *pH, diff --git a/drivers/gpu/pvr/buffer_manager.h b/drivers/gpu/pvr/buffer_manager.h index bf7c038..b78b0ae 100644 --- a/drivers/gpu/pvr/buffer_manager.h +++ b/drivers/gpu/pvr/buffer_manager.h @@ -116,8 +116,18 @@ struct _BM_CONTEXT_ struct _BM_CONTEXT_ **ppsThis; }; - - +typedef struct _XPROC_DATA_{ + IMG_UINT32 ui32RefCount; + IMG_UINT32 ui32AllocFlags; + IMG_UINT32 ui32Size; + IMG_UINT32 ui32PageSize; + RA_ARENA *psArena; + IMG_SYS_PHYADDR sSysPAddr; + IMG_VOID *pvCpuVAddr; + IMG_HANDLE hOSMemHandle; +} XPROC_DATA; + +extern XPROC_DATA gXProcWorkaroundShareData[]; typedef IMG_VOID *BM_HANDLE; #define BP_POOL_MASK 0x7 @@ -210,6 +220,25 @@ PVRSRV_ERROR BM_XProcWorkaroundSetShareIndex(IMG_UINT32 ui32Index); PVRSRV_ERROR BM_XProcWorkaroundUnsetShareIndex(IMG_UINT32 ui32Index); PVRSRV_ERROR BM_XProcWorkaroundFindNewBufferAndSetShareIndex(IMG_UINT32 *pui32Index); +#if defined(PVRSRV_REFCOUNT_DEBUG) +IMG_VOID _BM_XProcIndexAcquireDebug(const IMG_CHAR *pszFile, IMG_INT iLine, IMG_UINT32 ui32Index); +IMG_VOID _BM_XProcIndexReleaseDebug(const IMG_CHAR *pszFile, IMG_INT iLine, IMG_UINT32 ui32Index); + +#define BM_XProcIndexAcquire(x...) \ + _BM_XProcIndexAcquireDebug(__FILE__, __LINE__, x) +#define BM_XProcIndexRelease(x...) \ + _BM_XProcIndexReleaseDebug(__FILE__, __LINE__, x) + +#else +IMG_VOID _BM_XProcIndexAcquire(IMG_UINT32 ui32Index); +IMG_VOID _BM_XProcIndexRelease(IMG_UINT32 ui32Index); + +#define BM_XProcIndexAcquire(x...) \ + _BM_XProcIndexAcquire( x) +#define BM_XProcIndexRelease(x...) \ + _BM_XProcIndexRelease( x) +#endif + #if defined(__cplusplus) } diff --git a/drivers/gpu/pvr/deviceclass.c b/drivers/gpu/pvr/deviceclass.c index 2834287..233ac08 100644 --- a/drivers/gpu/pvr/deviceclass.c +++ b/drivers/gpu/pvr/deviceclass.c @@ -581,10 +581,6 @@ static PVRSRV_ERROR CloseDCDeviceCallBack(IMG_PVOID pvParam, psDCInfo->psFuncTable->pfnCloseDCDevice(psDCInfo->hExtDevice); PVRSRVKernelSyncInfoDecRef(psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); - if (psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo->ui32RefCount == 0) - { - PVRSRVFreeSyncInfoKM(psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo); - } psDCInfo->hDevMemContext = IMG_NULL; psDCInfo->hExtDevice = IMG_NULL; @@ -671,7 +667,7 @@ PVRSRV_ERROR PVRSRVOpenDCDeviceKM (PVRSRV_PER_PROCESS_DATA *psPerProc, { PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenDCDeviceKM: Failed to open external DC device")); psDCInfo->ui32RefCount--; - PVRSRVFreeSyncInfoKM(psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo); + PVRSRVKernelSyncInfoDecRef(psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); return eError; } @@ -681,11 +677,9 @@ PVRSRV_ERROR PVRSRVOpenDCDeviceKM (PVRSRV_PER_PROCESS_DATA *psPerProc, { PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenDCDeviceKM: Failed to get system buffer")); psDCInfo->ui32RefCount--; - PVRSRVFreeSyncInfoKM(psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo); + PVRSRVKernelSyncInfoDecRef(psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); return eError; } - - PVRSRVKernelSyncInfoIncRef(psDCInfo->sSystemBuffer.sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); psDCInfo->sSystemBuffer.sDeviceClassBuffer.ui32MemMapRefCount = 0; } else @@ -890,10 +884,6 @@ static PVRSRV_ERROR DestroyDCSwapChain(PVRSRV_DC_SWAPCHAIN *psSwapChain) if(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo) { PVRSRVKernelSyncInfoDecRef(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); - if (psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo->ui32RefCount == 0) - { - PVRSRVFreeSyncInfoKM(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo); - } } } @@ -1097,8 +1087,6 @@ PVRSRV_ERROR PVRSRVCreateDCSwapChainKM (PVRSRV_PER_PROCESS_DATA *psPerProc, goto ErrorExit; } - PVRSRVKernelSyncInfoIncRef(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); - psSwapChain->asBuffer[i].sDeviceClassBuffer.pfnGetBufferAddr = psDCInfo->psFuncTable->pfnGetBufferAddr; psSwapChain->asBuffer[i].sDeviceClassBuffer.hDevMemContext = psDCInfo->hDevMemContext; @@ -1198,10 +1186,6 @@ ErrorExit: if(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo) { PVRSRVKernelSyncInfoDecRef(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); - if (psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo->ui32RefCount == 0) - { - PVRSRVFreeSyncInfoKM(psSwapChain->asBuffer[i].sDeviceClassBuffer.psKernelSyncInfo); - } } } @@ -1620,7 +1604,6 @@ PVRSRV_ERROR PVRSRVSwapToDCBuffer2KM(IMG_HANDLE hDeviceKM, psCallbackData->pvPrivData = pvPrivData; psCallbackData->ui32PrivDataLength = ui32PrivDataLength; - if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(IMG_VOID *) * ui32NumMemSyncInfos, (IMG_VOID **)&ppvMemInfos, IMG_NULL, @@ -1662,7 +1645,6 @@ PVRSRV_ERROR PVRSRVSwapToDCBuffer2KM(IMG_HANDLE hDeviceKM, ui32NumCompiledSyncInfos = ui32NumMemSyncInfos + ui32NumUniqueSyncInfos; - if(OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(PVRSRV_KERNEL_SYNC_INFO *) * ui32NumCompiledSyncInfos, (IMG_VOID **)&ppsCompiledSyncInfos, IMG_NULL, @@ -1737,6 +1719,9 @@ PVRSRV_ERROR PVRSRVSwapToDCBuffer2KM(IMG_HANDLE hDeviceKM, psFlipCmd->ppsMemInfos = (PDC_MEM_INFO *)ppvMemInfos; psFlipCmd->ui32NumMemInfos = ui32NumMemSyncInfos; + + psFlipCmd->hUnused = IMG_NULL; + SysAcquireData(&psSysData); @@ -2137,10 +2122,6 @@ static PVRSRV_ERROR CloseBCDeviceCallBack(IMG_PVOID pvParam, if(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo) { PVRSRVKernelSyncInfoDecRef(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); - if (psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo->ui32RefCount == 0) - { - PVRSRVFreeSyncInfoKM(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo); - } } } @@ -2262,8 +2243,6 @@ PVRSRV_ERROR PVRSRVOpenBCDeviceKM (PVRSRV_PER_PROCESS_DATA *psPerProc, PVR_DPF((PVR_DBG_ERROR,"PVRSRVOpenBCDeviceKM: Failed sync info alloc")); goto ErrorExit; } - - PVRSRVKernelSyncInfoIncRef(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); @@ -2306,10 +2285,6 @@ ErrorExit: if(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo) { PVRSRVKernelSyncInfoDecRef(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo, IMG_NULL); - if (psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo->ui32RefCount == 0) - { - PVRSRVFreeSyncInfoKM(psBCInfo->psBuffer[i].sDeviceClassBuffer.psKernelSyncInfo); - } } } diff --git a/drivers/gpu/pvr/devicemem.c b/drivers/gpu/pvr/devicemem.c index cd22faa..8874d61 100644 --- a/drivers/gpu/pvr/devicemem.c +++ b/drivers/gpu/pvr/devicemem.c @@ -516,7 +516,14 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVAllocSyncInfoKM(IMG_HANDLE hDevCookie, return PVRSRV_ERROR_OUT_OF_MEMORY; } - psKernelSyncInfo->ui32RefCount = 0; + eError = OSAtomicAlloc(&psKernelSyncInfo->pvRefCount); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR,"PVRSRVAllocSyncInfoKM: Failed to allocate atomic")); + OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_SYNC_INFO), psKernelSyncInfo, IMG_NULL); + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + pBMContext = (BM_CONTEXT*)hDevMemContext; @@ -541,6 +548,7 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVAllocSyncInfoKM(IMG_HANDLE hDevCookie, { PVR_DPF((PVR_DBG_ERROR,"PVRSRVAllocSyncInfoKM: Failed to alloc memory")); + OSAtomicFree(psKernelSyncInfo->pvRefCount); OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_SYNC_INFO), psKernelSyncInfo, IMG_NULL); return PVRSRV_ERROR_OUT_OF_MEMORY; @@ -558,6 +566,7 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVAllocSyncInfoKM(IMG_HANDLE hDevCookie, psSyncData->ui32ReadOps2Complete = 0; psSyncData->ui32LastOpDumpVal = 0; psSyncData->ui32LastReadOpDumpVal = 0; + psSyncData->ui64LastWrite = 0; #if defined(PDUMP) PDUMPCOMMENT("Allocating kernel sync object"); @@ -577,34 +586,34 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVAllocSyncInfoKM(IMG_HANDLE hDevCookie, psKernelSyncInfo->psSyncDataMemInfoKM->psKernelSyncInfo = IMG_NULL; + OSAtomicInc(psKernelSyncInfo->pvRefCount); + *ppsKernelSyncInfo = psKernelSyncInfo; return PVRSRV_OK; } - IMG_EXPORT -PVRSRV_ERROR IMG_CALLCONV PVRSRVFreeSyncInfoKM(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo) +IMG_VOID PVRSRVAcquireSyncInfoKM(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo) { - PVRSRV_ERROR eError; + OSAtomicInc(psKernelSyncInfo->pvRefCount); +} - if (psKernelSyncInfo->ui32RefCount != 0) +IMG_EXPORT +IMG_VOID IMG_CALLCONV PVRSRVReleaseSyncInfoKM(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo) +{ + if (OSAtomicDecAndTest(psKernelSyncInfo->pvRefCount)) { - PVR_DPF((PVR_DBG_ERROR, "oops: sync info ref count not zero at destruction")); + FreeDeviceMem(psKernelSyncInfo->psSyncDataMemInfoKM); + + + psKernelSyncInfo->psSyncDataMemInfoKM = IMG_NULL; + psKernelSyncInfo->psSyncData = IMG_NULL; + OSAtomicFree(psKernelSyncInfo->pvRefCount); + (IMG_VOID)OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_SYNC_INFO), psKernelSyncInfo, IMG_NULL); - return PVRSRV_ERROR_OUT_OF_MEMORY; } - - eError = FreeDeviceMem(psKernelSyncInfo->psSyncDataMemInfoKM); - - - psKernelSyncInfo->psSyncDataMemInfoKM = IMG_NULL; - psKernelSyncInfo->psSyncData = IMG_NULL; - (IMG_VOID)OSFreeMem(PVRSRV_PAGEABLE_SELECT, sizeof(PVRSRV_KERNEL_SYNC_INFO), psKernelSyncInfo, IMG_NULL); - - - return eError; } static IMG_VOID freeWrapped(PVRSRV_KERNEL_MEM_INFO *psMemInfo) @@ -661,7 +670,7 @@ PVRSRV_ERROR _PollUntilAtLeast(volatile IMG_UINT32* pui32WatchedValue, if(psSysData->psGlobalEventObject) { eError = OSEventObjectOpenKM(psSysData->psGlobalEventObject, &hOSEventKM); - if (eError |= PVRSRV_OK) + if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "_PollUntilAtLeast: OSEventObjectOpen failed")); @@ -791,15 +800,11 @@ PVRSRV_ERROR FreeMemCallBackCommon(PVRSRV_KERNEL_MEM_INFO *psMemInfo, case PVRSRV_MEMTYPE_WRAPPED: freeWrapped(psMemInfo); case PVRSRV_MEMTYPE_DEVICE: + case PVRSRV_MEMTYPE_DEVICECLASS: if (psMemInfo->psKernelSyncInfo) { PVRSRVKernelSyncInfoDecRef(psMemInfo->psKernelSyncInfo, psMemInfo); - if (psMemInfo->psKernelSyncInfo->ui32RefCount == 0) - { - eError = PVRSRVFreeSyncInfoKM(psMemInfo->psKernelSyncInfo); - } } - case PVRSRV_MEMTYPE_DEVICECLASS: break; default: PVR_DPF((PVR_DBG_ERROR, "FreeMemCallBackCommon: Unknown memType")); @@ -921,8 +926,6 @@ PVRSRV_ERROR IMG_CALLCONV _PVRSRVAllocDeviceMemKM(IMG_HANDLE hDevCookie, { goto free_mainalloc; } - - PVRSRVKernelSyncInfoIncRef(psMemInfo->psKernelSyncInfo, psMemInfo); } @@ -957,6 +960,10 @@ PVRSRV_ERROR IMG_CALLCONV _PVRSRVAllocDeviceMemKM(IMG_HANDLE hDevCookie, return (PVRSRV_OK); free_mainalloc: + if (psMemInfo->psKernelSyncInfo) + { + PVRSRVKernelSyncInfoDecRef(psMemInfo->psKernelSyncInfo, psMemInfo); + } FreeDeviceMem(psMemInfo); return eError; @@ -1204,8 +1211,6 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVWrapExtMemoryKM(IMG_HANDLE hDevCookie, goto ErrorExitPhase4; } - PVRSRVKernelSyncInfoIncRef(psMemInfo->psKernelSyncInfo, psMemInfo); - PVRSRVKernelMemInfoIncRef(psMemInfo); @@ -1290,15 +1295,6 @@ static PVRSRV_ERROR UnmapDeviceMemoryCallBack(IMG_PVOID pvParam, if( psMapData->psMemInfo->psKernelSyncInfo ) { PVRSRVKernelSyncInfoDecRef(psMapData->psMemInfo->psKernelSyncInfo, psMapData->psMemInfo); - if (psMapData->psMemInfo->psKernelSyncInfo->ui32RefCount == 0) - { - eError = PVRSRVFreeSyncInfoKM(psMapData->psMemInfo->psKernelSyncInfo); - if(eError != PVRSRV_OK) - { - PVR_DPF((PVR_DBG_ERROR,"UnmapDeviceMemoryCallBack: Failed to free sync info")); - return eError; - } - } } eError = FreeDeviceMem(psMapData->psMemInfo); @@ -1723,6 +1719,12 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVMapDeviceClassMemoryKM(PVRSRV_PER_PROCESS_DATA * psMemInfo->uAllocSize = uByteSize; psMemInfo->psKernelSyncInfo = psDeviceClassBuffer->psKernelSyncInfo; + PVR_ASSERT(psMemInfo->psKernelSyncInfo != IMG_NULL); + if (psMemInfo->psKernelSyncInfo) + { + PVRSRVKernelSyncInfoIncRef(psMemInfo->psKernelSyncInfo, psMemInfo); + } + psMemInfo->pvSysBackupBuffer = IMG_NULL; @@ -1766,8 +1768,12 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVMapDeviceClassMemoryKM(PVRSRV_PER_PROCESS_DATA * #if defined(SUPPORT_PDUMP_MULTI_PROCESS) - PDUMPCOMMENT("Dump display surface"); - PDUMPMEM(IMG_NULL, psMemInfo, ui32Offset, psMemInfo->uAllocSize, PDUMP_FLAGS_CONTINUOUS, ((BM_BUF*)psMemInfo->sMemBlk.hBuffer)->pMapping); + if(psMemInfo->pvLinAddrKM) + { + + PDUMPCOMMENT("Dump display surface"); + PDUMPMEM(IMG_NULL, psMemInfo, ui32Offset, psMemInfo->uAllocSize, PDUMP_FLAGS_CONTINUOUS, ((BM_BUF*)psMemInfo->sMemBlk.hBuffer)->pMapping); + } #endif return PVRSRV_OK; @@ -1775,6 +1781,11 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVMapDeviceClassMemoryKM(PVRSRV_PER_PROCESS_DATA * ErrorExitPhase3: if(psMemInfo) { + if (psMemInfo->psKernelSyncInfo) + { + PVRSRVKernelSyncInfoDecRef(psMemInfo->psKernelSyncInfo, psMemInfo); + } + FreeDeviceMem(psMemInfo); diff --git a/drivers/gpu/pvr/ion.c b/drivers/gpu/pvr/ion.c index f897ffa..b00fdb1 100644 --- a/drivers/gpu/pvr/ion.c +++ b/drivers/gpu/pvr/ion.c @@ -43,10 +43,9 @@ extern struct ion_client *gpsIONClient; -struct ion_handle * -PVRSRVExportFDToIONHandle(int fd, struct ion_client **client) +void PVRSRVExportFDToIONHandles(int fd, struct ion_client **client, + struct ion_handle *handles[2]) { - struct ion_handle *psIONHandle = IMG_NULL; PVRSRV_FILE_PRIVATE_DATA *psPrivateData; PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; LinuxMemArea *psLinuxMemArea; @@ -89,7 +88,8 @@ PVRSRVExportFDToIONHandle(int fd, struct ion_client **client) goto err_fput; } - psIONHandle = psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle; + handles[0] = psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[0]; + handles[1] = psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[1]; if(client) *client = gpsIONClient; @@ -98,7 +98,15 @@ err_fput: err_unlock: /* Allow PVRSRV clients to communicate with srvkm again */ LinuxUnLockMutex(&gPVRSRVLock); - return psIONHandle; } +struct ion_handle * +PVRSRVExportFDToIONHandle(int fd, struct ion_client **client) +{ + struct ion_handle *psHandles[2] = { IMG_NULL, IMG_NULL }; + PVRSRVExportFDToIONHandles(fd, client, psHandles); + return psHandles[0]; +} + +EXPORT_SYMBOL(PVRSRVExportFDToIONHandles); EXPORT_SYMBOL(PVRSRVExportFDToIONHandle); diff --git a/drivers/gpu/pvr/ion.h b/drivers/gpu/pvr/ion.h index 13f20e6..9b0868c 100644 --- a/drivers/gpu/pvr/ion.h +++ b/drivers/gpu/pvr/ion.h @@ -30,6 +30,9 @@ #include <linux/ion.h> #include <linux/omap_ion.h> +void PVRSRVExportFDToIONHandles(int fd, struct ion_client **client, + struct ion_handle *handles[2]); + struct ion_handle *PVRSRVExportFDToIONHandle(int fd, struct ion_client **client); diff --git a/drivers/gpu/pvr/mm.c b/drivers/gpu/pvr/mm.c index 9054a55..80f0efe 100644 --- a/drivers/gpu/pvr/mm.c +++ b/drivers/gpu/pvr/mm.c @@ -24,6 +24,12 @@ * ******************************************************************************/ + + + + + + #include <linux/version.h> #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) @@ -32,6 +38,14 @@ #endif #endif +#if !defined(PVR_LINUX_MEM_AREA_POOL_MAX_PAGES) +#define PVR_LINUX_MEM_AREA_POOL_MAX_PAGES 0 +#endif + +#include <linux/kernel.h> +#include <asm/atomic.h> +#include <linux/list.h> +#include <linux/mutex.h> #include <linux/mm.h> #include <linux/vmalloc.h> #include <asm/io.h> @@ -42,6 +56,12 @@ #include <linux/highmem.h> #include <linux/sched.h> +#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) +#include <linux/shrinker.h> +#endif +#endif + #include "img_defs.h" #include "services.h" #include "servicesint.h" @@ -60,6 +80,8 @@ #include "lists.h" #endif +static atomic_t g_sPagePoolEntryCount = ATOMIC_INIT(0); + #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) typedef enum { DEBUG_MEM_ALLOC_TYPE_KMALLOC, @@ -68,9 +90,12 @@ typedef enum { DEBUG_MEM_ALLOC_TYPE_IOREMAP, DEBUG_MEM_ALLOC_TYPE_IO, DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE, - DEBUG_MEM_ALLOC_TYPE_COUNT, - DEBUG_MEM_ALLOC_TYPE_ION -}DEBUG_MEM_ALLOC_TYPE; + DEBUG_MEM_ALLOC_TYPE_ION, +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + DEBUG_MEM_ALLOC_TYPE_VMAP, +#endif + DEBUG_MEM_ALLOC_TYPE_COUNT +} DEBUG_MEM_ALLOC_TYPE; typedef struct _DEBUG_MEM_ALLOC_REC { @@ -86,7 +111,7 @@ typedef struct _DEBUG_MEM_ALLOC_REC struct _DEBUG_MEM_ALLOC_REC *psNext; struct _DEBUG_MEM_ALLOC_REC **ppsThis; -}DEBUG_MEM_ALLOC_REC; +} DEBUG_MEM_ALLOC_REC; static IMPLEMENT_LIST_ANY_VA_2(DEBUG_MEM_ALLOC_REC, IMG_BOOL, IMG_FALSE) static IMPLEMENT_LIST_ANY_VA(DEBUG_MEM_ALLOC_REC) @@ -100,8 +125,14 @@ static DEBUG_MEM_ALLOC_REC *g_MemoryRecords; static IMG_UINT32 g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_COUNT]; static IMG_UINT32 g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_COUNT]; -static IMG_UINT32 g_SysRAMWaterMark; -static IMG_UINT32 g_SysRAMHighWaterMark; +static IMG_UINT32 g_SysRAMWaterMark; +static IMG_UINT32 g_SysRAMHighWaterMark; + +static inline IMG_UINT32 +SysRAMTrueWaterMark(void) +{ + return g_SysRAMWaterMark + PAGES_TO_BYTES(atomic_read(&g_sPagePoolEntryCount)); +} static IMG_UINT32 g_IOMemWaterMark; static IMG_UINT32 g_IOMemHighWaterMark; @@ -120,7 +151,7 @@ static IMG_VOID DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE eAllocType, IMG_V static IMG_CHAR *DebugMemAllocRecordTypeToString(DEBUG_MEM_ALLOC_TYPE eAllocType); -static struct proc_dir_entry *g_SeqFileMemoryRecords =0; +static struct proc_dir_entry *g_SeqFileMemoryRecords; static void* ProcSeqNextMemoryRecords(struct seq_file *sfile,void* el,loff_t off); static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el); static void* ProcSeqOff2ElementMemoryRecords(struct seq_file * sfile, loff_t off); @@ -154,7 +185,7 @@ static IMG_UINT32 g_LinuxMemAreaWaterMark; static IMG_UINT32 g_LinuxMemAreaHighWaterMark; -static struct proc_dir_entry *g_SeqFileMemArea=0; +static struct proc_dir_entry *g_SeqFileMemArea; static void* ProcSeqNextMemArea(struct seq_file *sfile,void* el,loff_t off); static void ProcSeqShowMemArea(struct seq_file *sfile,void* el); @@ -170,8 +201,19 @@ static PVRSRV_LINUX_MUTEX g_sDebugMutex; static void ProcSeqStartstopDebugMutex(struct seq_file *sfile,IMG_BOOL start); #endif -static LinuxKMemCache *psLinuxMemAreaCache; +typedef struct +{ + + struct list_head sPagePoolItem; + struct page *psPage; +} LinuxPagePoolEntry; + +static LinuxKMemCache *g_PsLinuxMemAreaCache; +static LinuxKMemCache *g_PsLinuxPagePoolCache; + +static LIST_HEAD(g_sPagePoolList); +static int g_iPagePoolMaxEntries; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) static IMG_VOID ReservePages(IMG_VOID *pvAddress, IMG_UINT32 ui32Length); @@ -186,165 +228,26 @@ static DEBUG_LINUX_MEM_AREA_REC *DebugLinuxMemAreaRecordFind(LinuxMemArea *psLin static IMG_VOID DebugLinuxMemAreaRecordRemove(LinuxMemArea *psLinuxMemArea); #endif -PVRSRV_ERROR -LinuxMMInit(IMG_VOID) -{ -#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - LinuxInitMutex(&g_sDebugMutex); -#endif - -#if defined(DEBUG_LINUX_MEM_AREAS) - { - g_SeqFileMemArea = CreateProcReadEntrySeq( - "mem_areas", - NULL, - ProcSeqNextMemArea, - ProcSeqShowMemArea, - ProcSeqOff2ElementMemArea, - ProcSeqStartstopDebugMutex - ); - if(!g_SeqFileMemArea) - { - return PVRSRV_ERROR_OUT_OF_MEMORY; - } - } -#endif - -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - { - g_SeqFileMemoryRecords =CreateProcReadEntrySeq( - "meminfo", - NULL, - ProcSeqNextMemoryRecords, - ProcSeqShowMemoryRecords, - ProcSeqOff2ElementMemoryRecords, - ProcSeqStartstopDebugMutex - ); - if(!g_SeqFileMemoryRecords) - { - return PVRSRV_ERROR_OUT_OF_MEMORY; - } - } -#endif - - psLinuxMemAreaCache = KMemCacheCreateWrapper("img-mm", sizeof(LinuxMemArea), 0, 0); - if(!psLinuxMemAreaCache) - { - PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate kmem_cache", __FUNCTION__)); - return PVRSRV_ERROR_OUT_OF_MEMORY; - } - - return PVRSRV_OK; -} - -#if defined(DEBUG_LINUX_MEM_AREAS) -static IMG_VOID LinuxMMCleanup_MemAreas_ForEachCb(DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord) +static inline IMG_BOOL +AreaIsUncached(IMG_UINT32 ui32AreaFlags) { - LinuxMemArea *psLinuxMemArea; - - psLinuxMemArea = psCurrentRecord->psLinuxMemArea; - PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: Cleaning up Linux memory area (%p), type=%s, size=%d bytes", - __FUNCTION__, - psCurrentRecord->psLinuxMemArea, - LinuxMemAreaTypeToString(psCurrentRecord->psLinuxMemArea->eAreaType), - psCurrentRecord->psLinuxMemArea->ui32ByteSize)); - - LinuxMemAreaDeepFree(psLinuxMemArea); -} -#endif - -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) -static IMG_VOID LinuxMMCleanup_MemRecords_ForEachVa(DEBUG_MEM_ALLOC_REC *psCurrentRecord) - -{ - - PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: Cleaning up memory: " - "type=%s " - "CpuVAddr=%p " - "CpuPAddr=0x%08x, " - "allocated @ file=%s,line=%d", - __FUNCTION__, - DebugMemAllocRecordTypeToString(psCurrentRecord->eAllocType), - psCurrentRecord->pvCpuVAddr, - psCurrentRecord->ulCpuPAddr, - psCurrentRecord->pszFileName, - psCurrentRecord->ui32Line)); - switch(psCurrentRecord->eAllocType) - { - case DEBUG_MEM_ALLOC_TYPE_KMALLOC: - KFreeWrapper(psCurrentRecord->pvCpuVAddr); - break; - case DEBUG_MEM_ALLOC_TYPE_IOREMAP: - IOUnmapWrapper(psCurrentRecord->pvCpuVAddr); - break; - case DEBUG_MEM_ALLOC_TYPE_IO: - - DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_IO, psCurrentRecord->pvKey, __FILE__, __LINE__); - break; - case DEBUG_MEM_ALLOC_TYPE_VMALLOC: - VFreeWrapper(psCurrentRecord->pvCpuVAddr); - break; - case DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES: - - DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, psCurrentRecord->pvKey, __FILE__, __LINE__); - break; - case DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE: - KMemCacheFreeWrapper(psCurrentRecord->pvPrivateData, psCurrentRecord->pvCpuVAddr); - break; - default: - PVR_ASSERT(0); - } + return (ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) != 0; } -#endif - -IMG_VOID -LinuxMMCleanup(IMG_VOID) +static inline IMG_BOOL +CanFreeToPool(LinuxMemArea *psLinuxMemArea) { - -#if defined(DEBUG_LINUX_MEM_AREAS) - { - if(g_LinuxMemAreaCount) - { - PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: There are %d LinuxMemArea allocation unfreed (%d bytes)", - __FUNCTION__, g_LinuxMemAreaCount, g_LinuxMemAreaWaterMark)); - } - - List_DEBUG_LINUX_MEM_AREA_REC_ForEach(g_LinuxMemAreaRecords, - LinuxMMCleanup_MemAreas_ForEachCb); - - RemoveProcEntrySeq( g_SeqFileMemArea ); - } -#endif - - -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - { - - - List_DEBUG_MEM_ALLOC_REC_ForEach(g_MemoryRecords, - LinuxMMCleanup_MemRecords_ForEachVa); - - RemoveProcEntrySeq( g_SeqFileMemoryRecords ); - } -#endif - - if(psLinuxMemAreaCache) - { - KMemCacheDestroyWrapper(psLinuxMemAreaCache); - psLinuxMemAreaCache=NULL; - } + return AreaIsUncached(psLinuxMemArea->ui32AreaFlags) && !psLinuxMemArea->bNeedsCacheInvalidate; } - IMG_VOID * _KMallocWrapper(IMG_UINT32 ui32ByteSize, gfp_t uFlags, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) { IMG_VOID *pvRet; pvRet = kmalloc(ui32ByteSize, uFlags); #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - if(pvRet) + if (pvRet) { DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_KMALLOC, pvRet, @@ -354,7 +257,7 @@ _KMallocWrapper(IMG_UINT32 ui32ByteSize, gfp_t uFlags, IMG_CHAR *pszFileName, IM ui32ByteSize, pszFileName, ui32Line - ); + ); } #else PVR_UNREFERENCED_PARAMETER(pszFileName); @@ -407,27 +310,31 @@ DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE eAllocType, List_DEBUG_MEM_ALLOC_REC_Insert(&g_MemoryRecords, psRecord); g_WaterMarkData[eAllocType] += ui32Bytes; - if(g_WaterMarkData[eAllocType] > g_HighWaterMarkData[eAllocType]) + if (g_WaterMarkData[eAllocType] > g_HighWaterMarkData[eAllocType]) { g_HighWaterMarkData[eAllocType] = g_WaterMarkData[eAllocType]; } - if(eAllocType == DEBUG_MEM_ALLOC_TYPE_KMALLOC + if (eAllocType == DEBUG_MEM_ALLOC_TYPE_KMALLOC || eAllocType == DEBUG_MEM_ALLOC_TYPE_VMALLOC || eAllocType == DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES || eAllocType == DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE) { + IMG_UINT32 ui32SysRAMTrueWaterMark; + g_SysRAMWaterMark += ui32Bytes; - if(g_SysRAMWaterMark > g_SysRAMHighWaterMark) + ui32SysRAMTrueWaterMark = SysRAMTrueWaterMark(); + + if (ui32SysRAMTrueWaterMark > g_SysRAMHighWaterMark) { - g_SysRAMHighWaterMark = g_SysRAMWaterMark; + g_SysRAMHighWaterMark = ui32SysRAMTrueWaterMark; } } - else if(eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP + else if (eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP || eAllocType == DEBUG_MEM_ALLOC_TYPE_IO) { g_IOMemWaterMark += ui32Bytes; - if(g_IOMemWaterMark > g_IOMemHighWaterMark) + if (g_IOMemWaterMark > g_IOMemHighWaterMark) { g_IOMemHighWaterMark = g_IOMemWaterMark; } @@ -445,20 +352,20 @@ static IMG_BOOL DebugMemAllocRecordRemove_AnyVaCb(DEBUG_MEM_ALLOC_REC *psCurrent eAllocType = va_arg(va, DEBUG_MEM_ALLOC_TYPE); pvKey = va_arg(va, IMG_VOID*); - if(psCurrentRecord->eAllocType == eAllocType + if (psCurrentRecord->eAllocType == eAllocType && psCurrentRecord->pvKey == pvKey) { eAllocType = psCurrentRecord->eAllocType; g_WaterMarkData[eAllocType] -= psCurrentRecord->ui32Bytes; - if(eAllocType == DEBUG_MEM_ALLOC_TYPE_KMALLOC + if (eAllocType == DEBUG_MEM_ALLOC_TYPE_KMALLOC || eAllocType == DEBUG_MEM_ALLOC_TYPE_VMALLOC || eAllocType == DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES || eAllocType == DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE) { g_SysRAMWaterMark -= psCurrentRecord->ui32Bytes; } - else if(eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP + else if (eAllocType == DEBUG_MEM_ALLOC_TYPE_IOREMAP || eAllocType == DEBUG_MEM_ALLOC_TYPE_IO) { g_IOMemWaterMark -= psCurrentRecord->ui32Bytes; @@ -482,7 +389,7 @@ DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE eAllocType, IMG_VOID *pvKey, IMG_ LinuxLockMutex(&g_sDebugMutex); - if(!List_DEBUG_MEM_ALLOC_REC_IMG_BOOL_Any_va(g_MemoryRecords, + if (!List_DEBUG_MEM_ALLOC_REC_IMG_BOOL_Any_va(g_MemoryRecords, DebugMemAllocRecordRemove_AnyVaCb, eAllocType, pvKey)) @@ -505,24 +412,22 @@ DebugMemAllocRecordTypeToString(DEBUG_MEM_ALLOC_TYPE eAllocType) "ALLOC_PAGES", "IOREMAP", "IO", - "KMEM_CACHE_ALLOC" + "KMEM_CACHE_ALLOC", +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + "VMAP" +#endif }; return apszDebugMemoryRecordTypes[eAllocType]; } #endif - -IMG_VOID * -_VMallocWrapper(IMG_UINT32 ui32Bytes, - IMG_UINT32 ui32AllocFlags, - IMG_CHAR *pszFileName, - IMG_UINT32 ui32Line) +static IMG_BOOL +AllocFlagsToPGProt(pgprot_t *pPGProtFlags, IMG_UINT32 ui32AllocFlags) { pgprot_t PGProtFlags; - IMG_VOID *pvRet; - - switch(ui32AllocFlags & PVRSRV_HAP_CACHETYPE_MASK) + + switch (ui32AllocFlags & PVRSRV_HAP_CACHETYPE_MASK) { case PVRSRV_HAP_CACHED: PGProtFlags = PAGE_KERNEL; @@ -535,9 +440,28 @@ _VMallocWrapper(IMG_UINT32 ui32Bytes, break; default: PVR_DPF((PVR_DBG_ERROR, - "VMAllocWrapper: unknown mapping flags=0x%08x", - ui32AllocFlags)); + "%s: Unknown mapping flags=0x%08x", + __FUNCTION__, ui32AllocFlags)); dump_stack(); + return IMG_FALSE; + } + + *pPGProtFlags = PGProtFlags; + + return IMG_TRUE; +} + +IMG_VOID * +_VMallocWrapper(IMG_UINT32 ui32Bytes, + IMG_UINT32 ui32AllocFlags, + IMG_CHAR *pszFileName, + IMG_UINT32 ui32Line) +{ + pgprot_t PGProtFlags; + IMG_VOID *pvRet; + + if (!AllocFlagsToPGProt(&PGProtFlags, ui32AllocFlags)) + { return NULL; } @@ -545,7 +469,7 @@ _VMallocWrapper(IMG_UINT32 ui32Bytes, pvRet = __vmalloc(ui32Bytes, GFP_KERNEL | __GFP_HIGHMEM, PGProtFlags); #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - if(pvRet) + if (pvRet) { DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_VMALLOC, pvRet, @@ -555,7 +479,7 @@ _VMallocWrapper(IMG_UINT32 ui32Bytes, PAGE_ALIGN(ui32Bytes), pszFileName, ui32Line - ); + ); } #else PVR_UNREFERENCED_PARAMETER(pszFileName); @@ -579,31 +503,475 @@ _VFreeWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) } +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) +static IMG_VOID * +_VMapWrapper(struct page **ppsPageList, IMG_UINT32 ui32NumPages, IMG_UINT32 ui32AllocFlags, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ + pgprot_t PGProtFlags; + IMG_VOID *pvRet; + + if (!AllocFlagsToPGProt(&PGProtFlags, ui32AllocFlags)) + { + return NULL; + } + + pvRet = vmap(ppsPageList, ui32NumPages, GFP_KERNEL | __GFP_HIGHMEM, PGProtFlags); + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + if (pvRet) + { + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_VMAP, + pvRet, + pvRet, + 0, + NULL, + PAGES_TO_BYTES(ui32NumPages), + pszFileName, + ui32Line + ); + } +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + + return pvRet; +} + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags) _VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags, __FILE__, __LINE__) +#else +#define VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags) _VMapWrapper(ppsPageList, ui32Bytes, ui32AllocFlags, NULL, 0) +#endif + + +static IMG_VOID +_VUnmapWrapper(IMG_VOID *pvCpuVAddr, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_VMAP, pvCpuVAddr, pszFileName, ui32Line); +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + vunmap(pvCpuVAddr); +} + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +#define VUnmapWrapper(pvCpuVAddr) _VUnmapWrapper(pvCpuVAddr, __FILE__, __LINE__) +#else +#define VUnmapWrapper(pvCpuVAddr) _VUnmapWrapper(pvCpuVAddr, NULL, 0) +#endif + +#endif + + +IMG_VOID +_KMemCacheFreeWrapper(LinuxKMemCache *psCache, IMG_VOID *pvObject, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) +{ +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE, pvObject, pszFileName, ui32Line); +#else + PVR_UNREFERENCED_PARAMETER(pszFileName); + PVR_UNREFERENCED_PARAMETER(ui32Line); +#endif + + kmem_cache_free(psCache, pvObject); +} + + +const IMG_CHAR * +KMemCacheNameWrapper(LinuxKMemCache *psCache) +{ + PVR_UNREFERENCED_PARAMETER(psCache); + + + return ""; +} + + +static LinuxPagePoolEntry * +LinuxPagePoolEntryAlloc(IMG_VOID) +{ + return KMemCacheAllocWrapper(g_PsLinuxPagePoolCache, GFP_KERNEL); +} + +static IMG_VOID +LinuxPagePoolEntryFree(LinuxPagePoolEntry *psPagePoolEntry) +{ + KMemCacheFreeWrapper(g_PsLinuxPagePoolCache, psPagePoolEntry); +} + + +static struct page * +AllocPageFromLinux(void) +{ + struct page *psPage; + + psPage = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, 0); + if (!psPage) + { + return NULL; + + } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) + SetPageReserved(psPage); +#else + mem_map_reserve(psPage); +#endif +#endif + return psPage; +} + + +static IMG_VOID +FreePageToLinux(struct page *psPage) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) + ClearPageReserved(psPage); +#else + mem_map_reserve(psPage); +#endif +#endif + __free_pages(psPage, 0); +} + + +#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) +static DEFINE_MUTEX(g_sPagePoolMutex); + +static inline void +PagePoolLock(void) +{ + mutex_lock(&g_sPagePoolMutex); +} + +static inline void +PagePoolUnlock(void) +{ + mutex_unlock(&g_sPagePoolMutex); +} + +static inline int +PagePoolTrylock(void) +{ + return mutex_trylock(&g_sPagePoolMutex); +} + +#else +static inline void +PagePoolLock(void) +{ +} + +static inline void +PagePoolUnlock(void) +{ +} + +static inline int +PagePoolTrylock(void) +{ + return 1; +} +#endif + + +static inline void +AddEntryToPool(LinuxPagePoolEntry *psPagePoolEntry) +{ + list_add_tail(&psPagePoolEntry->sPagePoolItem, &g_sPagePoolList); + atomic_inc(&g_sPagePoolEntryCount); +} + +static inline void +RemoveEntryFromPool(LinuxPagePoolEntry *psPagePoolEntry) +{ + list_del(&psPagePoolEntry->sPagePoolItem); + atomic_dec(&g_sPagePoolEntryCount); +} + +static inline LinuxPagePoolEntry * +RemoveFirstEntryFromPool(void) +{ + LinuxPagePoolEntry *psPagePoolEntry; + + if (list_empty(&g_sPagePoolList)) + { + PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0); + + return NULL; + } + + PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) > 0); + + psPagePoolEntry = list_first_entry(&g_sPagePoolList, LinuxPagePoolEntry, sPagePoolItem); + + RemoveEntryFromPool(psPagePoolEntry); + + return psPagePoolEntry; +} + +static struct page * +AllocPage(IMG_UINT32 ui32AreaFlags, IMG_BOOL *pbFromPagePool) +{ + struct page *psPage = NULL; + + + if (AreaIsUncached(ui32AreaFlags) && atomic_read(&g_sPagePoolEntryCount) != 0) + { + LinuxPagePoolEntry *psPagePoolEntry; + + PagePoolLock(); + psPagePoolEntry = RemoveFirstEntryFromPool(); + PagePoolUnlock(); + + + if (psPagePoolEntry) + { + psPage = psPagePoolEntry->psPage; + LinuxPagePoolEntryFree(psPagePoolEntry); + *pbFromPagePool = IMG_TRUE; + } + } + + if (!psPage) + { + psPage = AllocPageFromLinux(); + if (psPage) + { + *pbFromPagePool = IMG_FALSE; + } + } + + return psPage; + +} + +static IMG_VOID +FreePage(IMG_BOOL bToPagePool, struct page *psPage) +{ + + if (bToPagePool && atomic_read(&g_sPagePoolEntryCount) < g_iPagePoolMaxEntries) + { + LinuxPagePoolEntry *psPagePoolEntry = LinuxPagePoolEntryAlloc(); + if (psPagePoolEntry) + { + psPagePoolEntry->psPage = psPage; + + PagePoolLock(); + AddEntryToPool(psPagePoolEntry); + PagePoolUnlock(); + + return; + } + } + + FreePageToLinux(psPage); +} + +static IMG_VOID +FreePagePool(IMG_VOID) +{ + LinuxPagePoolEntry *psPagePoolEntry, *psTempPoolEntry; + + PagePoolLock(); + +#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) + PVR_TRACE(("%s: Freeing %d pages from pool", __FUNCTION__, atomic_read(&g_sPagePoolEntryCount))); +#else + PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0); + PVR_ASSERT(list_empty(&g_sPagePoolList)); +#endif + + list_for_each_entry_safe(psPagePoolEntry, psTempPoolEntry, &g_sPagePoolList, sPagePoolItem) + { + RemoveEntryFromPool(psPagePoolEntry); + + FreePageToLinux(psPagePoolEntry->psPage); + LinuxPagePoolEntryFree(psPagePoolEntry); + } + + PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0); + + PagePoolUnlock(); +} + +#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK) +#if defined(PVRSRV_NEED_PVR_ASSERT) +static struct shrinker g_sShrinker; +#endif + +static int +ShrinkPagePool(struct shrinker *psShrinker, struct shrink_control *psShrinkControl) +{ + unsigned long uNumToScan = psShrinkControl->nr_to_scan; + + PVR_ASSERT(psShrinker == &g_sShrinker); + (void)psShrinker; + + if (uNumToScan != 0) + { + LinuxPagePoolEntry *psPagePoolEntry, *psTempPoolEntry; + + PVR_TRACE(("%s: Number to scan: %ld", __FUNCTION__, uNumToScan)); + PVR_TRACE(("%s: Pages in pool before scan: %d", __FUNCTION__, atomic_read(&g_sPagePoolEntryCount))); + + if (!PagePoolTrylock()) + { + PVR_TRACE(("%s: Couldn't get page pool lock", __FUNCTION__)); + return -1; + } + + list_for_each_entry_safe(psPagePoolEntry, psTempPoolEntry, &g_sPagePoolList, sPagePoolItem) + { + RemoveEntryFromPool(psPagePoolEntry); + + FreePageToLinux(psPagePoolEntry->psPage); + LinuxPagePoolEntryFree(psPagePoolEntry); + + if (--uNumToScan == 0) + { + break; + } + } + + if (list_empty(&g_sPagePoolList)) + { + PVR_ASSERT(atomic_read(&g_sPagePoolEntryCount) == 0); + } + + PagePoolUnlock(); + + PVR_TRACE(("%s: Pages in pool after scan: %d", __FUNCTION__, atomic_read(&g_sPagePoolEntryCount))); + } + + return atomic_read(&g_sPagePoolEntryCount); +} +#endif + +static IMG_BOOL +AllocPages(IMG_UINT32 ui32AreaFlags, struct page ***pppsPageList, IMG_HANDLE *phBlockPageList, IMG_UINT32 ui32NumPages, IMG_BOOL *pbFromPagePool) +{ + struct page **ppsPageList; + IMG_HANDLE hBlockPageList; + IMG_INT32 i; + PVRSRV_ERROR eError; + IMG_BOOL bFromPagePool = IMG_FALSE; + + eError = OSAllocMem(0, sizeof(*ppsPageList) * ui32NumPages, (IMG_VOID **)&ppsPageList, &hBlockPageList, + "Array of pages"); + if (eError != PVRSRV_OK) + { + goto failed_page_list_alloc; + } + + *pbFromPagePool = IMG_TRUE; + for(i = 0; i < (IMG_INT32)ui32NumPages; i++) + { + ppsPageList[i] = AllocPage(ui32AreaFlags, &bFromPagePool); + if (!ppsPageList[i]) + { + goto failed_alloc_pages; + } + *pbFromPagePool &= bFromPagePool; + } + + *pppsPageList = ppsPageList; + *phBlockPageList = hBlockPageList; + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, + ppsPageList, + 0, + 0, + NULL, + PAGES_TO_BYTES(ui32NumPages), + "unknown", + 0 + ); +#endif + + return IMG_TRUE; + +failed_alloc_pages: + for(i--; i >= 0; i--) + { + FreePage(*pbFromPagePool, ppsPageList[i]); + } + (IMG_VOID) OSFreeMem(0, sizeof(*ppsPageList) * ui32NumPages, ppsPageList, hBlockPageList); + +failed_page_list_alloc: + return IMG_FALSE; +} + + +static IMG_VOID +FreePages(IMG_BOOL bToPagePool, struct page **ppsPageList, IMG_HANDLE hBlockPageList, IMG_UINT32 ui32NumPages) +{ + IMG_INT32 i; + + for(i = 0; i < (IMG_INT32)ui32NumPages; i++) + { + FreePage(bToPagePool, ppsPageList[i]); + } + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, ppsPageList, __FILE__, __LINE__); +#endif + + (IMG_VOID) OSFreeMem(0, sizeof(*ppsPageList) * ui32NumPages, ppsPageList, hBlockPageList); +} + + LinuxMemArea * NewVMallocLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) { - LinuxMemArea *psLinuxMemArea; + LinuxMemArea *psLinuxMemArea = NULL; IMG_VOID *pvCpuVAddr; +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + IMG_UINT32 ui32NumPages = 0; + struct page **ppsPageList = NULL; + IMG_HANDLE hBlockPageList; +#endif + IMG_BOOL bFromPagePool = IMG_FALSE; psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { goto failed; } +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + ui32NumPages = RANGE_TO_PAGES(ui32Bytes); + + if (!AllocPages(ui32AreaFlags, &ppsPageList, &hBlockPageList, ui32NumPages, &bFromPagePool)) + { + goto failed; + } + + pvCpuVAddr = VMapWrapper(ppsPageList, ui32NumPages, ui32AreaFlags); +#else pvCpuVAddr = VMallocWrapper(ui32Bytes, ui32AreaFlags); - if(!pvCpuVAddr) + if (!pvCpuVAddr) { goto failed; } - #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) ReservePages(pvCpuVAddr, ui32Bytes); #endif +#endif psLinuxMemArea->eAreaType = LINUX_MEM_AREA_VMALLOC; psLinuxMemArea->uData.sVmalloc.pvVmallocAddress = pvCpuVAddr; +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + psLinuxMemArea->uData.sVmalloc.ppsPageList = ppsPageList; + psLinuxMemArea->uData.sVmalloc.hBlockPageList = hBlockPageList; +#endif psLinuxMemArea->ui32ByteSize = ui32Bytes; psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); @@ -613,15 +981,30 @@ NewVMallocLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) #endif - if(ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) + if (AreaIsUncached(ui32AreaFlags) && !bFromPagePool) + { +#if !defined(PVRSRV_AVOID_RANGED_INVALIDATE) OSInvalidateCPUCacheRangeKM(psLinuxMemArea, pvCpuVAddr, ui32Bytes); +#else + OSFlushCPUCacheKM(); +#endif + } return psLinuxMemArea; failed: PVR_DPF((PVR_DBG_ERROR, "%s: failed!", __FUNCTION__)); - if(psLinuxMemArea) +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + if (ppsPageList) + { + FreePages(bFromPagePool, ppsPageList, hBlockPageList, ui32NumPages); + } +#endif + if (psLinuxMemArea) + { LinuxMemAreaStructFree(psLinuxMemArea); + } + return NULL; } @@ -629,6 +1012,12 @@ failed: IMG_VOID FreeVMallocLinuxMemArea(LinuxMemArea *psLinuxMemArea) { +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + IMG_UINT32 ui32NumPages; + struct page **ppsPageList; + IMG_HANDLE hBlockPageList; +#endif + PVR_ASSERT(psLinuxMemArea); PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_VMALLOC); PVR_ASSERT(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress); @@ -637,14 +1026,25 @@ FreeVMallocLinuxMemArea(LinuxMemArea *psLinuxMemArea) DebugLinuxMemAreaRecordRemove(psLinuxMemArea); #endif + PVR_DPF((PVR_DBG_MESSAGE,"%s: pvCpuVAddr: %p", + __FUNCTION__, psLinuxMemArea->uData.sVmalloc.pvVmallocAddress)); + +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + VUnmapWrapper(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress); + + ui32NumPages = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize); + ppsPageList = psLinuxMemArea->uData.sVmalloc.ppsPageList; + hBlockPageList = psLinuxMemArea->uData.sVmalloc.hBlockPageList; + + FreePages(CanFreeToPool(psLinuxMemArea), ppsPageList, hBlockPageList, ui32NumPages); +#else #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) - UnreservePages(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress, + UnreservePages(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress, psLinuxMemArea->ui32ByteSize); #endif - PVR_DPF((PVR_DBG_MESSAGE,"%s: pvCpuVAddr: %p", - __FUNCTION__, psLinuxMemArea->uData.sVmalloc.pvVmallocAddress)); VFreeWrapper(psLinuxMemArea->uData.sVmalloc.pvVmallocAddress); +#endif LinuxMemAreaStructFree(psLinuxMemArea); } @@ -695,7 +1095,7 @@ _IORemapWrapper(IMG_CPU_PHYADDR BasePAddr, { IMG_VOID *pvIORemapCookie; - switch(ui32MappingFlags & PVRSRV_HAP_CACHETYPE_MASK) + switch (ui32MappingFlags & PVRSRV_HAP_CACHETYPE_MASK) { case PVRSRV_HAP_CACHED: pvIORemapCookie = (IMG_VOID *)IOREMAP(BasePAddr.uiAddr, ui32Bytes); @@ -712,7 +1112,7 @@ _IORemapWrapper(IMG_CPU_PHYADDR BasePAddr, } #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - if(pvIORemapCookie) + if (pvIORemapCookie) { DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_IOREMAP, pvIORemapCookie, @@ -722,7 +1122,7 @@ _IORemapWrapper(IMG_CPU_PHYADDR BasePAddr, ui32Bytes, pszFileName, ui32Line - ); + ); } #else PVR_UNREFERENCED_PARAMETER(pszFileName); @@ -755,13 +1155,13 @@ NewIORemapLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, IMG_VOID *pvIORemapCookie; psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { return NULL; } pvIORemapCookie = IORemapWrapper(BasePAddr, ui32Bytes, ui32AreaFlags); - if(!pvIORemapCookie) + if (!pvIORemapCookie) { LinuxMemAreaStructFree(psLinuxMemArea); return NULL; @@ -842,7 +1242,7 @@ LinuxMemArea *NewExternalKVLinuxMemArea(IMG_SYS_PHYADDR *pBasePAddr, IMG_VOID *p LinuxMemArea *psLinuxMemArea; psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { return NULL; } @@ -895,7 +1295,7 @@ NewIOLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, IMG_UINT32 ui32AreaFlags) { LinuxMemArea *psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { return NULL; } @@ -916,7 +1316,7 @@ NewIOLinuxMemArea(IMG_CPU_PHYADDR BasePAddr, ui32Bytes, "unknown", 0 - ); + ); #endif #if defined(DEBUG_LINUX_MEM_AREAS) @@ -951,68 +1351,33 @@ LinuxMemArea * NewAllocPagesLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) { LinuxMemArea *psLinuxMemArea; - IMG_UINT32 ui32PageCount; - struct page **pvPageList; + IMG_UINT32 ui32NumPages; + struct page **ppsPageList; IMG_HANDLE hBlockPageList; - IMG_INT32 i; - PVRSRV_ERROR eError; - + IMG_BOOL bFromPagePool; + psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { goto failed_area_alloc; } - ui32PageCount = RANGE_TO_PAGES(ui32Bytes); - eError = OSAllocMem(0, sizeof(*pvPageList) * ui32PageCount, (IMG_VOID **)&pvPageList, &hBlockPageList, - "Array of pages"); - if(eError != PVRSRV_OK) - { - goto failed_page_list_alloc; - } - - for(i=0; i<(IMG_INT32)ui32PageCount; i++) - { - pvPageList[i] = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, 0); - if(!pvPageList[i]) - { - goto failed_alloc_pages; - } -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) - -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) - SetPageReserved(pvPageList[i]); -#else - mem_map_reserve(pvPageList[i]); -#endif -#endif + ui32NumPages = RANGE_TO_PAGES(ui32Bytes); + if (!AllocPages(ui32AreaFlags, &ppsPageList, &hBlockPageList, ui32NumPages, &bFromPagePool)) + { + goto failed_alloc_pages; } -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, - pvPageList, - 0, - 0, - NULL, - PAGE_ALIGN(ui32Bytes), - "unknown", - 0 - ); -#endif - psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ALLOC_PAGES; - psLinuxMemArea->uData.sPageList.pvPageList = pvPageList; + psLinuxMemArea->uData.sPageList.ppsPageList = ppsPageList; psLinuxMemArea->uData.sPageList.hBlockPageList = hBlockPageList; psLinuxMemArea->ui32ByteSize = ui32Bytes; psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); - if(ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) - { - psLinuxMemArea->bNeedsCacheInvalidate = IMG_TRUE; - } + psLinuxMemArea->bNeedsCacheInvalidate = AreaIsUncached(ui32AreaFlags) && !bFromPagePool; #if defined(DEBUG_LINUX_MEM_AREAS) DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); @@ -1021,13 +1386,6 @@ NewAllocPagesLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags) return psLinuxMemArea; failed_alloc_pages: - for(i--; i >= 0; i--) - { - __free_pages(pvPageList[i], 0); - } - (IMG_VOID) OSFreeMem(0, sizeof(*pvPageList) * ui32PageCount, pvPageList, hBlockPageList); - psLinuxMemArea->uData.sPageList.pvPageList = IMG_NULL; -failed_page_list_alloc: LinuxMemAreaStructFree(psLinuxMemArea); failed_area_alloc: PVR_DPF((PVR_DBG_ERROR, "%s: failed", __FUNCTION__)); @@ -1039,10 +1397,9 @@ failed_area_alloc: IMG_VOID FreeAllocPagesLinuxMemArea(LinuxMemArea *psLinuxMemArea) { - IMG_UINT32 ui32PageCount; - struct page **pvPageList; + IMG_UINT32 ui32NumPages; + struct page **ppsPageList; IMG_HANDLE hBlockPageList; - IMG_INT32 i; PVR_ASSERT(psLinuxMemArea); PVR_ASSERT(psLinuxMemArea->eAreaType == LINUX_MEM_AREA_ALLOC_PAGES); @@ -1051,29 +1408,12 @@ FreeAllocPagesLinuxMemArea(LinuxMemArea *psLinuxMemArea) DebugLinuxMemAreaRecordRemove(psLinuxMemArea); #endif - ui32PageCount = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize); - pvPageList = psLinuxMemArea->uData.sPageList.pvPageList; + ui32NumPages = RANGE_TO_PAGES(psLinuxMemArea->ui32ByteSize); + ppsPageList = psLinuxMemArea->uData.sPageList.ppsPageList; hBlockPageList = psLinuxMemArea->uData.sPageList.hBlockPageList; -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, pvPageList, __FILE__, __LINE__); -#endif - - for(i=0;i<(IMG_INT32)ui32PageCount;i++) - { -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)) - ClearPageReserved(pvPageList[i]); -#else - mem_map_reserve(pvPageList[i]); -#endif -#endif - __free_pages(pvPageList[i], 0); - } - - (IMG_VOID) OSFreeMem(0, sizeof(*pvPageList) * ui32PageCount, pvPageList, hBlockPageList); - psLinuxMemArea->uData.sPageList.pvPageList = IMG_NULL; - + FreePages(CanFreeToPool(psLinuxMemArea), ppsPageList, hBlockPageList, ui32NumPages); + LinuxMemAreaStructFree(psLinuxMemArea); } @@ -1090,62 +1430,86 @@ LinuxMemArea * NewIONLinuxMemArea(IMG_UINT32 ui32Bytes, IMG_UINT32 ui32AreaFlags, IMG_PVOID pvPrivData, IMG_UINT32 ui32PrivDataLength) { - struct omap_ion_tiler_alloc_data sAllocData; + const IMG_UINT32 ui32AllocDataLen = + offsetof(struct omap_ion_tiler_alloc_data, handle); + struct omap_ion_tiler_alloc_data asAllocData[2] = {}; + u32 *pu32PageAddrs[2] = { NULL, NULL }; + IMG_UINT32 i, ui32NumHandlesPerFd; + IMG_BYTE *pbPrivData = pvPrivData; + IMG_CPU_PHYADDR *pCPUPhysAddrs; + int iNumPages[2] = { 0, 0 }; LinuxMemArea *psLinuxMemArea; - u32 *pu32PageAddrs; - int iNumPages; psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate LinuxMemArea struct", __func__)); goto err_out; } - BUG_ON(ui32PrivDataLength != offsetof(struct omap_ion_tiler_alloc_data, handle)); - memcpy(&sAllocData, pvPrivData, offsetof(struct omap_ion_tiler_alloc_data, handle)); + BUG_ON(ui32PrivDataLength != ui32AllocDataLen && + ui32PrivDataLength != ui32AllocDataLen * 2); + ui32NumHandlesPerFd = ui32PrivDataLength / ui32AllocDataLen; - if(omap_ion_tiler_alloc(gpsIONClient, &sAllocData) < 0) + + for(i = 0; i < ui32NumHandlesPerFd; i++) { - PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate via ion_tiler", __func__)); - goto err_free; + memcpy(&asAllocData[i], &pbPrivData[i * ui32AllocDataLen], ui32AllocDataLen); + + if (omap_ion_tiler_alloc(gpsIONClient, &asAllocData[i]) < 0) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate via ion_tiler", __func__)); + goto err_free; + } + + if (omap_tiler_pages(gpsIONClient, asAllocData[i].handle, &iNumPages[i], + &pu32PageAddrs[i]) < 0) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to compute tiler pages", __func__)); + goto err_free; + } } - if(omap_tiler_pages(gpsIONClient, sAllocData.handle, &iNumPages, - &pu32PageAddrs) < 0) - { - PVR_DPF((PVR_DBG_ERROR, "%s: Failed to compute tiler pages", __func__)); - goto err_free; - } + + BUG_ON(ui32Bytes != (iNumPages[0] + iNumPages[1]) * PAGE_SIZE); + BUG_ON(sizeof(IMG_CPU_PHYADDR) != sizeof(int)); - BUG_ON(ui32Bytes != iNumPages * PAGE_SIZE); + pCPUPhysAddrs = vmalloc(sizeof(IMG_CPU_PHYADDR) * (iNumPages[0] + iNumPages[1])); + if (!pCPUPhysAddrs) + { + PVR_DPF((PVR_DBG_ERROR, "%s: Failed to allocate page list", __func__)); + goto err_free; + } + for(i = 0; i < iNumPages[0]; i++) + pCPUPhysAddrs[i].uiAddr = pu32PageAddrs[0][i]; + for(i = 0; i < iNumPages[1]; i++) + pCPUPhysAddrs[iNumPages[0] + i].uiAddr = pu32PageAddrs[1][i]; #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) DebugMemAllocRecordAdd(DEBUG_MEM_ALLOC_TYPE_ION, - sAllocData.handle, + asAllocData[0].handle, 0, 0, NULL, PAGE_ALIGN(ui32Bytes), "unknown", 0 - ); + ); #endif + for(i = 0; i < 2; i++) + psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i] = asAllocData[i].handle; + psLinuxMemArea->eAreaType = LINUX_MEM_AREA_ION; - psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs = (IMG_CPU_PHYADDR *)pu32PageAddrs; - psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle = sAllocData.handle; + psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs = pCPUPhysAddrs; psLinuxMemArea->ui32ByteSize = ui32Bytes; psLinuxMemArea->ui32AreaFlags = ui32AreaFlags; INIT_LIST_HEAD(&psLinuxMemArea->sMMapOffsetStructList); - if(ui32AreaFlags & (PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_UNCACHED)) - { - psLinuxMemArea->bNeedsCacheInvalidate = IMG_TRUE; - } + psLinuxMemArea->bNeedsCacheInvalidate = AreaIsUncached(ui32AreaFlags); #if defined(DEBUG_LINUX_MEM_AREAS) DebugLinuxMemAreaRecordAdd(psLinuxMemArea, ui32AreaFlags); @@ -1164,21 +1528,29 @@ err_free: IMG_VOID FreeIONLinuxMemArea(LinuxMemArea *psLinuxMemArea) { + IMG_UINT32 i; + #if defined(DEBUG_LINUX_MEM_AREAS) DebugLinuxMemAreaRecordRemove(psLinuxMemArea); #endif - ion_free(gpsIONClient, psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle); - #if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ION, - psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle, + psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[0], __FILE__, __LINE__); #endif + for(i = 0; i < 2; i++) + { + if (!psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i]) + break; + ion_free(gpsIONClient, psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i]); + psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle[i] = IMG_NULL; + } + + vfree(psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs); psLinuxMemArea->uData.sIONTilerAlloc.pCPUPhysAddrs = IMG_NULL; - psLinuxMemArea->uData.sIONTilerAlloc.psIONHandle = IMG_NULL; LinuxMemAreaStructFree(psLinuxMemArea); } @@ -1192,11 +1564,11 @@ LinuxMemAreaOffsetToPage(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32PageIndex; IMG_CHAR *pui8Addr; - switch(psLinuxMemArea->eAreaType) + switch (psLinuxMemArea->eAreaType) { case LINUX_MEM_AREA_ALLOC_PAGES: ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset); - return psLinuxMemArea->uData.sPageList.pvPageList[ui32PageIndex]; + return psLinuxMemArea->uData.sPageList.ppsPageList[ui32PageIndex]; case LINUX_MEM_AREA_VMALLOC: pui8Addr = psLinuxMemArea->uData.sVmalloc.pvVmallocAddress; @@ -1230,7 +1602,7 @@ KMemCacheCreateWrapper(IMG_CHAR *pszName, #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) , NULL #endif - ); + ); } @@ -1264,7 +1636,7 @@ _KMemCacheAllocWrapper(LinuxKMemCache *psCache, kmem_cache_size(psCache), pszFileName, ui32Line - ); + ); #else PVR_UNREFERENCED_PARAMETER(pszFileName); PVR_UNREFERENCED_PARAMETER(ui32Line); @@ -1274,30 +1646,6 @@ _KMemCacheAllocWrapper(LinuxKMemCache *psCache, } -IMG_VOID -_KMemCacheFreeWrapper(LinuxKMemCache *psCache, IMG_VOID *pvObject, IMG_CHAR *pszFileName, IMG_UINT32 ui32Line) -{ -#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) - DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE, pvObject, pszFileName, ui32Line); -#else - PVR_UNREFERENCED_PARAMETER(pszFileName); - PVR_UNREFERENCED_PARAMETER(ui32Line); -#endif - - kmem_cache_free(psCache, pvObject); -} - - -const IMG_CHAR * -KMemCacheNameWrapper(LinuxKMemCache *psCache) -{ - PVR_UNREFERENCED_PARAMETER(psCache); - - - return ""; -} - - LinuxMemArea * NewSubLinuxMemArea(LinuxMemArea *psParentLinuxMemArea, IMG_UINT32 ui32ByteOffset, @@ -1308,7 +1656,7 @@ NewSubLinuxMemArea(LinuxMemArea *psParentLinuxMemArea, PVR_ASSERT((ui32ByteOffset+ui32Bytes) <= psParentLinuxMemArea->ui32ByteSize); psLinuxMemArea = LinuxMemAreaStructAlloc(); - if(!psLinuxMemArea) + if (!psLinuxMemArea) { return NULL; } @@ -1353,12 +1701,12 @@ LinuxMemAreaStructAlloc(IMG_VOID) { #if 0 LinuxMemArea *psLinuxMemArea; - psLinuxMemArea = kmem_cache_alloc(psLinuxMemAreaCache, GFP_KERNEL); + psLinuxMemArea = kmem_cache_alloc(g_PsLinuxMemAreaCache, GFP_KERNEL); printk(KERN_ERR "%s: psLinuxMemArea=%p\n", __FUNCTION__, psLinuxMemArea); dump_stack(); return psLinuxMemArea; #else - return KMemCacheAllocWrapper(psLinuxMemAreaCache, GFP_KERNEL); + return KMemCacheAllocWrapper(g_PsLinuxMemAreaCache, GFP_KERNEL); #endif } @@ -1366,7 +1714,7 @@ LinuxMemAreaStructAlloc(IMG_VOID) static IMG_VOID LinuxMemAreaStructFree(LinuxMemArea *psLinuxMemArea) { - KMemCacheFreeWrapper(psLinuxMemAreaCache, psLinuxMemArea); + KMemCacheFreeWrapper(g_PsLinuxMemAreaCache, psLinuxMemArea); } @@ -1375,7 +1723,7 @@ LinuxMemAreaStructFree(LinuxMemArea *psLinuxMemArea) IMG_VOID LinuxMemAreaDeepFree(LinuxMemArea *psLinuxMemArea) { - switch(psLinuxMemArea->eAreaType) + switch (psLinuxMemArea->eAreaType) { case LINUX_MEM_AREA_VMALLOC: FreeVMallocLinuxMemArea(psLinuxMemArea); @@ -1415,10 +1763,10 @@ DebugLinuxMemAreaRecordAdd(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Flags) LinuxLockMutex(&g_sDebugMutex); - if(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) + if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) { g_LinuxMemAreaWaterMark += psLinuxMemArea->ui32ByteSize; - if(g_LinuxMemAreaWaterMark > g_LinuxMemAreaHighWaterMark) + if (g_LinuxMemAreaWaterMark > g_LinuxMemAreaHighWaterMark) { g_LinuxMemAreaHighWaterMark = g_LinuxMemAreaWaterMark; } @@ -1427,7 +1775,7 @@ DebugLinuxMemAreaRecordAdd(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Flags) psNewRecord = kmalloc(sizeof(DEBUG_LINUX_MEM_AREA_REC), GFP_KERNEL); - if(psNewRecord) + if (psNewRecord) { psNewRecord->psLinuxMemArea = psLinuxMemArea; @@ -1445,7 +1793,7 @@ DebugLinuxMemAreaRecordAdd(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32Flags) pi8FlagsString = HAPFlagsToString(ui32Flags); - if(strstr(pi8FlagsString, "UNKNOWN")) + if (strstr(pi8FlagsString, "UNKNOWN")) { PVR_DPF((PVR_DBG_ERROR, "%s: Unexpected flags (0x%08x) associated with psLinuxMemArea @ %p", @@ -1466,7 +1814,7 @@ static IMG_VOID* MatchLinuxMemArea_AnyVaCb(DEBUG_LINUX_MEM_AREA_REC *psCurrentRe LinuxMemArea *psLinuxMemArea; psLinuxMemArea = va_arg(va, LinuxMemArea*); - if(psCurrentRecord->psLinuxMemArea == psLinuxMemArea) + if (psCurrentRecord->psLinuxMemArea == psLinuxMemArea) { return psCurrentRecord; } @@ -1500,7 +1848,7 @@ DebugLinuxMemAreaRecordRemove(LinuxMemArea *psLinuxMemArea) LinuxLockMutex(&g_sDebugMutex); - if(psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) + if (psLinuxMemArea->eAreaType != LINUX_MEM_AREA_SUB_ALLOC) { g_LinuxMemAreaWaterMark -= psLinuxMemArea->ui32ByteSize; } @@ -1510,7 +1858,7 @@ DebugLinuxMemAreaRecordRemove(LinuxMemArea *psLinuxMemArea) psCurrentRecord = List_DEBUG_LINUX_MEM_AREA_REC_Any_va(g_LinuxMemAreaRecords, MatchLinuxMemArea_AnyVaCb, psLinuxMemArea); - if(psCurrentRecord) + if (psCurrentRecord) { List_DEBUG_LINUX_MEM_AREA_REC_Remove(psCurrentRecord); @@ -1530,7 +1878,7 @@ DebugLinuxMemAreaRecordRemove(LinuxMemArea *psLinuxMemArea) IMG_VOID * LinuxMemAreaToCpuVAddr(LinuxMemArea *psLinuxMemArea) { - switch(psLinuxMemArea->eAreaType) + switch (psLinuxMemArea->eAreaType) { case LINUX_MEM_AREA_VMALLOC: return psLinuxMemArea->uData.sVmalloc.pvVmallocAddress; @@ -1542,7 +1890,7 @@ LinuxMemAreaToCpuVAddr(LinuxMemArea *psLinuxMemArea) { IMG_CHAR *pAddr = LinuxMemAreaToCpuVAddr(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea); - if(!pAddr) + if (!pAddr) { return NULL; } @@ -1561,7 +1909,7 @@ LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset) CpuPAddr.uiAddr = 0; - switch(psLinuxMemArea->eAreaType) + switch (psLinuxMemArea->eAreaType) { case LINUX_MEM_AREA_IOREMAP: { @@ -1612,7 +1960,7 @@ LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset) { struct page *page; IMG_UINT32 ui32PageIndex = PHYS_TO_PFN(ui32ByteOffset); - page = psLinuxMemArea->uData.sPageList.pvPageList[ui32PageIndex]; + page = psLinuxMemArea->uData.sPageList.ppsPageList[ui32PageIndex]; CpuPAddr.uiAddr = page_to_phys(page); CpuPAddr.uiAddr += ADDR_TO_PAGE_OFFSET(ui32ByteOffset); break; @@ -1627,9 +1975,8 @@ LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset) } default: { - PVR_DPF((PVR_DBG_ERROR, "%s: Unknown LinuxMemArea type (%d)", + PVR_DPF((PVR_DBG_ERROR, "%s: Unknown LinuxMemArea type (%d)\n", __FUNCTION__, psLinuxMemArea->eAreaType)); - dump_stack(); PVR_ASSERT(CpuPAddr.uiAddr); break; } @@ -1642,7 +1989,7 @@ LinuxMemAreaToCpuPAddr(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32ByteOffset) IMG_BOOL LinuxMemAreaPhysIsContig(LinuxMemArea *psLinuxMemArea) { - switch(psLinuxMemArea->eAreaType) + switch (psLinuxMemArea->eAreaType) { case LINUX_MEM_AREA_IOREMAP: case LINUX_MEM_AREA_IO: @@ -1661,7 +2008,7 @@ LinuxMemAreaPhysIsContig(LinuxMemArea *psLinuxMemArea) return LinuxMemAreaPhysIsContig(psLinuxMemArea->uData.sSubAlloc.psParentLinuxMemArea); default: - PVR_DPF((PVR_DBG_ERROR, "%s: Unknown LinuxMemArea type (%d)", + PVR_DPF((PVR_DBG_ERROR, "%s: Unknown LinuxMemArea type (%d)\n", __FUNCTION__, psLinuxMemArea->eAreaType)); break; } @@ -1673,7 +2020,7 @@ const IMG_CHAR * LinuxMemAreaTypeToString(LINUX_MEM_AREA_TYPE eMemAreaType) { - switch(eMemAreaType) + switch (eMemAreaType) { case LINUX_MEM_AREA_IOREMAP: return "LINUX_MEM_AREA_IOREMAP"; @@ -1700,7 +2047,7 @@ LinuxMemAreaTypeToString(LINUX_MEM_AREA_TYPE eMemAreaType) #if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) static void ProcSeqStartstopDebugMutex(struct seq_file *sfile, IMG_BOOL start) { - if(start) + if (start) { LinuxLockMutex(&g_sDebugMutex); } @@ -1740,7 +2087,7 @@ static void* ProcSeqNextMemArea(struct seq_file *sfile,void* el,loff_t off) static void* ProcSeqOff2ElementMemArea(struct seq_file * sfile, loff_t off) { DEBUG_LINUX_MEM_AREA_REC *psRecord; - if(!off) + if (!off) { return PVR_PROC_SEQ_START_TOKEN; } @@ -1756,11 +2103,11 @@ static void* ProcSeqOff2ElementMemArea(struct seq_file * sfile, loff_t off) static void ProcSeqShowMemArea(struct seq_file *sfile,void* el) { DEBUG_LINUX_MEM_AREA_REC *psRecord = (DEBUG_LINUX_MEM_AREA_REC*)el; - if(el == PVR_PROC_SEQ_START_TOKEN) + if (el == PVR_PROC_SEQ_START_TOKEN) { #if !defined(DEBUG_LINUX_XML_PROC_FILES) - seq_printf( sfile, + seq_printf(sfile, "Number of Linux Memory Areas: %u\n" "At the current water mark these areas correspond to %u bytes (excluding SUB areas)\n" "At the highest water mark these areas corresponded to %u bytes (excluding SUB areas)\n" @@ -1776,9 +2123,9 @@ static void ProcSeqShowMemArea(struct seq_file *sfile,void* el) "Bytes", "Pid", "Flags" - ); + ); #else - seq_printf( sfile, + seq_printf(sfile, "<mem_areas_header>\n" "\t<count>%u</count>\n" "\t<watermark key=\"mar0\" description=\"current\" bytes=\"%u\"/>\n" @@ -1787,12 +2134,12 @@ static void ProcSeqShowMemArea(struct seq_file *sfile,void* el) g_LinuxMemAreaCount, g_LinuxMemAreaWaterMark, g_LinuxMemAreaHighWaterMark - ); + ); #endif return; } - seq_printf( sfile, + seq_printf(sfile, #if !defined(DEBUG_LINUX_XML_PROC_FILES) "%8p %-24s %8p %08x %-8d %-5u %08x=(%s)\n", #else @@ -1801,9 +2148,9 @@ static void ProcSeqShowMemArea(struct seq_file *sfile,void* el) "\t<type>%s</type>\n" "\t<cpu_virtual>%8p</cpu_virtual>\n" "\t<cpu_physical>%08x</cpu_physical>\n" - "\t<bytes>%ld</bytes>\n" + "\t<bytes>%d</bytes>\n" "\t<pid>%u</pid>\n" - "\t<flags>%08lx</flags>\n" + "\t<flags>%08x</flags>\n" "\t<flags_string>%s</flags_string>\n" "</linux_mem_area>\n", #endif @@ -1815,7 +2162,7 @@ static void ProcSeqShowMemArea(struct seq_file *sfile,void* el) psRecord->pid, psRecord->ui32Flags, HAPFlagsToString(psRecord->ui32Flags) - ); + ); } @@ -1847,9 +2194,9 @@ static void* ProcSeqNextMemoryRecords(struct seq_file *sfile,void* el,loff_t off DecOffMemAllocRec_AnyVaCb, &off); #if defined(DEBUG_LINUX_XML_PROC_FILES) - if(!psRecord) + if (!psRecord) { - seq_printf( sfile, "</meminfo>\n"); + seq_printf(sfile, "</meminfo>\n"); } #endif @@ -1859,7 +2206,7 @@ static void* ProcSeqNextMemoryRecords(struct seq_file *sfile,void* el,loff_t off static void* ProcSeqOff2ElementMemoryRecords(struct seq_file *sfile, loff_t off) { DEBUG_MEM_ALLOC_REC *psRecord; - if(!off) + if (!off) { return PVR_PROC_SEQ_START_TOKEN; } @@ -1870,9 +2217,9 @@ static void* ProcSeqOff2ElementMemoryRecords(struct seq_file *sfile, loff_t off) &off); #if defined(DEBUG_LINUX_XML_PROC_FILES) - if(!psRecord) + if (!psRecord) { - seq_printf( sfile, "</meminfo>\n"); + seq_printf(sfile, "</meminfo>\n"); } #endif @@ -1882,64 +2229,76 @@ static void* ProcSeqOff2ElementMemoryRecords(struct seq_file *sfile, loff_t off) static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el) { DEBUG_MEM_ALLOC_REC *psRecord = (DEBUG_MEM_ALLOC_REC*)el; - if(el == PVR_PROC_SEQ_START_TOKEN) + if (el == PVR_PROC_SEQ_START_TOKEN) { #if !defined(DEBUG_LINUX_XML_PROC_FILES) - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Current Water Mark of bytes allocated via kmalloc", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Highest Water Mark of bytes allocated via kmalloc", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Current Water Mark of bytes allocated via vmalloc", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Highest Water Mark of bytes allocated via vmalloc", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Current Water Mark of bytes allocated via alloc_pages", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Highest Water Mark of bytes allocated via alloc_pages", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Current Water Mark of bytes allocated via ioremap", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Highest Water Mark of bytes allocated via ioremap", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Current Water Mark of bytes reserved for \"IO\" memory areas", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Highest Water Mark of bytes allocated for \"IO\" memory areas", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Current Water Mark of bytes allocated via kmem_cache_alloc", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "Highest Water Mark of bytes allocated via kmem_cache_alloc", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]); +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + seq_printf(sfile, "%-60s: %d bytes\n", + "Current Water Mark of bytes mapped via vmap", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]); + seq_printf(sfile, "%-60s: %d bytes\n", + "Highest Water Mark of bytes mapped via vmap", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]); +#endif +#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) + seq_printf(sfile, "%-60s: %d pages\n", + "Number of pages in page pool", + atomic_read(&g_sPagePoolEntryCount)); +#endif seq_printf( sfile, "\n"); - - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "The Current Water Mark for memory allocated from system RAM", - g_SysRAMWaterMark); - seq_printf( sfile, "%-60s: %d bytes\n", + SysRAMTrueWaterMark()); + seq_printf(sfile, "%-60s: %d bytes\n", "The Highest Water Mark for memory allocated from system RAM", g_SysRAMHighWaterMark); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "The Current Water Mark for memory allocated from IO memory", g_IOMemWaterMark); - seq_printf( sfile, "%-60s: %d bytes\n", + seq_printf(sfile, "%-60s: %d bytes\n", "The Highest Water Mark for memory allocated from IO memory", g_IOMemHighWaterMark); seq_printf( sfile, "\n"); - seq_printf( sfile, "Details for all known allocations:\n" + seq_printf(sfile, "Details for all known allocations:\n" "%-16s %-8s %-8s %-10s %-5s %-10s %s\n", "Type", "CpuVAddr", @@ -1952,67 +2311,78 @@ static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el) #else - seq_printf( sfile, "<meminfo>\n<meminfo_header>\n"); - seq_printf( sfile, + seq_printf(sfile, "<meminfo>\n<meminfo_header>\n"); + seq_printf(sfile, "<watermark key=\"mr0\" description=\"kmalloc_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr1\" description=\"kmalloc_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMALLOC]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr2\" description=\"vmalloc_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr3\" description=\"vmalloc_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMALLOC]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr4\" description=\"alloc_pages_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr5\" description=\"alloc_pages_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr6\" description=\"ioremap_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr7\" description=\"ioremap_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IOREMAP]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr8\" description=\"io_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr9\" description=\"io_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_IO]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr10\" description=\"kmem_cache_current\" bytes=\"%d\"/>\n", g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr11\" description=\"kmem_cache_high\" bytes=\"%d\"/>\n", g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE]); - seq_printf( sfile,"\n" ); - - seq_printf( sfile, +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + seq_printf(sfile, + "<watermark key=\"mr12\" description=\"vmap_current\" bytes=\"%d\"/>\n", + g_WaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]); + seq_printf(sfile, + "<watermark key=\"mr13\" description=\"vmap_high\" bytes=\"%d\"/>\n", + g_HighWaterMarkData[DEBUG_MEM_ALLOC_TYPE_VMAP]); +#endif + seq_printf(sfile, "<watermark key=\"mr14\" description=\"system_ram_current\" bytes=\"%d\"/>\n", - g_SysRAMWaterMark); - seq_printf( sfile, + SysRAMTrueWaterMark()); + seq_printf(sfile, "<watermark key=\"mr15\" description=\"system_ram_high\" bytes=\"%d\"/>\n", g_SysRAMHighWaterMark); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr16\" description=\"system_io_current\" bytes=\"%d\"/>\n", g_IOMemWaterMark); - seq_printf( sfile, + seq_printf(sfile, "<watermark key=\"mr17\" description=\"system_io_high\" bytes=\"%d\"/>\n", g_IOMemHighWaterMark); - seq_printf( sfile, "</meminfo_header>\n"); +#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) + seq_printf(sfile, + "<watermark key=\"mr18\" description=\"page_pool_current\" bytes=\"%d\"/>\n", + PAGES_TO_BYTES(atomic_read(&g_sPagePoolEntryCount))); +#endif + seq_printf(sfile, "</meminfo_header>\n"); #endif return; } - if(psRecord->eAllocType != DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE) + if (psRecord->eAllocType != DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE) { - seq_printf( sfile, + seq_printf(sfile, #if !defined(DEBUG_LINUX_XML_PROC_FILES) "%-16s %-8p %08x %-10d %-5d %-10s %s:%d\n", #else @@ -2038,7 +2408,7 @@ static void ProcSeqShowMemoryRecords(struct seq_file *sfile,void* el) } else { - seq_printf( sfile, + seq_printf(sfile, #if !defined(DEBUG_LINUX_XML_PROC_FILES) "%-16s %-8p %08x %-10d %-5d %-10s %s:%d\n", #else @@ -2090,30 +2460,30 @@ HAPFlagsToString(IMG_UINT32 ui32Flags) }; - if(ui32Flags & PVRSRV_HAP_UNCACHED){ - ui32CacheTypeIndex=0; - }else if(ui32Flags & PVRSRV_HAP_CACHED){ - ui32CacheTypeIndex=1; - }else if(ui32Flags & PVRSRV_HAP_WRITECOMBINE){ - ui32CacheTypeIndex=2; - }else{ - ui32CacheTypeIndex=3; + if (ui32Flags & PVRSRV_HAP_UNCACHED) { + ui32CacheTypeIndex = 0; + } else if (ui32Flags & PVRSRV_HAP_CACHED) { + ui32CacheTypeIndex = 1; + } else if (ui32Flags & PVRSRV_HAP_WRITECOMBINE) { + ui32CacheTypeIndex = 2; + } else { + ui32CacheTypeIndex = 3; PVR_DPF((PVR_DBG_ERROR, "%s: unknown cache type (%u)", __FUNCTION__, (ui32Flags & PVRSRV_HAP_CACHETYPE_MASK))); } - if(ui32Flags & PVRSRV_HAP_KERNEL_ONLY){ + if (ui32Flags & PVRSRV_HAP_KERNEL_ONLY) { ui32MapTypeIndex = 0; - }else if(ui32Flags & PVRSRV_HAP_SINGLE_PROCESS){ + } else if (ui32Flags & PVRSRV_HAP_SINGLE_PROCESS) { ui32MapTypeIndex = 1; - }else if(ui32Flags & PVRSRV_HAP_MULTI_PROCESS){ + } else if (ui32Flags & PVRSRV_HAP_MULTI_PROCESS) { ui32MapTypeIndex = 2; - }else if(ui32Flags & PVRSRV_HAP_FROM_EXISTING_PROCESS){ + } else if (ui32Flags & PVRSRV_HAP_FROM_EXISTING_PROCESS) { ui32MapTypeIndex = 3; - }else if(ui32Flags & PVRSRV_HAP_NO_CPU_VIRTUAL){ + } else if (ui32Flags & PVRSRV_HAP_NO_CPU_VIRTUAL) { ui32MapTypeIndex = 4; - }else{ + } else { ui32MapTypeIndex = 5; PVR_DPF((PVR_DBG_ERROR, "%s: unknown map type (%u)", __FUNCTION__, (ui32Flags & PVRSRV_HAP_MAPTYPE_MASK))); @@ -2135,3 +2505,213 @@ HAPFlagsToString(IMG_UINT32 ui32Flags) } #endif +#if defined(DEBUG_LINUX_MEM_AREAS) +static IMG_VOID LinuxMMCleanup_MemAreas_ForEachCb(DEBUG_LINUX_MEM_AREA_REC *psCurrentRecord) +{ + LinuxMemArea *psLinuxMemArea; + + psLinuxMemArea = psCurrentRecord->psLinuxMemArea; + PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: Cleaning up Linux memory area (%p), type=%s, size=%d bytes", + __FUNCTION__, + psCurrentRecord->psLinuxMemArea, + LinuxMemAreaTypeToString(psCurrentRecord->psLinuxMemArea->eAreaType), + psCurrentRecord->psLinuxMemArea->ui32ByteSize)); + + LinuxMemAreaDeepFree(psLinuxMemArea); +} +#endif + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) +static IMG_VOID LinuxMMCleanup_MemRecords_ForEachVa(DEBUG_MEM_ALLOC_REC *psCurrentRecord) + +{ + + PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: Cleaning up memory: " + "type=%s " + "CpuVAddr=%p " + "CpuPAddr=0x%08x, " + "allocated @ file=%s,line=%d", + __FUNCTION__, + DebugMemAllocRecordTypeToString(psCurrentRecord->eAllocType), + psCurrentRecord->pvCpuVAddr, + psCurrentRecord->ulCpuPAddr, + psCurrentRecord->pszFileName, + psCurrentRecord->ui32Line)); + switch (psCurrentRecord->eAllocType) + { + case DEBUG_MEM_ALLOC_TYPE_KMALLOC: + KFreeWrapper(psCurrentRecord->pvCpuVAddr); + break; + case DEBUG_MEM_ALLOC_TYPE_IOREMAP: + IOUnmapWrapper(psCurrentRecord->pvCpuVAddr); + break; + case DEBUG_MEM_ALLOC_TYPE_IO: + + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_IO, psCurrentRecord->pvKey, __FILE__, __LINE__); + break; + case DEBUG_MEM_ALLOC_TYPE_VMALLOC: + VFreeWrapper(psCurrentRecord->pvCpuVAddr); + break; + case DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES: + DebugMemAllocRecordRemove(DEBUG_MEM_ALLOC_TYPE_ALLOC_PAGES, psCurrentRecord->pvKey, __FILE__, __LINE__); + break; + case DEBUG_MEM_ALLOC_TYPE_KMEM_CACHE: + KMemCacheFreeWrapper(psCurrentRecord->pvPrivateData, psCurrentRecord->pvCpuVAddr); + break; +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + case DEBUG_MEM_ALLOC_TYPE_VMAP: + VUnmapWrapper(psCurrentRecord->pvCpuVAddr); + break; +#endif + default: + PVR_ASSERT(0); + } +} +#endif + + +#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK) +static struct shrinker g_sShrinker = +{ + .shrink = ShrinkPagePool, + .seeks = DEFAULT_SEEKS +}; + +static IMG_BOOL g_bShrinkerRegistered; +#endif + +IMG_VOID +LinuxMMCleanup(IMG_VOID) +{ +#if defined(DEBUG_LINUX_MEM_AREAS) + { + if (g_LinuxMemAreaCount) + { + PVR_DPF((PVR_DBG_ERROR, "%s: BUG!: There are %d LinuxMemArea allocation unfreed (%d bytes)", + __FUNCTION__, g_LinuxMemAreaCount, g_LinuxMemAreaWaterMark)); + } + + List_DEBUG_LINUX_MEM_AREA_REC_ForEach(g_LinuxMemAreaRecords, LinuxMMCleanup_MemAreas_ForEachCb); + + if (g_SeqFileMemArea) + { + RemoveProcEntrySeq(g_SeqFileMemArea); + } + } +#endif + +#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK) + if (g_bShrinkerRegistered) + { + unregister_shrinker(&g_sShrinker); + } +#endif + + + FreePagePool(); + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + { + + + List_DEBUG_MEM_ALLOC_REC_ForEach(g_MemoryRecords, LinuxMMCleanup_MemRecords_ForEachVa); + + if (g_SeqFileMemoryRecords) + { + RemoveProcEntrySeq(g_SeqFileMemoryRecords); + } + } +#endif + + if (g_PsLinuxMemAreaCache) + { + KMemCacheDestroyWrapper(g_PsLinuxMemAreaCache); + } + + if (g_PsLinuxPagePoolCache) + { + KMemCacheDestroyWrapper(g_PsLinuxPagePoolCache); + } +} + +PVRSRV_ERROR +LinuxMMInit(IMG_VOID) +{ +#if defined(DEBUG_LINUX_MEM_AREAS) || defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + LinuxInitMutex(&g_sDebugMutex); +#endif + +#if defined(DEBUG_LINUX_MEM_AREAS) + { + g_SeqFileMemArea = CreateProcReadEntrySeq( + "mem_areas", + NULL, + ProcSeqNextMemArea, + ProcSeqShowMemArea, + ProcSeqOff2ElementMemArea, + ProcSeqStartstopDebugMutex + ); + if (!g_SeqFileMemArea) + { + goto failed; + } + } +#endif + + +#if defined(DEBUG_LINUX_MEMORY_ALLOCATIONS) + { + g_SeqFileMemoryRecords = CreateProcReadEntrySeq( + "meminfo", + NULL, + ProcSeqNextMemoryRecords, + ProcSeqShowMemoryRecords, + ProcSeqOff2ElementMemoryRecords, + ProcSeqStartstopDebugMutex + ); + if (!g_SeqFileMemoryRecords) + { + goto failed; + } + } +#endif + + g_PsLinuxMemAreaCache = KMemCacheCreateWrapper("img-mm", sizeof(LinuxMemArea), 0, 0); + if (!g_PsLinuxMemAreaCache) + { + PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate mem area kmem_cache", __FUNCTION__)); + goto failed; + } + +#if (PVR_LINUX_MEM_AREA_POOL_MAX_PAGES != 0) + g_iPagePoolMaxEntries = PVR_LINUX_MEM_AREA_POOL_MAX_PAGES; + if (g_iPagePoolMaxEntries <= 0 || g_iPagePoolMaxEntries > INT_MAX/2) + { + g_iPagePoolMaxEntries = INT_MAX/2; + PVR_TRACE(("%s: No limit set for page pool size", __FUNCTION__)); + } + else + { + PVR_TRACE(("%s: Maximum page pool size: %d", __FUNCTION__, g_iPagePoolMaxEntries)); + } + + g_PsLinuxPagePoolCache = KMemCacheCreateWrapper("img-mm-pool", sizeof(LinuxPagePoolEntry), 0, 0); + if (!g_PsLinuxPagePoolCache) + { + PVR_DPF((PVR_DBG_ERROR,"%s: failed to allocate page pool kmem_cache", __FUNCTION__)); + goto failed; + } +#endif + +#if defined(PVR_LINUX_MEM_AREA_POOL_ALLOW_SHRINK) + register_shrinker(&g_sShrinker); + g_bShrinkerRegistered = IMG_TRUE; +#endif + + return PVRSRV_OK; + +failed: + LinuxMMCleanup(); + return PVRSRV_ERROR_OUT_OF_MEMORY; +} + diff --git a/drivers/gpu/pvr/mm.h b/drivers/gpu/pvr/mm.h index dac7815..e62bf33 100644 --- a/drivers/gpu/pvr/mm.h +++ b/drivers/gpu/pvr/mm.h @@ -48,6 +48,8 @@ #define ADDR_TO_PAGE_OFFSET(addr) (((unsigned long)(addr)) & (PAGE_SIZE - 1)) +#define PAGES_TO_BYTES(pages) ((pages) << PAGE_SHIFT) + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)) #define REMAP_PFN_RANGE(vma, addr, pfn, size, prot) remap_pfn_range(vma, addr, pfn, size, prot) #else @@ -78,13 +80,16 @@ static inline IMG_UINT32 VMallocToPhys(IMG_VOID *pCpuVAddr) typedef enum { LINUX_MEM_AREA_IOREMAP, - LINUX_MEM_AREA_EXTERNAL_KV, + LINUX_MEM_AREA_EXTERNAL_KV, LINUX_MEM_AREA_IO, LINUX_MEM_AREA_VMALLOC, LINUX_MEM_AREA_ALLOC_PAGES, LINUX_MEM_AREA_SUB_ALLOC, - LINUX_MEM_AREA_TYPE_COUNT, LINUX_MEM_AREA_ION, +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + LINUX_MEM_AREA_VMAP, +#endif + LINUX_MEM_AREA_TYPE_COUNT }LINUX_MEM_AREA_TYPE; typedef struct _LinuxMemArea LinuxMemArea; @@ -120,18 +125,22 @@ struct _LinuxMemArea { { IMG_VOID *pvVmallocAddress; +#if defined(PVR_LINUX_MEM_AREA_USE_VMAP) + struct page **ppsPageList; + IMG_HANDLE hBlockPageList; +#endif }sVmalloc; struct _sPageList { - struct page **pvPageList; + struct page **ppsPageList; IMG_HANDLE hBlockPageList; }sPageList; struct _sIONTilerAlloc { IMG_CPU_PHYADDR *pCPUPhysAddrs; - struct ion_handle *psIONHandle; + struct ion_handle *psIONHandle[2]; }sIONTilerAlloc; struct _sSubAlloc { diff --git a/drivers/gpu/pvr/module.c b/drivers/gpu/pvr/module.c index cc9334b..37eb5cc 100644 --- a/drivers/gpu/pvr/module.c +++ b/drivers/gpu/pvr/module.c @@ -79,6 +79,10 @@ #include <asm/uaccess.h> #endif +#if defined(PVR_LDM_MODULE) || defined(PVR_DRI_DRM_PLATFORM_DEV) +#include <asm/atomic.h> +#endif + #include "img_defs.h" #include "services.h" #include "kerneldisplay.h" @@ -99,6 +103,7 @@ #include "private_data.h" #include "lock.h" #include "linkage.h" +#include "buffer_manager.h" #if defined(SUPPORT_DRI_DRM) #include "pvr_drm.h" @@ -347,9 +352,17 @@ void PVRSRVDriverShutdown(struct drm_device *pDevice) PVR_MOD_STATIC void PVRSRVDriverShutdown(LDM_DEV *pDevice) #endif { + static atomic_t sDriverIsShutdown = ATOMIC_INIT(1); + PVR_TRACE(("PVRSRVDriverShutdown(pDevice=%p)", pDevice)); - (void) PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D3); + if (atomic_dec_and_test(&sDriverIsShutdown)) + { + + LinuxLockMutex(&gPVRSRVLock); + + (void) PVRSRVSetPowerStateKM(PVRSRV_SYS_POWER_STATE_D3); + } } #endif @@ -540,6 +553,12 @@ static int PVRSRVRelease(struct inode unref__ * pInode, struct file *pFile) } + if (psKernelMemInfo->sShareMemWorkaround.bInUse) + { + BM_XProcIndexRelease(psKernelMemInfo->sShareMemWorkaround.ui32ShareIndex); + } + + if(FreeMemCallBackCommon(psKernelMemInfo, 0, PVRSRV_FREE_CALLBACK_ORIGIN_EXTERNAL) != PVRSRV_OK) { diff --git a/drivers/gpu/pvr/osfunc.c b/drivers/gpu/pvr/osfunc.c index 13e4dd9..28e2b00 100644 --- a/drivers/gpu/pvr/osfunc.c +++ b/drivers/gpu/pvr/osfunc.c @@ -73,6 +73,7 @@ #include "event.h" #include "linkage.h" #include "pvr_uaccess.h" +#include "lock.h" #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) #define ON_EACH_CPU(func, info, wait) on_each_cpu(func, info, wait) @@ -2756,7 +2757,7 @@ static unsigned long AllocPagesAreaToPhys(LinuxMemArea *psLinuxMemArea, IMG_UINT32 ui32PageNum) { struct page *pPage; - pPage = psLinuxMemArea->uData.sPageList.pvPageList[ui32PageNumOffset + ui32PageNum]; + pPage = psLinuxMemArea->uData.sPageList.ppsPageList[ui32PageNumOffset + ui32PageNum]; return page_to_pfn(pPage) << PAGE_SHIFT; } @@ -3173,6 +3174,102 @@ IMG_BOOL OSInvalidateCPUCacheRangeKM(IMG_HANDLE hOSMemHandle, #endif +typedef struct _AtomicStruct +{ + atomic_t RefCount; +} AtomicStruct; + +PVRSRV_ERROR OSAtomicAlloc(IMG_PVOID *ppvRefCount) +{ + AtomicStruct *psRefCount; + + psRefCount = kmalloc(sizeof(AtomicStruct), GFP_KERNEL); + if (psRefCount == NULL) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + atomic_set(&psRefCount->RefCount, 0); + + *ppvRefCount = psRefCount; + return PVRSRV_OK; +} + +IMG_VOID OSAtomicFree(IMG_PVOID pvRefCount) +{ + AtomicStruct *psRefCount = pvRefCount; + + PVR_ASSERT(atomic_read(&psRefCount->RefCount) == 0); + kfree(psRefCount); +} + +IMG_VOID OSAtomicInc(IMG_PVOID pvRefCount) +{ + AtomicStruct *psRefCount = pvRefCount; + + atomic_inc(&psRefCount->RefCount); +} + +IMG_BOOL OSAtomicDecAndTest(IMG_PVOID pvRefCount) +{ + AtomicStruct *psRefCount = pvRefCount; + + return atomic_dec_and_test(&psRefCount->RefCount) ? IMG_TRUE:IMG_FALSE; +} + +IMG_UINT32 OSAtomicRead(IMG_PVOID pvRefCount) +{ + AtomicStruct *psRefCount = pvRefCount; + + return (IMG_UINT32) atomic_read(&psRefCount->RefCount); +} + +IMG_VOID OSReleaseBridgeLock(IMG_VOID) +{ + LinuxUnLockMutex(&gPVRSRVLock); +} + +IMG_VOID OSReacquireBridgeLock(IMG_VOID) +{ + LinuxLockMutex(&gPVRSRVLock); +} + +typedef struct _OSTime +{ + unsigned long ulTime; +} OSTime; + +PVRSRV_ERROR OSTimeCreateWithUSOffset(IMG_PVOID *pvRet, IMG_UINT32 ui32USOffset) +{ + OSTime *psOSTime; + + psOSTime = kmalloc(sizeof(OSTime), GFP_KERNEL); + if (psOSTime == IMG_NULL) + { + return PVRSRV_ERROR_OUT_OF_MEMORY; + } + + psOSTime->ulTime = usecs_to_jiffies(jiffies_to_usecs(jiffies) + ui32USOffset); + *pvRet = psOSTime; + return PVRSRV_OK; +} + + +IMG_BOOL OSTimeHasTimePassed(IMG_PVOID pvData) +{ + OSTime *psOSTime = pvData; + + if (time_is_before_jiffies(psOSTime->ulTime)) + { + return IMG_TRUE; + } + return IMG_FALSE; +} + +IMG_VOID OSTimeDestroy(IMG_PVOID pvData) +{ + kfree(pvData); +} + PVRSRV_ERROR PVROSFuncInit(IMG_VOID) { #if defined(PVR_LINUX_TIMERS_USING_WORKQUEUES) diff --git a/drivers/gpu/pvr/osfunc.h b/drivers/gpu/pvr/osfunc.h index 6e5cbd1..70caf57 100644 --- a/drivers/gpu/pvr/osfunc.h +++ b/drivers/gpu/pvr/osfunc.h @@ -613,6 +613,33 @@ static INLINE IMG_VOID OSMemoryBarrier(IMG_VOID) { } #endif +PVRSRV_ERROR OSAtomicAlloc(IMG_PVOID *ppvRefCount); +IMG_VOID OSAtomicFree(IMG_PVOID pvRefCount); +IMG_VOID OSAtomicInc(IMG_PVOID pvRefCount); +IMG_BOOL OSAtomicDecAndTest(IMG_PVOID pvRefCount); +IMG_UINT32 OSAtomicRead(IMG_PVOID pvRefCount); + +PVRSRV_ERROR OSTimeCreateWithUSOffset(IMG_PVOID *pvRet, IMG_UINT32 ui32MSOffset); +IMG_BOOL OSTimeHasTimePassed(IMG_PVOID pvData); +IMG_VOID OSTimeDestroy(IMG_PVOID pvData); + +#if defined(__linux__) +IMG_VOID OSReleaseBridgeLock(IMG_VOID); +IMG_VOID OSReacquireBridgeLock(IMG_VOID); +#else + +#ifdef INLINE_IS_PRAGMA +#pragma inline(OSReleaseBridgeLock) +#endif +static INLINE IMG_VOID OSReleaseBridgeLock(IMG_VOID) { } + +#ifdef INLINE_IS_PRAGMA +#pragma inline(OSReacquireBridgeLock) +#endif +static INLINE IMG_VOID OSReacquireBridgeLock(IMG_VOID) { } + +#endif + #if defined (__cplusplus) } #endif diff --git a/drivers/gpu/pvr/pdump_km.h b/drivers/gpu/pvr/pdump_km.h index 224e922..6c516e0 100644 --- a/drivers/gpu/pvr/pdump_km.h +++ b/drivers/gpu/pvr/pdump_km.h @@ -337,6 +337,7 @@ extern IMG_UINT32 g_ui32EveryLineCounter; #define PDUMPMALLOCPAGETABLE PDumpMallocPageTable #define PDUMPSETMMUCONTEXT PDumpSetMMUContext #define PDUMPCLEARMMUCONTEXT PDumpClearMMUContext + #define PDUMPPDDEVPADDR PDumpPDDevPAddrKM #define PDUMPFREEPAGES PDumpFreePages #define PDUMPFREEPAGETABLE PDumpFreePageTable #define PDUMPPDREG PDumpPDReg @@ -373,6 +374,7 @@ extern IMG_UINT32 g_ui32EveryLineCounter; #define PDUMPMALLOCPAGETABLE(args...) #define PDUMPSETMMUCONTEXT(args...) #define PDUMPCLEARMMUCONTEXT(args...) + #define PDUMPPDDEVPADDR(args...) #define PDUMPFREEPAGES(args...) #define PDUMPFREEPAGETABLE(args...) #define PDUMPPDREG(args...) diff --git a/drivers/gpu/pvr/pvr_bridge.h b/drivers/gpu/pvr/pvr_bridge.h index d3e5916..443ad1e 100644 --- a/drivers/gpu/pvr/pvr_bridge.h +++ b/drivers/gpu/pvr/pvr_bridge.h @@ -191,11 +191,7 @@ extern "C" { #define PVRSRV_BRIDGE_UNMAP_MEMINFO_MEM PVRSRV_IOWR(PVRSRV_BRIDGE_SHAREDMEM_CMD_FIRST+3) #define PVRSRV_BRIDGE_SHAREDMEM_CMD_LAST (PVRSRV_BRIDGE_SHAREDMEM_CMD_FIRST+3) -#define PVRSRV_BRIDGE_SERVICES4_TMP_CMD_FIRST (PVRSRV_BRIDGE_SHAREDMEM_CMD_LAST+1) -#define PVRSRV_BRIDGE_GETMMU_PD_DEVPADDR PVRSRV_IOWR(PVRSRV_BRIDGE_SERVICES4_TMP_CMD_FIRST+0) -#define PVRSRV_BRIDGE_SERVICES4_TMP_CMD_LAST (PVRSRV_BRIDGE_SERVICES4_TMP_CMD_FIRST+0) - -#define PVRSRV_BRIDGE_INITSRV_CMD_FIRST (PVRSRV_BRIDGE_SERVICES4_TMP_CMD_LAST+1) +#define PVRSRV_BRIDGE_INITSRV_CMD_FIRST (PVRSRV_BRIDGE_SHAREDMEM_CMD_LAST+1) #define PVRSRV_BRIDGE_INITSRV_CONNECT PVRSRV_IOWR(PVRSRV_BRIDGE_INITSRV_CMD_FIRST+0) #define PVRSRV_BRIDGE_INITSRV_DISCONNECT PVRSRV_IOWR(PVRSRV_BRIDGE_INITSRV_CMD_FIRST+1) #define PVRSRV_BRIDGE_INITSRV_CMD_LAST (PVRSRV_BRIDGE_INITSRV_CMD_FIRST+1) @@ -1595,22 +1591,6 @@ typedef struct PVRSRV_BRIDGE_OUT_UNMAP_MEMINFO_MEM_TAG PVRSRV_ERROR eError; }PVRSRV_BRIDGE_OUT_UNMAP_MEMINFO_MEM; -typedef struct PVRSRV_BRIDGE_IN_GETMMU_PD_DEVPADDR_TAG -{ - IMG_UINT32 ui32BridgeFlags; -#if defined (SUPPORT_SID_INTERFACE) - IMG_SID hDevMemContext; -#else - IMG_HANDLE hDevMemContext; -#endif -}PVRSRV_BRIDGE_IN_GETMMU_PD_DEVPADDR; - -typedef struct PVRSRV_BRIDGE_OUT_GETMMU_PD_DEVPADDR_TAG -{ - IMG_DEV_PHYADDR sPDDevPAddr; - PVRSRV_ERROR eError; -}PVRSRV_BRIDGE_OUT_GETMMU_PD_DEVPADDR; - typedef struct PVRSRV_BRIDGE_IN_EVENT_OBJECT_WAI_TAG { IMG_UINT32 ui32BridgeFlags; diff --git a/drivers/gpu/pvr/pvr_bridge_k.c b/drivers/gpu/pvr/pvr_bridge_k.c index 6d51200..9185df4 100644 --- a/drivers/gpu/pvr/pvr_bridge_k.c +++ b/drivers/gpu/pvr/pvr_bridge_k.c @@ -37,6 +37,7 @@ #include "pvr_bridge_km.h" #include "pvr_uaccess.h" #include "refcount.h" +#include "buffer_manager.h" #if defined(SUPPORT_DRI_DRM) #include <drm/drmP.h> @@ -382,12 +383,19 @@ PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsig PVRSRV_BRIDGE_OUT_EXPORTDEVICEMEM *psExportDeviceMemOUT = (PVRSRV_BRIDGE_OUT_EXPORTDEVICEMEM *)psBridgePackageKM->pvParamOut; PVRSRV_FILE_PRIVATE_DATA *psPrivateData = PRIVATE_DATA(pFile); + IMG_HANDLE hMemInfo; PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; + if (pvr_get_user(hMemInfo, &psExportDeviceMemOUT->hMemInfo) != 0) + { + err = -EFAULT; + goto unlock_and_return; + } + if(PVRSRVLookupHandle(KERNEL_HANDLE_BASE, (IMG_PVOID *)&psKernelMemInfo, - psExportDeviceMemOUT->hMemInfo, + hMemInfo, PVRSRV_HANDLE_TYPE_MEM_INFO) != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "%s: Failed to look up export handle", __FUNCTION__)); @@ -398,7 +406,13 @@ PVRSRV_BridgeDispatchKM(struct file *pFile, unsigned int unref__ ioctlCmd, unsig PVRSRVKernelMemInfoIncRef(psKernelMemInfo); - psPrivateData->hKernelMemInfo = psExportDeviceMemOUT->hMemInfo; + + if (psKernelMemInfo->sShareMemWorkaround.bInUse) + { + BM_XProcIndexAcquire(psKernelMemInfo->sShareMemWorkaround.ui32ShareIndex); + } + + psPrivateData->hKernelMemInfo = hMemInfo; #if defined(SUPPORT_MEMINFO_IDS) psPrivateData->ui64Stamp = ++ui64Stamp; diff --git a/drivers/gpu/pvr/pvr_bridge_km.h b/drivers/gpu/pvr/pvr_bridge_km.h index 184e999..e14f2fd 100644 --- a/drivers/gpu/pvr/pvr_bridge_km.h +++ b/drivers/gpu/pvr/pvr_bridge_km.h @@ -288,7 +288,9 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVAllocSyncInfoKM(IMG_HANDLE hDevCookie, IMG_HANDLE hDevMemContext, PVRSRV_KERNEL_SYNC_INFO **ppsKernelSyncInfo); IMG_IMPORT -PVRSRV_ERROR IMG_CALLCONV PVRSRVFreeSyncInfoKM(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo); +IMG_VOID IMG_CALLCONV PVRSRVAcquireSyncInfoKM(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo); +IMG_IMPORT +IMG_VOID IMG_CALLCONV PVRSRVReleaseSyncInfoKM(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo); IMG_IMPORT #if defined (SUPPORT_SID_INTERFACE) diff --git a/drivers/gpu/pvr/pvr_uaccess.h b/drivers/gpu/pvr/pvr_uaccess.h index 4b7114e..6e7f1d3 100644 --- a/drivers/gpu/pvr/pvr_uaccess.h +++ b/drivers/gpu/pvr/pvr_uaccess.h @@ -65,6 +65,7 @@ static inline unsigned long pvr_copy_from_user(void *pvTo, const void __user *pv } #define pvr_put_user put_user +#define pvr_get_user get_user #endif diff --git a/drivers/gpu/pvr/pvrversion.h b/drivers/gpu/pvr/pvrversion.h index 1065b1c..e883687 100644 --- a/drivers/gpu/pvr/pvrversion.h +++ b/drivers/gpu/pvr/pvrversion.h @@ -36,7 +36,7 @@ #define PVRVERSION_FAMILY "sgxddk" #define PVRVERSION_BRANCHNAME "1.8" -#define PVRVERSION_BUILD 785978 +#define PVRVERSION_BUILD 945322 #define PVRVERSION_BSCONTROL "CustomerGoogle_Android_ogles1_ogles2_GPL" #define PVRVERSION_STRING "CustomerGoogle_Android_ogles1_ogles2_GPL sgxddk 18 1.8@" PVR_STR2(PVRVERSION_BUILD) @@ -44,8 +44,8 @@ #define COPYRIGHT_TXT "Copyright (c) Imagination Technologies Ltd. All Rights Reserved." -#define PVRVERSION_BUILD_HI 78 -#define PVRVERSION_BUILD_LO 5978 +#define PVRVERSION_BUILD_HI 94 +#define PVRVERSION_BUILD_LO 5322 #define PVRVERSION_STRING_NUMERIC PVR_STR2(PVRVERSION_MAJ) "." PVR_STR2(PVRVERSION_MIN) "." PVR_STR2(PVRVERSION_BUILD_HI) "." PVR_STR2(PVRVERSION_BUILD_LO) #endif /* _PVRVERSION_H_ */ diff --git a/drivers/gpu/pvr/queue.c b/drivers/gpu/pvr/queue.c index 4682094..374bf7b 100644 --- a/drivers/gpu/pvr/queue.c +++ b/drivers/gpu/pvr/queue.c @@ -25,6 +25,7 @@ ******************************************************************************/ #include "services_headers.h" +#include "pvr_bridge_km.h" #include "lists.h" #include "ttrace.h" @@ -625,6 +626,8 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVInsertCommandKM(PVRSRV_QUEUE_INFO *psQueue, psCommand->psDstSync[i].ui32WriteOpsPending = PVRSRVGetWriteOpsPending(apsDstSync[i], IMG_FALSE); psCommand->psDstSync[i].ui32ReadOps2Pending = PVRSRVGetReadOpsPending(apsDstSync[i], IMG_FALSE); + PVRSRVKernelSyncInfoIncRef(apsDstSync[i], IMG_NULL); + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVInsertCommandKM: Dst %u RO-VA:0x%x WO-VA:0x%x ROP:0x%x WOP:0x%x", i, psCommand->psDstSync[i].psKernelSyncInfoKM->sReadOps2CompleteDevVAddr.uiAddr, psCommand->psDstSync[i].psKernelSyncInfoKM->sWriteOpsCompleteDevVAddr.uiAddr, @@ -642,6 +645,8 @@ PVRSRV_ERROR IMG_CALLCONV PVRSRVInsertCommandKM(PVRSRV_QUEUE_INFO *psQueue, psCommand->psSrcSync[i].ui32WriteOpsPending = PVRSRVGetWriteOpsPending(apsSrcSync[i], IMG_TRUE); psCommand->psSrcSync[i].ui32ReadOps2Pending = PVRSRVGetReadOpsPending(apsSrcSync[i], IMG_TRUE); + PVRSRVKernelSyncInfoIncRef(apsSrcSync[i], IMG_NULL); + PVR_DPF((PVR_DBG_MESSAGE, "PVRSRVInsertCommandKM: Src %u RO-VA:0x%x WO-VA:0x%x ROP:0x%x WOP:0x%x", i, psCommand->psSrcSync[i].psKernelSyncInfoKM->sReadOps2CompleteDevVAddr.uiAddr, psCommand->psSrcSync[i].psKernelSyncInfoKM->sWriteOpsCompleteDevVAddr.uiAddr, @@ -1003,6 +1008,8 @@ IMG_VOID PVRSRVCommandCompleteKM(IMG_HANDLE hCmdCookie, { psCmdCompleteData->psDstSync[i].psKernelSyncInfoKM->psSyncData->ui32WriteOpsComplete++; + PVRSRVKernelSyncInfoDecRef(psCmdCompleteData->psDstSync[i].psKernelSyncInfoKM, IMG_NULL); + PVR_TTRACE_SYNC_OBJECT(PVRSRV_TRACE_GROUP_QUEUE, QUEUE_TOKEN_UPDATE_DST, psCmdCompleteData->psDstSync[i].psKernelSyncInfoKM, PVRSRV_SYNCOP_COMPLETE); @@ -1019,6 +1026,8 @@ IMG_VOID PVRSRVCommandCompleteKM(IMG_HANDLE hCmdCookie, { psCmdCompleteData->psSrcSync[i].psKernelSyncInfoKM->psSyncData->ui32ReadOps2Complete++; + PVRSRVKernelSyncInfoDecRef(psCmdCompleteData->psSrcSync[i].psKernelSyncInfoKM, IMG_NULL); + PVR_TTRACE_SYNC_OBJECT(PVRSRV_TRACE_GROUP_QUEUE, QUEUE_TOKEN_UPDATE_SRC, psCmdCompleteData->psSrcSync[i].psKernelSyncInfoKM, PVRSRV_SYNCOP_COMPLETE); @@ -1185,6 +1194,7 @@ PVRSRV_ERROR PVRSRVRemoveCmdProcListKM(IMG_UINT32 ui32DevIndex, if (psCmdCompleteData != IMG_NULL) { + PVR_ASSERT(psCmdCompleteData->bInUse == IMG_FALSE); OSFreeMem(PVRSRV_OS_NON_PAGEABLE_HEAP, psCmdCompleteData->ui32AllocSize, psCmdCompleteData, IMG_NULL); psDeviceCommandData[ui32CmdTypeCounter].apsCmdCompleteData[ui32CmdCounter] = IMG_NULL; diff --git a/drivers/gpu/pvr/refcount.c b/drivers/gpu/pvr/refcount.c index 8b00972..5bc9b4a 100644 --- a/drivers/gpu/pvr/refcount.c +++ b/drivers/gpu/pvr/refcount.c @@ -1,7 +1,27 @@ -/*************************************************************************/ /*! -@Title Services reference count debugging -@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved -@License Strictly Confidential. +/********************************************************************** + * + * Copyright (C) Imagination Technologies Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful but, except + * as otherwise stated in writing, 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, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Contact Information: + * Imagination Technologies Ltd. <gpl-support@imgtec.com> + * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK + * */ /**************************************************************************/ #if defined(PVRSRV_REFCOUNT_DEBUG) @@ -26,10 +46,11 @@ static DEFINE_MUTEX(gsCCBLock); #define PVRSRV_REFCOUNT_CCB_DEBUG_MEMINFO (1U << 1) #define PVRSRV_REFCOUNT_CCB_DEBUG_BM_BUF (1U << 2) #define PVRSRV_REFCOUNT_CCB_DEBUG_BM_BUF2 (1U << 3) +#define PVRSRV_REFCOUNT_CCB_DEBUG_BM_XPROC (1U << 4) #if defined(__linux__) -#define PVRSRV_REFCOUNT_CCB_DEBUG_MMAP (1U << 4) -#define PVRSRV_REFCOUNT_CCB_DEBUG_MMAP2 (1U << 5) +#define PVRSRV_REFCOUNT_CCB_DEBUG_MMAP (1U << 16) +#define PVRSRV_REFCOUNT_CCB_DEBUG_MMAP2 (1U << 17) #else #define PVRSRV_REFCOUNT_CCB_DEBUG_MMAP 0 #define PVRSRV_REFCOUNT_CCB_DEBUG_MMAP2 0 @@ -95,6 +116,8 @@ void PVRSRVKernelSyncInfoIncRef2(const IMG_CHAR *pszFile, IMG_INT iLine, PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo, PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) { + IMG_UINT32 ui32RefValue = OSAtomicRead(psKernelSyncInfo->pvRefCount); + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_SYNCINFO)) goto skip; @@ -111,8 +134,8 @@ void PVRSRVKernelSyncInfoIncRef2(const IMG_CHAR *pszFile, IMG_INT iLine, psKernelMemInfo, NULL, (psKernelMemInfo) ? psKernelMemInfo->sMemBlk.hOSMemHandle : NULL, - psKernelSyncInfo->ui32RefCount, - psKernelSyncInfo->ui32RefCount + 1, + ui32RefValue, + ui32RefValue + 1, (psKernelMemInfo) ? psKernelMemInfo->uAllocSize : 0); gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; @@ -120,7 +143,7 @@ void PVRSRVKernelSyncInfoIncRef2(const IMG_CHAR *pszFile, IMG_INT iLine, PVRSRV_UNLOCK_CCB(); skip: - psKernelSyncInfo->ui32RefCount++; + PVRSRVAcquireSyncInfoKM(psKernelSyncInfo); } IMG_INTERNAL @@ -128,6 +151,8 @@ void PVRSRVKernelSyncInfoDecRef2(const IMG_CHAR *pszFile, IMG_INT iLine, PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo, PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) { + IMG_UINT32 ui32RefValue = OSAtomicRead(psKernelSyncInfo->pvRefCount); + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_SYNCINFO)) goto skip; @@ -144,8 +169,8 @@ void PVRSRVKernelSyncInfoDecRef2(const IMG_CHAR *pszFile, IMG_INT iLine, psKernelMemInfo, (psKernelMemInfo) ? psKernelMemInfo->sMemBlk.hOSMemHandle : NULL, NULL, - psKernelSyncInfo->ui32RefCount, - psKernelSyncInfo->ui32RefCount - 1, + ui32RefValue, + ui32RefValue - 1, (psKernelMemInfo) ? psKernelMemInfo->uAllocSize : 0); gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; @@ -153,7 +178,7 @@ void PVRSRVKernelSyncInfoDecRef2(const IMG_CHAR *pszFile, IMG_INT iLine, PVRSRV_UNLOCK_CCB(); skip: - psKernelSyncInfo->ui32RefCount--; + PVRSRVReleaseSyncInfoKM(psKernelSyncInfo); } IMG_INTERNAL @@ -344,6 +369,68 @@ skip: pBuf->ui32ExportCount--; } +IMG_INTERNAL +void PVRSRVBMXProcIncRef2(const IMG_CHAR *pszFile, IMG_INT iLine, IMG_UINT32 ui32Index) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_BM_XPROC)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "BM_XPROC", + NULL, + NULL, + gXProcWorkaroundShareData[ui32Index].hOSMemHandle, + (IMG_VOID *) ui32Index, + gXProcWorkaroundShareData[ui32Index].ui32RefCount, + gXProcWorkaroundShareData[ui32Index].ui32RefCount + 1, + gXProcWorkaroundShareData[ui32Index].ui32Size); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + gXProcWorkaroundShareData[ui32Index].ui32RefCount++; +} + +IMG_INTERNAL +void PVRSRVBMXProcDecRef2(const IMG_CHAR *pszFile, IMG_INT iLine, IMG_UINT32 ui32Index) +{ + if(!(guiDebugMask & PVRSRV_REFCOUNT_CCB_DEBUG_BM_XPROC)) + goto skip; + + PVRSRV_LOCK_CCB(); + + gsRefCountCCB[giOffset].pszFile = pszFile; + gsRefCountCCB[giOffset].iLine = iLine; + gsRefCountCCB[giOffset].ui32PID = OSGetCurrentProcessIDKM(); + snprintf(gsRefCountCCB[giOffset].pcMesg, + PVRSRV_REFCOUNT_CCB_MESG_MAX - 1, + PVRSRV_REFCOUNT_CCB_FMT_STRING, + "BM_XPROC", + NULL, + NULL, + gXProcWorkaroundShareData[ui32Index].hOSMemHandle, + (IMG_VOID *) ui32Index, + gXProcWorkaroundShareData[ui32Index].ui32RefCount, + gXProcWorkaroundShareData[ui32Index].ui32RefCount - 1, + gXProcWorkaroundShareData[ui32Index].ui32Size); + gsRefCountCCB[giOffset].pcMesg[PVRSRV_REFCOUNT_CCB_MESG_MAX - 1] = 0; + giOffset = (giOffset + 1) % PVRSRV_REFCOUNT_CCB_MAX; + + PVRSRV_UNLOCK_CCB(); + +skip: + gXProcWorkaroundShareData[ui32Index].ui32RefCount--; +} + #if defined(__linux__) /* mmap refcounting is Linux specific */ diff --git a/drivers/gpu/pvr/refcount.h b/drivers/gpu/pvr/refcount.h index d2fd9c7..92de65c 100644 --- a/drivers/gpu/pvr/refcount.h +++ b/drivers/gpu/pvr/refcount.h @@ -1,12 +1,34 @@ -/*************************************************************************/ /*! -@Title Services reference count debugging -@Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved -@License Strictly Confidential. +/********************************************************************** + * + * Copyright (C) Imagination Technologies Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful but, except + * as otherwise stated in writing, 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, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Contact Information: + * Imagination Technologies Ltd. <gpl-support@imgtec.com> + * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK + * */ /**************************************************************************/ #ifndef __REFCOUNT_H__ #define __REFCOUNT_H__ +#include "pvr_bridge_km.h" + #if defined(PVRSRV_REFCOUNT_DEBUG) void PVRSRVDumpRefCountCCB(void); @@ -46,6 +68,10 @@ void PVRSRVBMBufIncExport2(const IMG_CHAR *pszFile, IMG_INT iLine, BM_BUF *pBuf); void PVRSRVBMBufDecExport2(const IMG_CHAR *pszFile, IMG_INT iLine, BM_BUF *pBuf); +void PVRSRVBMXProcIncRef2(const IMG_CHAR *pszFile, IMG_INT iLine, + IMG_UINT32 ui32Index); +void PVRSRVBMXProcDecRef2(const IMG_CHAR *pszFile, IMG_INT iLine, + IMG_UINT32 ui32Index); #if defined(__linux__) @@ -80,14 +106,14 @@ static INLINE void PVRSRVKernelSyncInfoIncRef(PVRSRV_KERNEL_SYNC_INFO *psKernelS PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) { PVR_UNREFERENCED_PARAMETER(psKernelMemInfo); - psKernelSyncInfo->ui32RefCount++; + PVRSRVAcquireSyncInfoKM(psKernelSyncInfo); } static INLINE void PVRSRVKernelSyncInfoDecRef(PVRSRV_KERNEL_SYNC_INFO *psKernelSyncInfo, PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) { PVR_UNREFERENCED_PARAMETER(psKernelMemInfo); - psKernelSyncInfo->ui32RefCount--; + PVRSRVReleaseSyncInfoKM(psKernelSyncInfo); } static INLINE void PVRSRVKernelMemInfoIncRef(PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo) @@ -100,49 +126,59 @@ static INLINE void PVRSRVKernelMemInfoDecRef(PVRSRV_KERNEL_MEM_INFO *psKernelMem psKernelMemInfo->ui32RefCount--; } -#if defined(__linux__) +static INLINE void PVRSRVBMBufIncRef(BM_BUF *pBuf) +{ + pBuf->ui32RefCount++; +} -/* mmap refcounting is Linux specific */ -#include "mmap.h" +static INLINE void PVRSRVBMBufDecRef(BM_BUF *pBuf) +{ + pBuf->ui32RefCount--; +} -static INLINE void PVRSRVOffsetStructIncRef(PKV_OFFSET_STRUCT psOffsetStruct) +static INLINE void PVRSRVBMBufIncExport(BM_BUF *pBuf) { - psOffsetStruct->ui32RefCount++; + pBuf->ui32ExportCount++; } -static INLINE void PVRSRVOffsetStructDecRef(PKV_OFFSET_STRUCT psOffsetStruct) +static INLINE void PVRSRVBMBufDecExport(BM_BUF *pBuf) { - psOffsetStruct->ui32RefCount--; + pBuf->ui32ExportCount--; } -static INLINE void PVRSRVOffsetStructIncMapped(PKV_OFFSET_STRUCT psOffsetStruct) +static INLINE void PVRSRVBMXProcIncRef(IMG_UINT32 ui32Index) { - psOffsetStruct->ui32Mapped++; + gXProcWorkaroundShareData[ui32Index].ui32RefCount++; } -static INLINE void PVRSRVOffsetStructDecMapped(PKV_OFFSET_STRUCT psOffsetStruct) +static INLINE void PVRSRVBMXProcDecRef(IMG_UINT32 ui32Index) { - psOffsetStruct->ui32Mapped--; + gXProcWorkaroundShareData[ui32Index].ui32RefCount--; } -static INLINE void PVRSRVBMBufIncRef(BM_BUF *pBuf) +#if defined(__linux__) + +/* mmap refcounting is Linux specific */ +#include "mmap.h" + +static INLINE void PVRSRVOffsetStructIncRef(PKV_OFFSET_STRUCT psOffsetStruct) { - pBuf->ui32RefCount++; + psOffsetStruct->ui32RefCount++; } -static INLINE void PVRSRVBMBufDecRef(BM_BUF *pBuf) +static INLINE void PVRSRVOffsetStructDecRef(PKV_OFFSET_STRUCT psOffsetStruct) { - pBuf->ui32RefCount--; + psOffsetStruct->ui32RefCount--; } -static INLINE void PVRSRVBMBufIncExport(BM_BUF *pBuf) +static INLINE void PVRSRVOffsetStructIncMapped(PKV_OFFSET_STRUCT psOffsetStruct) { - pBuf->ui32ExportCount++; + psOffsetStruct->ui32Mapped++; } -static INLINE void PVRSRVBMBufDecExport(BM_BUF *pBuf) +static INLINE void PVRSRVOffsetStructDecMapped(PKV_OFFSET_STRUCT psOffsetStruct) { - pBuf->ui32ExportCount--; + psOffsetStruct->ui32Mapped--; } #endif /* defined(__linux__) */ diff --git a/drivers/gpu/pvr/resman.c b/drivers/gpu/pvr/resman.c index 5088c7f..8eddf77 100644 --- a/drivers/gpu/pvr/resman.c +++ b/drivers/gpu/pvr/resman.c @@ -598,17 +598,13 @@ static PVRSRV_ERROR FreeResourceByPtr(RESMAN_ITEM *psItem, (IMG_UINTPTR_T)psItem->pfnFreeResource, psItem->ui32Flags)); - List_RESMAN_ITEM_Remove(psItem); - - - RELEASE_SYNC_OBJ; if (bExecuteCallback) { eError = psItem->pfnFreeResource(psItem->pvParam, psItem->ui32Param, bForceCleanup); - if (eError != PVRSRV_OK) + if ((eError != PVRSRV_OK) && (eError != PVRSRV_ERROR_RETRY)) { PVR_DPF((PVR_DBG_ERROR, "FreeResourceByPtr: ERROR calling FreeResource function")); } @@ -617,8 +613,14 @@ static PVRSRV_ERROR FreeResourceByPtr(RESMAN_ITEM *psItem, ACQUIRE_SYNC_OBJ; - - OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(RESMAN_ITEM), psItem, IMG_NULL); + if (eError != PVRSRV_ERROR_RETRY) + { + + List_RESMAN_ITEM_Remove(psItem); + + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, sizeof(RESMAN_ITEM), psItem, IMG_NULL); + } return(eError); } @@ -679,7 +681,19 @@ static PVRSRV_ERROR FreeResourceByCriteria(PRESMAN_CONTEXT psResManContext, ui32Param)) != IMG_NULL && eError == PVRSRV_OK) { - eError = FreeResourceByPtr(psCurItem, bExecuteCallback, CLEANUP_WITH_POLL); + do + { + eError = FreeResourceByPtr(psCurItem, bExecuteCallback, CLEANUP_WITH_POLL); + if (eError == PVRSRV_ERROR_RETRY) + { + RELEASE_SYNC_OBJ; + OSReleaseBridgeLock(); + + OSSleepms(MAX_CLEANUP_TIME_WAIT_US/1000); + OSReacquireBridgeLock(); + ACQUIRE_SYNC_OBJ; + } + } while (eError == PVRSRV_ERROR_RETRY); } return eError; diff --git a/drivers/gpu/pvr/servicesext.h b/drivers/gpu/pvr/servicesext.h index af3e4aa..42558b9 100644 --- a/drivers/gpu/pvr/servicesext.h +++ b/drivers/gpu/pvr/servicesext.h @@ -645,6 +645,9 @@ typedef struct _PVRSRV_SYNC_DATA_ IMG_UINT32 ui32LastOpDumpVal; IMG_UINT32 ui32LastReadOpDumpVal; + + IMG_UINT64 ui64LastWrite; + } PVRSRV_SYNC_DATA; typedef struct _PVRSRV_CLIENT_SYNC_INFO_ diff --git a/drivers/gpu/pvr/servicesint.h b/drivers/gpu/pvr/servicesint.h index c9b565e..afe2bcf 100644 --- a/drivers/gpu/pvr/servicesint.h +++ b/drivers/gpu/pvr/servicesint.h @@ -33,6 +33,7 @@ extern "C" { #include "services.h" #include "sysinfo.h" +#include "sysconfig.h" #define HWREC_DEFAULT_TIMEOUT (500) @@ -47,6 +48,10 @@ extern "C" { #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif +#define MAX_CLEANUP_TIME_US (MAX_HW_TIME_US * 4) +#define MAX_CLEANUP_TRYS 100 +#define MAX_CLEANUP_TIME_WAIT_US (MAX_CLEANUP_TIME_US/MAX_CLEANUP_TRYS) + typedef enum _PVRSRV_MEMTYPE_ { PVRSRV_MEMTYPE_UNKNOWN = 0, @@ -144,7 +149,7 @@ typedef struct _PVRSRV_KERNEL_SYNC_INFO_ - IMG_UINT32 ui32RefCount; + IMG_PVOID pvRefCount; IMG_HANDLE hResItem; @@ -341,16 +346,6 @@ PVRSRV_ERROR PVRSRVQueueCommand(IMG_HANDLE hQueueInfo, PVRSRV_COMMAND *psCommand); - -IMG_IMPORT PVRSRV_ERROR IMG_CALLCONV -PVRSRVGetMMUContextPDDevPAddr(const PVRSRV_CONNECTION *psConnection, -#if defined (SUPPORT_SID_INTERFACE) - IMG_SID hDevMemContext, -#else - IMG_HANDLE hDevMemContext, -#endif - IMG_DEV_PHYADDR *sPDDevPAddr); - IMG_IMPORT PVRSRV_ERROR IMG_CALLCONV PVRSRVAllocSharedSysMem(const PVRSRV_CONNECTION *psConnection, IMG_UINT32 ui32Flags, diff --git a/drivers/gpu/pvr/sgx/bridged_sgx_bridge.c b/drivers/gpu/pvr/sgx/bridged_sgx_bridge.c index f616d83..4e4cf24 100644 --- a/drivers/gpu/pvr/sgx/bridged_sgx_bridge.c +++ b/drivers/gpu/pvr/sgx/bridged_sgx_bridge.c @@ -377,7 +377,7 @@ SGXDoKickBW(IMG_UINT32 ui32BridgeID, } #else - if (psDoKickIN->sCCBKick.ui32NumSrcSyncs > SGX_MAX_SRC_SYNCS) + if (psDoKickIN->sCCBKick.ui32NumSrcSyncs > SGX_MAX_SRC_SYNCS_TA) { psRetOUT->eError = PVRSRV_ERROR_INVALID_PARAMS; return 0; @@ -780,6 +780,90 @@ SGXSubmitTransferBW(IMG_UINT32 ui32BridgeID, return 0; } +static IMG_INT +SGXSetTransferContextPriorityBW(IMG_UINT32 ui32BridgeID, + PVRSRV_BRIDGE_IN_SGX_SET_TRANSFER_CONTEXT_PRIORITY *psSGXSetTransferContextPriorityIN, + PVRSRV_BRIDGE_RETURN *psRetOUT, + PVRSRV_PER_PROCESS_DATA *psPerProc) +{ + IMG_HANDLE hDevCookieInt; + IMG_HANDLE hTransferContextInt; + + PVRSRV_BRIDGE_ASSERT_CMD(ui32BridgeID, PVRSRV_BRIDGE_SGX_SET_TRANSFER_CONTEXT_PRIORITY); + + psRetOUT->eError = + PVRSRVLookupHandle(psPerProc->psHandleBase, + &hDevCookieInt, + psSGXSetTransferContextPriorityIN->hDevCookie, + PVRSRV_HANDLE_TYPE_DEV_NODE); + + if(psRetOUT->eError != PVRSRV_OK) + { + return 0; + } + + psRetOUT->eError = + PVRSRVLookupHandle(psPerProc->psHandleBase, + &hTransferContextInt, + psSGXSetTransferContextPriorityIN->hHWTransferContext, + PVRSRV_HANDLE_TYPE_SGX_HW_TRANSFER_CONTEXT); + + if(psRetOUT->eError != PVRSRV_OK) + { + return 0; + } + + psRetOUT->eError = SGXSetTransferContextPriorityKM( + hDevCookieInt, + hTransferContextInt, + psSGXSetTransferContextPriorityIN->ui32Priority, + psSGXSetTransferContextPriorityIN->ui32OffsetOfPriorityField); + + return 0; +} + +static IMG_INT +SGXSetRenderContextPriorityBW(IMG_UINT32 ui32BridgeID, + PVRSRV_BRIDGE_IN_SGX_SET_RENDER_CONTEXT_PRIORITY *psSGXSetRenderContextPriorityIN, + PVRSRV_BRIDGE_RETURN *psRetOUT, + PVRSRV_PER_PROCESS_DATA *psPerProc) +{ + IMG_HANDLE hDevCookieInt; + IMG_HANDLE hRenderContextInt; + + PVRSRV_BRIDGE_ASSERT_CMD(ui32BridgeID, PVRSRV_BRIDGE_SGX_SET_RENDER_CONTEXT_PRIORITY); + + psRetOUT->eError = + PVRSRVLookupHandle(psPerProc->psHandleBase, + &hDevCookieInt, + psSGXSetRenderContextPriorityIN->hDevCookie, + PVRSRV_HANDLE_TYPE_DEV_NODE); + + if(psRetOUT->eError != PVRSRV_OK) + { + return 0; + } + + psRetOUT->eError = + PVRSRVLookupHandle(psPerProc->psHandleBase, + &hRenderContextInt, + psSGXSetRenderContextPriorityIN->hHWRenderContext, + PVRSRV_HANDLE_TYPE_SGX_HW_RENDER_CONTEXT); + + if(psRetOUT->eError != PVRSRV_OK) + { + return 0; + } + + psRetOUT->eError = SGXSetRenderContextPriorityKM( + hDevCookieInt, + hRenderContextInt, + psSGXSetRenderContextPriorityIN->ui32Priority, + psSGXSetRenderContextPriorityIN->ui32OffsetOfPriorityField); + + return 0; +} + #if defined(SGX_FEATURE_2D_HARDWARE) static IMG_INT @@ -1288,7 +1372,7 @@ SGXDevInitPart2BW(IMG_UINT32 ui32BridgeID, #endif -#if defined(FIX_HW_BRN_31542) +#if defined(FIX_HW_BRN_31542) || defined(FIX_HW_BRN_36513) eError = PVRSRVLookupHandle(psPerProc->psHandleBase, &hDummy, psSGXDevInitPart2IN->sInitInfo.hKernelClearClipWAVDMStreamMemInfo, @@ -1356,7 +1440,7 @@ SGXDevInitPart2BW(IMG_UINT32 ui32BridgeID, } #endif -#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31425) +#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31559) eError = PVRSRVLookupHandle(psPerProc->psHandleBase, &hDummy, psSGXDevInitPart2IN->sInitInfo.hKernelVDMSnapShotBufferMemInfo, @@ -1615,7 +1699,7 @@ SGXDevInitPart2BW(IMG_UINT32 ui32BridgeID, #endif -#if defined(FIX_HW_BRN_31542) +#if defined(FIX_HW_BRN_31542) || defined(FIX_HW_BRN_36513) eError = PVRSRVLookupAndReleaseHandle(psPerProc->psHandleBase, #if defined (SUPPORT_SID_INTERFACE) &asInitInfoKM.hKernelClearClipWAVDMStreamMemInfo, @@ -1713,7 +1797,7 @@ SGXDevInitPart2BW(IMG_UINT32 ui32BridgeID, bReleaseFailed = IMG_TRUE; } #endif -#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31425) +#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31559) eError = PVRSRVLookupAndReleaseHandle(psPerProc->psHandleBase, &psSGXDevInitPart2IN->sInitInfo.hKernelVDMSnapShotBufferMemInfo, psSGXDevInitPart2IN->sInitInfo.hKernelVDMSnapShotBufferMemInfo, @@ -1943,7 +2027,7 @@ SGXDevInitPart2BW(IMG_UINT32 ui32BridgeID, #endif #endif -#if defined(FIX_HW_BRN_31542) +#if defined(FIX_HW_BRN_31542) || defined(FIX_HW_BRN_36513) #if defined (SUPPORT_SID_INTERFACE) eError = PVRSRVDissociateDeviceMemKM(hDevCookieInt, asInitInfoKM.hKernelClearClipWAVDMStreamMemInfo); if (eError != PVRSRV_OK) @@ -2026,7 +2110,7 @@ SGXDevInitPart2BW(IMG_UINT32 ui32BridgeID, #endif #endif -#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31425) +#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31559) eError = PVRSRVDissociateDeviceMemKM(hDevCookieInt, psSGXDevInitPart2IN->sInitInfo.hKernelVDMSnapShotBufferMemInfo); bDissociateFailed |= (IMG_BOOL)(eError != PVRSRV_OK); @@ -2176,8 +2260,12 @@ SGXRegisterHWRenderContextBW(IMG_UINT32 ui32BridgeID, hHWRenderContextInt = SGXRegisterHWRenderContextKM(hDevCookieInt, - &psSGXRegHWRenderContextIN->sHWRenderContextDevVAddr, - psPerProc); + psSGXRegHWRenderContextIN->pHWRenderContextCpuVAddr, + psSGXRegHWRenderContextIN->ui32HWRenderContextSize, + psSGXRegHWRenderContextIN->ui32OffsetToPDDevPAddr, + psSGXRegHWRenderContextIN->hDevMemContext, + &psSGXRegHWRenderContextOUT->sHWRenderContextDevVAddr, + psPerProc); if (hHWRenderContextInt == IMG_NULL) { @@ -2258,7 +2346,11 @@ SGXRegisterHWTransferContextBW(IMG_UINT32 ui32BridgeID, hHWTransferContextInt = SGXRegisterHWTransferContextKM(hDevCookieInt, - &psSGXRegHWTransferContextIN->sHWTransferContextDevVAddr, + psSGXRegHWTransferContextIN->pHWTransferContextCpuVAddr, + psSGXRegHWTransferContextIN->ui32HWTransferContextSize, + psSGXRegHWTransferContextIN->ui32OffsetToPDDevPAddr, + psSGXRegHWTransferContextIN->hDevMemContext, + &psSGXRegHWTransferContextOUT->sHWTransferContextDevVAddr, psPerProc); if (hHWTransferContextInt == IMG_NULL) @@ -2345,7 +2437,11 @@ SGXRegisterHW2DContextBW(IMG_UINT32 ui32BridgeID, hHW2DContextInt = SGXRegisterHW2DContextKM(hDevCookieInt, - &psSGXRegHW2DContextIN->sHW2DContextDevVAddr, + psSGXRegHW2DContextIN->pHW2DContextCpuVAddr, + psSGXRegHW2DContextIN->ui32HW2DContextSize, + psSGXRegHW2DContextIN->ui32OffsetToPDDevPAddr, + psSGXRegHW2DContextIN->hDevMemContext, + &psSGXRegHW2DContextOUT->sHW2DContextDevVAddr, psPerProc); if (hHW2DContextInt == IMG_NULL) @@ -3627,8 +3723,6 @@ IMG_VOID SetSGXDispatchTableEntry(IMG_VOID) SetDispatchTableEntry(PVRSRV_BRIDGE_SGX_2DQUERYBLTSCOMPLETE, SGX2DQueryBlitsCompleteBW); - SetDispatchTableEntry(PVRSRV_BRIDGE_SGX_GETMMUPDADDR, DummyBW); - #if defined(TRANSFER_QUEUE) SetDispatchTableEntry(PVRSRV_BRIDGE_SGX_SUBMITTRANSFER, SGXSubmitTransferBW); #endif @@ -3653,6 +3747,8 @@ IMG_VOID SetSGXDispatchTableEntry(IMG_VOID) SetDispatchTableEntry(PVRSRV_BRIDGE_SGX_SCHEDULE_PROCESS_QUEUES, SGXScheduleProcessQueuesBW); SetDispatchTableEntry(PVRSRV_BRIDGE_SGX_READ_HWPERF_CB, SGXReadHWPerfCBBW); + SetDispatchTableEntry(PVRSRV_BRIDGE_SGX_SET_RENDER_CONTEXT_PRIORITY, SGXSetRenderContextPriorityBW); + SetDispatchTableEntry(PVRSRV_BRIDGE_SGX_SET_TRANSFER_CONTEXT_PRIORITY, SGXSetTransferContextPriorityBW); #if defined(PDUMP) SetDispatchTableEntry(PVRSRV_BRIDGE_SGX_PDUMP_BUFFER_ARRAY, SGXPDumpBufferArrayBW); diff --git a/drivers/gpu/pvr/sgx/mmu.c b/drivers/gpu/pvr/sgx/mmu.c index 774026d..9d124f5 100644 --- a/drivers/gpu/pvr/sgx/mmu.c +++ b/drivers/gpu/pvr/sgx/mmu.c @@ -248,6 +248,111 @@ static INLINE IMG_VOID CheckPT(MMU_PT_INFO *psPTInfoList) } #endif +#if defined(PVRSRV_MMU_MAKE_READWRITE_ON_DEMAND) + +#include <linux/version.h> + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)) +#ifndef AUTOCONF_INCLUDED +#include <linux/config.h> +#endif +#else +#include <generated/autoconf.h> +#endif + +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/highmem.h> +#include <asm/pgtable.h> +#include <asm/tlbflush.h> + +static IMG_VOID MakeKernelPageReadWrite(IMG_PVOID ulCPUVAddr) +{ + pgd_t *psPGD; + pud_t *psPUD; + pmd_t *psPMD; + pte_t *psPTE; + pte_t ptent; + IMG_UINT32 ui32CPUVAddr = (IMG_UINT32) ulCPUVAddr; + + psPGD = pgd_offset_k(ui32CPUVAddr); + if (pgd_none(*psPGD) || pgd_bad(*psPGD)) + { + PVR_ASSERT(0); + } + + psPUD = pud_offset(psPGD, ui32CPUVAddr); + if (pud_none(*psPUD) || pud_bad(*psPUD)) + { + PVR_ASSERT(0); + } + + psPMD = pmd_offset(psPUD, ui32CPUVAddr); + if (pmd_none(*psPMD) || pmd_bad(*psPMD)) + { + PVR_ASSERT(0); + } + psPTE = (pte_t *)pte_offset_kernel(psPMD, ui32CPUVAddr); + + ptent = ptep_modify_prot_start(&init_mm, ui32CPUVAddr, psPTE); + ptent = pte_mkwrite(ptent); + ptep_modify_prot_commit(&init_mm, ui32CPUVAddr, psPTE, ptent); + + flush_tlb_all(); +} + +static IMG_VOID MakeKernelPageReadOnly(IMG_PVOID ulCPUVAddr) +{ + pgd_t *psPGD; + pud_t *psPUD; + pmd_t *psPMD; + pte_t *psPTE; + pte_t ptent; + IMG_UINT32 ui32CPUVAddr = (IMG_UINT32) ulCPUVAddr; + + OSWriteMemoryBarrier(); + + psPGD = pgd_offset_k(ui32CPUVAddr); + if (pgd_none(*psPGD) || pgd_bad(*psPGD)) + { + PVR_ASSERT(0); + } + + psPUD = pud_offset(psPGD, ui32CPUVAddr); + if (pud_none(*psPUD) || pud_bad(*psPUD)) + { + PVR_ASSERT(0); + } + + psPMD = pmd_offset(psPUD, ui32CPUVAddr); + if (pmd_none(*psPMD) || pmd_bad(*psPMD)) + { + PVR_ASSERT(0); + } + + psPTE = (pte_t *)pte_offset_kernel(psPMD, ui32CPUVAddr); + + ptent = ptep_modify_prot_start(&init_mm, ui32CPUVAddr, psPTE); + ptent = pte_wrprotect(ptent); + ptep_modify_prot_commit(&init_mm, ui32CPUVAddr, psPTE, ptent); + + flush_tlb_all(); + +} + +#else + +static INLINE IMG_VOID MakeKernelPageReadWrite(IMG_PVOID ulCPUVAddr) +{ + PVR_UNREFERENCED_PARAMETER(ulCPUVAddr); +} + +static INLINE IMG_VOID MakeKernelPageReadOnly(IMG_PVOID ulCPUVAddr) +{ + PVR_UNREFERENCED_PARAMETER(ulCPUVAddr); +} + +#endif IMG_BOOL MMU_IsHeapShared(MMU_HEAP* pMMUHeap) { @@ -401,11 +506,13 @@ static IMG_BOOL BRN31620FreePageTable(MMU_HEAP *psMMUHeap, IMG_UINT32 ui32PDInde psMMUContextWalker->ui32PDChangeMask[ui32PDBitMaskIndex] |= 1 << ui32PDBitMaskShift; + MakeKernelPageReadWrite(psMMUContextWalker->pvPDCpuVAddr); pui32Tmp = (IMG_UINT32 *) psMMUContextWalker->pvPDCpuVAddr; pui32Tmp[ui32PDIndexStart + BRN31620_DUMMY_PDE_INDEX] = (psDevInfo->sBRN31620DummyPTDevPAddr.uiAddr>>SGX_MMU_PDE_ADDR_ALIGNSHIFT) | SGX_MMU_PDE_PAGE_SIZE_4K | SGX_MMU_PDE_DUMMY_PAGE | SGX_MMU_PDE_VALID; + MakeKernelPageReadOnly(psMMUContextWalker->pvPDCpuVAddr); PDUMPCOMMENT("BRN31620 Re-wire dummy PT due to releasing PT allocation block"); PDUMPPDENTRIES(&psMMUHeap->sMMUAttrib, psMMUContextWalker->hPDOSMemHandle, (IMG_VOID*)&pui32Tmp[ui32PDIndexStart + BRN31620_DUMMY_PDE_INDEX], sizeof(IMG_UINT32), 0, IMG_FALSE, PDUMP_PT_UNIQUETAG, PDUMP_PT_UNIQUETAG); @@ -417,11 +524,13 @@ static IMG_BOOL BRN31620FreePageTable(MMU_HEAP *psMMUHeap, IMG_UINT32 ui32PDInde psMMUContext->ui32PDChangeMask[ui32PDBitMaskIndex] |= 1 << ui32PDBitMaskShift; + MakeKernelPageReadWrite(psMMUContext->pvPDCpuVAddr); pui32Tmp = (IMG_UINT32 *) psMMUContext->pvPDCpuVAddr; pui32Tmp[ui32PDIndexStart + BRN31620_DUMMY_PDE_INDEX] = (psDevInfo->sBRN31620DummyPTDevPAddr.uiAddr>>SGX_MMU_PDE_ADDR_ALIGNSHIFT) | SGX_MMU_PDE_PAGE_SIZE_4K | SGX_MMU_PDE_DUMMY_PAGE | SGX_MMU_PDE_VALID; + MakeKernelPageReadOnly(psMMUContext->pvPDCpuVAddr); PDUMPCOMMENT("BRN31620 Re-wire dummy PT due to releasing PT allocation block"); PDUMPPDENTRIES(&psMMUHeap->sMMUAttrib, psMMUContext->hPDOSMemHandle, (IMG_VOID*)&pui32Tmp[ui32PDIndexStart + BRN31620_DUMMY_PDE_INDEX], sizeof(IMG_UINT32), 0, IMG_FALSE, PDUMP_PT_UNIQUETAG, PDUMP_PT_UNIQUETAG); @@ -461,6 +570,11 @@ _AllocPageTableMemory (MMU_HEAP *pMMUHeap, } + + + MakeKernelPageReadOnly(psPTInfoList->PTPageCpuVAddr); + + if(psPTInfoList->PTPageCpuVAddr) { sCpuPAddr = OSMapLinToCPUPhys(psPTInfoList->hPTPageOSMemHandle, @@ -518,6 +632,7 @@ _AllocPageTableMemory (MMU_HEAP *pMMUHeap, #endif } + MakeKernelPageReadWrite(psPTInfoList->PTPageCpuVAddr); #if defined(SUPPORT_SGX_MMU_DUMMY_PAGE) { IMG_UINT32 *pui32Tmp; @@ -540,6 +655,7 @@ _AllocPageTableMemory (MMU_HEAP *pMMUHeap, OSMemSet(psPTInfoList->PTPageCpuVAddr, 0, pMMUHeap->ui32PTSize); #endif + MakeKernelPageReadOnly(psPTInfoList->PTPageCpuVAddr); #if defined(PDUMP) { @@ -572,6 +688,9 @@ _FreePageTableMemory (MMU_HEAP *pMMUHeap, MMU_PT_INFO *psPTInfoList) if(pMMUHeap->psDevArena->psDeviceMemoryHeapInfo->psLocalDevMemArena == IMG_NULL) { + MakeKernelPageReadWrite(psPTInfoList->PTPageCpuVAddr); + + OSFreePages(PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_KERNEL_ONLY, pMMUHeap->ui32PTSize, psPTInfoList->PTPageCpuVAddr, @@ -659,6 +778,7 @@ _DeferredFreePageTable (MMU_HEAP *pMMUHeap, IMG_UINT32 ui32PTIndex, IMG_BOOL bOS while(psMMUContext) { + MakeKernelPageReadWrite(psMMUContext->pvPDCpuVAddr); pui32PDEntry = (IMG_UINT32*)psMMUContext->pvPDCpuVAddr; pui32PDEntry += ui32PDIndex; @@ -675,6 +795,7 @@ _DeferredFreePageTable (MMU_HEAP *pMMUHeap, IMG_UINT32 ui32PTIndex, IMG_BOOL bOS pui32PDEntry[ui32PTIndex] = 0; } #endif + MakeKernelPageReadOnly(psMMUContext->pvPDCpuVAddr); #if defined(PDUMP) #if defined(SUPPORT_PDUMP_MULTI_PROCESS) @@ -692,6 +813,7 @@ _DeferredFreePageTable (MMU_HEAP *pMMUHeap, IMG_UINT32 ui32PTIndex, IMG_BOOL bOS case DEVICE_MEMORY_HEAP_PERCONTEXT : case DEVICE_MEMORY_HEAP_KERNEL : { + MakeKernelPageReadWrite(pMMUHeap->psMMUContext->pvPDCpuVAddr); pui32PDEntry = (IMG_UINT32*)pMMUHeap->psMMUContext->pvPDCpuVAddr; pui32PDEntry += ui32PDIndex; @@ -709,6 +831,7 @@ _DeferredFreePageTable (MMU_HEAP *pMMUHeap, IMG_UINT32 ui32PTIndex, IMG_BOOL bOS pui32PDEntry[ui32PTIndex] = 0; } #endif + MakeKernelPageReadOnly(pMMUHeap->psMMUContext->pvPDCpuVAddr); PDUMPPDENTRIES(&pMMUHeap->sMMUAttrib, pMMUHeap->psMMUContext->hPDOSMemHandle, (IMG_VOID*)&pui32PDEntry[ui32PTIndex], sizeof(IMG_UINT32), 0, IMG_FALSE, PDUMP_PD_UNIQUETAG, PDUMP_PT_UNIQUETAG); @@ -728,6 +851,7 @@ _DeferredFreePageTable (MMU_HEAP *pMMUHeap, IMG_UINT32 ui32PTIndex, IMG_BOOL bOS { IMG_PUINT32 pui32Tmp; + MakeKernelPageReadWrite(ppsPTInfoList[ui32PTIndex]->PTPageCpuVAddr); pui32Tmp = (IMG_UINT32*)ppsPTInfoList[ui32PTIndex]->PTPageCpuVAddr; @@ -738,6 +862,7 @@ _DeferredFreePageTable (MMU_HEAP *pMMUHeap, IMG_UINT32 ui32PTIndex, IMG_BOOL bOS pui32Tmp[i] = 0; } + MakeKernelPageReadOnly(ppsPTInfoList[ui32PTIndex]->PTPageCpuVAddr); @@ -1040,6 +1165,7 @@ _DeferredAllocPagetables(MMU_HEAP *pMMUHeap, IMG_DEV_VIRTADDR DevVAddr, IMG_UINT while(psMMUContext) { + MakeKernelPageReadWrite(psMMUContext->pvPDCpuVAddr); pui32PDEntry = (IMG_UINT32*)psMMUContext->pvPDCpuVAddr; pui32PDEntry += ui32PDIndex; @@ -1048,6 +1174,7 @@ _DeferredAllocPagetables(MMU_HEAP *pMMUHeap, IMG_DEV_VIRTADDR DevVAddr, IMG_UINT pui32PDEntry[i] = (sDevPAddr.uiAddr>>SGX_MMU_PDE_ADDR_ALIGNSHIFT) | pMMUHeap->ui32PDEPageSizeCtrl | SGX_MMU_PDE_VALID; + MakeKernelPageReadOnly(psMMUContext->pvPDCpuVAddr); #if defined(PDUMP) #if defined(SUPPORT_PDUMP_MULTI_PROCESS) @@ -1069,11 +1196,12 @@ _DeferredAllocPagetables(MMU_HEAP *pMMUHeap, IMG_DEV_VIRTADDR DevVAddr, IMG_UINT case DEVICE_MEMORY_HEAP_PERCONTEXT : case DEVICE_MEMORY_HEAP_KERNEL : { + MakeKernelPageReadWrite(pMMUHeap->psMMUContext->pvPDCpuVAddr); pui32PDEntry[i] = (sDevPAddr.uiAddr>>SGX_MMU_PDE_ADDR_ALIGNSHIFT) | pMMUHeap->ui32PDEPageSizeCtrl | SGX_MMU_PDE_VALID; - + MakeKernelPageReadOnly(pMMUHeap->psMMUContext->pvPDCpuVAddr); PDUMPPDENTRIES(&pMMUHeap->sMMUAttrib, pMMUHeap->psMMUContext->hPDOSMemHandle, (IMG_VOID*)&pui32PDEntry[i], sizeof(IMG_UINT32), 0, IMG_FALSE, PDUMP_PD_UNIQUETAG, PDUMP_PT_UNIQUETAG); @@ -1185,13 +1313,14 @@ _DeferredAllocPagetables(MMU_HEAP *pMMUHeap, IMG_DEV_VIRTADDR DevVAddr, IMG_UINT PVR_ASSERT(psTempPTInfo != IMG_NULL); + MakeKernelPageReadWrite(psTempPTInfo->PTPageCpuVAddr); pui32Tmp = (IMG_UINT32 *) psTempPTInfo->PTPageCpuVAddr; PVR_ASSERT(pui32Tmp != IMG_NULL); pui32Tmp[BRN31620_DUMMY_PTE_INDEX] = (psDevInfo->sBRN31620DummyPageDevPAddr.uiAddr>>SGX_MMU_PTE_ADDR_ALIGNSHIFT) | SGX_MMU_PTE_DUMMY_PAGE | SGX_MMU_PTE_READONLY | SGX_MMU_PTE_VALID; - + MakeKernelPageReadOnly(psTempPTInfo->PTPageCpuVAddr); PDUMPCOMMENT("BRN31620 Dump PTE for dummy page after wireing up new PT"); PDUMPMEMPTENTRIES(&pMMUHeap->sMMUAttrib, psTempPTInfo->hPTPageOSMemHandle, (IMG_VOID *) &pui32Tmp[BRN31620_DUMMY_PTE_INDEX], sizeof(IMG_UINT32), 0, IMG_FALSE, PDUMP_PT_UNIQUETAG, PDUMP_PT_UNIQUETAG); } @@ -1564,11 +1693,13 @@ MMU_Initialise (PVRSRV_DEVICE_NODE *psDeviceNode, MMU_CONTEXT **ppsMMUContext, I return PVRSRV_ERROR_FAILED_TO_MAP_PAGE_TABLE; } + MakeKernelPageReadWrite(psDevInfo->pvBRN31620DummyPageCpuVAddr); pui32Tmp = (IMG_UINT32 *)psDevInfo->pvBRN31620DummyPageCpuVAddr; for(j=0; j<(SGX_MMU_PAGE_SIZE/4); j++) { pui32Tmp[j] = BRN31620_DUMMY_PAGE_SIGNATURE; } + MakeKernelPageReadOnly(psDevInfo->pvBRN31620DummyPageCpuVAddr); PDUMPMALLOCPAGETABLE(&psDeviceNode->sDevId, psDevInfo->hBRN31620DummyPageOSMemHandle, 0, psDevInfo->pvBRN31620DummyPageCpuVAddr, SGX_MMU_PAGE_SIZE, 0, PDUMP_PT_UNIQUETAG); @@ -1659,6 +1790,7 @@ MMU_Initialise (PVRSRV_DEVICE_NODE *psDeviceNode, MMU_CONTEXT **ppsMMUContext, I #if defined(SUPPORT_SGX_MMU_DUMMY_PAGE) + MakeKernelPageReadWrite(pvPDCpuVAddr); for(i=0; i<SGX_MMU_PD_SIZE; i++) { @@ -1666,40 +1798,47 @@ MMU_Initialise (PVRSRV_DEVICE_NODE *psDeviceNode, MMU_CONTEXT **ppsMMUContext, I | SGX_MMU_PDE_PAGE_SIZE_4K | SGX_MMU_PDE_VALID; } + MakeKernelPageReadOnly(pvPDCpuVAddr); if(!psDevInfo->pvMMUContextList) { + MakeKernelPageReadWrite(psDevInfo->pvDummyPTPageCpuVAddr); pui32Tmp = (IMG_UINT32 *)psDevInfo->pvDummyPTPageCpuVAddr; for(i=0; i<SGX_MMU_PT_SIZE; i++) { pui32Tmp[i] = (psDevInfo->sDummyDataDevPAddr.uiAddr>>SGX_MMU_PTE_ADDR_ALIGNSHIFT) | SGX_MMU_PTE_VALID; } + MakeKernelPageReadOnly(psDevInfo->pvDummyPTPageCpuVAddr); PDUMPCOMMENT("Dummy Page table contents"); PDUMPMEMPTENTRIES(&sMMUAttrib, psDevInfo->hDummyPTOSMemHandle, psDevInfo->pvDummyPTPageCpuVAddr, SGX_MMU_PAGE_SIZE, 0, IMG_TRUE, PDUMP_PD_UNIQUETAG, PDUMP_PT_UNIQUETAG); + MakeKernelPageReadWrite(psDevInfo->pvDummyDataPageCpuVAddr); pui32Tmp = (IMG_UINT32 *)psDevInfo->pvDummyDataPageCpuVAddr; for(i=0; i<(SGX_MMU_PAGE_SIZE/4); i++) { pui32Tmp[i] = DUMMY_DATA_PAGE_SIGNATURE; } + MakeKernelPageReadOnly(psDevInfo->pvDummyDataPageCpuVAddr); PDUMPCOMMENT("Dummy Data Page contents"); PDUMPMEMPTENTRIES(PVRSRV_DEVICE_TYPE_SGX, psDevInfo->hDummyDataPageOSMemHandle, psDevInfo->pvDummyDataPageCpuVAddr, SGX_MMU_PAGE_SIZE, 0, IMG_TRUE, PDUMP_PD_UNIQUETAG, PDUMP_PT_UNIQUETAG); } #else + MakeKernelPageReadWrite(pvPDCpuVAddr); for(i=0; i<SGX_MMU_PD_SIZE; i++) { pui32Tmp[i] = 0; } + MakeKernelPageReadOnly(pvPDCpuVAddr); #endif #if defined(PDUMP) @@ -1721,12 +1860,13 @@ MMU_Initialise (PVRSRV_DEVICE_NODE *psDeviceNode, MMU_CONTEXT **ppsMMUContext, I PDUMPCOMMENT("BRN31620 Set up dummy PT"); + MakeKernelPageReadWrite(psDevInfo->pvBRN31620DummyPTCpuVAddr); pui32PT = (IMG_UINT32 *) psDevInfo->pvBRN31620DummyPTCpuVAddr; pui32PT[BRN31620_DUMMY_PTE_INDEX] = (psDevInfo->sBRN31620DummyPageDevPAddr.uiAddr>>SGX_MMU_PTE_ADDR_ALIGNSHIFT) | SGX_MMU_PTE_DUMMY_PAGE | SGX_MMU_PTE_READONLY | SGX_MMU_PTE_VALID; - + MakeKernelPageReadOnly(psDevInfo->pvBRN31620DummyPTCpuVAddr); #if defined(PDUMP) @@ -1749,10 +1889,12 @@ MMU_Initialise (PVRSRV_DEVICE_NODE *psDeviceNode, MMU_CONTEXT **ppsMMUContext, I if (ui32PDCount == BRN31620_DUMMY_PDE_INDEX) { + MakeKernelPageReadWrite(pvPDCpuVAddr); pui32Tmp[i] = (psDevInfo->sBRN31620DummyPTDevPAddr.uiAddr>>SGX_MMU_PDE_ADDR_ALIGNSHIFT) | SGX_MMU_PDE_PAGE_SIZE_4K | SGX_MMU_PDE_DUMMY_PAGE | SGX_MMU_PDE_VALID; + MakeKernelPageReadOnly(pvPDCpuVAddr); } PDUMPMEMPTENTRIES(&sMMUAttrib, hPDOSMemHandle, (IMG_VOID *) &pui32Tmp[i], sizeof(IMG_UINT32), 0, IMG_FALSE, PDUMP_PT_UNIQUETAG, PDUMP_PT_UNIQUETAG); ui32PDCount++; @@ -1876,12 +2018,14 @@ MMU_Finalise (MMU_CONTEXT *psMMUContext) pui32Tmp = (IMG_UINT32 *)psMMUContext->pvPDCpuVAddr; + MakeKernelPageReadWrite(psMMUContext->pvPDCpuVAddr); for(i=0; i<SGX_MMU_PD_SIZE; i++) { pui32Tmp[i] = 0; } + MakeKernelPageReadOnly(psMMUContext->pvPDCpuVAddr); @@ -1892,6 +2036,7 @@ MMU_Finalise (MMU_CONTEXT *psMMUContext) #if defined(FIX_HW_BRN_31620) PVRSRV_SGXDEV_INFO *psDevInfo = (PVRSRV_SGXDEV_INFO*)psMMUContext->psDevInfo; #endif + MakeKernelPageReadWrite(psMMUContext->pvPDCpuVAddr); OSFreePages(PVRSRV_HAP_WRITECOMBINE | PVRSRV_HAP_KERNEL_ONLY, SGX_MMU_PAGE_SIZE, psMMUContext->pvPDCpuVAddr, @@ -2073,9 +2218,10 @@ MMU_InsertHeap(MMU_CONTEXT *psMMUContext, MMU_HEAP *psMMUHeap) PVR_ASSERT(pui32PDCpuVAddr[ui32PDEntry] == 0); #endif - + MakeKernelPageReadWrite(psMMUContext->pvPDCpuVAddr); pui32PDCpuVAddr[ui32PDEntry] = pui32KernelPDCpuVAddr[ui32PDEntry]; + MakeKernelPageReadOnly(psMMUContext->pvPDCpuVAddr); if (pui32PDCpuVAddr[ui32PDEntry]) { @@ -2179,7 +2325,7 @@ MMU_UnmapPagesAndFreePTs (MMU_HEAP *psMMUHeap, PVR_ASSERT((IMG_INT32)ppsPTInfoList[0]->ui32ValidPTECount >= 0); - + MakeKernelPageReadWrite(ppsPTInfoList[0]->PTPageCpuVAddr); #if defined(SUPPORT_SGX_MMU_DUMMY_PAGE) pui32Tmp[ui32PTIndex] = (psMMUHeap->psMMUContext->psDevInfo->sDummyDataDevPAddr.uiAddr>>SGX_MMU_PTE_ADDR_ALIGNSHIFT) @@ -2192,7 +2338,7 @@ MMU_UnmapPagesAndFreePTs (MMU_HEAP *psMMUHeap, pui32Tmp[ui32PTIndex] = 0; #endif #endif - + MakeKernelPageReadOnly(ppsPTInfoList[0]->PTPageCpuVAddr); CheckPT(ppsPTInfoList[0]); } @@ -2771,12 +2917,13 @@ MMU_MapPage (MMU_HEAP *pMMUHeap, ppsPTInfoList[0]->ui32ValidPTECount++; + MakeKernelPageReadWrite(ppsPTInfoList[0]->PTPageCpuVAddr); pui32Tmp[ui32Index] = ((DevPAddr.uiAddr>>SGX_MMU_PTE_ADDR_ALIGNSHIFT) & ((~pMMUHeap->ui32DataPageMask)>>SGX_MMU_PTE_ADDR_ALIGNSHIFT)) | SGX_MMU_PTE_VALID | ui32MMUFlags; - + MakeKernelPageReadOnly(ppsPTInfoList[0]->PTPageCpuVAddr); CheckPT(ppsPTInfoList[0]); } @@ -3061,6 +3208,7 @@ MMU_UnmapPages (MMU_HEAP *psMMUHeap, PVR_ASSERT((IMG_INT32)ppsPTInfoList[0]->ui32ValidPTECount >= 0); + MakeKernelPageReadWrite(ppsPTInfoList[0]->PTPageCpuVAddr); #if defined(SUPPORT_SGX_MMU_DUMMY_PAGE) pui32Tmp[ui32PTIndex] = (psMMUHeap->psMMUContext->psDevInfo->sDummyDataDevPAddr.uiAddr>>SGX_MMU_PTE_ADDR_ALIGNSHIFT) @@ -3073,6 +3221,7 @@ MMU_UnmapPages (MMU_HEAP *psMMUHeap, pui32Tmp[ui32PTIndex] = 0; #endif #endif + MakeKernelPageReadOnly(ppsPTInfoList[0]->PTPageCpuVAddr); CheckPT(ppsPTInfoList[0]); @@ -3292,6 +3441,48 @@ IMG_VOID MMU_BIFResetPDFree(PVRSRV_SGXDEV_INFO *psDevInfo) } } +IMG_VOID MMU_CheckFaultAddr(PVRSRV_SGXDEV_INFO *psDevInfo, IMG_UINT32 ui32PDDevPAddr, IMG_UINT32 ui32FaultAddr) +{ + MMU_CONTEXT *psMMUContext = psDevInfo->pvMMUContextList; + + while (psMMUContext && (psMMUContext->sPDDevPAddr.uiAddr != ui32PDDevPAddr)) + { + psMMUContext = psMMUContext->psNext; + } + + if (psMMUContext) + { + IMG_UINT32 ui32PTIndex; + IMG_UINT32 ui32PDIndex; + + PVR_LOG(("Found MMU context for page fault 0x%08x", ui32FaultAddr)); + + ui32PTIndex = (ui32FaultAddr & SGX_MMU_PT_MASK) >> SGX_MMU_PAGE_SHIFT; + ui32PDIndex = (ui32FaultAddr & SGX_MMU_PD_MASK) >> (SGX_MMU_PT_SHIFT + SGX_MMU_PAGE_SHIFT); + + if (psMMUContext->apsPTInfoList[ui32PDIndex]) + { + if (psMMUContext->apsPTInfoList[ui32PDIndex]->PTPageCpuVAddr) + { + IMG_UINT32 *pui32Ptr = psMMUContext->apsPTInfoList[ui32PDIndex]->PTPageCpuVAddr; + IMG_UINT32 ui32PTE = pui32Ptr[ui32PTIndex]; + + PVR_LOG(("PDE valid: PTE = 0x%08x (PhysAddr = 0x%08x, %s)", + ui32PTE, + ui32PTE & SGX_MMU_PTE_ADDR_MASK, + ui32PTE & SGX_MMU_PTE_VALID?"valid":"Invalid")); + } + else + { + PVR_LOG(("Found PT info but no CPU address")); + } + } + else + { + PVR_LOG(("No PDE found")); + } + } +} #if defined(FIX_HW_BRN_22997) && defined(FIX_HW_BRN_23030) && defined(SGX_FEATURE_HOST_PORT) PVRSRV_ERROR WorkaroundBRN22997Alloc(PVRSRV_DEVICE_NODE *psDeviceNode) @@ -3597,10 +3788,11 @@ PVRSRV_ERROR MMU_MapExtSystemCacheRegs(PVRSRV_DEVICE_NODE *psDeviceNode) pui32PT = (IMG_UINT32 *) psDeviceNode->sDevMemoryInfo.pBMKernelContext->psMMUContext->apsPTInfoList[ui32PDIndex]->PTPageCpuVAddr; + MakeKernelPageReadWrite(pui32PT); pui32PT[ui32PTIndex] = (psDevInfo->sExtSysCacheRegsDevPBase.uiAddr>>SGX_MMU_PTE_ADDR_ALIGNSHIFT) | SGX_MMU_PTE_VALID; - + MakeKernelPageReadOnly(pui32PT); #if defined(PDUMP) { @@ -3676,7 +3868,9 @@ PVRSRV_ERROR MMU_UnmapExtSystemCacheRegs(PVRSRV_DEVICE_NODE *psDeviceNode) } } + MakeKernelPageReadWrite(pui32PT); pui32PT[ui32PTIndex] = 0; + MakeKernelPageReadOnly(pui32PT); PDUMPMEMPTENTRIES(&sMMUAttrib, psDeviceNode->sDevMemoryInfo.pBMKernelContext->psMMUContext->hPDOSMemHandle, &pui32PT[ui32PTIndex], sizeof(IMG_UINT32), 0, IMG_FALSE, PDUMP_PD_UNIQUETAG, PDUMP_PT_UNIQUETAG); diff --git a/drivers/gpu/pvr/sgx/mmu.h b/drivers/gpu/pvr/sgx/mmu.h index 59b24c4..dd92bf0 100644 --- a/drivers/gpu/pvr/sgx/mmu.h +++ b/drivers/gpu/pvr/sgx/mmu.h @@ -147,6 +147,8 @@ IMG_VOID MMU_GetPDPhysAddr(MMU_CONTEXT *pMMUContext, IMG_DEV_PHYADDR *psDevPAddr #endif +IMG_VOID MMU_CheckFaultAddr(PVRSRV_SGXDEV_INFO *psDevInfo, IMG_UINT32 ui32PDDevPAddr, IMG_UINT32 ui32RegVal); + #if defined(PDUMP) IMG_UINT32 MMU_GetPDumpContextID(IMG_HANDLE hDevMemContext); #endif diff --git a/drivers/gpu/pvr/sgx/sgxinfokm.h b/drivers/gpu/pvr/sgx/sgxinfokm.h index 7e2b3f9..44a48be 100644 --- a/drivers/gpu/pvr/sgx/sgxinfokm.h +++ b/drivers/gpu/pvr/sgx/sgxinfokm.h @@ -128,7 +128,7 @@ typedef struct _PVRSRV_SGXDEV_INFO_ #if defined(FIX_HW_BRN_29823) PPVRSRV_KERNEL_MEM_INFO psKernelDummyTermStreamMemInfo; #endif -#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31425) +#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31559) PPVRSRV_KERNEL_MEM_INFO psKernelVDMSnapShotBufferMemInfo; PPVRSRV_KERNEL_MEM_INFO psKernelVDMCtrlStreamBufferMemInfo; #endif @@ -139,9 +139,6 @@ typedef struct _PVRSRV_SGXDEV_INFO_ #if defined(PVRSRV_USSE_EDM_STATUS_DEBUG) PPVRSRV_KERNEL_MEM_INFO psKernelEDMStatusBufferMemInfo; #endif -#if defined(SGX_FEATURE_OVERLAPPED_SPM) - PPVRSRV_KERNEL_MEM_INFO psKernelTmpRgnHeaderMemInfo; -#endif IMG_UINT32 ui32ClientRefCount; @@ -373,9 +370,6 @@ typedef struct _SGX_BRIDGE_INIT_INFO_KM_ #if defined(PVRSRV_USSE_EDM_STATUS_DEBUG) IMG_HANDLE hKernelEDMStatusBufferMemInfo; #endif -#if defined(SGX_FEATURE_OVERLAPPED_SPM) - IMG_HANDLE hKernelTmpRgnHeaderMemInfo; -#endif IMG_UINT32 ui32EDMTaskReg0; IMG_UINT32 ui32EDMTaskReg1; @@ -441,7 +435,7 @@ typedef struct _SGX_CCB_KICK_KM_ #else IMG_UINT32 ui32NumSrcSyncs; - IMG_HANDLE ahSrcKernelSyncInfo[SGX_MAX_SRC_SYNCS]; + IMG_HANDLE ahSrcKernelSyncInfo[SGX_MAX_SRC_SYNCS_TA]; #endif diff --git a/drivers/gpu/pvr/sgx/sgxinit.c b/drivers/gpu/pvr/sgx/sgxinit.c index 636620d..846035a 100644 --- a/drivers/gpu/pvr/sgx/sgxinit.c +++ b/drivers/gpu/pvr/sgx/sgxinit.c @@ -53,6 +53,23 @@ #include "srvkm.h" #include "ttrace.h" +#if defined(PVRSRV_USSE_EDM_STATUS_DEBUG) + +static const IMG_CHAR *SGXUKernelStatusString(IMG_UINT32 code) +{ + switch(code) + { +#define MKTC_ST(x) \ + case x: \ + return #x; +#include "sgx_ukernel_status_codes.h" + default: + return "(Unknown)"; + } +} + +#endif + #define VAR(x) #x @@ -161,7 +178,7 @@ static PVRSRV_ERROR InitDevInfo(PVRSRV_PER_PROCESS_DATA *psPerProc, #if defined(FIX_HW_BRN_29823) psDevInfo->psKernelDummyTermStreamMemInfo = (PVRSRV_KERNEL_MEM_INFO *)psInitInfo->hKernelDummyTermStreamMemInfo; #endif -#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31425) +#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31559) psDevInfo->psKernelVDMSnapShotBufferMemInfo = (PVRSRV_KERNEL_MEM_INFO *)psInitInfo->hKernelVDMSnapShotBufferMemInfo; psDevInfo->psKernelVDMCtrlStreamBufferMemInfo = (PVRSRV_KERNEL_MEM_INFO *)psInitInfo->hKernelVDMCtrlStreamBufferMemInfo; #endif @@ -172,9 +189,6 @@ static PVRSRV_ERROR InitDevInfo(PVRSRV_PER_PROCESS_DATA *psPerProc, #if defined(PVRSRV_USSE_EDM_STATUS_DEBUG) psDevInfo->psKernelEDMStatusBufferMemInfo = (PVRSRV_KERNEL_MEM_INFO *)psInitInfo->hKernelEDMStatusBufferMemInfo; #endif -#if defined(SGX_FEATURE_OVERLAPPED_SPM) - psDevInfo->psKernelTmpRgnHeaderMemInfo = (PVRSRV_KERNEL_MEM_INFO *)psInitInfo->hKernelTmpRgnHeaderMemInfo; -#endif psDevInfo->ui32ClientBuildOptions = psInitInfo->ui32ClientBuildOptions; @@ -1003,6 +1017,19 @@ static PVRSRV_ERROR DevDeInitSGX (IMG_VOID *pvDeviceNode) } +#if defined(RESTRICTED_REGISTERS) && defined(SGX_FEATURE_MP) + +static IMG_VOID SGXDumpMasterDebugReg (PVRSRV_SGXDEV_INFO *psDevInfo, + IMG_CHAR *pszName, + IMG_UINT32 ui32RegAddr) +{ + IMG_UINT32 ui32RegVal; + ui32RegVal = OSReadHWReg(psDevInfo->pvRegsBaseKM, ui32RegAddr); + PVR_LOG(("(HYD) %s%08X", pszName, ui32RegVal)); +} + +#endif + static IMG_VOID SGXDumpDebugReg (PVRSRV_SGXDEV_INFO *psDevInfo, IMG_UINT32 ui32CoreNum, IMG_CHAR *pszName, @@ -1027,7 +1054,27 @@ IMG_VOID SGXDumpDebugInfo (PVRSRV_SGXDEV_INFO *psDevInfo, SGXDumpDebugReg(psDevInfo, 0, "EUR_CR_CORE_ID: ", EUR_CR_CORE_ID); SGXDumpDebugReg(psDevInfo, 0, "EUR_CR_CORE_REVISION: ", EUR_CR_CORE_REVISION); - +#if defined(RESTRICTED_REGISTERS) && defined(SGX_FEATURE_MP) + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_BIF_INT_STAT: ", EUR_CR_MASTER_BIF_INT_STAT); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_BIF_FAULT: ",EUR_CR_MASTER_BIF_FAULT); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_CLKGATESTATUS2: ",EUR_CR_MASTER_CLKGATESTATUS2 ); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_VDM_PIM_STATUS: ",EUR_CR_MASTER_VDM_PIM_STATUS); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_BIF_BANK_SET: ",EUR_CR_MASTER_BIF_BANK_SET); + + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_EVENT_STATUS: ",EUR_CR_MASTER_EVENT_STATUS); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_EVENT_STATUS2: ",EUR_CR_MASTER_EVENT_STATUS2); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_MP_PRIMITIVE: ",EUR_CR_MASTER_MP_PRIMITIVE); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_DPM_DPLIST_STATUS: ",EUR_CR_MASTER_DPM_DPLIST_STATUS); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_DPM_PROACTIVE_PIM_SPEC: ",EUR_CR_MASTER_DPM_PROACTIVE_PIM_SPEC); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_PAGE_MANAGEOP: ",EUR_CR_MASTER_DPM_PAGE_MANAGEOP); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_VDM_CONTEXT_STORE_SNAPSHOT: ",EUR_CR_MASTER_VDM_CONTEXT_STORE_SNAPSHOT); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_VDM_CONTEXT_LOAD_STATUS: ",EUR_CR_MASTER_VDM_CONTEXT_LOAD_STATUS); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_VDM_CONTEXT_STORE_STREAM: ",EUR_CR_MASTER_VDM_CONTEXT_STORE_STREAM); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_VDM_CONTEXT_STORE_STATUS: ",EUR_CR_MASTER_VDM_CONTEXT_STORE_STATUS); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_VDM_CONTEXT_STORE_STATE0: ",EUR_CR_MASTER_VDM_CONTEXT_STORE_STATE0); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_VDM_CONTEXT_STORE_STATE1: ",EUR_CR_MASTER_VDM_CONTEXT_STORE_STATE1); + SGXDumpMasterDebugReg(psDevInfo, "EUR_CR_MASTER_VDM_WAIT_FOR_KICK: ",EUR_CR_MASTER_VDM_WAIT_FOR_KICK); +#endif for (ui32CoreNum = 0; ui32CoreNum < SGX_FEATURE_MP_CORE_COUNT_3D; ui32CoreNum++) { @@ -1045,6 +1092,26 @@ IMG_VOID SGXDumpDebugInfo (PVRSRV_SGXDEV_INFO *psDevInfo, SGXDumpDebugReg(psDevInfo, ui32CoreNum, "EUR_CR_PDS_PC_BASE: ", EUR_CR_PDS_PC_BASE); #endif } + +#if !defined(SGX_FEATURE_MULTIPLE_MEM_CONTEXTS) && !defined(FIX_HW_BRN_31620) + { + IMG_UINT32 ui32RegVal; + IMG_UINT32 ui32PDDevPAddr; + + + + + ui32RegVal = OSReadHWReg(psDevInfo->pvRegsBaseKM, EUR_CR_BIF_INT_STAT); + if (ui32RegVal & EUR_CR_BIF_INT_STAT_PF_N_RW_MASK) + { + ui32RegVal = OSReadHWReg(psDevInfo->pvRegsBaseKM, EUR_CR_BIF_FAULT); + ui32RegVal &= EUR_CR_BIF_FAULT_ADDR_MASK; + ui32PDDevPAddr = OSReadHWReg(psDevInfo->pvRegsBaseKM, EUR_CR_BIF_DIR_LIST_BASE0); + ui32PDDevPAddr &= EUR_CR_BIF_DIR_LIST_BASE0_ADDR_MASK; + MMU_CheckFaultAddr(psDevInfo, ui32PDDevPAddr, ui32RegVal); + } + } +#endif } @@ -1104,7 +1171,8 @@ IMG_VOID SGXDumpDebugInfo (PVRSRV_SGXDEV_INFO *psDevInfo, ui32WriteOffset = *pui32MKTraceBuffer; pui32MKTraceBuffer++; - PVR_LOG(("Last SGX microkernel status code: %08X", ui32LastStatusCode)); + PVR_LOG(("Last SGX microkernel status code: %08X %s", + ui32LastStatusCode, SGXUKernelStatusString(ui32LastStatusCode))); #if defined(PVRSRV_DUMP_MK_TRACE) @@ -1119,8 +1187,9 @@ IMG_VOID SGXDumpDebugInfo (PVRSRV_SGXDEV_INFO *psDevInfo, IMG_UINT32 *pui32BufPtr; pui32BufPtr = pui32MKTraceBuffer + (((ui32WriteOffset + ui32LoopCounter) % SGXMK_TRACE_BUFFER_SIZE) * 4); - PVR_LOG(("\t(MKT-%X) %08X %08X %08X %08X", ui32LoopCounter, - pui32BufPtr[2], pui32BufPtr[3], pui32BufPtr[1], pui32BufPtr[0])); + PVR_LOG(("\t(MKT-%X) %08X %08X %08X %08X %s", ui32LoopCounter, + pui32BufPtr[2], pui32BufPtr[3], pui32BufPtr[1], pui32BufPtr[0], + SGXUKernelStatusString(pui32BufPtr[0]))); } } #endif @@ -1294,14 +1363,14 @@ IMG_VOID SGXOSTimer(IMG_VOID *pvData) ui32BIFCtrl = OSReadHWReg(psDevInfo->pvRegsBaseKM, EUR_CR_BIF_CTRL); OSWriteHWReg(psDevInfo->pvRegsBaseKM, EUR_CR_BIF_CTRL, ui32BIFCtrl | EUR_CR_BIF_CTRL_PAUSE_MASK); - OSWaitus(200 * 1000000 / psDevInfo->ui32CoreClockSpeed); + SGXWaitClocks(psDevInfo, 200); #endif bBRN31093Inval = IMG_TRUE; OSWriteHWReg(psDevInfo->pvRegsBaseKM, EUR_CR_BIF_CTRL_INVAL, EUR_CR_BIF_CTRL_INVAL_PTE_MASK); - OSWaitus(200 * 1000000 / psDevInfo->ui32CoreClockSpeed); + SGXWaitClocks(psDevInfo, 200); #if defined(FIX_HW_BRN_29997) diff --git a/drivers/gpu/pvr/sgx/sgxkick.c b/drivers/gpu/pvr/sgx/sgxkick.c index 019a75cb..d5441b2 100644 --- a/drivers/gpu/pvr/sgx/sgxkick.c +++ b/drivers/gpu/pvr/sgx/sgxkick.c @@ -242,6 +242,7 @@ PVRSRV_ERROR SGXDoKickKM(IMG_HANDLE hDevHandle, SGX_CCB_KICK *psCCBKick) if (psSyncInfo) { + psSyncInfo->psSyncData->ui64LastWrite = ui64KickCount; PVR_TTRACE_SYNC_OBJECT(PVRSRV_TRACE_GROUP_KICK, KICK_TOKEN_DST_SYNC, psSyncInfo, PVRSRV_SYNCOP_SAMPLE); diff --git a/drivers/gpu/pvr/sgx/sgxpower.c b/drivers/gpu/pvr/sgx/sgxpower.c index 696941f..b647b68 100644 --- a/drivers/gpu/pvr/sgx/sgxpower.c +++ b/drivers/gpu/pvr/sgx/sgxpower.c @@ -271,6 +271,7 @@ PVRSRV_ERROR SGXPrePowerState (IMG_HANDLE hDevHandle, IMG_FALSE) != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR,"SGXPrePowerState: Wait for SGX ukernel power transition failed.")); + SGXDumpDebugInfo(psDevInfo, IMG_FALSE); PVR_DBG_BREAK; } #endif diff --git a/drivers/gpu/pvr/sgx/sgxreset.c b/drivers/gpu/pvr/sgx/sgxreset.c index 7050371..45e6d79 100644 --- a/drivers/gpu/pvr/sgx/sgxreset.c +++ b/drivers/gpu/pvr/sgx/sgxreset.c @@ -29,6 +29,7 @@ #include "services_headers.h" #include "sgxinfokm.h" #include "sgxconfig.h" +#include "sgxutils.h" #include "pdump_km.h" @@ -168,7 +169,7 @@ static IMG_VOID SGXResetSleep(PVRSRV_SGXDEV_INFO *psDevInfo, #endif - OSWaitus(100 * 1000000 / psDevInfo->ui32CoreClockSpeed); + SGXWaitClocks(psDevInfo, 100); if (bPDump) { PDUMPIDLWITHFLAGS(30, ui32PDUMPFlags); @@ -570,7 +571,7 @@ IMG_VOID SGXReset(PVRSRV_SGXDEV_INFO *psDevInfo, #if defined(SGX_FEATURE_SYSTEM_CACHE) #if defined(SGX_BYPASS_SYSTEM_CACHE) - #error SGX_BYPASS_SYSTEM_CACHE not supported + ui32RegVal = EUR_CR_MASTER_SLC_CTRL_BYPASS_ALL_MASK; #else ui32RegVal = EUR_CR_MASTER_SLC_CTRL_USSE_INVAL_REQ0_MASK | #if defined(FIX_HW_BRN_30954) @@ -598,10 +599,10 @@ IMG_VOID SGXReset(PVRSRV_SGXDEV_INFO *psDevInfo, EUR_CR_MASTER_SLC_CTRL_BYPASS_REQ_USE3_MASK | EUR_CR_MASTER_SLC_CTRL_BYPASS_REQ_TA_MASK; #endif + #endif OSWriteHWReg(psDevInfo->pvRegsBaseKM, EUR_CR_MASTER_SLC_CTRL_BYPASS, ui32RegVal); PDUMPCOMMENTWITHFLAGS(ui32PDUMPFlags, "Initialise the hydra SLC bypass control\r\n"); PDUMPREG(SGX_PDUMPREG_NAME, EUR_CR_MASTER_SLC_CTRL_BYPASS, ui32RegVal); - #endif #endif SGXResetSleep(psDevInfo, ui32PDUMPFlags, IMG_TRUE); diff --git a/drivers/gpu/pvr/sgx/sgxtransfer.c b/drivers/gpu/pvr/sgx/sgxtransfer.c index d40773e..f0ce1b5 100644 --- a/drivers/gpu/pvr/sgx/sgxtransfer.c +++ b/drivers/gpu/pvr/sgx/sgxtransfer.c @@ -222,6 +222,8 @@ IMG_EXPORT PVRSRV_ERROR SGXSubmitTransferKM(IMG_HANDLE hDevHandle, PVRSRV_TRANSF { psSyncInfo = (PVRSRV_KERNEL_SYNC_INFO *)psKick->ahDstSyncInfo[loop]; + psSyncInfo->psSyncData->ui64LastWrite = ui64KickCount; + PVR_TTRACE_SYNC_OBJECT(PVRSRV_TRACE_GROUP_TRANSFER, TRANSFER_TOKEN_DST_SYNC, psSyncInfo, PVRSRV_SYNCOP_SAMPLE); diff --git a/drivers/gpu/pvr/sgx/sgxutils.c b/drivers/gpu/pvr/sgx/sgxutils.c index c9a733b..528490b 100644 --- a/drivers/gpu/pvr/sgx/sgxutils.c +++ b/drivers/gpu/pvr/sgx/sgxutils.c @@ -49,6 +49,8 @@ #include <stdio.h> #endif +IMG_UINT64 ui64KickCount; + #if defined(SYS_CUSTOM_POWERDOWN) PVRSRV_ERROR SysPowerDownMISR(PVRSRV_DEVICE_NODE * psDeviceNode, IMG_UINT32 ui32CallerID); @@ -527,6 +529,7 @@ PVRSRV_ERROR SGXScheduleCCBCommand(PVRSRV_DEVICE_NODE *psDeviceNode, *psKernelCCB->pui32ReadOffset = (*psKernelCCB->pui32ReadOffset + 1) & 255; #endif + ui64KickCount++; Exit: return eError; } @@ -563,11 +566,16 @@ PVRSRV_ERROR SGXScheduleCCBCommandKM(PVRSRV_DEVICE_NODE *psDeviceNode, { if (ui32CallerID == ISR_ID) { + SYS_DATA *psSysData; + psDeviceNode->bReProcessDeviceCommandComplete = IMG_TRUE; eError = PVRSRV_OK; + + SysAcquireData(&psSysData); + OSScheduleMISR(psSysData); } else { @@ -689,11 +697,18 @@ PVRSRV_ERROR SGXCleanupRequest(PVRSRV_DEVICE_NODE *psDeviceNode, #if defined(PDUMP) + + + + + + + PDUMPCOMMENTWITHFLAGS(0, "Host Control - Poll for clean-up request to complete"); PDUMPMEMPOL(psHostCtlMemInfo, offsetof(SGXMKIF_HOST_CTL, ui32CleanupStatus), - PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE, - PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE, + PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE | PVRSRV_USSE_EDM_CLEANUPCMD_DONE, + PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE | PVRSRV_USSE_EDM_CLEANUPCMD_DONE, PDUMP_POLL_OPERATOR_EQUAL, 0, MAKEUNIQUETAG(psHostCtlMemInfo)); @@ -704,8 +719,20 @@ PVRSRV_ERROR SGXCleanupRequest(PVRSRV_DEVICE_NODE *psDeviceNode, return eError; } } + + if (psHostCtl->ui32CleanupStatus & PVRSRV_USSE_EDM_CLEANUPCMD_BUSY) + { + + PVR_ASSERT((psHostCtl->ui32CleanupStatus & PVRSRV_USSE_EDM_CLEANUPCMD_DONE) == 0); + eError = PVRSRV_ERROR_RETRY; + psHostCtl->ui32CleanupStatus &= ~(PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE | PVRSRV_USSE_EDM_CLEANUPCMD_BUSY); + } + else + { + eError = PVRSRV_OK; + psHostCtl->ui32CleanupStatus &= ~(PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE | PVRSRV_USSE_EDM_CLEANUPCMD_DONE); + } - psHostCtl->ui32CleanupStatus &= ~(PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE); PDUMPMEM(IMG_NULL, psHostCtlMemInfo, offsetof(SGXMKIF_HOST_CTL, ui32CleanupStatus), sizeof(IMG_UINT32), 0, MAKEUNIQUETAG(psHostCtlMemInfo)); @@ -714,16 +741,18 @@ PVRSRV_ERROR SGXCleanupRequest(PVRSRV_DEVICE_NODE *psDeviceNode, #else psDevInfo->ui32CacheControl |= SGXMKIF_CC_INVAL_DATA; #endif - return PVRSRV_OK; + return eError; } typedef struct _SGX_HW_RENDER_CONTEXT_CLEANUP_ { PVRSRV_DEVICE_NODE *psDeviceNode; - IMG_DEV_VIRTADDR sHWRenderContextDevVAddr; - IMG_HANDLE hBlockAlloc; + PVRSRV_KERNEL_MEM_INFO *psHWRenderContextMemInfo; + IMG_HANDLE hBlockAlloc; PRESMAN_ITEM psResItem; + IMG_BOOL bCleanupTimerRunning; + IMG_PVOID pvTimeData; } SGX_HW_RENDER_CONTEXT_CLEANUP; @@ -737,15 +766,48 @@ static PVRSRV_ERROR SGXCleanupHWRenderContextCallback(IMG_PVOID pvParam, PVR_UNREFERENCED_PARAMETER(ui32Param); eError = SGXCleanupRequest(psCleanup->psDeviceNode, - &psCleanup->sHWRenderContextDevVAddr, + &psCleanup->psHWRenderContextMemInfo->sDevVAddr, PVRSRV_CLEANUPCMD_RC, bForceCleanup); - OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, - sizeof(SGX_HW_RENDER_CONTEXT_CLEANUP), - psCleanup, - psCleanup->hBlockAlloc); + if (eError == PVRSRV_ERROR_RETRY) + { + if (!psCleanup->bCleanupTimerRunning) + { + OSTimeCreateWithUSOffset(&psCleanup->pvTimeData, MAX_CLEANUP_TIME_US); + psCleanup->bCleanupTimerRunning = IMG_TRUE; + } + else + { + if (OSTimeHasTimePassed(psCleanup->pvTimeData)) + { + eError = PVRSRV_ERROR_TIMEOUT_POLLING_FOR_VALUE; + psCleanup->bCleanupTimerRunning = IMG_FALSE; + OSTimeDestroy(psCleanup->pvTimeData); + } + } + } + else + { + if (psCleanup->bCleanupTimerRunning) + { + OSTimeDestroy(psCleanup->pvTimeData); + } + } + + if (eError != PVRSRV_ERROR_RETRY) + { + + PVRSRVFreeDeviceMemKM(psCleanup->psDeviceNode, + psCleanup->psHWRenderContextMemInfo); + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(SGX_HW_RENDER_CONTEXT_CLEANUP), + psCleanup, + psCleanup->hBlockAlloc); + + } return eError; } @@ -753,9 +815,11 @@ static PVRSRV_ERROR SGXCleanupHWRenderContextCallback(IMG_PVOID pvParam, typedef struct _SGX_HW_TRANSFER_CONTEXT_CLEANUP_ { PVRSRV_DEVICE_NODE *psDeviceNode; - IMG_DEV_VIRTADDR sHWTransferContextDevVAddr; + PVRSRV_KERNEL_MEM_INFO *psHWTransferContextMemInfo; IMG_HANDLE hBlockAlloc; PRESMAN_ITEM psResItem; + IMG_BOOL bCleanupTimerRunning; + IMG_PVOID pvTimeData; } SGX_HW_TRANSFER_CONTEXT_CLEANUP; @@ -769,27 +833,73 @@ static PVRSRV_ERROR SGXCleanupHWTransferContextCallback(IMG_PVOID pvParam, PVR_UNREFERENCED_PARAMETER(ui32Param); eError = SGXCleanupRequest(psCleanup->psDeviceNode, - &psCleanup->sHWTransferContextDevVAddr, + &psCleanup->psHWTransferContextMemInfo->sDevVAddr, PVRSRV_CLEANUPCMD_TC, bForceCleanup); - OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, - sizeof(SGX_HW_TRANSFER_CONTEXT_CLEANUP), - psCleanup, - psCleanup->hBlockAlloc); + if (eError == PVRSRV_ERROR_RETRY) + { + if (!psCleanup->bCleanupTimerRunning) + { + OSTimeCreateWithUSOffset(&psCleanup->pvTimeData, MAX_CLEANUP_TIME_US); + psCleanup->bCleanupTimerRunning = IMG_TRUE; + } + else + { + if (OSTimeHasTimePassed(psCleanup->pvTimeData)) + { + eError = PVRSRV_ERROR_TIMEOUT_POLLING_FOR_VALUE; + psCleanup->bCleanupTimerRunning = IMG_FALSE; + OSTimeDestroy(psCleanup->pvTimeData); + } + } + } + else + { + if (psCleanup->bCleanupTimerRunning) + { + OSTimeDestroy(psCleanup->pvTimeData); + } + } + + if (eError != PVRSRV_ERROR_RETRY) + { + + PVRSRVFreeDeviceMemKM(psCleanup->psDeviceNode, + psCleanup->psHWTransferContextMemInfo); + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(SGX_HW_TRANSFER_CONTEXT_CLEANUP), + psCleanup, + psCleanup->hBlockAlloc); + + } return eError; } IMG_EXPORT -IMG_HANDLE SGXRegisterHWRenderContextKM(IMG_HANDLE psDeviceNode, - IMG_DEV_VIRTADDR *psHWRenderContextDevVAddr, +IMG_HANDLE SGXRegisterHWRenderContextKM(IMG_HANDLE hDeviceNode, + IMG_CPU_VIRTADDR *psHWRenderContextCpuVAddr, + IMG_UINT32 ui32HWRenderContextSize, + IMG_UINT32 ui32OffsetToPDDevPAddr, + IMG_HANDLE hDevMemContext, + IMG_DEV_VIRTADDR *psHWRenderContextDevVAddr, PVRSRV_PER_PROCESS_DATA *psPerProc) { PVRSRV_ERROR eError; IMG_HANDLE hBlockAlloc; SGX_HW_RENDER_CONTEXT_CLEANUP *psCleanup; + PVRSRV_DEVICE_NODE *psDeviceNode = (PVRSRV_DEVICE_NODE *)hDeviceNode; + DEVICE_MEMORY_INFO *psDevMemoryInfo; + DEVICE_MEMORY_HEAP_INFO *psHeapInfo; + IMG_HANDLE hDevMemContextInt; + MMU_CONTEXT *psMMUContext; + IMG_DEV_PHYADDR sPDDevPAddr; + int iPtrByte; + IMG_UINT8 *pSrc; + IMG_UINT8 *pDst; PRESMAN_ITEM psResItem; eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, @@ -801,12 +911,99 @@ IMG_HANDLE SGXRegisterHWRenderContextKM(IMG_HANDLE psDeviceNode, if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: Couldn't allocate memory for SGX_HW_RENDER_CONTEXT_CLEANUP structure")); - return IMG_NULL; + goto exit0; + } + + psDevMemoryInfo = &psDeviceNode->sDevMemoryInfo; + psHeapInfo = &psDevMemoryInfo->psDeviceMemoryHeap[SGX_KERNEL_DATA_HEAP_ID]; + + eError = PVRSRVAllocDeviceMemKM(hDeviceNode, + psPerProc, + psHeapInfo->hDevMemHeap, + PVRSRV_MEM_READ | PVRSRV_MEM_WRITE + | PVRSRV_MEM_NO_SYNCOBJ | PVRSRV_MEM_EDM_PROTECT + | PVRSRV_MEM_CACHE_CONSISTENT, + ui32HWRenderContextSize, + 32, + IMG_NULL, + 0, + &psCleanup->psHWRenderContextMemInfo, + "HW Render Context"); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: Couldn't allocate device memory for HW Render Context")); + goto exit1; + } + + eError = OSCopyFromUser(psPerProc, + psCleanup->psHWRenderContextMemInfo->pvLinAddrKM, + psHWRenderContextCpuVAddr, + ui32HWRenderContextSize); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: Couldn't copy user-mode copy of HWContext into device memory")); + goto exit2; } + + psHWRenderContextDevVAddr->uiAddr = psCleanup->psHWRenderContextMemInfo->sDevVAddr.uiAddr; + + + eError = PVRSRVLookupHandle(psPerProc->psHandleBase, + &hDevMemContextInt, + hDevMemContext, + PVRSRV_HANDLE_TYPE_DEV_MEM_CONTEXT); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: Can't lookup DevMem Context")); + goto exit2; + } + + psMMUContext = BM_GetMMUContextFromMemContext(hDevMemContextInt); + sPDDevPAddr = psDeviceNode->pfnMMUGetPDDevPAddr(psMMUContext); + + + + + + + pSrc = (IMG_UINT8 *)&sPDDevPAddr; + pDst = (IMG_UINT8 *)psCleanup->psHWRenderContextMemInfo->pvLinAddrKM; + pDst += ui32OffsetToPDDevPAddr; + + for (iPtrByte = 0; iPtrByte < sizeof(IMG_DEV_PHYADDR); iPtrByte++) + { + pDst[iPtrByte] = pSrc[iPtrByte]; + } + +#if defined(PDUMP) + + PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "HW Render context struct"); + + PDUMPMEM( + IMG_NULL, + psCleanup->psHWRenderContextMemInfo, + 0, + ui32HWRenderContextSize, + PDUMP_FLAGS_CONTINUOUS, + MAKEUNIQUETAG(psCleanup->psHWRenderContextMemInfo)); + + + PDUMPCOMMENT("Page directory address in HW render context"); + PDUMPPDDEVPADDR( + psCleanup->psHWRenderContextMemInfo, + ui32OffsetToPDDevPAddr, + sPDDevPAddr, + MAKEUNIQUETAG(psCleanup->psHWRenderContextMemInfo), + PDUMP_PD_UNIQUETAG); +#endif + psCleanup->hBlockAlloc = hBlockAlloc; psCleanup->psDeviceNode = psDeviceNode; - psCleanup->sHWRenderContextDevVAddr = *psHWRenderContextDevVAddr; + psCleanup->bCleanupTimerRunning = IMG_FALSE; psResItem = ResManRegisterRes(psPerProc->hResManContext, RESMAN_TYPE_HW_RENDER_CONTEXT, @@ -814,21 +1011,27 @@ IMG_HANDLE SGXRegisterHWRenderContextKM(IMG_HANDLE psDeviceNode, 0, &SGXCleanupHWRenderContextCallback); - if (psResItem == IMG_NULL) - { - PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: ResManRegisterRes failed")); - OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, - sizeof(SGX_HW_RENDER_CONTEXT_CLEANUP), - psCleanup, - psCleanup->hBlockAlloc); - - - return IMG_NULL; - } + if (psResItem == IMG_NULL) + { + PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWRenderContextKM: ResManRegisterRes failed")); + goto exit2; + } psCleanup->psResItem = psResItem; return (IMG_HANDLE)psCleanup; + +exit2: + PVRSRVFreeDeviceMemKM(hDeviceNode, + psCleanup->psHWRenderContextMemInfo); +exit1: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(SGX_HW_RENDER_CONTEXT_CLEANUP), + psCleanup, + psCleanup->hBlockAlloc); + +exit0: + return IMG_NULL; } IMG_EXPORT @@ -854,13 +1057,26 @@ PVRSRV_ERROR SGXUnregisterHWRenderContextKM(IMG_HANDLE hHWRenderContext, IMG_BOO IMG_EXPORT -IMG_HANDLE SGXRegisterHWTransferContextKM(IMG_HANDLE psDeviceNode, - IMG_DEV_VIRTADDR *psHWTransferContextDevVAddr, +IMG_HANDLE SGXRegisterHWTransferContextKM(IMG_HANDLE hDeviceNode, + IMG_CPU_VIRTADDR *psHWTransferContextCpuVAddr, + IMG_UINT32 ui32HWTransferContextSize, + IMG_UINT32 ui32OffsetToPDDevPAddr, + IMG_HANDLE hDevMemContext, + IMG_DEV_VIRTADDR *psHWTransferContextDevVAddr, PVRSRV_PER_PROCESS_DATA *psPerProc) { PVRSRV_ERROR eError; IMG_HANDLE hBlockAlloc; SGX_HW_TRANSFER_CONTEXT_CLEANUP *psCleanup; + PVRSRV_DEVICE_NODE *psDeviceNode = (PVRSRV_DEVICE_NODE *)hDeviceNode; + DEVICE_MEMORY_INFO *psDevMemoryInfo; + DEVICE_MEMORY_HEAP_INFO *psHeapInfo; + IMG_HANDLE hDevMemContextInt; + MMU_CONTEXT *psMMUContext; + IMG_DEV_PHYADDR sPDDevPAddr; + int iPtrByte; + IMG_UINT8 *pSrc; + IMG_UINT8 *pDst; PRESMAN_ITEM psResItem; eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, @@ -872,12 +1088,100 @@ IMG_HANDLE SGXRegisterHWTransferContextKM(IMG_HANDLE psDeviceNode, if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWTransferContextKM: Couldn't allocate memory for SGX_HW_TRANSFER_CONTEXT_CLEANUP structure")); - return IMG_NULL; + goto exit0; } + psDevMemoryInfo = &psDeviceNode->sDevMemoryInfo; + psHeapInfo = &psDevMemoryInfo->psDeviceMemoryHeap[SGX_KERNEL_DATA_HEAP_ID]; + + eError = PVRSRVAllocDeviceMemKM(hDeviceNode, + psPerProc, + psHeapInfo->hDevMemHeap, + PVRSRV_MEM_READ | PVRSRV_MEM_WRITE + | PVRSRV_MEM_NO_SYNCOBJ | PVRSRV_MEM_EDM_PROTECT + | PVRSRV_MEM_CACHE_CONSISTENT, + ui32HWTransferContextSize, + 32, + IMG_NULL, + 0, + &psCleanup->psHWTransferContextMemInfo, + "HW Render Context"); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWTransferContextKM: Couldn't allocate device memory for HW Render Context")); + goto exit1; + } + + eError = OSCopyFromUser(psPerProc, + psCleanup->psHWTransferContextMemInfo->pvLinAddrKM, + psHWTransferContextCpuVAddr, + ui32HWTransferContextSize); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWTransferContextKM: Couldn't copy user-mode copy of HWContext into device memory")); + goto exit2; + } + + + psHWTransferContextDevVAddr->uiAddr = psCleanup->psHWTransferContextMemInfo->sDevVAddr.uiAddr; + + + eError = PVRSRVLookupHandle(psPerProc->psHandleBase, + &hDevMemContextInt, + hDevMemContext, + PVRSRV_HANDLE_TYPE_DEV_MEM_CONTEXT); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWTransferContextKM: Can't lookup DevMem Context")); + goto exit2; + } + + psMMUContext = BM_GetMMUContextFromMemContext(hDevMemContextInt); + sPDDevPAddr = psDeviceNode->pfnMMUGetPDDevPAddr(psMMUContext); + + + + + + + pSrc = (IMG_UINT8 *)&sPDDevPAddr; + pDst = (IMG_UINT8 *)psCleanup->psHWTransferContextMemInfo->pvLinAddrKM; + pDst += ui32OffsetToPDDevPAddr; + + for (iPtrByte = 0; iPtrByte < sizeof(IMG_DEV_PHYADDR); iPtrByte++) + { + pDst[iPtrByte] = pSrc[iPtrByte]; + } + +#if defined(PDUMP) + + PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "HW Transfer context struct"); + + PDUMPMEM( + IMG_NULL, + psCleanup->psHWTransferContextMemInfo, + 0, + ui32HWTransferContextSize, + PDUMP_FLAGS_CONTINUOUS, + MAKEUNIQUETAG(psCleanup->psHWTransferContextMemInfo)); + + + PDUMPCOMMENT("Page directory address in HW transfer context"); + + PDUMPPDDEVPADDR( + psCleanup->psHWTransferContextMemInfo, + ui32OffsetToPDDevPAddr, + sPDDevPAddr, + MAKEUNIQUETAG(psCleanup->psHWTransferContextMemInfo), + PDUMP_PD_UNIQUETAG); +#endif + psCleanup->hBlockAlloc = hBlockAlloc; psCleanup->psDeviceNode = psDeviceNode; - psCleanup->sHWTransferContextDevVAddr = *psHWTransferContextDevVAddr; + psCleanup->bCleanupTimerRunning = IMG_FALSE; psResItem = ResManRegisterRes(psPerProc->hResManContext, RESMAN_TYPE_HW_TRANSFER_CONTEXT, @@ -888,18 +1192,25 @@ IMG_HANDLE SGXRegisterHWTransferContextKM(IMG_HANDLE psDeviceNode, if (psResItem == IMG_NULL) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHWTransferContextKM: ResManRegisterRes failed")); - OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, - sizeof(SGX_HW_TRANSFER_CONTEXT_CLEANUP), - psCleanup, - psCleanup->hBlockAlloc); - - - return IMG_NULL; - } + goto exit2; + } psCleanup->psResItem = psResItem; return (IMG_HANDLE)psCleanup; + +exit2: + PVRSRVFreeDeviceMemKM(hDeviceNode, + psCleanup->psHWTransferContextMemInfo); +exit1: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(SGX_HW_TRANSFER_CONTEXT_CLEANUP), + psCleanup, + psCleanup->hBlockAlloc); + + +exit0: + return IMG_NULL; } IMG_EXPORT @@ -923,13 +1234,99 @@ PVRSRV_ERROR SGXUnregisterHWTransferContextKM(IMG_HANDLE hHWTransferContext, IMG return eError; } +IMG_EXPORT +PVRSRV_ERROR SGXSetTransferContextPriorityKM( + IMG_HANDLE hDeviceNode, + IMG_HANDLE hHWTransferContext, + IMG_UINT32 ui32Priority, + IMG_UINT32 ui32OffsetOfPriorityField) +{ + SGX_HW_TRANSFER_CONTEXT_CLEANUP *psCleanup; + IMG_UINT8 *pSrc; + IMG_UINT8 *pDst; + int iPtrByte; + PVR_UNREFERENCED_PARAMETER(hDeviceNode); + + if (hHWTransferContext != IMG_NULL) + { + psCleanup = (SGX_HW_TRANSFER_CONTEXT_CLEANUP *)hHWTransferContext; + + if ((ui32OffsetOfPriorityField + sizeof(ui32Priority)) + >= psCleanup->psHWTransferContextMemInfo->uAllocSize) + { + PVR_DPF(( + PVR_DBG_ERROR, + "SGXSetTransferContextPriorityKM: invalid context prioirty offset")); + + return PVRSRV_ERROR_INVALID_PARAMS; + } + + + + + pDst = (IMG_UINT8 *)psCleanup->psHWTransferContextMemInfo->pvLinAddrKM; + pDst += ui32OffsetOfPriorityField; + pSrc = (IMG_UINT8 *)&ui32Priority; + + for (iPtrByte = 0; iPtrByte < sizeof(ui32Priority); iPtrByte++) + { + pDst[iPtrByte] = pSrc[iPtrByte]; + } + } + return PVRSRV_OK; +} + +IMG_EXPORT +PVRSRV_ERROR SGXSetRenderContextPriorityKM( + IMG_HANDLE hDeviceNode, + IMG_HANDLE hHWRenderContext, + IMG_UINT32 ui32Priority, + IMG_UINT32 ui32OffsetOfPriorityField) +{ + SGX_HW_RENDER_CONTEXT_CLEANUP *psCleanup; + IMG_UINT8 *pSrc; + IMG_UINT8 *pDst; + int iPtrByte; + PVR_UNREFERENCED_PARAMETER(hDeviceNode); + + if (hHWRenderContext != IMG_NULL) + { + psCleanup = (SGX_HW_RENDER_CONTEXT_CLEANUP *)hHWRenderContext; + if ((ui32OffsetOfPriorityField + sizeof(ui32Priority)) + >= psCleanup->psHWRenderContextMemInfo->uAllocSize) + { + PVR_DPF(( + PVR_DBG_ERROR, + "SGXSetContextPriorityKM: invalid HWRenderContext prioirty offset")); + + return PVRSRV_ERROR_INVALID_PARAMS; + } + + + + + pDst = (IMG_UINT8 *)psCleanup->psHWRenderContextMemInfo->pvLinAddrKM; + pDst += ui32OffsetOfPriorityField; + + pSrc = (IMG_UINT8 *)&ui32Priority; + + for (iPtrByte = 0; iPtrByte < sizeof(ui32Priority); iPtrByte++) + { + pDst[iPtrByte] = pSrc[iPtrByte]; + } + } + return PVRSRV_OK; +} + #if defined(SGX_FEATURE_2D_HARDWARE) typedef struct _SGX_HW_2D_CONTEXT_CLEANUP_ { PVRSRV_DEVICE_NODE *psDeviceNode; - IMG_DEV_VIRTADDR sHW2DContextDevVAddr; + PVRSRV_KERNEL_MEM_INFO *psHW2DContextMemInfo; IMG_HANDLE hBlockAlloc; PRESMAN_ITEM psResItem; + IMG_BOOL bCleanupTimerRunning; + IMG_PVOID pvTimeData; } SGX_HW_2D_CONTEXT_CLEANUP; static PVRSRV_ERROR SGXCleanupHW2DContextCallback(IMG_PVOID pvParam, @@ -941,28 +1338,73 @@ static PVRSRV_ERROR SGXCleanupHW2DContextCallback(IMG_PVOID pvParam, PVR_UNREFERENCED_PARAMETER(ui32Param); - eError = SGXCleanupRequest(psCleanup->psDeviceNode, - &psCleanup->sHW2DContextDevVAddr, + + eError = SGXCleanupRequest(psCleanup->psDeviceNode, + &psCleanup->psHW2DContextMemInfo->sDevVAddr, PVRSRV_CLEANUPCMD_2DC, bForceCleanup); - OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, - sizeof(SGX_HW_2D_CONTEXT_CLEANUP), - psCleanup, - psCleanup->hBlockAlloc); - + if (eError == PVRSRV_ERROR_RETRY) + { + if (!psCleanup->bCleanupTimerRunning) + { + OSTimeCreateWithUSOffset(&psCleanup->pvTimeData, MAX_CLEANUP_TIME_US); + psCleanup->bCleanupTimerRunning = IMG_TRUE; + } + else + { + if (OSTimeHasTimePassed(psCleanup->pvTimeData)) + { + eError = PVRSRV_ERROR_TIMEOUT_POLLING_FOR_VALUE; + psCleanup->bCleanupTimerRunning = IMG_FALSE; + OSTimeDestroy(psCleanup->pvTimeData); + } + } + } + else + { + if (psCleanup->bCleanupTimerRunning) + { + OSTimeDestroy(psCleanup->pvTimeData); + } + } + if (eError != PVRSRV_ERROR_RETRY) + { + + PVRSRVFreeDeviceMemKM(psCleanup->psDeviceNode, + psCleanup->psHW2DContextMemInfo); + + + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(SGX_HW_2D_CONTEXT_CLEANUP), + psCleanup, + psCleanup->hBlockAlloc); + + } return eError; } -IMG_EXPORT -IMG_HANDLE SGXRegisterHW2DContextKM(IMG_HANDLE psDeviceNode, - IMG_DEV_VIRTADDR *psHW2DContextDevVAddr, +IMG_HANDLE SGXRegisterHW2DContextKM(IMG_HANDLE hDeviceNode, + IMG_CPU_VIRTADDR *psHW2DContextCpuVAddr, + IMG_UINT32 ui32HW2DContextSize, + IMG_UINT32 ui32OffsetToPDDevPAddr, + IMG_HANDLE hDevMemContext, + IMG_DEV_VIRTADDR *psHW2DContextDevVAddr, PVRSRV_PER_PROCESS_DATA *psPerProc) { PVRSRV_ERROR eError; IMG_HANDLE hBlockAlloc; SGX_HW_2D_CONTEXT_CLEANUP *psCleanup; + PVRSRV_DEVICE_NODE *psDeviceNode = (PVRSRV_DEVICE_NODE *)hDeviceNode; + DEVICE_MEMORY_INFO *psDevMemoryInfo; + DEVICE_MEMORY_HEAP_INFO *psHeapInfo; + IMG_HANDLE hDevMemContextInt; + MMU_CONTEXT *psMMUContext; + IMG_DEV_PHYADDR sPDDevPAddr; + int iPtrByte; + IMG_UINT8 *pSrc; + IMG_UINT8 *pDst; PRESMAN_ITEM psResItem; eError = OSAllocMem(PVRSRV_OS_PAGEABLE_HEAP, @@ -974,12 +1416,98 @@ IMG_HANDLE SGXRegisterHW2DContextKM(IMG_HANDLE psDeviceNode, if (eError != PVRSRV_OK) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHW2DContextKM: Couldn't allocate memory for SGX_HW_2D_CONTEXT_CLEANUP structure")); - return IMG_NULL; + goto exit0; + } + + psDevMemoryInfo = &psDeviceNode->sDevMemoryInfo; + psHeapInfo = &psDevMemoryInfo->psDeviceMemoryHeap[SGX_KERNEL_DATA_HEAP_ID]; + + eError = PVRSRVAllocDeviceMemKM(hDeviceNode, + psPerProc, + psHeapInfo->hDevMemHeap, + PVRSRV_MEM_READ | PVRSRV_MEM_WRITE + | PVRSRV_MEM_NO_SYNCOBJ | PVRSRV_MEM_EDM_PROTECT + | PVRSRV_MEM_CACHE_CONSISTENT, + ui32HW2DContextSize, + 32, + IMG_NULL, + 0, + &psCleanup->psHW2DContextMemInfo, + "HW 2D Context"); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHW2DContextKM: Couldn't allocate device memory for HW Render Context")); + goto exit1; + } + + eError = OSCopyFromUser(psPerProc, + psCleanup->psHW2DContextMemInfo->pvLinAddrKM, + psHW2DContextCpuVAddr, + ui32HW2DContextSize); + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHW2DContextKM: Couldn't copy user-mode copy of HWContext into device memory")); + goto exit2; } + + + psHW2DContextDevVAddr->uiAddr = psCleanup->psHW2DContextMemInfo->sDevVAddr.uiAddr; + + + eError = PVRSRVLookupHandle(psPerProc->psHandleBase, + &hDevMemContextInt, + hDevMemContext, + PVRSRV_HANDLE_TYPE_DEV_MEM_CONTEXT); + + if (eError != PVRSRV_OK) + { + PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHW2DContextKM: Can't lookup DevMem Context")); + goto exit2; + } + + psMMUContext = BM_GetMMUContextFromMemContext(hDevMemContextInt); + sPDDevPAddr = psDeviceNode->pfnMMUGetPDDevPAddr(psMMUContext); + + + + + + + pSrc = (IMG_UINT8 *)&sPDDevPAddr; + pDst = (IMG_UINT8 *)psCleanup->psHW2DContextMemInfo->pvLinAddrKM; + pDst += ui32OffsetToPDDevPAddr; + + for (iPtrByte = 0; iPtrByte < sizeof(IMG_DEV_PHYADDR); iPtrByte++) + { + pDst[iPtrByte] = pSrc[iPtrByte]; + } + +#if defined(PDUMP) + + PDUMPCOMMENTWITHFLAGS(PDUMP_FLAGS_CONTINUOUS, "HW 2D context struct"); + + PDUMPMEM( + IMG_NULL, + psCleanup->psHW2DContextMemInfo, + 0, + ui32HW2DContextSize, + PDUMP_FLAGS_CONTINUOUS, + MAKEUNIQUETAG(psCleanup->psHW2DContextMemInfo)); + + + PDUMPCOMMENT("Page directory address in HW 2D transfer context"); + PDUMPPDDEVPADDR( + psCleanup->psHW2DContextMemInfo, + ui32OffsetToPDDevPAddr, + sPDDevPAddr, + MAKEUNIQUETAG(psCleanup->psHW2DContextMemInfo), + PDUMP_PD_UNIQUETAG); +#endif psCleanup->hBlockAlloc = hBlockAlloc; psCleanup->psDeviceNode = psDeviceNode; - psCleanup->sHW2DContextDevVAddr = *psHW2DContextDevVAddr; + psCleanup->bCleanupTimerRunning = IMG_FALSE; psResItem = ResManRegisterRes(psPerProc->hResManContext, RESMAN_TYPE_HW_2D_CONTEXT, @@ -990,18 +1518,24 @@ IMG_HANDLE SGXRegisterHW2DContextKM(IMG_HANDLE psDeviceNode, if (psResItem == IMG_NULL) { PVR_DPF((PVR_DBG_ERROR, "SGXRegisterHW2DContextKM: ResManRegisterRes failed")); - OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, - sizeof(SGX_HW_2D_CONTEXT_CLEANUP), - psCleanup, - psCleanup->hBlockAlloc); - - - return IMG_NULL; + goto exit2; } psCleanup->psResItem = psResItem; return (IMG_HANDLE)psCleanup; + +exit2: + PVRSRVFreeDeviceMemKM(hDeviceNode, + psCleanup->psHW2DContextMemInfo); +exit1: + OSFreeMem(PVRSRV_OS_PAGEABLE_HEAP, + sizeof(SGX_HW_2D_CONTEXT_CLEANUP), + psCleanup, + psCleanup->hBlockAlloc); + +exit0: + return IMG_NULL; } IMG_EXPORT @@ -1142,6 +1676,15 @@ IMG_UINT32 SGXConvertTimeStamp(PVRSRV_SGXDEV_INFO *psDevInfo, } +IMG_VOID SGXWaitClocks(PVRSRV_SGXDEV_INFO *psDevInfo, + IMG_UINT32 ui32SGXClocks) +{ + + + OSWaitus(1 + (ui32SGXClocks * 1000000 / psDevInfo->ui32CoreClockSpeed)); +} + + IMG_EXPORT PVRSRV_ERROR PVRSRVGetSGXRevDataKM(PVRSRV_DEVICE_NODE* psDeviceNode, IMG_UINT32 *pui32SGXCoreRev, diff --git a/drivers/gpu/pvr/sgx/sgxutils.h b/drivers/gpu/pvr/sgx/sgxutils.h index bc60fdd..9017acf 100644 --- a/drivers/gpu/pvr/sgx/sgxutils.h +++ b/drivers/gpu/pvr/sgx/sgxutils.h @@ -36,6 +36,8 @@ ((type *)(((IMG_CHAR *)(psCCBMemInfo)->pvLinAddrKM) + \ (psCCBKick)->offset)) +extern IMG_UINT64 ui64KickCount; + IMG_IMPORT IMG_VOID SGXTestActivePowerEvent(PVRSRV_DEVICE_NODE *psDeviceNode, @@ -66,13 +68,21 @@ IMG_BOOL SGXIsDevicePowered(PVRSRV_DEVICE_NODE *psDeviceNode); IMG_IMPORT IMG_HANDLE SGXRegisterHWRenderContextKM(IMG_HANDLE psDeviceNode, - IMG_DEV_VIRTADDR *psHWRenderContextDevVAddr, + IMG_CPU_VIRTADDR *psHWRenderContextCpuVAddr, + IMG_UINT32 ui32HWRenderContextSize, + IMG_UINT32 ui32OffsetToPDDevPAddr, + IMG_HANDLE hDevMemContext, + IMG_DEV_VIRTADDR *psHWRenderContextDevVAddr, PVRSRV_PER_PROCESS_DATA *psPerProc); IMG_IMPORT -IMG_HANDLE SGXRegisterHWTransferContextKM(IMG_HANDLE psDeviceNode, - IMG_DEV_VIRTADDR *psHWTransferContextDevVAddr, - PVRSRV_PER_PROCESS_DATA *psPerProc); +IMG_HANDLE SGXRegisterHWTransferContextKM(IMG_HANDLE psDeviceNode, + IMG_CPU_VIRTADDR *psHWTransferContextCpuVAddr, + IMG_UINT32 ui32HWTransferContextSize, + IMG_UINT32 ui32OffsetToPDDevPAddr, + IMG_HANDLE hDevMemContext, + IMG_DEV_VIRTADDR *psHWTransferContextDevVAddr, + PVRSRV_PER_PROCESS_DATA *psPerProc); IMG_IMPORT PVRSRV_ERROR SGXFlushHWRenderTargetKM(IMG_HANDLE psSGXDevInfo, @@ -85,10 +95,26 @@ PVRSRV_ERROR SGXUnregisterHWRenderContextKM(IMG_HANDLE hHWRenderContext, IMG_BOO IMG_IMPORT PVRSRV_ERROR SGXUnregisterHWTransferContextKM(IMG_HANDLE hHWTransferContext, IMG_BOOL bForceCleanup); +IMG_IMPORT +PVRSRV_ERROR SGXSetRenderContextPriorityKM(IMG_HANDLE hDeviceNode, + IMG_HANDLE hHWRenderContext, + IMG_UINT32 ui32Priority, + IMG_UINT32 ui32OffsetOfPriorityField); + +IMG_IMPORT +PVRSRV_ERROR SGXSetTransferContextPriorityKM(IMG_HANDLE hDeviceNode, + IMG_HANDLE hHWTransferContext, + IMG_UINT32 ui32Priority, + IMG_UINT32 ui32OffsetOfPriorityField); + #if defined(SGX_FEATURE_2D_HARDWARE) IMG_IMPORT IMG_HANDLE SGXRegisterHW2DContextKM(IMG_HANDLE psDeviceNode, - IMG_DEV_VIRTADDR *psHW2DContextDevVAddr, + IMG_CPU_VIRTADDR *psHW2DContextCpuVAddr, + IMG_UINT32 ui32HW2DContextSize, + IMG_UINT32 ui32OffsetToPDDevPAddr, + IMG_HANDLE hDevMemContext, + IMG_DEV_VIRTADDR *psHW2DContextDevVAddr, PVRSRV_PER_PROCESS_DATA *psPerProc); IMG_IMPORT @@ -99,6 +125,9 @@ IMG_UINT32 SGXConvertTimeStamp(PVRSRV_SGXDEV_INFO *psDevInfo, IMG_UINT32 ui32TimeWraps, IMG_UINT32 ui32Time); +IMG_VOID SGXWaitClocks(PVRSRV_SGXDEV_INFO *psDevInfo, + IMG_UINT32 ui32SGXClocks); + PVRSRV_ERROR SGXCleanupRequest(PVRSRV_DEVICE_NODE *psDeviceNode, IMG_DEV_VIRTADDR *psHWDataDevVAddr, IMG_UINT32 ui32CleanupType, diff --git a/drivers/gpu/pvr/sgx_bridge.h b/drivers/gpu/pvr/sgx_bridge.h index 204189c..ec630a5 100644 --- a/drivers/gpu/pvr/sgx_bridge.h +++ b/drivers/gpu/pvr/sgx_bridge.h @@ -50,8 +50,6 @@ extern "C" { #define PVRSRV_BRIDGE_SGX_2DQUERYBLTSCOMPLETE PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+9) -#define PVRSRV_BRIDGE_SGX_GETMMUPDADDR PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+10) - #if defined(TRANSFER_QUEUE) #define PVRSRV_BRIDGE_SGX_SUBMITTRANSFER PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+13) #endif @@ -75,20 +73,22 @@ extern "C" { #define PVRSRV_BRIDGE_SGX_SCHEDULE_PROCESS_QUEUES PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+28) -#define PVRSRV_BRIDGE_SGX_READ_HWPERF_CB PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+30) +#define PVRSRV_BRIDGE_SGX_READ_HWPERF_CB PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+29) +#define PVRSRV_BRIDGE_SGX_SET_RENDER_CONTEXT_PRIORITY PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+30) +#define PVRSRV_BRIDGE_SGX_SET_TRANSFER_CONTEXT_PRIORITY PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+31) #if defined(PDUMP) -#define PVRSRV_BRIDGE_SGX_PDUMP_BUFFER_ARRAY PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+31) -#define PVRSRV_BRIDGE_SGX_PDUMP_3D_SIGNATURE_REGISTERS PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+32) -#define PVRSRV_BRIDGE_SGX_PDUMP_COUNTER_REGISTERS PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+33) -#define PVRSRV_BRIDGE_SGX_PDUMP_TA_SIGNATURE_REGISTERS PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+34) -#define PVRSRV_BRIDGE_SGX_PDUMP_HWPERFCB PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+35) -#define PVRSRV_BRIDGE_SGX_PDUMP_SAVEMEM PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+36) +#define PVRSRV_BRIDGE_SGX_PDUMP_BUFFER_ARRAY PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+32) +#define PVRSRV_BRIDGE_SGX_PDUMP_3D_SIGNATURE_REGISTERS PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+33) +#define PVRSRV_BRIDGE_SGX_PDUMP_COUNTER_REGISTERS PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+34) +#define PVRSRV_BRIDGE_SGX_PDUMP_TA_SIGNATURE_REGISTERS PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+35) +#define PVRSRV_BRIDGE_SGX_PDUMP_HWPERFCB PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+36) +#define PVRSRV_BRIDGE_SGX_PDUMP_SAVEMEM PVRSRV_IOWR(PVRSRV_BRIDGE_SGX_CMD_BASE+37) #endif -#define PVRSRV_BRIDGE_LAST_SGX_CMD (PVRSRV_BRIDGE_SGX_CMD_BASE+36) +#define PVRSRV_BRIDGE_LAST_SGX_CMD (PVRSRV_BRIDGE_SGX_CMD_BASE+37) typedef struct PVRSRV_BRIDGE_IN_GETPHYSPAGEADDR @@ -107,24 +107,34 @@ typedef struct PVRSRV_BRIDGE_OUT_GETPHYSPAGEADDR }PVRSRV_BRIDGE_OUT_GETPHYSPAGEADDR; -typedef struct PVRSRV_BRIDGE_IN_SGX_GETMMU_PDADDR_TAG +typedef struct PVRSRV_BRIDGE_IN_SGX_SET_TRANSFER_CONTEXT_PRIORITY_TAG + { + IMG_UINT32 ui32BridgeFlags; + #if defined (SUPPORT_SID_INTERFACE) + IMG_SID hDevCookie; + IMG_SID hHWTransferContext; + #else + IMG_HANDLE hDevCookie; + IMG_HANDLE hHWTransferContext; + #endif + IMG_UINT32 ui32Priority; + IMG_UINT32 ui32OffsetOfPriorityField; +}PVRSRV_BRIDGE_IN_SGX_SET_TRANSFER_CONTEXT_PRIORITY; + + +typedef struct PVRSRV_BRIDGE_IN_SGX_SET_RENDER_CONTEXT_PRIORITY_TAG { IMG_UINT32 ui32BridgeFlags; #if defined (SUPPORT_SID_INTERFACE) IMG_SID hDevCookie; - IMG_SID hDevMemContext; + IMG_SID hHWRenderContext; #else IMG_HANDLE hDevCookie; - IMG_HANDLE hDevMemContext; + IMG_HANDLE hHWRenderContext; #endif -}PVRSRV_BRIDGE_IN_SGX_GETMMU_PDADDR; - - -typedef struct PVRSRV_BRIDGE_OUT_SGX_GETMMU_PDADDR_TAG -{ - IMG_DEV_PHYADDR sPDDevPAddr; - PVRSRV_ERROR eError; -}PVRSRV_BRIDGE_OUT_SGX_GETMMU_PDADDR; + IMG_UINT32 ui32Priority; + IMG_UINT32 ui32OffsetOfPriorityField; +}PVRSRV_BRIDGE_IN_SGX_SET_RENDER_CONTEXT_PRIORITY; typedef struct PVRSRV_BRIDGE_IN_GETCLIENTINFO_TAG @@ -505,7 +515,10 @@ typedef struct PVRSRV_BRIDGE_IN_SGX_REGISTER_HW_RENDER_CONTEXT_TAG #else IMG_HANDLE hDevCookie; #endif - IMG_DEV_VIRTADDR sHWRenderContextDevVAddr; + IMG_CPU_VIRTADDR pHWRenderContextCpuVAddr; + IMG_UINT32 ui32HWRenderContextSize; + IMG_UINT32 ui32OffsetToPDDevPAddr; + IMG_HANDLE hDevMemContext; }PVRSRV_BRIDGE_IN_SGX_REGISTER_HW_RENDER_CONTEXT; typedef struct PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_RENDER_CONTEXT_TAG @@ -516,6 +529,7 @@ typedef struct PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_RENDER_CONTEXT_TAG #else IMG_HANDLE hHWRenderContext; #endif + IMG_DEV_VIRTADDR sHWRenderContextDevVAddr; }PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_RENDER_CONTEXT; typedef struct PVRSRV_BRIDGE_IN_SGX_UNREGISTER_HW_RENDER_CONTEXT_TAG @@ -539,7 +553,10 @@ typedef struct PVRSRV_BRIDGE_IN_SGX_REGISTER_HW_TRANSFER_CONTEXT_TAG #else IMG_HANDLE hDevCookie; #endif - IMG_DEV_VIRTADDR sHWTransferContextDevVAddr; + IMG_CPU_VIRTADDR pHWTransferContextCpuVAddr; + IMG_UINT32 ui32HWTransferContextSize; + IMG_UINT32 ui32OffsetToPDDevPAddr; + IMG_HANDLE hDevMemContext; }PVRSRV_BRIDGE_IN_SGX_REGISTER_HW_TRANSFER_CONTEXT; typedef struct PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_TRANSFER_CONTEXT_TAG @@ -550,6 +567,7 @@ typedef struct PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_TRANSFER_CONTEXT_TAG #else IMG_HANDLE hHWTransferContext; #endif + IMG_DEV_VIRTADDR sHWTransferContextDevVAddr; }PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_TRANSFER_CONTEXT; typedef struct PVRSRV_BRIDGE_IN_SGX_UNREGISTER_HW_TRANSFER_CONTEXT_TAG @@ -586,7 +604,10 @@ typedef struct PVRSRV_BRIDGE_IN_SGX_REGISTER_HW_2D_CONTEXT_TAG #else IMG_HANDLE hDevCookie; #endif - IMG_DEV_VIRTADDR sHW2DContextDevVAddr; + IMG_CPU_VIRTADDR pHW2DContextCpuVAddr; + IMG_UINT32 ui32HW2DContextSize; + IMG_UINT32 ui32OffsetToPDDevPAddr; + IMG_HANDLE hDevMemContext; }PVRSRV_BRIDGE_IN_SGX_REGISTER_HW_2D_CONTEXT; typedef struct PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_2D_CONTEXT_TAG @@ -597,6 +618,7 @@ typedef struct PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_2D_CONTEXT_TAG #else IMG_HANDLE hHW2DContext; #endif + IMG_DEV_VIRTADDR sHW2DContextDevVAddr; }PVRSRV_BRIDGE_OUT_SGX_REGISTER_HW_2D_CONTEXT; typedef struct PVRSRV_BRIDGE_IN_SGX_UNREGISTER_HW_2D_CONTEXT_TAG diff --git a/drivers/gpu/pvr/sgx_mkif_km.h b/drivers/gpu/pvr/sgx_mkif_km.h index 2d78baa..9f4f41f 100644 --- a/drivers/gpu/pvr/sgx_mkif_km.h +++ b/drivers/gpu/pvr/sgx_mkif_km.h @@ -131,6 +131,8 @@ typedef struct _SGXMKIF_CMDTA_SHARED_ IMG_UINT32 ui323DTQSyncReadOpsPendingVal; IMG_DEV_VIRTADDR s3DTQSyncReadOpsCompleteDevVAddr; + + PVRSRV_DEVICE_SYNC_OBJECT sTA3DDependency; #if defined(SUPPORT_SGX_GENERALISED_SYNCOBJECTS) @@ -143,12 +145,9 @@ typedef struct _SGXMKIF_CMDTA_SHARED_ #else IMG_UINT32 ui32NumSrcSyncs; - PVRSRV_DEVICE_SYNC_OBJECT asSrcSyncs[SGX_MAX_SRC_SYNCS]; + PVRSRV_DEVICE_SYNC_OBJECT asSrcSyncs[SGX_MAX_SRC_SYNCS_TA]; #endif - - PVRSRV_DEVICE_SYNC_OBJECT sTA3DDependency; - CTL_STATUS sCtlTAStatusInfo[SGX_MAX_TA_STATUS_VALS]; CTL_STATUS sCtl3DStatusInfo[SGX_MAX_3D_STATUS_VALS]; @@ -170,11 +169,11 @@ typedef struct _SGXMKIF_TRANSFERCMD_SHARED_ IMG_UINT32 ui32NumSrcSyncs; - PVRSRV_DEVICE_SYNC_OBJECT asSrcSyncs[SGX_MAX_SRC_SYNCS]; + PVRSRV_DEVICE_SYNC_OBJECT asSrcSyncs[SGX_MAX_SRC_SYNCS_TQ]; IMG_UINT32 ui32NumDstSyncs; - PVRSRV_DEVICE_SYNC_OBJECT asDstSyncs[SGX_MAX_DST_SYNCS]; + PVRSRV_DEVICE_SYNC_OBJECT asDstSyncs[SGX_MAX_DST_SYNCS_TQ]; IMG_UINT32 ui32TASyncWriteOpsPendingVal; IMG_DEV_VIRTADDR sTASyncWriteOpsCompleteDevVAddr; @@ -231,6 +230,8 @@ typedef struct _SGXMKIF_HWDEVICE_SYNC_LIST_ #define PVRSRV_USSE_EDM_INTERRUPT_IDLE (1UL << 2) #define PVRSRV_USSE_EDM_CLEANUPCMD_COMPLETE (1UL << 0) +#define PVRSRV_USSE_EDM_CLEANUPCMD_BUSY (1UL << 1) +#define PVRSRV_USSE_EDM_CLEANUPCMD_DONE (1UL << 2) #if defined(FIX_HW_BRN_28889) #define PVRSRV_USSE_EDM_BIF_INVAL_COMPLETE (1UL << 0) diff --git a/drivers/gpu/pvr/sgx_ukernel_status_codes.h b/drivers/gpu/pvr/sgx_ukernel_status_codes.h new file mode 100644 index 0000000..4a9eaf8 --- /dev/null +++ b/drivers/gpu/pvr/sgx_ukernel_status_codes.h @@ -0,0 +1,940 @@ +/********************************************************************** + * + * Copyright (C) Imagination Technologies Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful but, except + * as otherwise stated in writing, 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, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Contact Information: + * Imagination Technologies Ltd. <gpl-support@imgtec.com> + * Home Park Estate, Kings Langley, Herts, WD4 8LZ, UK + * + +******************************************************************************/ + +#ifndef __SGX_UKERNEL_STATUS_CODES_H__ +#define __SGX_UKERNEL_STATUS_CODES_H__ + +/* + NOTE: Do not add any conditional macros to this file! There must be + no use of #if defined(). This file is included in srvkm to print + stringified ukernel status codes, it must build identically to + srvinit. +*/ + +/* + Users of this header might define this macro to do something + clever; the primary use right now is to generate a switch/case + LUT for debugging in srvkm. If you add a new code, make sure it + has a corresponding MKTC_ST. +*/ +#ifndef MKTC_ST +#define MKTC_ST(x) +#endif + +/* + It would be nice to put these definitions into an enumeration, but USEASM + only has access to the C preprocessor so macros are required. +*/ + +/* + Bits 24-31 of these codes (0xAD) are a magic number used to help + distinguish between them and other debug information which can be + optionally dumped into the status buffer, e.g. sync object values. +*/ + +/* + Microkernel trace codes +*/ +#define MKTC_EHEVENT_3DMEMFREE 0xAD000001 +MKTC_ST(MKTC_EHEVENT_3DMEMFREE) +#define MKTC_EHEVENT_PIXELENDRENDER 0xAD000002 +MKTC_ST(MKTC_EHEVENT_PIXELENDRENDER) +#define MKTC_EHEVENT_ISPBREAKPOINT 0xAD000004 +MKTC_ST(MKTC_EHEVENT_ISPBREAKPOINT) +#define MKTC_EHEVENT_TAFINISHED 0xAD000005 +MKTC_ST(MKTC_EHEVENT_TAFINISHED) +#define MKTC_EHEVENT_OUTOFMEM 0xAD000007 +MKTC_ST(MKTC_EHEVENT_OUTOFMEM) +#define MKTC_EHEVENT_TATERMINATE 0xAD000008 +MKTC_ST(MKTC_EHEVENT_TATERMINATE) +#define MKTC_EHEVENT_TIMER 0xAD000009 +MKTC_ST(MKTC_EHEVENT_TIMER) +#define MKTC_EHEVENT_SWEVENT 0xAD00000A +MKTC_ST(MKTC_EHEVENT_SWEVENT) +#define MKTC_EHEVENT_2DCOMPLETE 0xAD00000B +MKTC_ST(MKTC_EHEVENT_2DCOMPLETE) + +#define MKTC_3DEVENT_3DMEMFREE 0xAD000100 +MKTC_ST(MKTC_3DEVENT_3DMEMFREE) +#define MKTC_3DEVENT_PIXELENDRENDER 0xAD000101 +MKTC_ST(MKTC_3DEVENT_PIXELENDRENDER) +#define MKTC_3DEVENT_ISPBREAKPOINT 0xAD000102 +MKTC_ST(MKTC_3DEVENT_ISPBREAKPOINT) +#define MKTC_3DEVENT_END 0xAD000104 +MKTC_ST(MKTC_3DEVENT_END) +#define MKTC_3DLB_3DMEMFREE 0xAD000180 +MKTC_ST(MKTC_3DLB_3DMEMFREE) +#define MKTC_3DLB_PIXELENDRENDER 0xAD000181 +MKTC_ST(MKTC_3DLB_PIXELENDRENDER) +#define MKTC_3DLB_ISPBREAKPOINT 0xAD000182 +MKTC_ST(MKTC_3DLB_ISPBREAKPOINT) +#define MKTC_3DLB_FIND3D 0xAD000183 +MKTC_ST(MKTC_3DLB_FIND3D) +#define MKTC_3DLB_END 0xAD000184 +MKTC_ST(MKTC_3DLB_END) + +#define MKTC_TAEVENT_TAFINISHED 0xAD000200 +MKTC_ST(MKTC_TAEVENT_TAFINISHED) +#define MKTC_TAEVENT_END 0xAD000202 +MKTC_ST(MKTC_TAEVENT_END) +#define MKTC_TALB_TAFINISHED 0xAD000280 +MKTC_ST(MKTC_TALB_TAFINISHED) +#define MKTC_TALB_FINDTA 0xAD000281 +MKTC_ST(MKTC_TALB_FINDTA) +#define MKTC_TALB_END 0xAD000282 +MKTC_ST(MKTC_TALB_END) + +#define MKTC_CRRL_WRITEOPSBLOCKED 0xAD000300 +MKTC_ST(MKTC_CRRL_WRITEOPSBLOCKED) +#define MKTC_CRRL_READOPSBLOCKED 0xAD000301 +MKTC_ST(MKTC_CRRL_READOPSBLOCKED) +#define MKTC_CRRL_FOUNDRENDER 0xAD000302 +MKTC_ST(MKTC_CRRL_FOUNDRENDER) +#define MKTC_CRRL_NORENDER 0xAD000303 +MKTC_ST(MKTC_CRRL_NORENDER) +#define MKTC_CRRL_TARC_DIFFERENT 0xAD000304 +MKTC_ST(MKTC_CRRL_TARC_DIFFERENT) +#define MKTC_CRRL_BLOCKEDRC 0xAD000309 +MKTC_ST(MKTC_CRRL_BLOCKEDRC) +#define MKTC_CRRL_BLOCKEDRTDATA 0xAD00030A +MKTC_ST(MKTC_CRRL_BLOCKEDRTDATA) +#define MKTC_CRRL_CONTEXT_SUSPENDED 0xAD00030B +MKTC_ST(MKTC_CRRL_CONTEXT_SUSPENDED) +#define MKTC_CRRL_TAWAITINGFORMEM 0xAD00030C +MKTC_ST(MKTC_CRRL_TAWAITINGFORMEM) +#define MKTC_CRRL_TAOOMBUTPRIOINV 0xAD00030D +MKTC_ST(MKTC_CRRL_TAOOMBUTPRIOINV) +#define MKTC_CRRL_READOPS2BLOCKED 0xAD00030E +MKTC_ST(MKTC_CRRL_READOPS2BLOCKED) +#define MKTC_CRRL_SRC_WRITEOPSBLOCKED 0xAD00030F +MKTC_ST(MKTC_CRRL_SRC_WRITEOPSBLOCKED) +#define MKTC_CRRL_SRC_READOPSBLOCKED 0xAD000310 +MKTC_ST(MKTC_CRRL_SRC_READOPSBLOCKED) + +#define MKTC_KICKRENDER_START 0xAD000400 +MKTC_ST(MKTC_KICKRENDER_START) +#define MKTC_KICKRENDER_OVERLAP 0xAD000401 +MKTC_ST(MKTC_KICKRENDER_OVERLAP) +#define MKTC_KICKRENDER_ISP_START 0xAD000402 +MKTC_ST(MKTC_KICKRENDER_ISP_START) +#define MKTC_KICKRENDER_RESUME 0xAD000403 +MKTC_ST(MKTC_KICKRENDER_RESUME) +#define MKTC_KICKRENDER_CONFIG_REGION_HDRS 0xAD000404 +MKTC_ST(MKTC_KICKRENDER_CONFIG_REGION_HDRS) +#define MKTC_KICKRENDER_END 0xAD000408 +MKTC_ST(MKTC_KICKRENDER_END) +#define MKTC_KICKRENDER_RENDERCONTEXT 0xAD000409 +MKTC_ST(MKTC_KICKRENDER_RENDERCONTEXT) +#define MKTC_KICKRENDER_RTDATA 0xAD00040A +MKTC_ST(MKTC_KICKRENDER_RTDATA) +#define MKTC_KICKRENDER_PID 0xAD00040B +MKTC_ST(MKTC_KICKRENDER_PID) + +#define MKTC_RENDERFINISHED_START 0xAD000500 +MKTC_ST(MKTC_RENDERFINISHED_START) +#define MKTC_RF_START_NEXT_MT 0xAD000501 +MKTC_ST(MKTC_RF_START_NEXT_MT) +#define MKTC_RF_ALL_MTS_DONE 0xAD000502 +MKTC_ST(MKTC_RF_ALL_MTS_DONE) +#define MKTC_RENDERFINISHED_END 0xAD000503 +MKTC_ST(MKTC_RENDERFINISHED_END) +#define MKTC_VISQUERY_START 0xAD000504 +MKTC_ST(MKTC_VISQUERY_START) +#define MKTC_VISQUERY_END 0xAD000505 +MKTC_ST(MKTC_VISQUERY_END) +#define MKTC_TRANSFERRENDERFINISHED_START 0xAD000508 +MKTC_ST(MKTC_TRANSFERRENDERFINISHED_START) +#define MKTC_TRANSFERRENDERFINISHED_END 0xAD000509 +MKTC_ST(MKTC_TRANSFERRENDERFINISHED_END) +#define MKTC_TRF_UPDATESTATUSVALS 0xAD00050A +MKTC_ST(MKTC_TRF_UPDATESTATUSVALS) +#define MKTC_TRF_UPDATESTATUSVALS_DONE 0xAD00050B +MKTC_ST(MKTC_TRF_UPDATESTATUSVALS_DONE) + +#define MKTC_PIXELENDRENDER_START 0xAD000600 +MKTC_ST(MKTC_PIXELENDRENDER_START) +#define MKTC_PIXELENDRENDER_AFTERLOCK 0xAD000601 +MKTC_ST(MKTC_PIXELENDRENDER_AFTERLOCK) +#define MKTC_PIXELENDRENDER_END 0xAD000602 +MKTC_ST(MKTC_PIXELENDRENDER_END) +#define MKTC_PIXELENDRENDER_TLQEND 0xAD000603 +MKTC_ST(MKTC_PIXELENDRENDER_TLQEND) + +#define MKTC_3DMEMFREE_START 0xAD000700 +MKTC_ST(MKTC_3DMEMFREE_START) +#define MKTC_3DMEMFREE_AFTERLOCK 0xAD000701 +MKTC_ST(MKTC_3DMEMFREE_AFTERLOCK) +#define MKTC_3DMEMFREE_TESTEOR 0xAD000702 +MKTC_ST(MKTC_3DMEMFREE_TESTEOR) +#define MKTC_3DMEMFREE_END 0xAD000703 +MKTC_ST(MKTC_3DMEMFREE_END) + +#define MKTC_KICKTA_START 0xAD000800 +MKTC_ST(MKTC_KICKTA_START) +#define MKTC_KICKTA_OVERLAP 0xAD000801 +MKTC_ST(MKTC_KICKTA_OVERLAP) +#define MKTC_KICKTA_RESETCONTEXT 0xAD000802 +MKTC_ST(MKTC_KICKTA_RESETCONTEXT) +#define MKTC_KICKTA_VDM_START 0xAD000803 +MKTC_ST(MKTC_KICKTA_VDM_START) +#define MKTC_KICKTA_END 0xAD000804 +MKTC_ST(MKTC_KICKTA_END) +#define MKTC_KICKTA_RENDERCONTEXT 0xAD000805 +MKTC_ST(MKTC_KICKTA_RENDERCONTEXT) +#define MKTC_KICKTA_RTDATA 0xAD000806 +MKTC_ST(MKTC_KICKTA_RTDATA) +#define MKTC_KICKTA_RESET_VDMCSSTATUS 0xAD000807 +MKTC_ST(MKTC_KICKTA_RESET_VDMCSSTATUS) +#define MKTC_KICKTA_RESET_BUFFERS 0xAD000808 +MKTC_ST(MKTC_KICKTA_RESET_BUFFERS) +#define MKTC_KICKTA_PID 0xAD000809 +MKTC_ST(MKTC_KICKTA_PID) +#define MKTC_KICKTA_TACMD_DEBUG 0xAD00080A +MKTC_ST(MKTC_KICKTA_TACMD_DEBUG) +#define MKTC_KICKTA_FREECONTEXT 0xAD00080B +MKTC_ST(MKTC_KICKTA_FREECONTEXT) +#define MKTC_KICKTA_PIM_PATCHING 0xAD00080C +MKTC_ST(MKTC_KICKTA_PIM_PATCHING) + +#define MKTC_KICKTA_CHKPT_START_DUMMY_CS 0xAD0008A1 +MKTC_ST(MKTC_KICKTA_CHKPT_START_DUMMY_CS) +#define MKTC_KICKTA_CHKPT_START_DUMMY_TAK 0xAD0008A2 +MKTC_ST(MKTC_KICKTA_CHKPT_START_DUMMY_TAK) +#define MKTC_KICKTA_CHKPT_WAIT_FOR_DUMMY_KICK 0xAD0008A3 +MKTC_ST(MKTC_KICKTA_CHKPT_WAIT_FOR_DUMMY_KICK) +#define MKTC_KICKTA_CHKPT_WAIT_NEXT_CORE 0xAD0008A4 +MKTC_ST(MKTC_KICKTA_CHKPT_WAIT_NEXT_CORE) +#define MKTC_KICKTA_CHKPT_RESET_COMPLETE 0xAD0008A5 +MKTC_ST(MKTC_KICKTA_CHKPT_RESET_COMPLETE) +#define MKTC_KICKTA_CHKPT_CHECK_SWITCH 0xAD0008A6 +MKTC_ST(MKTC_KICKTA_CHKPT_CHECK_SWITCH) + +#define MKTC_HOSTKICK_START 0xAD000900 +MKTC_ST(MKTC_HOSTKICK_START) +#define MKTC_HOSTKICK_END 0xAD000901 +MKTC_ST(MKTC_HOSTKICK_END) +#define MKTC_HOSTKICK_PROCESS_QUEUES_END 0xAD000902 +MKTC_ST(MKTC_HOSTKICK_PROCESS_QUEUES_END) +#define MKTC_HOSTKICK_2D 0xAD000903 +MKTC_ST(MKTC_HOSTKICK_2D) +#define MKTC_HOSTKICK_TRANSFER 0xAD000904 +MKTC_ST(MKTC_HOSTKICK_TRANSFER) +#define MKTC_HOSTKICK_TA 0xAD000905 +MKTC_ST(MKTC_HOSTKICK_TA) +#define MKTC_HOSTKICK_PROCESS_QUEUES 0xAD000906 +MKTC_ST(MKTC_HOSTKICK_PROCESS_QUEUES) +#define MKTC_HOSTKICK_RESUME 0xAD000908 +MKTC_ST(MKTC_HOSTKICK_RESUME) +#define MKTC_HOSTKICK_POWEROFF 0xAD000909 +MKTC_ST(MKTC_HOSTKICK_POWEROFF) +#define MKTC_HOSTKICK_IDLE 0xAD00090A +MKTC_ST(MKTC_HOSTKICK_IDLE) +#define MKTC_HOSTKICK_CTXSUSPEND 0xAD00090B +MKTC_ST(MKTC_HOSTKICK_CTXSUSPEND) +#define MKTC_HOSTKICK_CTXRESUME 0xAD00090C +MKTC_ST(MKTC_HOSTKICK_CTXRESUME) + +#define MKTC_TIMER_POTENTIAL_TA_LOCKUP 0xAD000A00 +MKTC_ST(MKTC_TIMER_POTENTIAL_TA_LOCKUP) +#define MKTC_TIMER_POTENTIAL_3D_LOCKUP 0xAD000A01 +MKTC_ST(MKTC_TIMER_POTENTIAL_3D_LOCKUP) +#define MKTC_TIMER_CTAL_START 0xAD000A02 +MKTC_ST(MKTC_TIMER_CTAL_START) +#define MKTC_TIMER_CTAL_END 0xAD000A03 +MKTC_ST(MKTC_TIMER_CTAL_END) +#define MKTC_TIMER_C3DL_START 0xAD000A04 +MKTC_ST(MKTC_TIMER_C3DL_START) +#define MKTC_TIMER_C3DL_END 0xAD000A05 +MKTC_ST(MKTC_TIMER_C3DL_END) +#define MKTC_TIMER_LOCKUP 0xAD000A0A +MKTC_ST(MKTC_TIMER_LOCKUP) +#define MKTC_TIMER_NOT_TA_LOCKUP 0xAD000A0B +MKTC_ST(MKTC_TIMER_NOT_TA_LOCKUP) +#define MKTC_TIMER_NOT_3D_LOCKUP 0xAD000A0C +MKTC_ST(MKTC_TIMER_NOT_3D_LOCKUP) +#define MKTC_TIMER_2D_LOCKUP 0xAD000A0D +MKTC_ST(MKTC_TIMER_2D_LOCKUP) +#define MKTC_TIMER_POTENTIAL_2D_LOCKUP 0xAD000A10 +MKTC_ST(MKTC_TIMER_POTENTIAL_2D_LOCKUP) +#define MKTC_TIMER_C2DL_START 0xAD000A11 +MKTC_ST(MKTC_TIMER_C2DL_START) +#define MKTC_TIMER_C2DL_END 0xAD000A12 +MKTC_ST(MKTC_TIMER_C2DL_END) +#define MKTC_TIMER_NOT_2D_LOCKUP 0xAD000A13 +MKTC_ST(MKTC_TIMER_NOT_2D_LOCKUP) +#define MKTC_TIMER_ABORTALL 0xAD000A0E +MKTC_ST(MKTC_TIMER_ABORTALL) +#define MKTC_TIMER_END 0xAD000A0F +MKTC_ST(MKTC_TIMER_END) + +#define MKTC_HWR_START 0xAD000B00 +MKTC_ST(MKTC_HWR_START) +#define MKTC_HWR_END 0xAD000B01 +MKTC_ST(MKTC_HWR_END) +#define MKTC_HWR_HKS 0xAD000B02 +MKTC_ST(MKTC_HWR_HKS) +#define MKTC_HWR_PRL 0xAD000B03 +MKTC_ST(MKTC_HWR_PRL) +#define MKTC_HWR_PRL_DP 0xAD000B04 +MKTC_ST(MKTC_HWR_PRL_DP) +#define MKTC_HWR_CRL 0xAD000B05 +MKTC_ST(MKTC_HWR_CRL) +#define MKTC_HWR_CRL_DP 0xAD000B06 +MKTC_ST(MKTC_HWR_CRL_DP) +#define MKTC_HWR_TRL 0xAD000B07 +MKTC_ST(MKTC_HWR_TRL) +#define MKTC_HWR_TRL_DP 0xAD000B08 +MKTC_ST(MKTC_HWR_TRL_DP) +#define MKTC_HWR_ISC 0xAD000B09 +MKTC_ST(MKTC_HWR_ISC) +#define MKTC_HWR_2DL 0xAD000B0A +MKTC_ST(MKTC_HWR_2DL) + +#define MKTC_URSV_START 0xAD000C00 +MKTC_ST(MKTC_URSV_START) +#define MKTC_URSV_UPDATEWRITEOPS 0xAD000C01 +MKTC_ST(MKTC_URSV_UPDATEWRITEOPS) +#define MKTC_URSV_UPDATESTATUSVALS 0xAD000C03 +MKTC_ST(MKTC_URSV_UPDATESTATUSVALS) +#define MKTC_URSV_UPDATESTATUSVALS_DONE 0xAD000C04 +MKTC_ST(MKTC_URSV_UPDATESTATUSVALS_DONE) +#define MKTC_URSV_END 0xAD000C05 +MKTC_ST(MKTC_URSV_END) + +#define MKTC_STORETACONTEXT_START 0xAD000D00 +MKTC_ST(MKTC_STORETACONTEXT_START) +#define MKTC_STORETACONTEXT_END 0xAD000D01 +MKTC_ST(MKTC_STORETACONTEXT_END) +#define MKTC_LOADTACONTEXT_START 0xAD000D02 +MKTC_ST(MKTC_LOADTACONTEXT_START) +#define MKTC_LOADTACONTEXT_END 0xAD000D03 +MKTC_ST(MKTC_LOADTACONTEXT_END) +#define MKTC_STORE3DCONTEXT_START 0xAD000D04 +MKTC_ST(MKTC_STORE3DCONTEXT_START) +#define MKTC_STORE3DCONTEXT_END 0xAD000D05 +MKTC_ST(MKTC_STORE3DCONTEXT_END) +#define MKTC_LOAD3DCONTEXT_START 0xAD000D06 +MKTC_ST(MKTC_LOAD3DCONTEXT_START) +#define MKTC_LOAD3DCONTEXT_END 0xAD000D07 +MKTC_ST(MKTC_LOAD3DCONTEXT_END) + +#define MKTC_FINDTA_POWERREQUEST 0xAD000E00 +MKTC_ST(MKTC_FINDTA_POWERREQUEST) +#define MKTC_FINDTA_TA3D_OVERLAP_BLOCKED 0xAD000E01 +MKTC_ST(MKTC_FINDTA_TA3D_OVERLAP_BLOCKED) +#define MKTC_FINDTA_RTDATA_RENDERING 0xAD000E02 +MKTC_ST(MKTC_FINDTA_RTDATA_RENDERING) +#define MKTC_FINDTA_3DRC_DIFFERENT 0xAD000E03 +MKTC_ST(MKTC_FINDTA_3DRC_DIFFERENT) +#define MKTC_FINDTA_WRITEOPSBLOCKED 0xAD000E04 +MKTC_ST(MKTC_FINDTA_WRITEOPSBLOCKED) +#define MKTC_FINDTA_READOPSBLOCKED 0xAD000E05 +MKTC_ST(MKTC_FINDTA_READOPSBLOCKED) +#define MKTC_FINDTA_RESIZE_PB 0xAD000E06 +MKTC_ST(MKTC_FINDTA_RESIZE_PB) +#define MKTC_FINDTA_RESIZE_PB_BLOCKED 0xAD000E07 +MKTC_ST(MKTC_FINDTA_RESIZE_PB_BLOCKED) +#define MKTC_FINDTA_SHRINK_PB 0xAD000E08 +MKTC_ST(MKTC_FINDTA_SHRINK_PB) +#define MKTC_FINDTA_TAPB_DIFFERENT 0xAD000E09 +MKTC_ST(MKTC_FINDTA_TAPB_DIFFERENT) +#define MKTC_FINDTA_TACONTEXT_DIFFERENT 0xAD000E0A +MKTC_ST(MKTC_FINDTA_TACONTEXT_DIFFERENT) +#define MKTC_FINDTA_TA2D_OVERLAP_BLOCKED 0xAD000E0B +MKTC_ST(MKTC_FINDTA_TA2D_OVERLAP_BLOCKED) +#define MKTC_FINDTA_CONTEXT_SUSPENDED 0xAD000E0C +MKTC_ST(MKTC_FINDTA_CONTEXT_SUSPENDED) +#define MKTC_FINDTA_SRC_READOPSBLOCKED 0xAD000E0D +MKTC_ST(MKTC_FINDTA_SRC_READOPSBLOCKED) +#define MKTC_FINDTA_SRC_WRITEOPSBLOCKED 0xAD000E0E +MKTC_ST(MKTC_FINDTA_SRC_WRITEOPSBLOCKED) +#define MKTC_FINDTA_READOPS2BLOCKED 0xAD000E0F +MKTC_ST(MKTC_FINDTA_READOPS2BLOCKED) + +#define MKTC_CTRL_SRCREADOPSBLOCKED 0xAD000F00 +MKTC_ST(MKTC_CTRL_SRCREADOPSBLOCKED) +#define MKTC_CTRL_SRCWRITEOPSBLOCKED 0xAD000F01 +MKTC_ST(MKTC_CTRL_SRCWRITEOPSBLOCKED) +#define MKTC_CTRL_DSTREADOPSBLOCKED 0xAD000F02 +MKTC_ST(MKTC_CTRL_DSTREADOPSBLOCKED) +#define MKTC_CTRL_DSTWRITEOPSBLOCKED 0xAD000F03 +MKTC_ST(MKTC_CTRL_DSTWRITEOPSBLOCKED) +#define MKTC_CTRL_TARC_DIFFERENT 0xAD000F04 +MKTC_ST(MKTC_CTRL_TARC_DIFFERENT) +#define MKTC_CTRL_CONTEXT_SUSPENDED 0xAD000F05 +MKTC_ST(MKTC_CTRL_CONTEXT_SUSPENDED) +#define MKTC_CTRL_SRCREADOPS2BLOCKED 0xAD000F06 +MKTC_ST(MKTC_CTRL_SRCREADOPS2BLOCKED) + +#define MKTC_DPTA_START 0xAD001000 +MKTC_ST(MKTC_DPTA_START) +#define MKTC_DPTA_UPDATESTATUSVALS 0xAD001001 +MKTC_ST(MKTC_DPTA_UPDATESTATUSVALS) +#define MKTC_DPTA_UPDATESTATUSVALS_DONE 0xAD001002 +MKTC_ST(MKTC_DPTA_UPDATESTATUSVALS_DONE) +#define MKTC_DPTA_NORENDER 0xAD001003 +MKTC_ST(MKTC_DPTA_NORENDER) +#define MKTC_DPTA_MEMFREE 0xAD001004 +MKTC_ST(MKTC_DPTA_MEMFREE) +#define MKTC_DPTA_INC_COMPLETECOUNT 0xAD001005 +MKTC_ST(MKTC_DPTA_INC_COMPLETECOUNT) + +#define MKTC_INVALDC 0xAD001100 +MKTC_ST(MKTC_INVALDC) +#define MKTC_INVALPT 0xAD001101 +MKTC_ST(MKTC_INVALPT) +#define MKTC_INVALSLC 0xAD001102 +MKTC_ST(MKTC_INVALSLC) +#define MKTC_INVALDATA 0xAD001103 +MKTC_ST(MKTC_INVALDATA) + +#define MKTC_RESTARTTA 0xAD001200 +MKTC_ST(MKTC_RESTARTTA) +#define MKTC_CSABORTNONGBL 0xAD001201 +MKTC_ST(MKTC_CSABORTNONGBL) +#define MKTC_CSABORTALL 0xAD001202 +MKTC_ST(MKTC_CSABORTALL) +#define MKTC_CSRENDERINPROGRESS 0xAD001203 +MKTC_ST(MKTC_CSRENDERINPROGRESS) +#define MKTC_TATERMRENDERINPROGRESS 0xAD001204 +MKTC_ST(MKTC_TATERMRENDERINPROGRESS) +#define MKTC_RESTARTTANORENDER 0xAD001205 +MKTC_ST(MKTC_RESTARTTANORENDER) +#define MKTC_SPM_KICKRENDER 0xAD001206 +MKTC_ST(MKTC_SPM_KICKRENDER) +#define MKTC_SPM_RESUME_ABORTCOMPLETE 0xAD001208 +MKTC_ST(MKTC_SPM_RESUME_ABORTCOMPLETE) +#define MKTC_RESUMEVDM 0xAD001209 +MKTC_ST(MKTC_RESUMEVDM) +#define MKTC_REMOVE_RESERVE_MEM 0xAD00120A +MKTC_ST(MKTC_REMOVE_RESERVE_MEM) +#define MKTC_INCREASEZLSTHRESHOLD 0xAD00120B +MKTC_ST(MKTC_INCREASEZLSTHRESHOLD) +#define MKTC_CSFORCEABORTALL 0xAD00120C +MKTC_ST(MKTC_CSFORCEABORTALL) + +#define MKTC_DUMMY_DEPTH 0xAD00120D +MKTC_ST(MKTC_DUMMY_DEPTH) +#define MKTC_DUMMY_DEPTH_CS 0xAD00120E +MKTC_ST(MKTC_DUMMY_DEPTH_CS) + +#define MKTC_MTETE_OOM 0xAD00120F +MKTC_ST(MKTC_MTETE_OOM) +#define MKTC_MTETE_OOM_FIRST_STORE_REF 0xAD001210 +MKTC_ST(MKTC_MTETE_OOM_FIRST_STORE_REF) +#define MKTC_MERGE_STATE_TABLES 0xAD001211 +MKTC_ST(MKTC_MERGE_STATE_TABLES) +#define MKTC_NO_PAGES_LEFT_FOR_23055 0xAD001212 +MKTC_ST(MKTC_NO_PAGES_LEFT_FOR_23055) +#define MKTC_NO_STATE_MODS 0xAD001213 +MKTC_ST(MKTC_NO_STATE_MODS) +#define MKTC_FIND_MTE_PAGE_IN_STATE 0xAD001214 +MKTC_ST(MKTC_FIND_MTE_PAGE_IN_STATE) +#define MKTC_MTE_PAGE_FOUND 0xAD001215 +MKTC_ST(MKTC_MTE_PAGE_FOUND) +#define MKTC_MOVE_MTE_PAGE_TO_TA_STATE 0xAD001216 +MKTC_ST(MKTC_MOVE_MTE_PAGE_TO_TA_STATE) +#define MKTC_MOVE_MTE_PAGE_TO_TA_STATE_END 0xAD001217 +MKTC_ST(MKTC_MOVE_MTE_PAGE_TO_TA_STATE_END) +#define MKTC_ZERO_ZLS_THRESHOLD 0xAD001218 +MKTC_ST(MKTC_ZERO_ZLS_THRESHOLD) +#define MKTC_RESTORE_ZLS_THRESHOLD 0xAD001219 +MKTC_ST(MKTC_RESTORE_ZLS_THRESHOLD) +#define MKTC_FIND_MTE_PAGE_IN_CSM 0xAD00121A +MKTC_ST(MKTC_FIND_MTE_PAGE_IN_CSM) +#define MKTC_REISSUE_MTE_PAGE 0xAD00121B +MKTC_ST(MKTC_REISSUE_MTE_PAGE) +#define MKTC_REISSUE_MTE_PAGE_REQUIRED 0xAD00121C +MKTC_ST(MKTC_REISSUE_MTE_PAGE_REQUIRED) +#define MKTC_REISSUE_MTE_PAGE_END 0xAD00121D +MKTC_ST(MKTC_REISSUE_MTE_PAGE_END) +#define MKTC_RESET_TE_PSG 0xAD00121E +MKTC_ST(MKTC_RESET_TE_PSG) + +#define MKTC_OOM_WRITEOPSBLOCKED 0xAD00121F +MKTC_ST(MKTC_OOM_WRITEOPSBLOCKED) +#define MKTC_OOM_READOPSBLOCKED 0xAD001220 +MKTC_ST(MKTC_OOM_READOPSBLOCKED) +#define MKTC_OOM_SRC_WRITEOPSBLOCKED 0xAD001221 +MKTC_ST(MKTC_OOM_SRC_WRITEOPSBLOCKED) +#define MKTC_OOM_SRC_READOPSBLOCKED 0xAD001222 +MKTC_ST(MKTC_OOM_SRC_READOPSBLOCKED) +#define MKTC_OOM_SPM_DEADLOCK 0xAD001223 +MKTC_ST(MKTC_OOM_SPM_DEADLOCK) +#define MKTC_OOM_SPM_DEADLOCK_MEM_ADDED 0xAD001224 +MKTC_ST(MKTC_OOM_SPM_DEADLOCK_MEM_ADDED) +#define MKTC_RESET 0xAD001225 +MKTC_ST(MKTC_RESET) +#define MKTC_SPM_INVALID_ZLSCONFIG 0xAD001226 +MKTC_ST(MKTC_SPM_INVALID_ZLSCONFIG) + +#define MKTC_OOM_TYPE_MT 0xAD00122A +MKTC_ST(MKTC_OOM_TYPE_MT) +#define MKTC_OOM_TYPE_GLOBAL 0xAD001230 +MKTC_ST(MKTC_OOM_TYPE_GLOBAL) +#define MKTC_OOM_CAUSE_GBL_OOM 0xAD001231 +MKTC_ST(MKTC_OOM_CAUSE_GBL_OOM) +#define MKTC_OOM_RESTORE_LIST_SIZE 0xAD001232 +MKTC_ST(MKTC_OOM_RESTORE_LIST_SIZE) + +#define MKTC_CHECK_MTE_PAGE_REISSUE 0xAD001240 +MKTC_ST(MKTC_CHECK_MTE_PAGE_REISSUE) +#define MKTC_CPRI_VALID_ENTRIES 0xAD001241 +MKTC_ST(MKTC_CPRI_VALID_ENTRIES) +#define MKTC_CPRI_STORE_DPLIST 0xAD001242 +MKTC_ST(MKTC_CPRI_STORE_DPLIST) +#define MKTC_CPRI_STORE_OTPM_CSM 0xAD001243 +MKTC_ST(MKTC_CPRI_STORE_OTPM_CSM) +#define MKTC_CPRI_ABORT_MT_IDX 0xAD001244 +MKTC_ST(MKTC_CPRI_ABORT_MT_IDX) +#define MKTC_CPRI_ABORT_CORE_IDX 0xAD001245 +MKTC_ST(MKTC_CPRI_ABORT_CORE_IDX) +#define MKTC_CPRI_CSM_TABLE_DATA 0xAD001246 +MKTC_ST(MKTC_CPRI_CSM_TABLE_DATA) +#define MKTC_CPRI_PIM_DATA 0xAD001247 +MKTC_ST(MKTC_CPRI_PIM_DATA) +#define MKTC_CPRI_DO_CIRCULAR_TEST 0xAD001248 +MKTC_ST(MKTC_CPRI_DO_CIRCULAR_TEST) +#define MKTC_CPRI_WRITE_ENTRIES 0xAD001249 +MKTC_ST(MKTC_CPRI_WRITE_ENTRIES) + +#define MKTC_MTE_ENTRY_NOT_IN_ANY_LIST 0xAD001250 +MKTC_ST(MKTC_MTE_ENTRY_NOT_IN_ANY_LIST) + +#define MKTC_SPMAC_IGNORE_TERMINATE 0xAD001251 +MKTC_ST(MKTC_SPMAC_IGNORE_TERMINATE) + +#define MKTC_SPMAC_REQUEST_3D_TIMEOUT 0xAD001252 +MKTC_ST(MKTC_SPMAC_REQUEST_3D_TIMEOUT) +#define MKTC_SPMAC_3D_TIMEOUT_COMPLETE 0xAD001253 +MKTC_ST(MKTC_SPMAC_3D_TIMEOUT_COMPLETE) +#define MKTC_OOM_READOPS2BLOCKED 0xAD001254 +MKTC_ST(MKTC_OOM_READOPS2BLOCKED) + +/* PB Load/store status */ +#define MKTC_LOADTAPB_START 0xAD001300 +MKTC_ST(MKTC_LOADTAPB_START) +#define MKTC_LOADTAPB_END 0xAD001301 +MKTC_ST(MKTC_LOADTAPB_END) +#define MKTC_STORETAPB_START 0xAD001302 +MKTC_ST(MKTC_STORETAPB_START) +#define MKTC_STORETAPB_END 0xAD001303 +MKTC_ST(MKTC_STORETAPB_END) +#define MKTC_LOAD3DPB_START 0xAD001304 +MKTC_ST(MKTC_LOAD3DPB_START) +#define MKTC_LOAD3DPB_END 0xAD001305 +MKTC_ST(MKTC_LOAD3DPB_END) +#define MKTC_STORE3DPB_START 0xAD001306 +MKTC_ST(MKTC_STORE3DPB_START) +#define MKTC_STORE3DPB_END 0xAD001307 +MKTC_ST(MKTC_STORE3DPB_END) +#define MKTC_LOADTAPB_PAGETABLE_DONE 0xAD001308 +MKTC_ST(MKTC_LOADTAPB_PAGETABLE_DONE) +#define MKTC_LOAD3DPB_PAGETABLE_DONE 0xAD001309 +MKTC_ST(MKTC_LOAD3DPB_PAGETABLE_DONE) + +#define MKTC_TIMER_RC_CLEANUP 0xAD001400 +MKTC_ST(MKTC_TIMER_RC_CLEANUP) +#define MKTC_TIMER_RC_CLEANUP_DONE 0xAD001401 +MKTC_ST(MKTC_TIMER_RC_CLEANUP_DONE) +#define MKTC_TIMER_RC_CLEANUP_BUSY 0xAD001402 +MKTC_ST(MKTC_TIMER_RC_CLEANUP_BUSY) +#define MKTC_TIMER_RT_CLEANUP 0xAD001410 +MKTC_ST(MKTC_TIMER_RT_CLEANUP) +#define MKTC_TIMER_RT_CLEANUP_DONE 0xAD001411 +MKTC_ST(MKTC_TIMER_RT_CLEANUP_DONE) +#define MKTC_TIMER_RT_CLEANUP_PENDING 0xAD001412 +MKTC_ST(MKTC_TIMER_RT_CLEANUP_PENDING) +#define MKTC_TIMER_RT_CLEANUP_TIDYPARTIALLIST 0xAD001413 +MKTC_ST(MKTC_TIMER_RT_CLEANUP_TIDYPARTIALLIST) +#define MKTC_TIMER_RT_CLEANUP_BUSY 0xAD001414 +MKTC_ST(MKTC_TIMER_RT_CLEANUP_BUSY) +#define MKTC_TIMER_TC_CLEANUP 0xAD001420 +MKTC_ST(MKTC_TIMER_TC_CLEANUP) +#define MKTC_TIMER_TC_CLEANUP_DONE 0xAD001421 +MKTC_ST(MKTC_TIMER_TC_CLEANUP_DONE) +#define MKTC_TIMER_TC_CLEANUP_BUSY 0xAD001422 +MKTC_ST(MKTC_TIMER_TC_CLEANUP_BUSY) +#define MKTC_TIMER_2DC_CLEANUP 0xAD001430 +MKTC_ST(MKTC_TIMER_2DC_CLEANUP) +#define MKTC_TIMER_2DC_CLEANUP_DONE 0xAD001431 +MKTC_ST(MKTC_TIMER_2DC_CLEANUP_DONE) +#define MKTC_TIMER_2DC_CLEANUP_BUSY 0xAD001432 +MKTC_ST(MKTC_TIMER_2DC_CLEANUP_BUSY) +#define MKTC_TIMER_SHAREDPBDESC_CLEANUP 0xAD001440 +MKTC_ST(MKTC_TIMER_SHAREDPBDESC_CLEANUP) + + +#define MKTC_TIMER_ISP_SWITCH_POTENTIAL_LOCKUP 0xAD001450 +MKTC_ST(MKTC_TIMER_ISP_SWITCH_POTENTIAL_LOCKUP) +#define MKTC_TIMER_ISP_SWITCH_FORCE_SWITCH 0xAD001451 +MKTC_ST(MKTC_TIMER_ISP_SWITCH_FORCE_SWITCH) + +#define MKTC_UTSO_UPDATEREADOPS 0xAD001600 +MKTC_ST(MKTC_UTSO_UPDATEREADOPS) +#define MKTC_UTSO_UPDATEWRITEOPS 0xAD001601 +MKTC_ST(MKTC_UTSO_UPDATEWRITEOPS) + +#define MKTC_TAFINISHED_UPDATESTATUSVALS 0xAD001700 +MKTC_ST(MKTC_TAFINISHED_UPDATESTATUSVALS) +#define MKTC_TAFINISHED_UPDATESTATUSVALS_DONE 0xAD001701 +MKTC_ST(MKTC_TAFINISHED_UPDATESTATUSVALS_DONE) +#define MKTC_TAFINISHED_NORENDER 0xAD001702 +MKTC_ST(MKTC_TAFINISHED_NORENDER) +#define MKTC_TAFINISHED_LASTKICK 0xAD001703 +MKTC_ST(MKTC_TAFINISHED_LASTKICK) +#define MKTC_TAFINISHED_FINDRENDER 0xAD001704 +MKTC_ST(MKTC_TAFINISHED_FINDRENDER) +#define MKTC_TAFINISHED_FINDTA 0xAD001705 +MKTC_ST(MKTC_TAFINISHED_FINDTA) +#define MKTC_TAFINISHED_END 0xAD001706 +MKTC_ST(MKTC_TAFINISHED_END) +#define MKTC_TAF_SPM_DEADLOCK_MEM_REMOVED 0xAD001707 +MKTC_ST(MKTC_TAF_SPM_DEADLOCK_MEM_REMOVED) +#define MKTC_TAF_RESERVE_MEM 0xAD001708 +MKTC_ST(MKTC_TAF_RESERVE_MEM) +#define MKTC_TAF_RESERVE_MEM_REQUEST_RENDER 0xAD001709 +MKTC_ST(MKTC_TAF_RESERVE_MEM_REQUEST_RENDER) +#define MKTC_TAF_RESERVE_FREE_RENDER_FINISHED 0xAD00170A +MKTC_ST(MKTC_TAF_RESERVE_FREE_RENDER_FINISHED) +#define MKTC_TAF_RESERVE_FREE_DUMMY_RENDER 0xAD00170B +MKTC_ST(MKTC_TAF_RESERVE_FREE_DUMMY_RENDER) +#define MKTC_TAF_DEBUG_SAS 0xAD00170C +MKTC_ST(MKTC_TAF_DEBUG_SAS) +#define MKTC_TAFINISHED_NOCONTEXTSWITCH 0xAD00170D +MKTC_ST(MKTC_TAFINISHED_NOCONTEXTSWITCH) + +#define MKTC_TAFINISHED_TERM_COMPLETE_START 0xAD001710 +MKTC_ST(MKTC_TAFINISHED_TERM_COMPLETE_START) +#define MKTC_TAFINISHED_TERM_COMPLETE_END 0xAD001711 +MKTC_ST(MKTC_TAFINISHED_TERM_COMPLETE_END) + +#define MKTC_TAFINISHED_DPMPAGERECYCLING 0xAD001720 +MKTC_ST(MKTC_TAFINISHED_DPMPAGERECYCLING) + +#define MKTC_2DEVENT_2DCOMPLETE 0xAD001800 +MKTC_ST(MKTC_2DEVENT_2DCOMPLETE) +#define MKTC_2DEVENT_END 0xAD001801 +MKTC_ST(MKTC_2DEVENT_END) +#define MKTC_2DLB_2DCOMPLETE 0xAD001802 +MKTC_ST(MKTC_2DLB_2DCOMPLETE) +#define MKTC_2DLB_FIND2D 0xAD001803 +MKTC_ST(MKTC_2DLB_FIND2D) +#define MKTC_2DLB_END 0xAD001804 +MKTC_ST(MKTC_2DLB_END) +#define MKTC_2DCOMPLETE_START 0xAD001805 +MKTC_ST(MKTC_2DCOMPLETE_START) +#define MKTC_2DCOMPLETE_END 0xAD001806 +MKTC_ST(MKTC_2DCOMPLETE_END) +#define MKTC_KICK2D_START 0xAD001807 +MKTC_ST(MKTC_KICK2D_START) +#define MKTC_KICK2D_END 0xAD001808 +MKTC_ST(MKTC_KICK2D_END) +#define MKTC_DUMMYPROC2D 0xAD001809 +MKTC_ST(MKTC_DUMMYPROC2D) +#define MKTC_FTD_SRCREADOPSBLOCKED 0xAD00180A +MKTC_ST(MKTC_FTD_SRCREADOPSBLOCKED) +#define MKTC_FTD_SRCWRITEOPSBLOCKED 0xAD00180B +MKTC_ST(MKTC_FTD_SRCWRITEOPSBLOCKED) +#define MKTC_FTD_DSTREADOPSBLOCKED 0xAD00180C +MKTC_ST(MKTC_FTD_DSTREADOPSBLOCKED) +#define MKTC_FTD_DSTWRITEOPSBLOCKED 0xAD00180D +MKTC_ST(MKTC_FTD_DSTWRITEOPSBLOCKED) +#define MKTC_FTD_TA2D_OVERLAP_BLOCKED 0xAD00180E +MKTC_ST(MKTC_FTD_TA2D_OVERLAP_BLOCKED) +#define MKTC_U2DSO_UPDATEREADOPS 0xAD00180F +MKTC_ST(MKTC_U2DSO_UPDATEREADOPS) +#define MKTC_U2DSO_UPDATEWRITEOPS 0xAD001810 +MKTC_ST(MKTC_U2DSO_UPDATEWRITEOPS) +#define MKTC_FTD_TAOPSBLOCKED 0xAD001811 +MKTC_ST(MKTC_FTD_TAOPSBLOCKED) +#define MKTC_KICK2D_2DSLAVEPORT 0xAD001812 +MKTC_ST(MKTC_KICK2D_2DSLAVEPORT) +#define MKTC_KICK2D_2DSLAVEPORT_DONE 0xAD001813 +MKTC_ST(MKTC_KICK2D_2DSLAVEPORT_DONE) +#define MKTC_FTD_CONTEXT_SUSPENDED 0xAD001814 +MKTC_ST(MKTC_FTD_CONTEXT_SUSPENDED) +#define MKTC_KICK2D_PID 0xAD001815 +MKTC_ST(MKTC_KICK2D_PID) +#define MKTC_FIND2D_ADDR_SPACE_DIFFERENT 0xAD001816 +MKTC_ST(MKTC_FIND2D_ADDR_SPACE_DIFFERENT) +#define MKTC_FTD_3DOPSBLOCKED 0xAD001817 +MKTC_ST(MKTC_FTD_3DOPSBLOCKED) +#define MKTC_FTD_DSTREADOPS2BLOCKED 0xAD001818 +MKTC_ST(MKTC_FTD_DSTREADOPS2BLOCKED) + +#define MKTC_FCM_START 0xAD001900 +MKTC_ST(MKTC_FCM_START) +#define MKTC_FCM_END 0xAD001901 +MKTC_ST(MKTC_FCM_END) + +#define MKTC_TIMER_ACTIVE_POWER 0xAD001A00 +MKTC_ST(MKTC_TIMER_ACTIVE_POWER) +#define MKTC_TIMER_POWER_3D_ACTIVE 0xAD001A01 +MKTC_ST(MKTC_TIMER_POWER_3D_ACTIVE) +#define MKTC_TIMER_POWER_TA_ACTIVE 0xAD001A02 +MKTC_ST(MKTC_TIMER_POWER_TA_ACTIVE) +#define MKTC_TIMER_POWER_2D_ACTIVE 0xAD001A03 +MKTC_ST(MKTC_TIMER_POWER_2D_ACTIVE) +#define MKTC_TIMER_POWER_PENDING_EVENTS 0xAD001A04 +MKTC_ST(MKTC_TIMER_POWER_PENDING_EVENTS) +#define MKTC_TIMER_POWER_IDLE 0xAD001A05 +MKTC_ST(MKTC_TIMER_POWER_IDLE) +#define MKTC_TIMER_POWER_OFF 0xAD001A06 +MKTC_ST(MKTC_TIMER_POWER_OFF) +#define MKTC_TIMER_POWER_CCB_ERROR 0xAD001A07 +MKTC_ST(MKTC_TIMER_POWER_CCB_ERROR) +#define MKTC_TIMER_POWER_RESTART_IMMEDIATE 0xAD001A08 +MKTC_ST(MKTC_TIMER_POWER_RESTART_IMMEDIATE) + +#define MKTC_3DCONTEXT_SWITCH 0xAD001B00 +MKTC_ST(MKTC_3DCONTEXT_SWITCH) +#define MKTC_3DCONTEXT_SWITCH_END 0xAD001B01 +MKTC_ST(MKTC_3DCONTEXT_SWITCH_END) + +#define MKTC_TACONTEXT_SWITCH 0xAD001C00 +MKTC_ST(MKTC_TACONTEXT_SWITCH) +#define MKTC_TACONTEXT_SWITCH_END 0xAD001C02 +MKTC_ST(MKTC_TACONTEXT_SWITCH_END) + +#define MKTC_GETMISCINFO_MEMREAD_START 0xAD001D00 +MKTC_ST(MKTC_GETMISCINFO_MEMREAD_START) +#define MKTC_GETMISCINFO_MEMREAD_END 0xAD001D01 +MKTC_ST(MKTC_GETMISCINFO_MEMREAD_END) +#define MKTC_GETMISCINFO_MEMWRITE_START 0xAD001D02 +MKTC_ST(MKTC_GETMISCINFO_MEMWRITE_START) +#define MKTC_GETMISCINFO_MEMWRITE_END 0xAD001D03 +MKTC_ST(MKTC_GETMISCINFO_MEMWRITE_END) + +#define MKTC_HALTTA 0xAD001E00 +MKTC_ST(MKTC_HALTTA) +#define MKTC_HTA_SET_FLAG 0xAD001E01 +MKTC_ST(MKTC_HTA_SET_FLAG) +#define MKTC_HTA_SAVE_COMPLEX_PTR 0xAD001E02 +MKTC_ST(MKTC_HTA_SAVE_COMPLEX_PTR) +#define MKTC_HALTTA_END 0xAD001E03 +MKTC_ST(MKTC_HALTTA_END) + +#define MKTC_RESUMETA 0xAD001F00 +MKTC_ST(MKTC_RESUMETA) +#define MKTC_RTA_CONTEXT_LOADED 0xAD001F01 +MKTC_ST(MKTC_RTA_CONTEXT_LOADED) +#define MKTC_RTA_MTE_STATE_KICKED 0xAD001F02 +MKTC_ST(MKTC_RTA_MTE_STATE_KICKED) +#define MKTC_RTA_CMPLX_GEOM_PRESENT 0xAD001F03 +MKTC_ST(MKTC_RTA_CMPLX_GEOM_PRESENT) +#define MKTC_RTA_CMPLX_STATE_KICKED 0xAD001F04 +MKTC_ST(MKTC_RTA_CMPLX_STATE_KICKED) +#define MKTC_RTA_CHECK_NEXT_SA_PROG 0xAD001F05 +MKTC_ST(MKTC_RTA_CHECK_NEXT_SA_PROG) +#define MKTC_RTA_CORE_COMPLETED 0xAD001F06 +MKTC_ST(MKTC_RTA_CORE_COMPLETED) +#define MKTC_RTA_DEBUG_SAS 0xAD001F07 +MKTC_ST(MKTC_RTA_DEBUG_SAS) +#define MKTC_RESUMETA_END 0xAD001F0F +MKTC_ST(MKTC_RESUMETA_END) + +#define MKTC_RENDERHALT 0xAD002000 +MKTC_ST(MKTC_RENDERHALT) +#define MKTC_RH_CLEARFLAGS 0xAD002001 +MKTC_ST(MKTC_RH_CLEARFLAGS) +#define MKTC_RH_CTRL_ADDR 0xAD002002 +MKTC_ST(MKTC_RH_CTRL_ADDR) +#define MKTC_RH_RGN_ADDR 0xAD002003 +MKTC_ST(MKTC_RH_RGN_ADDR) +#define MKTC_RH_EMPTY_TILE 0xAD002004 +MKTC_ST(MKTC_RH_EMPTY_TILE) +#define MKTC_RH_EMPTY_LAST_TILE 0xAD002005 +MKTC_ST(MKTC_RH_EMPTY_LAST_TILE) +#define MKTC_RH_3D_TIMEOUT 0xAD002006 +MKTC_ST(MKTC_RH_3D_TIMEOUT) +#define MKTC_RH_NOT_EMPTY 0xAD002007 +MKTC_ST(MKTC_RH_NOT_EMPTY) +#define MKTC_RH_OBJECT_COMPLETE 0xAD002008 +MKTC_ST(MKTC_RH_OBJECT_COMPLETE) +#define MKTC_RH_STREAM_LINK 0xAD002009 +MKTC_ST(MKTC_RH_STREAM_LINK) +#define MKTC_RH_OBJECT_INCOMPLETE 0xAD00200A +MKTC_ST(MKTC_RH_OBJECT_INCOMPLETE) +#define MKTC_RH_PRIM_MASK_PRESENT 0xAD00200B +MKTC_ST(MKTC_RH_PRIM_MASK_PRESENT) +#define MKTC_RH_BYTE_MASK_PRESENT 0xAD00200C +MKTC_ST(MKTC_RH_BYTE_MASK_PRESENT) +#define MKTC_RH_BYTE_MASK_ZERO 0xAD00200D +MKTC_ST(MKTC_RH_BYTE_MASK_ZERO) +#define MKTC_RH_PRIM_MASK_ZERO 0xAD00200E +MKTC_ST(MKTC_RH_PRIM_MASK_ZERO) +#define MKTC_RH_INVALIDATE_OBJECTS 0xAD00200F +MKTC_ST(MKTC_RH_INVALIDATE_OBJECTS) +#define MKTC_RH_OBJECTS_INVALIDATED 0xAD002010 +MKTC_ST(MKTC_RH_OBJECTS_INVALIDATED) +#define MKTC_RH_DPM_RGN_PARSER_IDLE 0xAD002011 +MKTC_ST(MKTC_RH_DPM_RGN_PARSER_IDLE) +#define MKTC_RH_NEXT_RGN_BASE 0xAD002012 +MKTC_ST(MKTC_RH_NEXT_RGN_BASE) +#define MKTC_RH_OCC_EXIT 0xAD002013 +MKTC_ST(MKTC_RH_OCC_EXIT) +#define MKTC_RH_STILL_RUNNING 0xAD002020 +MKTC_ST(MKTC_RH_STILL_RUNNING) +#define MKTC_RH_CLEARMCI 0xAD002021 +MKTC_ST(MKTC_RH_CLEARMCI) +#define MKTC_RH_EOR 0xAD002022 +MKTC_ST(MKTC_RH_EOR) +#define MKTC_RENDERHALT_END 0xAD002030 +MKTC_ST(MKTC_RENDERHALT_END) + +#define MKTC_FIND3D_POWERREQUEST 0xAD002100 +MKTC_ST(MKTC_FIND3D_POWERREQUEST) + +#define MKTC_FIND2D_POWERREQUEST 0xAD002200 +MKTC_ST(MKTC_FIND2D_POWERREQUEST) + +#define MKTC_UKERNEL_INIT 0xAD002300 +MKTC_ST(MKTC_UKERNEL_INIT) +#define MKTC_UKERNEL_INIT_DCS_COMPLETE 0xAD002301 +MKTC_ST(MKTC_UKERNEL_INIT_DCS_COMPLETE) +#define MKTC_UKERNEL_INIT_VDMKICK_COMPLETE 0xAD002303 +MKTC_ST(MKTC_UKERNEL_INIT_VDMKICK_COMPLETE) + +#define MKTC_KICKTRANSFERRENDER_START 0xAD002400 +MKTC_ST(MKTC_KICKTRANSFERRENDER_START) +#define MKTC_KICKTRANSFERRENDER_ISP_START 0xAD002401 +MKTC_ST(MKTC_KICKTRANSFERRENDER_ISP_START) +#define MKTC_KICKTRANSFERRENDER_END 0xAD002402 +MKTC_ST(MKTC_KICKTRANSFERRENDER_END) +#define MKTC_DUMMYPROCTRANSFER 0xAD002403 +MKTC_ST(MKTC_DUMMYPROCTRANSFER) +#define MKTC_KTR_TQFENCE 0xAD002404 +MKTC_ST(MKTC_KTR_TQFENCE) +#define MKTC_KICKTRANSFERRENDER_PID 0xAD002405 +MKTC_ST(MKTC_KICKTRANSFERRENDER_PID) + +#define MKTC_HOSTKICK_CLEANUP_RT 0xAD002500 +MKTC_ST(MKTC_HOSTKICK_CLEANUP_RT) +#define MKTC_HOSTKICK_CLEANUP_RC 0xAD002501 +MKTC_ST(MKTC_HOSTKICK_CLEANUP_RC) +#define MKTC_HOSTKICK_CLEANUP_TC 0xAD002502 +MKTC_ST(MKTC_HOSTKICK_CLEANUP_TC) +#define MKTC_HOSTKICK_CLEANUP_2DC 0xAD002503 +MKTC_ST(MKTC_HOSTKICK_CLEANUP_2DC) +#define MKTC_HOSTKICK_CLEANUP_PB 0xAD002504 +MKTC_ST(MKTC_HOSTKICK_CLEANUP_PB) +#define MKTC_HOSTKICK_GETMISCINFO 0xAD002505 +MKTC_ST(MKTC_HOSTKICK_GETMISCINFO) +#define MKTC_HOSTKICK_DATABREAKPOINT 0xAD002506 +MKTC_ST(MKTC_HOSTKICK_DATABREAKPOINT) +#define MKTC_HOSTKICK_SETHWPERFSTATUS 0xAD002507 +MKTC_ST(MKTC_HOSTKICK_SETHWPERFSTATUS) + +#define MKTC_ZEROPC 0xAD002600 +MKTC_ST(MKTC_ZEROPC) + +#define MKTC_ASSERT_FAIL 0xAD002700 +MKTC_ST(MKTC_ASSERT_FAIL) + +#define MKTC_SDLB_ILLEGAL 0xAD002800 +MKTC_ST(MKTC_SDLB_ILLEGAL) + +#define MKTC_SPMEVENT_OUTOFMEM 0xAD002901 +MKTC_ST(MKTC_SPMEVENT_OUTOFMEM) +#define MKTC_SPMEVENT_TATERMINATE 0xAD002902 +MKTC_ST(MKTC_SPMEVENT_TATERMINATE) +#define MKTC_SPMEVENT_END 0xAD002904 +MKTC_ST(MKTC_SPMEVENT_END) + +#define MKTC_SPMLB_OUTOFMEM 0xAD002981 +MKTC_ST(MKTC_SPMLB_OUTOFMEM) +#define MKTC_SPMLB_TATERMINATE 0xAD002982 +MKTC_ST(MKTC_SPMLB_TATERMINATE) +#define MKTC_SPMLB_SPMRENDERFINSHED 0xAD002983 +MKTC_ST(MKTC_SPMLB_SPMRENDERFINSHED) +#define MKTC_SPMLB_END 0xAD002985 +MKTC_ST(MKTC_SPMLB_END) + +#define MKTC_SPM_CHECK_MT_DEADLOCK 0xAD002991 +MKTC_ST(MKTC_SPM_CHECK_MT_DEADLOCK) +#define MKTC_SPM_CHECK_GLOBAL_DEADLOCK 0xAD002992 +MKTC_ST(MKTC_SPM_CHECK_GLOBAL_DEADLOCK) +#define MKTC_SPM_RESERVE_ADDED 0xAD002993 +MKTC_ST(MKTC_SPM_RESERVE_ADDED) +#define MKTC_SPM_FORCE_GLOBAL_OOM_FAILED 0xAD00299E +MKTC_ST(MKTC_SPM_FORCE_GLOBAL_OOM_FAILED) +#define MKTC_SPM_DEADLOCK_MEM_FAILED 0xAD00299F +MKTC_ST(MKTC_SPM_DEADLOCK_MEM_FAILED) + +#define MKTC_IBC_ILLEGAL 0xAD002A00 +MKTC_ST(MKTC_IBC_ILLEGAL) + +#define MKTC_HWP_CLEARCOUNTERS 0xAD002B00 +MKTC_ST(MKTC_HWP_CLEARCOUNTERS) + +#define MKTC_TA_FRAMENUM 0xAD002C00 +MKTC_ST(MKTC_TA_FRAMENUM) +#define MKTC_3D_FRAMENUM 0xAD002C01 +MKTC_ST(MKTC_3D_FRAMENUM) +#define MKTC_SPM3D_FRAMENUM 0xAD002C02 +MKTC_ST(MKTC_SPM3D_FRAMENUM) + +#define MKTC_HKTA_RENDERCONTEXT 0xAD002D00 +MKTC_ST(MKTC_HKTA_RENDERCONTEXT) +#define MKTC_IDLECORE_REFCOUNT_FAIL 0xAD002E00 +MKTC_ST(MKTC_IDLECORE_REFCOUNT_FAIL) + +#define MKTC_MCISTATE_NOT_CLEARED 0xAD002F00 +MKTC_ST(MKTC_MCISTATE_NOT_CLEARED) + +#define MKTC_LOWERED_TO_PDS_THRESHOLD 0xAD003000 +MKTC_ST(MKTC_LOWERED_TO_PDS_THRESHOLD) +#define MKTC_REDUCE_MAX_VTX_PARTITIONS 0xAD003001 +MKTC_ST(MKTC_REDUCE_MAX_VTX_PARTITIONS) +#define MKTC_KTAOVERRIDE_MAX_VTX_PARTITIONS 0xAD003002 +MKTC_ST(MKTC_KTAOVERRIDE_MAX_VTX_PARTITIONS) +#define MKTC_KTANOOVERRIDE_MAX_VTX_PARTITIONS 0xAD003003 +MKTC_ST(MKTC_KTANOOVERRIDE_MAX_VTX_PARTITIONS) + +#define MKTC_IPRB_NORENDERDETAILS 0xAD003010 +MKTC_ST(MKTC_IPRB_NORENDERDETAILS) +#define MKTC_IPRB_HAVERENDERDETAILS 0xAD003011 +MKTC_ST(MKTC_IPRB_HAVERENDERDETAILS) + +#define MKTC_RENDER_OUT_OF_ORDER 0xAD003020 +MKTC_ST(MKTC_RENDER_OUT_OF_ORDER) +#define MKTC_RENDER_NOT_OUT_OF_ORDER 0xAD003021 +MKTC_ST(MKTC_RENDER_NOT_OUT_OF_ORDER) + +#define MKTC_ZLS_IDLE_BEGIN 0xAD003030 +MKTC_ST(MKTC_ZLS_IDLE_BEGIN) +#define MKTC_ZLS_ISP_CLK_GATING_EN 0xAD003031 +MKTC_ST(MKTC_ZLS_ISP_CLK_GATING_EN) +#define MKTC_ZLS_IDLE_END 0xAD003032 +MKTC_ST(MKTC_ZLS_IDLE_END) + +#endif /* __SGX_UKERNEL_STATUS_CODES_H__ */ + +/****************************************************************************** + End of file (sgx_ukernel_status_codes.h) +******************************************************************************/ diff --git a/drivers/gpu/pvr/sgxapi_km.h b/drivers/gpu/pvr/sgxapi_km.h index bd8dcb0..eaf45eb 100644 --- a/drivers/gpu/pvr/sgxapi_km.h +++ b/drivers/gpu/pvr/sgxapi_km.h @@ -102,9 +102,11 @@ extern "C" { /* note: there is implicitly 1 3D Dst Sync */ #else /* sync info structure array size */ -#define SGX_MAX_SRC_SYNCS 8 -#define SGX_MAX_DST_SYNCS 1 +#define SGX_MAX_SRC_SYNCS_TA 8 +#define SGX_MAX_DST_SYNCS_TA 1 /* note: there is implicitly 1 3D Dst Sync */ +#define SGX_MAX_SRC_SYNCS_TQ 8 +#define SGX_MAX_DST_SYNCS_TQ 1 #endif diff --git a/drivers/gpu/pvr/sgxerrata.h b/drivers/gpu/pvr/sgxerrata.h index 8a3632f..05fd45f 100644 --- a/drivers/gpu/pvr/sgxerrata.h +++ b/drivers/gpu/pvr/sgxerrata.h @@ -39,7 +39,6 @@ #define FIX_HW_BRN_28889 #else #if SGX_CORE_REV == 111 - #define FIX_HW_BRN_28889 #else #if SGX_CORE_REV == SGX_CORE_REV_HEAD @@ -267,7 +266,7 @@ #define FIX_HW_BRN_31272 #define FIX_HW_BRN_31278 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31620 #define FIX_HW_BRN_31780 @@ -277,6 +276,7 @@ #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33920 + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 122 #define FIX_HW_BRN_29954 @@ -287,7 +287,7 @@ #define FIX_HW_BRN_31272 #define FIX_HW_BRN_31278 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31620 #define FIX_HW_BRN_31780 @@ -298,6 +298,7 @@ #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33920 + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 1221 @@ -306,7 +307,7 @@ #define FIX_HW_BRN_31272 #define FIX_HW_BRN_31278 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31542 #define FIX_HW_BRN_31671 @@ -317,6 +318,7 @@ #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33920 + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 140 @@ -327,7 +329,7 @@ #define FIX_HW_BRN_31272 #define FIX_HW_BRN_31278 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31620 #define FIX_HW_BRN_31780 @@ -338,6 +340,7 @@ #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) #define FIX_HW_BRN_33657 #endif + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 1401 @@ -347,7 +350,7 @@ #define FIX_HW_BRN_31272 #define FIX_HW_BRN_31278 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31620 #define FIX_HW_BRN_31542 @@ -358,30 +361,33 @@ #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) #define FIX_HW_BRN_33657 #endif + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 141 #define FIX_HW_BRN_29954 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31671 #define FIX_HW_BRN_31780 #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) #define FIX_HW_BRN_33657 #endif + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 142 #define FIX_HW_BRN_29954 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31671 #define FIX_HW_BRN_31780 #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) #define FIX_HW_BRN_33657 #endif + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 211 @@ -390,7 +396,7 @@ #define FIX_HW_BRN_31272 #define FIX_HW_BRN_31278 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31620 #define FIX_HW_BRN_31780 @@ -401,6 +407,7 @@ #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33920 + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 2111 @@ -410,7 +417,7 @@ #define FIX_HW_BRN_31272 #define FIX_HW_BRN_31278 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31620 #define FIX_HW_BRN_31780 @@ -421,12 +428,13 @@ #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33920 + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 213 #define FIX_HW_BRN_31272 - #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #if defined(SGX_FEATURE_MP) + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31671 #define FIX_HW_BRN_31780 @@ -435,12 +443,14 @@ #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33920 + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 216 #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) #define FIX_HW_BRN_33657 #endif + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 302 #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) @@ -486,14 +496,14 @@ #if SGX_CORE_REV == 100 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #else #if SGX_CORE_REV == 102 #define FIX_HW_BRN_29954 #define FIX_HW_BRN_31272 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31780 #define FIX_HW_BRN_32085 @@ -501,12 +511,15 @@ #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33920 + #if defined(SGX_FEATURE_MP) + #define FIX_HW_BRN_36513 + #endif #else #if SGX_CORE_REV == 103 #define FIX_HW_BRN_29954 #define FIX_HW_BRN_31272 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31780 #define FIX_HW_BRN_32085 @@ -514,6 +527,7 @@ #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33920 + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 104 #define FIX_HW_BRN_29954 @@ -522,7 +536,7 @@ #define FIX_HW_BRN_31272 #define FIX_HW_BRN_31278 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31542 #define FIX_HW_BRN_31620 @@ -534,29 +548,28 @@ #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33920 + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 105 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31780 #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33920 + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == 106 #define FIX_HW_BRN_31272 #define FIX_HW_BRN_31780 - #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_33657 - #endif #define FIX_HW_BRN_33920 #else #if SGX_CORE_REV == 110 #define FIX_HW_BRN_31272 #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 + #define FIX_HW_BRN_31559 #endif #define FIX_HW_BRN_31780 #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) @@ -569,31 +582,28 @@ #define FIX_HW_BRN_33920 #else #if SGX_CORE_REV == 114 - #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 - #endif #define FIX_HW_BRN_31780 #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) #define FIX_HW_BRN_33657 #endif #else #if SGX_CORE_REV == 115 - #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 - #endif #define FIX_HW_BRN_31780 #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) #define FIX_HW_BRN_33657 #endif + #if defined(SGX_FEATURE_MP) + #if SGX_FEATURE_MP_CORE_COUNT > 1 + #define FIX_HW_BRN_36513 + #endif + #endif #else #if SGX_CORE_REV == 116 - #if defined(SGX_FEATURE_MP) - #define FIX_HW_BRN_31425 - #endif #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) #define FIX_HW_BRN_33657 #endif #define FIX_HW_BRN_33809 + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == SGX_CORE_REV_HEAD #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) @@ -677,6 +687,7 @@ #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) && defined(SGX_FEATURE_MP) #define FIX_HW_BRN_33657 #endif + #define FIX_HW_BRN_36513 #else #if SGX_CORE_REV == SGX_CORE_REV_HEAD diff --git a/drivers/gpu/pvr/sgxfeaturedefs.h b/drivers/gpu/pvr/sgxfeaturedefs.h index 2553022..0679671 100644 --- a/drivers/gpu/pvr/sgxfeaturedefs.h +++ b/drivers/gpu/pvr/sgxfeaturedefs.h @@ -28,18 +28,21 @@ #define SGX_CORE_FRIENDLY_NAME "SGX520" #define SGX_CORE_ID SGX_CORE_ID_520 #define SGX_FEATURE_ADDRESS_SPACE_SIZE (28) + #define SGX_FEATURE_NUM_USE_PIPES (1) #define SGX_FEATURE_AUTOCLOCKGATING #else #if defined(SGX530) #define SGX_CORE_FRIENDLY_NAME "SGX530" #define SGX_CORE_ID SGX_CORE_ID_530 #define SGX_FEATURE_ADDRESS_SPACE_SIZE (28) + #define SGX_FEATURE_NUM_USE_PIPES (2) #define SGX_FEATURE_AUTOCLOCKGATING #else #if defined(SGX531) #define SGX_CORE_FRIENDLY_NAME "SGX531" #define SGX_CORE_ID SGX_CORE_ID_531 #define SGX_FEATURE_ADDRESS_SPACE_SIZE (28) + #define SGX_FEATURE_NUM_USE_PIPES (2) #define SGX_FEATURE_AUTOCLOCKGATING #define SGX_FEATURE_MULTI_EVENT_KICK #else @@ -50,6 +53,7 @@ #define SGX_FEATURE_MULTIPLE_MEM_CONTEXTS #define SGX_FEATURE_BIF_NUM_DIRLISTS (16) #define SGX_FEATURE_2D_HARDWARE + #define SGX_FEATURE_NUM_USE_PIPES (2) #define SGX_FEATURE_AUTOCLOCKGATING #define SUPPORT_SGX_GENERAL_MAPPING_HEAP #define SGX_FEATURE_EDM_VERTEX_PDSADDR_FULL_RANGE @@ -58,6 +62,7 @@ #define SGX_CORE_FRIENDLY_NAME "SGX540" #define SGX_CORE_ID SGX_CORE_ID_540 #define SGX_FEATURE_ADDRESS_SPACE_SIZE (28) + #define SGX_FEATURE_NUM_USE_PIPES (4) #define SGX_FEATURE_AUTOCLOCKGATING #define SGX_FEATURE_MULTI_EVENT_KICK #else @@ -69,6 +74,7 @@ #define SGX_FEATURE_ADDRESS_SPACE_SIZE (32) #define SGX_FEATURE_MULTIPLE_MEM_CONTEXTS #define SGX_FEATURE_BIF_NUM_DIRLISTS (8) + #define SGX_FEATURE_NUM_USE_PIPES (4) #define SGX_FEATURE_AUTOCLOCKGATING #define SGX_FEATURE_MONOLITHIC_UKERNEL #define SGX_FEATURE_MULTI_EVENT_KICK @@ -95,6 +101,7 @@ #define SGX_FEATURE_ADDRESS_SPACE_SIZE (32) #define SGX_FEATURE_MULTIPLE_MEM_CONTEXTS #define SGX_FEATURE_BIF_NUM_DIRLISTS (8) + #define SGX_FEATURE_NUM_USE_PIPES (4) #define SGX_FEATURE_AUTOCLOCKGATING #define SGX_FEATURE_MONOLITHIC_UKERNEL #define SGX_FEATURE_MULTI_EVENT_KICK @@ -103,8 +110,8 @@ #if defined(SUPPORT_SGX_LOW_LATENCY_SCHEDULING) #if defined(SGX_FEATURE_MP) #define SGX_FEATURE_MASTER_VDM_CONTEXT_SWITCH - #endif #define SGX_FEATURE_SLAVE_VDM_CONTEXT_SWITCH + #endif #define SGX_FEATURE_SW_ISP_CONTEXT_SWITCH #endif #else @@ -146,6 +153,7 @@ #define SGX_FEATURE_ADDRESS_SPACE_SIZE (32) #define SGX_FEATURE_MULTIPLE_MEM_CONTEXTS #define SGX_FEATURE_BIF_NUM_DIRLISTS (8) + #define SGX_FEATURE_NUM_USE_PIPES (8) #define SGX_FEATURE_AUTOCLOCKGATING #define SGX_FEATURE_MONOLITHIC_UKERNEL #define SGX_FEATURE_MULTI_EVENT_KICK diff --git a/drivers/gpu/pvr/sgxinfo.h b/drivers/gpu/pvr/sgxinfo.h index c32dc31..dec8577 100644 --- a/drivers/gpu/pvr/sgxinfo.h +++ b/drivers/gpu/pvr/sgxinfo.h @@ -135,7 +135,7 @@ typedef struct _SGX_BRIDGE_INIT_INFO_ #endif #endif -#if defined(FIX_HW_BRN_31542) +#if defined(FIX_HW_BRN_31542) || defined(FIX_HW_BRN_36513) #if defined (SUPPORT_SID_INTERFACE) IMG_SID hKernelClearClipWAVDMStreamMemInfo; IMG_SID hKernelClearClipWAIndexStreamMemInfo; @@ -157,7 +157,7 @@ typedef struct _SGX_BRIDGE_INIT_INFO_ #endif #endif -#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31425) +#if defined(SGX_FEATURE_VDM_CONTEXT_SWITCH) && defined(FIX_HW_BRN_31559) IMG_HANDLE hKernelVDMSnapShotBufferMemInfo; IMG_HANDLE hKernelVDMCtrlStreamBufferMemInfo; #endif @@ -172,13 +172,6 @@ typedef struct _SGX_BRIDGE_INIT_INFO_ IMG_HANDLE hKernelEDMStatusBufferMemInfo; #endif #endif -#if defined(SGX_FEATURE_OVERLAPPED_SPM) -#if defined (SUPPORT_SID_INTERFACE) - IMG_SID hKernelTmpRgnHeaderMemInfo; -#else - IMG_HANDLE hKernelTmpRgnHeaderMemInfo; -#endif -#endif IMG_UINT32 ui32EDMTaskReg0; IMG_UINT32 ui32EDMTaskReg1; @@ -315,9 +308,9 @@ typedef struct _SGX_CCB_KICK_ IMG_UINT32 ui32NumSrcSyncs; #if defined (SUPPORT_SID_INTERFACE) - IMG_SID ahSrcKernelSyncInfo[SGX_MAX_SRC_SYNCS]; + IMG_SID ahSrcKernelSyncInfo[SGX_MAX_SRC_SYNCS_TA]; #else - IMG_HANDLE ahSrcKernelSyncInfo[SGX_MAX_SRC_SYNCS]; + IMG_HANDLE ahSrcKernelSyncInfo[SGX_MAX_SRC_SYNCS_TA]; #endif #endif diff --git a/drivers/gpu/pvr/ttrace_common.h b/drivers/gpu/pvr/ttrace_common.h index 5895b6c..5aa6fec 100644 --- a/drivers/gpu/pvr/ttrace_common.h +++ b/drivers/gpu/pvr/ttrace_common.h @@ -76,6 +76,9 @@ #define PVRSRV_TRACE_SYNC_WO_DEV_VADDR 5 #define PVRSRV_TRACE_SYNC_RO_DEV_VADDR 6 #define PVRSRV_TRACE_SYNC_OP 7 -#define PVRSRV_TRACE_TYPE_SYNC_SIZE ((PVRSRV_TRACE_SYNC_OP + 1) * sizeof(IMG_UINT32)) + #define PVRSRV_TRACE_SYNC_RO2P 8 + #define PVRSRV_TRACE_SYNC_RO2C 9 + #define PVRSRV_TRACE_SYNC_RO2_DEV_VADDR 10 +#define PVRSRV_TRACE_TYPE_SYNC_SIZE ((PVRSRV_TRACE_SYNC_RO2_DEV_VADDR + 1) * sizeof(IMG_UINT32)) #endif diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 5c5f9db..6288d7d 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -45,6 +45,7 @@ struct evdev_client { unsigned int packet_head; /* [future] position of the first element of next packet */ spinlock_t buffer_lock; /* protects access to buffer, head and tail */ struct wake_lock wake_lock; + bool use_wake_lock; char name[28]; struct fasync_struct *fasync; struct evdev *evdev; @@ -62,7 +63,6 @@ static void evdev_pass_event(struct evdev_client *client, /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); - wake_lock_timeout(&client->wake_lock, 5 * HZ); client->buffer[client->head++] = *event; client->head &= client->bufsize - 1; @@ -79,10 +79,14 @@ static void evdev_pass_event(struct evdev_client *client, client->buffer[client->tail].value = 0; client->packet_head = client->tail; + if (client->use_wake_lock) + wake_unlock(&client->wake_lock); } if (event->type == EV_SYN && event->code == SYN_REPORT) { client->packet_head = client->head; + if (client->use_wake_lock) + wake_lock(&client->wake_lock); kill_fasync(&client->fasync, SIGIO, POLL_IN); } @@ -262,7 +266,8 @@ static int evdev_release(struct inode *inode, struct file *file) mutex_unlock(&evdev->mutex); evdev_detach_client(evdev, client); - wake_lock_destroy(&client->wake_lock); + if (client->use_wake_lock) + wake_lock_destroy(&client->wake_lock); kfree(client); evdev_close_device(evdev); @@ -316,7 +321,6 @@ static int evdev_open(struct inode *inode, struct file *file) spin_lock_init(&client->buffer_lock); snprintf(client->name, sizeof(client->name), "%s-%d", dev_name(&evdev->dev), task_tgid_vnr(current)); - wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name); client->evdev = evdev; evdev_attach_client(evdev, client); @@ -331,7 +335,6 @@ static int evdev_open(struct inode *inode, struct file *file) err_free_client: evdev_detach_client(evdev, client); - wake_lock_destroy(&client->wake_lock); kfree(client); err_put_evdev: put_device(&evdev->dev); @@ -385,7 +388,8 @@ static int evdev_fetch_next_event(struct evdev_client *client, if (have_event) { *event = client->buffer[client->tail++]; client->tail &= client->bufsize - 1; - if (client->head == client->tail) + if (client->use_wake_lock && + client->packet_head == client->tail) wake_unlock(&client->wake_lock); } @@ -400,7 +404,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; - int retval; + int retval = 0; if (count < input_event_size()) return -EINVAL; @@ -635,6 +639,35 @@ static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) return input_set_keycode(dev, &ke); } +static int evdev_enable_suspend_block(struct evdev *evdev, + struct evdev_client *client) +{ + if (client->use_wake_lock) + return 0; + + spin_lock_irq(&client->buffer_lock); + wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name); + client->use_wake_lock = true; + if (client->packet_head != client->tail) + wake_lock(&client->wake_lock); + spin_unlock_irq(&client->buffer_lock); + return 0; +} + +static int evdev_disable_suspend_block(struct evdev *evdev, + struct evdev_client *client) +{ + if (!client->use_wake_lock) + return 0; + + spin_lock_irq(&client->buffer_lock); + client->use_wake_lock = false; + wake_lock_destroy(&client->wake_lock); + spin_unlock_irq(&client->buffer_lock); + + return 0; +} + static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { @@ -708,6 +741,15 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, case EVIOCSKEYCODE_V2: return evdev_handle_set_keycode_v2(dev, p); + + case EVIOCGSUSPENDBLOCK: + return put_user(client->use_wake_lock, ip); + + case EVIOCSSUSPENDBLOCK: + if (p) + return evdev_enable_suspend_block(evdev, client); + else + return evdev_disable_suspend_block(evdev, client); } size = _IOC_SIZE(cmd); diff --git a/drivers/media/video/samsung/fimc/fimc.h b/drivers/media/video/samsung/fimc/fimc.h index 3e61a98..de137c2 100644 --- a/drivers/media/video/samsung/fimc/fimc.h +++ b/drivers/media/video/samsung/fimc/fimc.h @@ -597,6 +597,7 @@ extern int fimc_hwset_enable_irq(struct fimc_control *ctrl, extern int fimc_hwset_disable_irq(struct fimc_control *ctrl); extern int fimc_hwset_clear_irq(struct fimc_control *ctrl); extern int fimc_hwset_reset(struct fimc_control *ctrl); +extern int fimc_hwset_sw_reset(struct fimc_control *ctrl); extern int fimc_hwset_clksrc(struct fimc_control *ctrl, int src_clk); extern int fimc_hwget_overflow_state(struct fimc_control *ctrl); extern int fimc_hwset_camera_offset(struct fimc_control *ctrl); diff --git a/drivers/media/video/samsung/fimc/fimc_capture.c b/drivers/media/video/samsung/fimc/fimc_capture.c index 2367bf6..2028bf9 100644 --- a/drivers/media/video/samsung/fimc/fimc_capture.c +++ b/drivers/media/video/samsung/fimc/fimc_capture.c @@ -690,6 +690,12 @@ int fimc_enum_fmt_vid_capture(struct file *file, void *fh, fimc_dbg("%s\n", __func__); + if (ctrl->out) { + fimc_err("%s: fimc is already used for output mode\n", + __func__); + return -EINVAL; + } + if (!ctrl->cam || !ctrl->cam->sd) { fimc_err("%s: No capture device.\n", __func__); return -ENODEV; diff --git a/drivers/media/video/samsung/fimc/fimc_dev.c b/drivers/media/video/samsung/fimc/fimc_dev.c index 39bb530..17945d4 100644 --- a/drivers/media/video/samsung/fimc/fimc_dev.c +++ b/drivers/media/video/samsung/fimc/fimc_dev.c @@ -364,11 +364,19 @@ static inline void fimc_irq_out(struct fimc_control *ctrl) struct fimc_ctx *ctx; u32 wakeup = 1; int ctx_num = ctrl->out->idxs.active.ctx; - ctx = &ctrl->out->ctx[ctx_num]; /* Interrupt pendding clear */ fimc_hwset_clear_irq(ctrl); + /* check context num */ + if (ctx_num < 0 || ctx_num >= FIMC_MAX_CTXS) { + fimc_err("fimc_irq_out: invalid ctx (ctx=%d)\n", ctx_num); + wake_up(&ctrl->wq); + return; + } + + ctx = &ctrl->out->ctx[ctx_num]; + switch (ctx->overlay.mode) { case FIMC_OVLY_NONE_SINGLE_BUF: wakeup = fimc_irq_out_single_buf(ctrl, ctx); @@ -381,6 +389,8 @@ static inline void fimc_irq_out(struct fimc_control *ctrl) wakeup = fimc_irq_out_dma(ctrl, ctx); break; default: + fimc_err("[ctx=%d] fimc_irq_out: wrong overlay.mode (%d)\n", + ctx_num, ctx->overlay.mode); break; } @@ -1503,13 +1513,12 @@ int fimc_suspend(struct platform_device *pdev, pm_message_t state) if (ctrl->out) fimc_suspend_out(ctrl); - else if (ctrl->cap) fimc_suspend_cap(ctrl); else ctrl->status = FIMC_OFF_SLEEP; - if (atomic_read(&ctrl->in_use)) + if (atomic_read(&ctrl->in_use) && ctrl->status != FIMC_OFF_SLEEP) fimc_clk_en(ctrl, false); return 0; @@ -1626,12 +1635,11 @@ int fimc_resume(struct platform_device *pdev) in_use = atomic_read(&ctrl->in_use); - if (in_use) + if (in_use && ctrl->status != FIMC_OFF_SLEEP) fimc_clk_en(ctrl, true); if (ctrl->out) fimc_resume_out(ctrl); - else if (ctrl->cap) fimc_resume_cap(ctrl); else diff --git a/drivers/media/video/samsung/fimc/fimc_output.c b/drivers/media/video/samsung/fimc/fimc_output.c index c66f995..2269d6a 100644 --- a/drivers/media/video/samsung/fimc/fimc_output.c +++ b/drivers/media/video/samsung/fimc/fimc_output.c @@ -79,6 +79,7 @@ static int fimc_outdev_stop_camif(void *param) fimc_hwset_disable_autoload(ctrl); fimc_hwset_stop_scaler(ctrl); fimc_hwset_disable_capture(ctrl); + fimc_hwset_sw_reset(ctrl); fimc_clk_en(ctrl, false); return 0; @@ -1851,18 +1852,18 @@ static int fimc_qbuf_output_single_buf(struct fimc_control *ctrl, return -EINVAL; } - ret = fimc_outdev_start_camif(ctrl); - if (ret < 0) { - fimc_err("Fail: fimc_start_camif\n"); - return -EINVAL; - } - ctrl->out->idxs.active.idx = idx; ctrl->out->idxs.active.ctx = ctx->ctx_num; ctrl->status = FIMC_STREAMON; ctx->status = FIMC_STREAMON; + ret = fimc_outdev_start_camif(ctrl); + if (ret < 0) { + fimc_err("Fail: fimc_start_camif\n"); + return -EINVAL; + } + return 0; } @@ -2112,6 +2113,7 @@ int fimc_dqbuf_output(void *fh, struct v4l2_buffer *b) if (ret == 0) { fimc_dump_context(ctrl, ctx); fimc_err("[0] out_queue is empty\n"); + ctx->status = FIMC_STREAMON_IDLE; return -EAGAIN; } else if (ret == -ERESTARTSYS) { fimc_print_signal(ctrl); @@ -2158,6 +2160,12 @@ int fimc_g_fmt_vid_out(struct file *filp, void *fh, struct v4l2_format *f) fimc_info1("%s: called\n", __func__); + if (ctrl->cap) { + fimc_err("%s: fimc is already used for capture mode\n", + __func__); + return -EINVAL; + } + if (!out) { out = kzalloc(sizeof(*out), GFP_KERNEL); if (!out) { diff --git a/drivers/media/video/samsung/fimc/fimc_regs.c b/drivers/media/video/samsung/fimc/fimc_regs.c index b360f1a..227945a 100644 --- a/drivers/media/video/samsung/fimc/fimc_regs.c +++ b/drivers/media/video/samsung/fimc/fimc_regs.c @@ -294,6 +294,22 @@ int fimc_hwset_reset(struct fimc_control *ctrl) return 0; } +int fimc_hwset_sw_reset(struct fimc_control *ctrl) +{ + u32 cfg = 0; + + /* s/w reset */ + cfg = readl(ctrl->regs + S3C_CIGCTRL); + cfg |= (S3C_CIGCTRL_SWRST); + writel(cfg, ctrl->regs + S3C_CIGCTRL); + + cfg = readl(ctrl->regs + S3C_CIGCTRL); + cfg &= ~S3C_CIGCTRL_SWRST; + writel(cfg, ctrl->regs + S3C_CIGCTRL); + + return 0; +} + int fimc_hwset_clksrc(struct fimc_control *ctrl, int src_clk) { u32 cfg = readl(ctrl->regs + S3C_MISC_FIMC); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 965195e..849bd4d 100755 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -61,25 +61,6 @@ config AD525X_DPOT_SPI To compile this driver as a module, choose M here: the module will be called ad525x_dpot-spi. -config ANDROID_PMEM - bool "Android pmem allocator" - default y - -if ANDROID_PMEM - comment "Reserved memory configurations" -config ANDROID_PMEM_MEMSIZE_PMEM - int "Memory size in kbytes for android surface using pmem" - default "8192" - -config ANDROID_PMEM_MEMSIZE_PMEM_GPU1 - int "Memory size in kbytes for android surface using pmem_gpu1" - default "3072" - -config ANDROID_PMEM_MEMSIZE_PMEM_ADSP - int "Memory size in kbytes for android surface using pmem_adsp" - default "6144" -endif - config ATMEL_PWM tristate "Atmel AT32/AT91 PWM support" depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 12dc327..77a764b 100755 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_PHANTOM) += phantom.o obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o -obj-$(CONFIG_ANDROID_PMEM) += pmem.o obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o diff --git a/drivers/misc/fsa9480.c b/drivers/misc/fsa9480.c index 2956cbf..85aefd5 100755 --- a/drivers/misc/fsa9480.c +++ b/drivers/misc/fsa9480.c @@ -555,12 +555,22 @@ static int fsa9480_resume(struct i2c_client *client) { struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client); + if (client->irq) + enable_irq(client->irq); /* device detection */ fsa9480_detect_dev(usbsw); return 0; } +static int fsa9480_suspend(struct i2c_client *client, pm_message_t state) +{ + if (client->irq) + disable_irq(client->irq); + + return 0; +} + #else #define fsa9480_suspend NULL @@ -581,6 +591,7 @@ static struct i2c_driver fsa9480_i2c_driver = { .probe = fsa9480_probe, .remove = __devexit_p(fsa9480_remove), .resume = fsa9480_resume, + .suspend = fsa9480_suspend, .id_table = fsa9480_id, }; diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c deleted file mode 100644 index abb73c1..0000000 --- a/drivers/misc/pmem.c +++ /dev/null @@ -1,1345 +0,0 @@ -/* drivers/android/pmem.c - * - * Copyright (C) 2007 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - * - */ - -#include <linux/miscdevice.h> -#include <linux/platform_device.h> -#include <linux/fs.h> -#include <linux/file.h> -#include <linux/mm.h> -#include <linux/list.h> -#include <linux/mutex.h> -#include <linux/debugfs.h> -#include <linux/android_pmem.h> -#include <linux/mempolicy.h> -#include <linux/sched.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <asm/cacheflush.h> - -#define PMEM_MAX_DEVICES 10 -#define PMEM_MAX_ORDER 128 -#define PMEM_MIN_ALLOC PAGE_SIZE - -#define PMEM_DEBUG 1 - -/* indicates that a refernce to this file has been taken via get_pmem_file, - * the file should not be released until put_pmem_file is called */ -#define PMEM_FLAGS_BUSY 0x1 -/* indicates that this is a suballocation of a larger master range */ -#define PMEM_FLAGS_CONNECTED 0x1 << 1 -/* indicates this is a master and not a sub allocation and that it is mmaped */ -#define PMEM_FLAGS_MASTERMAP 0x1 << 2 -/* submap and unsubmap flags indicate: - * 00: subregion has never been mmaped - * 10: subregion has been mmaped, reference to the mm was taken - * 11: subretion has ben released, refernece to the mm still held - * 01: subretion has been released, reference to the mm has been released - */ -#define PMEM_FLAGS_SUBMAP 0x1 << 3 -#define PMEM_FLAGS_UNSUBMAP 0x1 << 4 - - -struct pmem_data { - /* in alloc mode: an index into the bitmap - * in no_alloc mode: the size of the allocation */ - int index; - /* see flags above for descriptions */ - unsigned int flags; - /* protects this data field, if the mm_mmap sem will be held at the - * same time as this sem, the mm sem must be taken first (as this is - * the order for vma_open and vma_close ops */ - struct rw_semaphore sem; - /* info about the mmaping process */ - struct vm_area_struct *vma; - /* task struct of the mapping process */ - struct task_struct *task; - /* process id of teh mapping process */ - pid_t pid; - /* file descriptor of the master */ - int master_fd; - /* file struct of the master */ - struct file *master_file; - /* a list of currently available regions if this is a suballocation */ - struct list_head region_list; - /* a linked list of data so we can access them for debugging */ - struct list_head list; -#if PMEM_DEBUG - int ref; -#endif -}; - -struct pmem_bits { - unsigned allocated:1; /* 1 if allocated, 0 if free */ - unsigned order:7; /* size of the region in pmem space */ -}; - -struct pmem_region_node { - struct pmem_region region; - struct list_head list; -}; - -#define PMEM_DEBUG_MSGS 0 -#if PMEM_DEBUG_MSGS -#define DLOG(fmt,args...) \ - do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \ - ##args); } \ - while (0) -#else -#define DLOG(x...) do {} while (0) -#endif - -struct pmem_info { - struct miscdevice dev; - /* physical start address of the remaped pmem space */ - unsigned long base; - /* vitual start address of the remaped pmem space */ - unsigned char __iomem *vbase; - /* total size of the pmem space */ - unsigned long size; - /* number of entries in the pmem space */ - unsigned long num_entries; - /* pfn of the garbage page in memory */ - unsigned long garbage_pfn; - /* index of the garbage page in the pmem space */ - int garbage_index; - /* the bitmap for the region indicating which entries are allocated - * and which are free */ - struct pmem_bits *bitmap; - /* indicates the region should not be managed with an allocator */ - unsigned no_allocator; - /* indicates maps of this region should be cached, if a mix of - * cached and uncached is desired, set this and open the device with - * O_SYNC to get an uncached region */ - unsigned cached; - unsigned buffered; - /* in no_allocator mode the first mapper gets the whole space and sets - * this flag */ - unsigned allocated; - /* for debugging, creates a list of pmem file structs, the - * data_list_lock should be taken before pmem_data->sem if both are - * needed */ - struct mutex data_list_lock; - struct list_head data_list; - /* pmem_sem protects the bitmap array - * a write lock should be held when modifying entries in bitmap - * a read lock should be held when reading data from bits or - * dereferencing a pointer into bitmap - * - * pmem_data->sem protects the pmem data of a particular file - * Many of the function that require the pmem_data->sem have a non- - * locking version for when the caller is already holding that sem. - * - * IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER: - * down(pmem_data->sem) => down(bitmap_sem) - */ - struct rw_semaphore bitmap_sem; - - long (*ioctl)(struct file *, unsigned int, unsigned long); - int (*release)(struct inode *, struct file *); -}; - -static struct pmem_info pmem[PMEM_MAX_DEVICES]; -static int id_count; - -#define PMEM_IS_FREE(id, index) !(pmem[id].bitmap[index].allocated) -#define PMEM_ORDER(id, index) pmem[id].bitmap[index].order -#define PMEM_BUDDY_INDEX(id, index) (index ^ (1 << PMEM_ORDER(id, index))) -#define PMEM_NEXT_INDEX(id, index) (index + (1 << PMEM_ORDER(id, index))) -#define PMEM_OFFSET(index) (index * PMEM_MIN_ALLOC) -#define PMEM_START_ADDR(id, index) (PMEM_OFFSET(index) + pmem[id].base) -#define PMEM_LEN(id, index) ((1 << PMEM_ORDER(id, index)) * PMEM_MIN_ALLOC) -#define PMEM_END_ADDR(id, index) (PMEM_START_ADDR(id, index) + \ - PMEM_LEN(id, index)) -#define PMEM_START_VADDR(id, index) (PMEM_OFFSET(id, index) + pmem[id].vbase) -#define PMEM_END_VADDR(id, index) (PMEM_START_VADDR(id, index) + \ - PMEM_LEN(id, index)) -#define PMEM_REVOKED(data) (data->flags & PMEM_FLAGS_REVOKED) -#define PMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK))) -#define PMEM_IS_SUBMAP(data) ((data->flags & PMEM_FLAGS_SUBMAP) && \ - (!(data->flags & PMEM_FLAGS_UNSUBMAP))) - -static int pmem_release(struct inode *, struct file *); -static int pmem_mmap(struct file *, struct vm_area_struct *); -static int pmem_open(struct inode *, struct file *); -static long pmem_ioctl(struct file *, unsigned int, unsigned long); - -struct file_operations pmem_fops = { - .release = pmem_release, - .mmap = pmem_mmap, - .open = pmem_open, - .unlocked_ioctl = pmem_ioctl, -}; - -static int get_id(struct file *file) -{ - return MINOR(file->f_dentry->d_inode->i_rdev); -} - -int is_pmem_file(struct file *file) -{ - int id; - - if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode)) - return 0; - id = get_id(file); - if (unlikely(id >= PMEM_MAX_DEVICES)) - return 0; - if (unlikely(file->f_dentry->d_inode->i_rdev != - MKDEV(MISC_MAJOR, pmem[id].dev.minor))) - return 0; - return 1; -} - -static int has_allocation(struct file *file) -{ - struct pmem_data *data; - /* check is_pmem_file first if not accessed via pmem_file_ops */ - - if (unlikely(!file->private_data)) - return 0; - data = (struct pmem_data *)file->private_data; - if (unlikely(data->index < 0)) - return 0; - return 1; -} - -static int is_master_owner(struct file *file) -{ - struct file *master_file; - struct pmem_data *data; - int put_needed, ret = 0; - - if (!is_pmem_file(file) || !has_allocation(file)) - return 0; - data = (struct pmem_data *)file->private_data; - if (PMEM_FLAGS_MASTERMAP & data->flags) - return 1; - master_file = fget_light(data->master_fd, &put_needed); - if (master_file && data->master_file == master_file) - ret = 1; - fput_light(master_file, put_needed); - return ret; -} - -static int pmem_free(int id, int index) -{ - /* caller should hold the write lock on pmem_sem! */ - int buddy, curr = index; - DLOG("index %d\n", index); - - if (pmem[id].no_allocator) { - pmem[id].allocated = 0; - return 0; - } - /* clean up the bitmap, merging any buddies */ - pmem[id].bitmap[curr].allocated = 0; - /* find a slots buddy Buddy# = Slot# ^ (1 << order) - * if the buddy is also free merge them - * repeat until the buddy is not free or end of the bitmap is reached - */ - do { - buddy = PMEM_BUDDY_INDEX(id, curr); - if (PMEM_IS_FREE(id, buddy) && - PMEM_ORDER(id, buddy) == PMEM_ORDER(id, curr)) { - PMEM_ORDER(id, buddy)++; - PMEM_ORDER(id, curr)++; - curr = min(buddy, curr); - } else { - break; - } - } while (curr < pmem[id].num_entries); - - return 0; -} - -static void pmem_revoke(struct file *file, struct pmem_data *data); - -static int pmem_release(struct inode *inode, struct file *file) -{ - struct pmem_data *data = (struct pmem_data *)file->private_data; - struct pmem_region_node *region_node; - struct list_head *elt, *elt2; - int id = get_id(file), ret = 0; - - - mutex_lock(&pmem[id].data_list_lock); - /* if this file is a master, revoke all the memory in the connected - * files */ - if (PMEM_FLAGS_MASTERMAP & data->flags) { - struct pmem_data *sub_data; - list_for_each(elt, &pmem[id].data_list) { - sub_data = list_entry(elt, struct pmem_data, list); - down_read(&sub_data->sem); - if (PMEM_IS_SUBMAP(sub_data) && - file == sub_data->master_file) { - up_read(&sub_data->sem); - pmem_revoke(file, sub_data); - } else - up_read(&sub_data->sem); - } - } - list_del(&data->list); - mutex_unlock(&pmem[id].data_list_lock); - - - down_write(&data->sem); - - /* if its not a conencted file and it has an allocation, free it */ - if (!(PMEM_FLAGS_CONNECTED & data->flags) && has_allocation(file)) { - down_write(&pmem[id].bitmap_sem); - ret = pmem_free(id, data->index); - up_write(&pmem[id].bitmap_sem); - } - - /* if this file is a submap (mapped, connected file), downref the - * task struct */ - if (PMEM_FLAGS_SUBMAP & data->flags) - if (data->task) { - put_task_struct(data->task); - data->task = NULL; - } - - file->private_data = NULL; - - list_for_each_safe(elt, elt2, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, list); - list_del(elt); - kfree(region_node); - } - BUG_ON(!list_empty(&data->region_list)); - - up_write(&data->sem); - kfree(data); - if (pmem[id].release) - ret = pmem[id].release(inode, file); - - return ret; -} - -static int pmem_open(struct inode *inode, struct file *file) -{ - struct pmem_data *data; - int id = get_id(file); - int ret = 0; - - DLOG("current %u file %p(%d)\n", current->pid, file, file_count(file)); - /* setup file->private_data to indicate its unmapped */ - /* you can only open a pmem device one time */ - if (file->private_data != NULL) - return -1; - data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL); - if (!data) { - printk("pmem: unable to allocate memory for pmem metadata."); - return -1; - } - data->flags = 0; - data->index = -1; - data->task = NULL; - data->vma = NULL; - data->pid = 0; - data->master_file = NULL; -#if PMEM_DEBUG - data->ref = 0; -#endif - INIT_LIST_HEAD(&data->region_list); - init_rwsem(&data->sem); - - file->private_data = data; - INIT_LIST_HEAD(&data->list); - - mutex_lock(&pmem[id].data_list_lock); - list_add(&data->list, &pmem[id].data_list); - mutex_unlock(&pmem[id].data_list_lock); - return ret; -} - -static unsigned long pmem_order(unsigned long len) -{ - int i; - - len = (len + PMEM_MIN_ALLOC - 1)/PMEM_MIN_ALLOC; - len--; - for (i = 0; i < sizeof(len)*8; i++) - if (len >> i == 0) - break; - return i; -} - -static int pmem_allocate(int id, unsigned long len) -{ - /* caller should hold the write lock on pmem_sem! */ - /* return the corresponding pdata[] entry */ - int curr = 0; - int end = pmem[id].num_entries; - int best_fit = -1; - unsigned long order = pmem_order(len); - - if (pmem[id].no_allocator) { - DLOG("no allocator"); - if ((len > pmem[id].size) || pmem[id].allocated) - return -1; - pmem[id].allocated = 1; - return len; - } - - if (order > PMEM_MAX_ORDER) - return -1; - DLOG("order %lx\n", order); - - /* look through the bitmap: - * if you find a free slot of the correct order use it - * otherwise, use the best fit (smallest with size > order) slot - */ - while (curr < end) { - if (PMEM_IS_FREE(id, curr)) { - if (PMEM_ORDER(id, curr) == (unsigned char)order) { - /* set the not free bit and clear others */ - best_fit = curr; - break; - } - if (PMEM_ORDER(id, curr) > (unsigned char)order && - (best_fit < 0 || - PMEM_ORDER(id, curr) < PMEM_ORDER(id, best_fit))) - best_fit = curr; - } - curr = PMEM_NEXT_INDEX(id, curr); - } - - /* if best_fit < 0, there are no suitable slots, - * return an error - */ - if (best_fit < 0) { - printk("pmem: no space left to allocate!\n"); - return -1; - } - - /* now partition the best fit: - * split the slot into 2 buddies of order - 1 - * repeat until the slot is of the correct order - */ - while (PMEM_ORDER(id, best_fit) > (unsigned char)order) { - int buddy; - PMEM_ORDER(id, best_fit) -= 1; - buddy = PMEM_BUDDY_INDEX(id, best_fit); - PMEM_ORDER(id, buddy) = PMEM_ORDER(id, best_fit); - } - pmem[id].bitmap[best_fit].allocated = 1; - return best_fit; -} - -static pgprot_t pmem_access_prot(struct file *file, pgprot_t vma_prot) -{ - int id = get_id(file); -#ifdef pgprot_noncached - if (pmem[id].cached == 0 || file->f_flags & O_SYNC) - return pgprot_noncached(vma_prot); -#endif -#ifdef pgprot_ext_buffered - else if (pmem[id].buffered) - return pgprot_ext_buffered(vma_prot); -#endif - return vma_prot; -} - -static unsigned long pmem_start_addr(int id, struct pmem_data *data) -{ - if (pmem[id].no_allocator) - return PMEM_START_ADDR(id, 0); - else - return PMEM_START_ADDR(id, data->index); - -} - -static void *pmem_start_vaddr(int id, struct pmem_data *data) -{ - return pmem_start_addr(id, data) - pmem[id].base + pmem[id].vbase; -} - -static unsigned long pmem_len(int id, struct pmem_data *data) -{ - if (pmem[id].no_allocator) - return data->index; - else - return PMEM_LEN(id, data->index); -} - -static int pmem_map_garbage(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - int i, garbage_pages = len >> PAGE_SHIFT; - - vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP | VM_SHARED | VM_WRITE; - for (i = 0; i < garbage_pages; i++) { - if (vm_insert_pfn(vma, vma->vm_start + offset + (i * PAGE_SIZE), - pmem[id].garbage_pfn)) - return -EAGAIN; - } - return 0; -} - -static int pmem_unmap_pfn_range(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - int garbage_pages; - DLOG("unmap offset %lx len %lx\n", offset, len); - - BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); - - garbage_pages = len >> PAGE_SHIFT; - zap_page_range(vma, vma->vm_start + offset, len, NULL); - pmem_map_garbage(id, vma, data, offset, len); - return 0; -} - -static int pmem_map_pfn_range(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - DLOG("map offset %lx len %lx\n", offset, len); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_start)); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_end)); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); - BUG_ON(!PMEM_IS_PAGE_ALIGNED(offset)); - - if (io_remap_pfn_range(vma, vma->vm_start + offset, - (pmem_start_addr(id, data) + offset) >> PAGE_SHIFT, - len, vma->vm_page_prot)) { - return -EAGAIN; - } - return 0; -} - -static int pmem_remap_pfn_range(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) -{ - /* hold the mm semp for the vma you are modifying when you call this */ - BUG_ON(!vma); - zap_page_range(vma, vma->vm_start + offset, len, NULL); - return pmem_map_pfn_range(id, vma, data, offset, len); -} - -static void pmem_vma_open(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct pmem_data *data = file->private_data; - int id = get_id(file); - /* this should never be called as we don't support copying pmem - * ranges via fork */ - BUG_ON(!has_allocation(file)); - down_write(&data->sem); - /* remap the garbage pages, forkers don't get access to the data */ - pmem_unmap_pfn_range(id, vma, data, 0, vma->vm_start - vma->vm_end); - up_write(&data->sem); -} - -static void pmem_vma_close(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct pmem_data *data = file->private_data; - - DLOG("current %u ppid %u file %p count %d\n", current->pid, - current->parent->pid, file, file_count(file)); - if (unlikely(!is_pmem_file(file) || !has_allocation(file))) { - printk(KERN_WARNING "pmem: something is very wrong, you are " - "closing a vm backing an allocation that doesn't " - "exist!\n"); - return; - } - down_write(&data->sem); - if (data->vma == vma) { - data->vma = NULL; - if ((data->flags & PMEM_FLAGS_CONNECTED) && - (data->flags & PMEM_FLAGS_SUBMAP)) - data->flags |= PMEM_FLAGS_UNSUBMAP; - } - /* the kernel is going to free this vma now anyway */ - up_write(&data->sem); -} - -static struct vm_operations_struct vm_ops = { - .open = pmem_vma_open, - .close = pmem_vma_close, -}; - -static int pmem_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct pmem_data *data; - int index; - unsigned long vma_size = vma->vm_end - vma->vm_start; - int ret = 0, id = get_id(file); - - if (vma->vm_pgoff || !PMEM_IS_PAGE_ALIGNED(vma_size)) { -#if PMEM_DEBUG - printk(KERN_ERR "pmem: mmaps must be at offset zero, aligned" - " and a multiple of pages_size.\n"); -#endif - return -EINVAL; - } - - data = (struct pmem_data *)file->private_data; - down_write(&data->sem); - /* check this file isn't already mmaped, for submaps check this file - * has never been mmaped */ - if ((data->flags & PMEM_FLAGS_SUBMAP) || - (data->flags & PMEM_FLAGS_UNSUBMAP)) { -#if PMEM_DEBUG - printk(KERN_ERR "pmem: you can only mmap a pmem file once, " - "this file is already mmaped. %x\n", data->flags); -#endif - ret = -EINVAL; - goto error; - } - /* if file->private_data == unalloced, alloc*/ - if (data && data->index == -1) { - down_write(&pmem[id].bitmap_sem); - index = pmem_allocate(id, vma->vm_end - vma->vm_start); - up_write(&pmem[id].bitmap_sem); - data->index = index; - } - /* either no space was available or an error occured */ - if (!has_allocation(file)) { - ret = -EINVAL; - printk("pmem: could not find allocation for map.\n"); - goto error; - } - - if (pmem_len(id, data) < vma_size) { -#if PMEM_DEBUG - printk(KERN_WARNING "pmem: mmap size [%lu] does not match" - "size of backing region [%lu].\n", vma_size, - pmem_len(id, data)); -#endif - ret = -EINVAL; - goto error; - } - - vma->vm_pgoff = pmem_start_addr(id, data) >> PAGE_SHIFT; - vma->vm_page_prot = pmem_access_prot(file, vma->vm_page_prot); - - if (data->flags & PMEM_FLAGS_CONNECTED) { - struct pmem_region_node *region_node; - struct list_head *elt; - if (pmem_map_garbage(id, vma, data, 0, vma_size)) { - printk("pmem: mmap failed in kernel!\n"); - ret = -EAGAIN; - goto error; - } - list_for_each(elt, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, - list); - DLOG("remapping file: %p %lx %lx\n", file, - region_node->region.offset, - region_node->region.len); - if (pmem_remap_pfn_range(id, vma, data, - region_node->region.offset, - region_node->region.len)) { - ret = -EAGAIN; - goto error; - } - } - data->flags |= PMEM_FLAGS_SUBMAP; - get_task_struct(current->group_leader); - data->task = current->group_leader; - data->vma = vma; -#if PMEM_DEBUG - data->pid = current->pid; -#endif - DLOG("submmapped file %p vma %p pid %u\n", file, vma, - current->pid); - } else { - if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) { - printk(KERN_INFO "pmem: mmap failed in kernel!\n"); - ret = -EAGAIN; - goto error; - } - data->flags |= PMEM_FLAGS_MASTERMAP; - data->pid = current->pid; - } - vma->vm_ops = &vm_ops; -error: - up_write(&data->sem); - return ret; -} - -/* the following are the api for accessing pmem regions by other drivers - * from inside the kernel */ -int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *len) -{ - struct pmem_data *data; - if (!is_pmem_file(file) || !has_allocation(file)) { -#if PMEM_DEBUG - printk(KERN_INFO "pmem: requested pmem data from invalid" - "file.\n"); -#endif - return -1; - } - data = (struct pmem_data *)file->private_data; - down_read(&data->sem); - if (data->vma) { - *start = data->vma->vm_start; - *len = data->vma->vm_end - data->vma->vm_start; - } else { - *start = 0; - *len = 0; - } - up_read(&data->sem); - return 0; -} - -int get_pmem_addr(struct file *file, unsigned long *start, - unsigned long *vstart, unsigned long *len) -{ - struct pmem_data *data; - int id; - - if (!is_pmem_file(file) || !has_allocation(file)) { - return -1; - } - - data = (struct pmem_data *)file->private_data; - if (data->index == -1) { -#if PMEM_DEBUG - printk(KERN_INFO "pmem: requested pmem data from file with no " - "allocation.\n"); - return -1; -#endif - } - id = get_id(file); - - down_read(&data->sem); - *start = pmem_start_addr(id, data); - *len = pmem_len(id, data); - *vstart = (unsigned long)pmem_start_vaddr(id, data); - up_read(&data->sem); -#if PMEM_DEBUG - down_write(&data->sem); - data->ref++; - up_write(&data->sem); -#endif - return 0; -} - -int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, - unsigned long *len, struct file **filp) -{ - struct file *file; - - file = fget(fd); - if (unlikely(file == NULL)) { - printk(KERN_INFO "pmem: requested data from file descriptor " - "that doesn't exist."); - return -1; - } - - if (get_pmem_addr(file, start, vstart, len)) - goto end; - - if (filp) - *filp = file; - return 0; -end: - fput(file); - return -1; -} - -void put_pmem_file(struct file *file) -{ - struct pmem_data *data; - int id; - - if (!is_pmem_file(file)) - return; - id = get_id(file); - data = (struct pmem_data *)file->private_data; -#if PMEM_DEBUG - down_write(&data->sem); - if (data->ref == 0) { - printk("pmem: pmem_put > pmem_get %s (pid %d)\n", - pmem[id].dev.name, data->pid); - BUG(); - } - data->ref--; - up_write(&data->sem); -#endif - fput(file); -} - -void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len) -{ - struct pmem_data *data; - int id; - void *vaddr; - struct pmem_region_node *region_node; - struct list_head *elt; - void *flush_start, *flush_end; - - if (!is_pmem_file(file) || !has_allocation(file)) { - return; - } - - id = get_id(file); - data = (struct pmem_data *)file->private_data; - if (!pmem[id].cached || file->f_flags & O_SYNC) - return; - - down_read(&data->sem); - vaddr = pmem_start_vaddr(id, data); - /* if this isn't a submmapped file, flush the whole thing */ - if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) { - dmac_flush_range(vaddr, vaddr + pmem_len(id, data)); - goto end; - } - /* otherwise, flush the region of the file we are drawing */ - list_for_each(elt, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, list); - if ((offset >= region_node->region.offset) && - ((offset + len) <= (region_node->region.offset + - region_node->region.len))) { - flush_start = vaddr + region_node->region.offset; - flush_end = flush_start + region_node->region.len; - dmac_flush_range(flush_start, flush_end); - break; - } - } -end: - up_read(&data->sem); -} - -static int pmem_connect(unsigned long connect, struct file *file) -{ - struct pmem_data *data = (struct pmem_data *)file->private_data; - struct pmem_data *src_data; - struct file *src_file; - int ret = 0, put_needed; - - down_write(&data->sem); - /* retrieve the src file and check it is a pmem file with an alloc */ - src_file = fget_light(connect, &put_needed); - DLOG("connect %p to %p\n", file, src_file); - if (!src_file) { - printk("pmem: src file not found!\n"); - ret = -EINVAL; - goto err_no_file; - } - if (unlikely(!is_pmem_file(src_file) || !has_allocation(src_file))) { - printk(KERN_INFO "pmem: src file is not a pmem file or has no " - "alloc!\n"); - ret = -EINVAL; - goto err_bad_file; - } - src_data = (struct pmem_data *)src_file->private_data; - - if (has_allocation(file) && (data->index != src_data->index)) { - printk("pmem: file is already mapped but doesn't match this" - " src_file!\n"); - ret = -EINVAL; - goto err_bad_file; - } - data->index = src_data->index; - data->flags |= PMEM_FLAGS_CONNECTED; - data->master_fd = connect; - data->master_file = src_file; - -err_bad_file: - fput_light(src_file, put_needed); -err_no_file: - up_write(&data->sem); - return ret; -} - -static void pmem_unlock_data_and_mm(struct pmem_data *data, - struct mm_struct *mm) -{ - up_write(&data->sem); - if (mm != NULL) { - up_write(&mm->mmap_sem); - mmput(mm); - } -} - -static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data, - struct mm_struct **locked_mm) -{ - int ret = 0; - struct mm_struct *mm = NULL; - *locked_mm = NULL; -lock_mm: - down_read(&data->sem); - if (PMEM_IS_SUBMAP(data)) { - mm = get_task_mm(data->task); - if (!mm) { -#if PMEM_DEBUG - printk("pmem: can't remap task is gone!\n"); -#endif - up_read(&data->sem); - return -1; - } - } - up_read(&data->sem); - - if (mm) - down_write(&mm->mmap_sem); - - down_write(&data->sem); - /* check that the file didn't get mmaped before we could take the - * data sem, this should be safe b/c you can only submap each file - * once */ - if (PMEM_IS_SUBMAP(data) && !mm) { - pmem_unlock_data_and_mm(data, mm); - up_write(&data->sem); - goto lock_mm; - } - /* now check that vma.mm is still there, it could have been - * deleted by vma_close before we could get the data->sem */ - if ((data->flags & PMEM_FLAGS_UNSUBMAP) && (mm != NULL)) { - /* might as well release this */ - if (data->flags & PMEM_FLAGS_SUBMAP) { - put_task_struct(data->task); - data->task = NULL; - /* lower the submap flag to show the mm is gone */ - data->flags &= ~(PMEM_FLAGS_SUBMAP); - } - pmem_unlock_data_and_mm(data, mm); - return -1; - } - *locked_mm = mm; - return ret; -} - -int pmem_remap(struct pmem_region *region, struct file *file, - unsigned operation) -{ - int ret; - struct pmem_region_node *region_node; - struct mm_struct *mm = NULL; - struct list_head *elt, *elt2; - int id = get_id(file); - struct pmem_data *data = (struct pmem_data *)file->private_data; - - /* pmem region must be aligned on a page boundry */ - if (unlikely(!PMEM_IS_PAGE_ALIGNED(region->offset) || - !PMEM_IS_PAGE_ALIGNED(region->len))) { -#if PMEM_DEBUG - printk("pmem: request for unaligned pmem suballocation " - "%lx %lx\n", region->offset, region->len); -#endif - return -EINVAL; - } - - /* if userspace requests a region of len 0, there's nothing to do */ - if (region->len == 0) - return 0; - - /* lock the mm and data */ - ret = pmem_lock_data_and_mm(file, data, &mm); - if (ret) - return 0; - - /* only the owner of the master file can remap the client fds - * that back in it */ - if (!is_master_owner(file)) { -#if PMEM_DEBUG - printk("pmem: remap requested from non-master process\n"); -#endif - ret = -EINVAL; - goto err; - } - - /* check that the requested range is within the src allocation */ - if (unlikely((region->offset > pmem_len(id, data)) || - (region->len > pmem_len(id, data)) || - (region->offset + region->len > pmem_len(id, data)))) { -#if PMEM_DEBUG - printk(KERN_INFO "pmem: suballoc doesn't fit in src_file!\n"); -#endif - ret = -EINVAL; - goto err; - } - - if (operation == PMEM_MAP) { - region_node = kmalloc(sizeof(struct pmem_region_node), - GFP_KERNEL); - if (!region_node) { - ret = -ENOMEM; -#if PMEM_DEBUG - printk(KERN_INFO "No space to allocate metadata!"); -#endif - goto err; - } - region_node->region = *region; - list_add(®ion_node->list, &data->region_list); - } else if (operation == PMEM_UNMAP) { - int found = 0; - list_for_each_safe(elt, elt2, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, - list); - if (region->len == 0 || - (region_node->region.offset == region->offset && - region_node->region.len == region->len)) { - list_del(elt); - kfree(region_node); - found = 1; - } - } - if (!found) { -#if PMEM_DEBUG - printk("pmem: Unmap region does not map any mapped " - "region!"); -#endif - ret = -EINVAL; - goto err; - } - } - - if (data->vma && PMEM_IS_SUBMAP(data)) { - if (operation == PMEM_MAP) - ret = pmem_remap_pfn_range(id, data->vma, data, - region->offset, region->len); - else if (operation == PMEM_UNMAP) - ret = pmem_unmap_pfn_range(id, data->vma, data, - region->offset, region->len); - } - -err: - pmem_unlock_data_and_mm(data, mm); - return ret; -} - -static void pmem_revoke(struct file *file, struct pmem_data *data) -{ - struct pmem_region_node *region_node; - struct list_head *elt, *elt2; - struct mm_struct *mm = NULL; - int id = get_id(file); - int ret = 0; - - data->master_file = NULL; - ret = pmem_lock_data_and_mm(file, data, &mm); - /* if lock_data_and_mm fails either the task that mapped the fd, or - * the vma that mapped it have already gone away, nothing more - * needs to be done */ - if (ret) - return; - /* unmap everything */ - /* delete the regions and region list nothing is mapped any more */ - if (data->vma) - list_for_each_safe(elt, elt2, &data->region_list) { - region_node = list_entry(elt, struct pmem_region_node, - list); - pmem_unmap_pfn_range(id, data->vma, data, - region_node->region.offset, - region_node->region.len); - list_del(elt); - kfree(region_node); - } - /* delete the master file */ - pmem_unlock_data_and_mm(data, mm); -} - -static void pmem_get_size(struct pmem_region *region, struct file *file) -{ - struct pmem_data *data = (struct pmem_data *)file->private_data; - int id = get_id(file); - - if (!has_allocation(file)) { - region->offset = 0; - region->len = 0; - return; - } else { - region->offset = pmem_start_addr(id, data); - region->len = pmem_len(id, data); - } - DLOG("offset %lx len %lx\n", region->offset, region->len); -} - - -static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct pmem_data *data; - int id = get_id(file); - - switch (cmd) { - case PMEM_GET_PHYS: - { - struct pmem_region region; - DLOG("get_phys\n"); - if (!has_allocation(file)) { - region.offset = 0; - region.len = 0; - } else { - data = (struct pmem_data *)file->private_data; - region.offset = pmem_start_addr(id, data); - region.len = pmem_len(id, data); - } - printk(KERN_INFO "pmem: request for physical address of pmem region " - "from process %d.\n", current->pid); - if (copy_to_user((void __user *)arg, ®ion, - sizeof(struct pmem_region))) - return -EFAULT; - break; - } - case PMEM_MAP: - { - struct pmem_region region; - if (copy_from_user(®ion, (void __user *)arg, - sizeof(struct pmem_region))) - return -EFAULT; - data = (struct pmem_data *)file->private_data; - return pmem_remap(®ion, file, PMEM_MAP); - } - break; - case PMEM_UNMAP: - { - struct pmem_region region; - if (copy_from_user(®ion, (void __user *)arg, - sizeof(struct pmem_region))) - return -EFAULT; - data = (struct pmem_data *)file->private_data; - return pmem_remap(®ion, file, PMEM_UNMAP); - break; - } - case PMEM_GET_SIZE: - { - struct pmem_region region; - DLOG("get_size\n"); - pmem_get_size(®ion, file); - if (copy_to_user((void __user *)arg, ®ion, - sizeof(struct pmem_region))) - return -EFAULT; - break; - } - case PMEM_GET_TOTAL_SIZE: - { - struct pmem_region region; - DLOG("get total size\n"); - region.offset = 0; - get_id(file); - region.len = pmem[id].size; - if (copy_to_user((void __user *)arg, ®ion, - sizeof(struct pmem_region))) - return -EFAULT; - break; - } - case PMEM_ALLOCATE: - { - if (has_allocation(file)) - return -EINVAL; - data = (struct pmem_data *)file->private_data; - data->index = pmem_allocate(id, arg); - break; - } - case PMEM_CONNECT: - DLOG("connect\n"); - return pmem_connect(arg, file); - break; - case PMEM_CACHE_FLUSH: - { - struct pmem_region region; - DLOG("flush\n"); - if (copy_from_user(®ion, (void __user *)arg, - sizeof(struct pmem_region))) - return -EFAULT; - flush_pmem_file(file, region.offset, region.len); - break; - } - default: - if (pmem[id].ioctl) - return pmem[id].ioctl(file, cmd, arg); - return -EINVAL; - } - return 0; -} - -#if PMEM_DEBUG -static ssize_t debug_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t debug_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - struct list_head *elt, *elt2; - struct pmem_data *data; - struct pmem_region_node *region_node; - int id = (int)file->private_data; - const int debug_bufmax = 4096; - static char buffer[4096]; - int n = 0; - - DLOG("debug open\n"); - n = scnprintf(buffer, debug_bufmax, - "pid #: mapped regions (offset, len) (offset,len)...\n"); - - mutex_lock(&pmem[id].data_list_lock); - list_for_each(elt, &pmem[id].data_list) { - data = list_entry(elt, struct pmem_data, list); - down_read(&data->sem); - n += scnprintf(buffer + n, debug_bufmax - n, "pid %u:", - data->pid); - list_for_each(elt2, &data->region_list) { - region_node = list_entry(elt2, struct pmem_region_node, - list); - n += scnprintf(buffer + n, debug_bufmax - n, - "(%lx,%lx) ", - region_node->region.offset, - region_node->region.len); - } - n += scnprintf(buffer + n, debug_bufmax - n, "\n"); - up_read(&data->sem); - } - mutex_unlock(&pmem[id].data_list_lock); - - n++; - buffer[n] = 0; - return simple_read_from_buffer(buf, count, ppos, buffer, n); -} - -static struct file_operations debug_fops = { - .read = debug_read, - .open = debug_open, -}; -#endif - -#if 0 -static struct miscdevice pmem_dev = { - .name = "pmem", - .fops = &pmem_fops, -}; -#endif - -int pmem_setup(struct android_pmem_platform_data *pdata, - long (*ioctl)(struct file *, unsigned int, unsigned long), - int (*release)(struct inode *, struct file *)) -{ - int err = 0; - int i, index = 0; - int id = id_count; - id_count++; - - pmem[id].no_allocator = pdata->no_allocator; - pmem[id].cached = pdata->cached; - pmem[id].buffered = pdata->buffered; - pmem[id].base = pdata->start; - pmem[id].size = pdata->size; - pmem[id].ioctl = ioctl; - pmem[id].release = release; - init_rwsem(&pmem[id].bitmap_sem); - mutex_init(&pmem[id].data_list_lock); - INIT_LIST_HEAD(&pmem[id].data_list); - pmem[id].dev.name = pdata->name; - pmem[id].dev.minor = id; - pmem[id].dev.fops = &pmem_fops; - printk(KERN_INFO "%s: %d init\n", pdata->name, pdata->cached); - - err = misc_register(&pmem[id].dev); - if (err) { - printk(KERN_ALERT "Unable to register pmem driver!\n"); - goto err_cant_register_device; - } - pmem[id].num_entries = pmem[id].size / PMEM_MIN_ALLOC; - - pmem[id].bitmap = kmalloc(pmem[id].num_entries * - sizeof(struct pmem_bits), GFP_KERNEL); - if (!pmem[id].bitmap) - goto err_no_mem_for_metadata; - - memset(pmem[id].bitmap, 0, sizeof(struct pmem_bits) * - pmem[id].num_entries); - - for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--) { - if ((pmem[id].num_entries) & 1<<i) { - PMEM_ORDER(id, index) = i; - index = PMEM_NEXT_INDEX(id, index); - } - } - - if (pmem[id].cached) - pmem[id].vbase = ioremap_cached(pmem[id].base, - pmem[id].size); -#ifdef ioremap_ext_buffered - else if (pmem[id].buffered) - pmem[id].vbase = ioremap_ext_buffered(pmem[id].base, - pmem[id].size); -#endif - else - pmem[id].vbase = ioremap(pmem[id].base, pmem[id].size); - - if (pmem[id].vbase == 0) - goto error_cant_remap; - - pmem[id].garbage_pfn = page_to_pfn(alloc_page(GFP_KERNEL)); - if (pmem[id].no_allocator) - pmem[id].allocated = 0; - -#if PMEM_DEBUG - debugfs_create_file(pdata->name, S_IFREG | S_IRUGO, NULL, (void *)id, - &debug_fops); -#endif - return 0; -error_cant_remap: - kfree(pmem[id].bitmap); -err_no_mem_for_metadata: - misc_deregister(&pmem[id].dev); -err_cant_register_device: - return -1; -} - -static int pmem_probe(struct platform_device *pdev) -{ - struct android_pmem_platform_data *pdata; - - if (!pdev || !pdev->dev.platform_data) { - printk(KERN_ALERT "Unable to probe pmem!\n"); - return -1; - } - pdata = pdev->dev.platform_data; - return pmem_setup(pdata, NULL, NULL); -} - - -static int pmem_remove(struct platform_device *pdev) -{ - int id = pdev->id; - __free_page(pfn_to_page(pmem[id].garbage_pfn)); - misc_deregister(&pmem[id].dev); - return 0; -} - -static struct platform_driver pmem_driver = { - .probe = pmem_probe, - .remove = pmem_remove, - .driver = { .name = "android_pmem" } -}; - - -static int __init pmem_init(void) -{ - return platform_driver_register(&pmem_driver); -} - -static void __exit pmem_exit(void) -{ - platform_driver_unregister(&pmem_driver); -} - -module_init(pmem_init); -module_exit(pmem_exit); - diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index d685f74..862ad52 100755..100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2316,7 +2316,6 @@ out: int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) { int ret = 0; - struct mmc_host *mmc = host->mmc; sdhci_disable_card_detection(host); @@ -2327,8 +2326,11 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) host->flags &= ~SDHCI_NEEDS_RETUNING; } - if (mmc->card && (mmc->card->type != MMC_TYPE_SDIO)) - ret = mmc_suspend_host(host->mmc); + ret = mmc_suspend_host(host->mmc); + if (ret) { + sdhci_enable_card_detection(host); + return ret; + } sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); @@ -2348,7 +2350,6 @@ EXPORT_SYMBOL_GPL(sdhci_suspend_host); int sdhci_resume_host(struct sdhci_host *host) { int ret = 0; - struct mmc_host *mmc = host->mmc; if (host->vmmc) { int ret = regulator_enable(host->vmmc); @@ -2368,8 +2369,7 @@ int sdhci_resume_host(struct sdhci_host *host) sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER)); mmiowb(); - if (mmc->card && (mmc->card->type != MMC_TYPE_SDIO)) - ret = mmc_resume_host(host->mmc); + ret = mmc_resume_host(host->mmc); sdhci_enable_card_detection(host); diff --git a/drivers/net/wimax/cmc7xx/download.h b/drivers/net/wimax/cmc7xx/download.h index f99de4a..34851e0 100755 --- a/drivers/net/wimax/cmc7xx/download.h +++ b/drivers/net/wimax/cmc7xx/download.h @@ -24,7 +24,8 @@ #define MAX_IMAGE_DATA_LENGTH 3564 #define MAX_IMAGE_DATA_MSG_LENGTH 4096 -#define FWDOWNLOAD_TIMEOUT 5 +#define MODEM_RESP_RETRY 15 +#define FWDOWNLOAD_TIMEOUT 12 #define MAX_WIMAXFW_SIZE 2100000 /* used for host boot (firmware download) */ diff --git a/drivers/net/wimax/cmc7xx/hardware.c b/drivers/net/wimax/cmc7xx/hardware.c index 34c1b2e..87100d4 100755 --- a/drivers/net/wimax/cmc7xx/hardware.c +++ b/drivers/net/wimax/cmc7xx/hardware.c @@ -84,6 +84,7 @@ static void cmc732_release_wake_irq(struct net_adapter *adapter) int wimax_hw_start(struct net_adapter *adapter) { + int retry = 0; struct wimax732_platform_data *pdata = adapter->pdata; pdata->g_cfg->wimax_status = WIMAX_STATE_READY; @@ -113,7 +114,17 @@ int wimax_hw_start(struct net_adapter *adapter) if (adapter->downloading) { - send_cmd_packet(adapter, MSG_DRIVER_OK_REQ); + while (!adapter->modem_resp) { + send_cmd_packet(adapter, MSG_DRIVER_OK_REQ); + wait_event_interruptible_timeout( + adapter->modem_resp_event, + adapter->modem_resp, + HZ/10); + if (!adapter->modem_resp) + pr_err("no modem response"); + if (++retry > MODEM_RESP_RETRY) + goto download_fail; + } switch (wait_event_interruptible_timeout( adapter->download_event, @@ -151,6 +162,9 @@ int wimax_hw_start(struct net_adapter *adapter) return STATUS_SUCCESS; download_fail: + adapter->halted = true; + wake_up_interruptible(&adapter->receive_event); + msleep(20); unload_wimax_image(adapter); return STATUS_UNSUCCESSFUL; } @@ -192,6 +206,7 @@ int wimax_hw_init(struct net_adapter *adapter) spin_lock_init(&adapter->hw.lock); init_waitqueue_head(&adapter->download_event); + init_waitqueue_head(&adapter->modem_resp_event); init_completion(&adapter->wakeup_event); return STATUS_SUCCESS; diff --git a/drivers/net/wimax/cmc7xx/receive.c b/drivers/net/wimax/cmc7xx/receive.c index 37cf885..7126a62 100755 --- a/drivers/net/wimax/cmc7xx/receive.c +++ b/drivers/net/wimax/cmc7xx/receive.c @@ -29,6 +29,8 @@ static void process_indicate_packet(struct net_adapter *adapter, u8 *buffer) switch (be16_to_cpu(packet->id)) { case MSG_DRIVER_OK_RESP: + adapter->modem_resp = true; + wake_up_interruptible(&adapter->modem_resp_event); pr_debug("%s: MSG_DRIVER_OK_RESP\n", __func__); send_image_info_packet(adapter, MSG_IMAGE_INFO_REQ); break; diff --git a/drivers/net/wimax/cmc7xx/wimax_sdio.c b/drivers/net/wimax/cmc7xx/wimax_sdio.c index 25c15b0..99830da 100755 --- a/drivers/net/wimax/cmc7xx/wimax_sdio.c +++ b/drivers/net/wimax/cmc7xx/wimax_sdio.c @@ -359,7 +359,6 @@ static struct net_device_stats *adapter_netdev_stats(struct net_device *dev) static int adapter_start_xmit(struct sk_buff *skb, struct net_device *net) { struct net_adapter *adapter = netdev_priv(net); - int len; if (!adapter->media_state || adapter->halted) { pr_debug("Driver already halted. Returning Failure..."); @@ -370,8 +369,7 @@ static int adapter_start_xmit(struct sk_buff *skb, struct net_device *net) return 0; } - len = ((skb->len) & 0x3f) ? skb->len : skb->len + 1; - hw_send_data(adapter, skb->data, len); + hw_send_data(adapter, skb->data, skb->len); dev_kfree_skb(skb); if (!adapter->media_state) @@ -460,6 +458,8 @@ static void adapter_interrupt(struct sdio_func *func) } else { pr_debug("adapter->halted=true in \ adapter_interrupt !!!!!!!!!"); + intrd = sdio_readb(func, SDIO_INT_STATUS_REG, NULL); + sdio_writeb(func, intrd, SDIO_INT_STATUS_CLR_REG, NULL); /* send stop message */ hdr.id0 = 'W'; @@ -577,7 +577,6 @@ static int adapter_probe(struct sdio_func *func, misc_deregister(&adapter->uwibro_dev); pr_debug("wimax_hw_start failed"); goto regchar_fail; - goto regchar_fail; } return 0; @@ -630,6 +629,8 @@ static void adapter_remove(struct sdio_func *func) adapter->removed = true; adapter->download_complete = true; wake_up_interruptible(&adapter->download_event); + adapter->modem_resp = true; + wake_up_interruptible(&adapter->modem_resp_event); } if (!completion_done(&adapter->wakeup_event)) @@ -704,12 +705,30 @@ int wimax_resume(struct platform_device *pdev) mutex_unlock(&pdata->g_cfg->suspend_mutex); return 0; } +static int adapter_suspend(struct device *dev) +{ + pr_debug("adapter_suspend sdio"); + return 0; +} + +static int adapter_resume(struct device *dev) +{ + pr_debug("adapter_resume sdio"); + return 0; +} +static const struct dev_pm_ops adapter_pm_ops = { + .suspend = adapter_suspend, + .resume = adapter_resume, +}; static struct sdio_driver adapter_driver = { .name = "C730SDIO", .probe = adapter_probe, .remove = adapter_remove, .id_table = adapter_table, + .drv = { + .pm = &adapter_pm_ops, + }, }; static int wimax_probe(struct platform_device *pdev) diff --git a/drivers/net/wimax/cmc7xx/wimax_sdio.h b/drivers/net/wimax/cmc7xx/wimax_sdio.h index 9594758..b9de36a 100755 --- a/drivers/net/wimax/cmc7xx/wimax_sdio.h +++ b/drivers/net/wimax/cmc7xx/wimax_sdio.h @@ -52,10 +52,12 @@ struct net_adapter { struct completion wakeup_event; struct wimax732_platform_data *pdata; wait_queue_head_t download_event; + wait_queue_head_t modem_resp_event; wait_queue_head_t receive_event; wait_queue_head_t send_event; u8 downloading; /* firmware downloading */ u8 download_complete; + u8 modem_resp; u8 mac_ready; u8 media_state;/* mac completion */ diff --git a/drivers/net/wireless/bcmdhd/Kconfig b/drivers/net/wireless/bcmdhd/Kconfig index 205c813..8b6b92f 100644 --- a/drivers/net/wireless/bcmdhd/Kconfig +++ b/drivers/net/wireless/bcmdhd/Kconfig @@ -37,4 +37,18 @@ config DHD_USE_STATIC_BUF depends on BCMDHD default n ---help--- - Use memory preallocated in platform
\ No newline at end of file + Use memory preallocated in platform + +config DHD_USE_SCHED_SCAN + bool "Use CFG80211 sched scan" + depends on BCMDHD && CFG80211 + default n + ---help--- + Use CFG80211 sched scan + +config DHD_ENABLE_P2P + bool "Enable Wifi Direct" + depends on BCMDHD && CFG80211 + default n + ---help--- + Use Enable Wifi Direct diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile index e82c985..eda803e 100644 --- a/drivers/net/wireless/bcmdhd/Makefile +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -8,23 +8,30 @@ DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER \ -DNEW_COMPAT_WIRELESS -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT \ -DKEEP_ALIVE -DCSCAN -DGET_CUSTOM_MAC_ENABLE -DPKT_FILTER_SUPPORT \ -DEMBEDDED_PLATFORM -DENABLE_INSMOD_NO_FW_LOAD -DPNO_SUPPORT \ + -DSET_RANDOM_MAC_SOFTAP -DWL_CFG80211_STA_EVENT \ -Idrivers/net/wireless/bcmdhd -Idrivers/net/wireless/bcmdhd/include DHDOFILES = aiutils.o bcmsdh_sdmmc_linux.o dhd_linux.o siutils.o bcmutils.o \ dhd_linux_sched.o bcmwifi.o dhd_sdio.o bcmevent.o dhd_bta.o hndpmu.o \ bcmsdh.o dhd_cdc.o bcmsdh_linux.o dhd_common.o linux_osl.o \ - bcmsdh_sdmmc.o dhd_custom_gpio.o sbutils.o wldev_common.o wl_android.o + bcmsdh_sdmmc.o dhd_custom_gpio.o sbutils.o wldev_common.o wl_android.o dhd_cfg80211.o obj-$(CONFIG_BCMDHD) += bcmdhd.o bcmdhd-objs += $(DHDOFILES) ifneq ($(CONFIG_WIRELESS_EXT),) bcmdhd-objs += wl_iw.o -DHDCFLAGS += -DSOFTAP +DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT endif ifneq ($(CONFIG_CFG80211),) -bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o dhd_linux_mon.o +bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o DHDCFLAGS += -DWL_CFG80211 endif +ifneq ($(CONFIG_DHD_USE_SCHED_SCAN),) +DHDCFLAGS += -DWL_SCHED_SCAN +endif +ifneq ($(CONFIG_DHD_ENABLE_P2P),) +DHDCFLAGS += -DWL_ENABLE_P2P_IF +endif EXTRA_CFLAGS = $(DHDCFLAGS) ifeq ($(CONFIG_BCMDHD),m) EXTRA_LDFLAGS += --strip-debug diff --git a/drivers/net/wireless/bcmdhd/aiutils.c b/drivers/net/wireless/bcmdhd/aiutils.c index 059df89..5ca0993 100644 --- a/drivers/net/wireless/bcmdhd/aiutils.c +++ b/drivers/net/wireless/bcmdhd/aiutils.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: aiutils.c,v 1.26.2.1 2010-03-09 18:41:21 Exp $ + * $Id: aiutils.c,v 1.26.2.1 2010-03-09 18:41:21 $ */ diff --git a/drivers/net/wireless/bcmdhd/bcmevent.c b/drivers/net/wireless/bcmdhd/bcmevent.c index 24581dd..6a25d9a 100644 --- a/drivers/net/wireless/bcmdhd/bcmevent.c +++ b/drivers/net/wireless/bcmdhd/bcmevent.c @@ -20,7 +20,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: bcmevent.c,v 1.8.2.7 2011-02-01 06:23:39 Exp $ + * $Id: bcmevent.c,v 1.8.2.7 2011-02-01 06:23:39 $ */ #include <typedefs.h> @@ -29,7 +29,7 @@ #include <proto/bcmeth.h> #include <proto/bcmevent.h> -#if WLC_E_LAST != 85 +#if WLC_E_LAST != 87 #error "You need to add an entry to bcmevent_names[] for the new event" #endif @@ -117,8 +117,10 @@ const bcmevent_name_t bcmevent_names[] = { { WLC_E_PFN_SCAN_NONE, "PFN_SCAN_NONE" }, { WLC_E_PFN_SCAN_ALLGONE, "PFN_SCAN_ALLGONE" }, #ifdef SOFTAP - { WLC_E_GTK_PLUMBED, "GTK_PLUMBED" } + { WLC_E_GTK_PLUMBED, "GTK_PLUMBED" }, #endif + { WLC_E_ASSOC_REQ_IE, "ASSOC_REQ_IE" }, + { WLC_E_ASSOC_RESP_IE, "ASSOC_RESP_IE" } }; diff --git a/drivers/net/wireless/bcmdhd/bcmsdh.c b/drivers/net/wireless/bcmdhd/bcmsdh.c index 918c8e6..f67b13a 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh.c 275784 2011-08-04 22:41:49Z $ + * $Id: bcmsdh.c 300445 2011-12-03 05:37:20Z $ */ /** diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c index 04c43a3..d257dda 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_linux.c,v 1.72.6.5 2010-12-23 01:13:15 Exp $ + * $Id: bcmsdh_linux.c 312788 2012-02-03 23:06:32Z $ */ /** @@ -147,17 +147,6 @@ static int __devexit bcmsdh_remove(struct device *dev); #endif /* BCMLXSDMMC */ #ifndef BCMLXSDMMC -static struct device_driver bcmsdh_driver = { - .name = "pxa2xx-mci", - .bus = &platform_bus_type, - .probe = bcmsdh_probe, - .remove = bcmsdh_remove, - .suspend = NULL, - .resume = NULL, - }; -#endif /* BCMLXSDMMC */ - -#ifndef BCMLXSDMMC static #endif /* BCMLXSDMMC */ int bcmsdh_probe(struct device *dev) @@ -238,9 +227,9 @@ int bcmsdh_probe(struct device *dev) /* chain SDIO Host Controller info together */ sdhc->next = sdhcinfo; sdhcinfo = sdhc; + /* Read the vendor/device ID from the CIS */ vendevid = bcmsdh_query_device(sdh); - /* try to attach to the target device */ if (!(sdhc->ch = drvinfo.attach((vendevid >> 16), (vendevid & 0xFFFF), 0, 0, 0, 0, @@ -274,6 +263,7 @@ int bcmsdh_remove(struct device *dev) sdhc = sdhcinfo; drvinfo.detach(sdhc->ch); bcmsdh_detach(sdhc->osh, sdhc->sdh); + /* find the SDIO Host Controller state for this pdev and take it out from the list */ for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) { if (sdhc->dev == (void *)dev) { @@ -290,7 +280,6 @@ int bcmsdh_remove(struct device *dev) return 0; } - /* release SDIO Host Controller info */ osh = sdhc->osh; MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); @@ -531,13 +520,8 @@ bcmsdh_register(bcmsdh_driver_t *driver) drvinfo = *driver; #if defined(BCMPLATFORM_BUS) -#if defined(BCMLXSDMMC) SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n")); error = sdio_function_init(); -#else - SDLX_MSG(("Intel PXA270 SDIO Driver\n")); - error = driver_register(&bcmsdh_driver); -#endif /* defined(BCMLXSDMMC) */ return error; #endif /* defined(BCMPLATFORM_BUS) */ @@ -565,14 +549,12 @@ bcmsdh_unregister(void) if (bcmsdh_pci_driver.node.next) #endif -#if defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) - driver_unregister(&bcmsdh_driver); -#endif #if defined(BCMLXSDMMC) sdio_function_cleanup(); #endif /* BCMLXSDMMC */ + #if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) - pci_unregister_driver(&bcmsdh_pci_driver); + pci_unregister_driver(&bcmsdh_pci_driver); #endif /* BCMPLATFORM_BUS */ } @@ -611,13 +593,6 @@ static irqreturn_t wlan_oob_irq(int irq, void *dev_id) return IRQ_HANDLED; } -void *bcmsdh_get_drvdata(void) -{ - if (!sdhcinfo) - return NULL; - return dev_get_drvdata(sdhcinfo->dev); -} - int bcmsdh_register_oob_intr(void * dhdp) { int error = 0; @@ -671,6 +646,16 @@ void bcmsdh_unregister_oob_intr(void) } } #endif /* defined(OOB_INTR_ONLY) */ + +#if defined(BCMLXSDMMC) +void *bcmsdh_get_drvdata(void) +{ + if (!sdhcinfo) + return NULL; + return dev_get_drvdata(sdhcinfo->dev); +} +#endif + /* Module parameters specific to each host-controller driver */ extern uint sd_msglevel; /* Debug message level */ @@ -694,6 +679,10 @@ module_param(sd_hiok, uint, 0); extern uint sd_f2_blocksize; module_param(sd_f2_blocksize, int, 0); +#ifdef BCMSDIOH_STD +extern int sd_uhsimode; +module_param(sd_uhsimode, int, 0); +#endif #ifdef BCMSDH_MODULE EXPORT_SYMBOL(bcmsdh_attach); diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c index aedb508..374154f 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_sdmmc.c 282820 2011-09-09 15:40:35Z $ + * $Id: bcmsdh_sdmmc.c 314904 2012-02-14 21:36:04Z $ */ #include <typedefs.h> @@ -35,6 +35,7 @@ #include <sdiovar.h> /* ioctl/iovars */ #include <linux/mmc/core.h> +#include <linux/mmc/card.h> #include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_ids.h> @@ -148,6 +149,7 @@ sdioh_attach(osl_t *osh, void *bar0, uint irq) sd->sd_blockmode = TRUE; sd->use_client_ints = TRUE; sd->client_block_size[0] = 64; + sd->use_rxchain = FALSE; gInstance->sd = sd; @@ -448,6 +450,7 @@ sdioh_iovar_op(sdioh_info_t *si, const char *name, bcopy(params, &int_val, sizeof(int_val)); bool_val = (int_val != 0) ? TRUE : FALSE; + BCM_REFERENCE(bool_val); actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); switch (actionid) { @@ -511,7 +514,7 @@ sdioh_iovar_op(sdioh_info_t *si, const char *name, } case IOV_GVAL(IOV_RXCHAIN): - int_val = FALSE; + int_val = (int32)si->use_rxchain; bcopy(&int_val, arg, val_size); break; @@ -678,14 +681,9 @@ sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable) uint8 data; if (enable) - data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; /* enable hw oob interrupt */ + data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE | SDIO_SEPINT_ACT_HI; else - data = SDIO_SEPINT_ACT_HI; /* disable hw oob interrupt */ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) - /* Needed for Android Linux Kernel 2.6.35 */ - data |= SDIO_SEPINT_ACT_HI; /* Active HIGH */ -#endif + data = SDIO_SEPINT_ACT_HI; status = sdioh_request_byte(sd, SDIOH_WRITE, 0, SDIOD_CCCR_BRCM_SEPINT, &data); return status; @@ -901,8 +899,12 @@ sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func, bool fifo = (fix_inc == SDIOH_DATA_FIX); uint32 SGCount = 0; int err_ret = 0; - - void *pnext; + void *pnext, *pprev; + uint ttl_len, dma_len, lft_len, xfred_len, pkt_len; + uint blk_num; + struct mmc_request mmc_req; + struct mmc_command mmc_cmd; + struct mmc_data mmc_dat; sd_trace(("%s: Enter\n", __FUNCTION__)); @@ -910,66 +912,148 @@ sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func, DHD_PM_RESUME_WAIT(sdioh_request_packet_wait); DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); - /* Claim host controller */ - sdio_claim_host(gInstance->func[func]); - for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) { - uint pkt_len = PKTLEN(sd->osh, pnext); - pkt_len += 3; - pkt_len &= 0xFFFFFFFC; + ttl_len = xfred_len = 0; + /* at least 4 bytes alignment of skb buff is guaranteed */ + for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) + ttl_len += PKTLEN(sd->osh, pnext); -#ifdef CONFIG_MMC_MSM7X00A - if ((pkt_len % 64) == 32) { - sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__)); - pkt_len += 32; - } -#endif /* CONFIG_MMC_MSM7X00A */ - /* Make sure the packet is aligned properly. If it isn't, then this - * is the fault of sdioh_request_buffer() which is supposed to give - * us something we can work with. - */ - ASSERT(((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) == 0); - - if ((write) && (!fifo)) { - err_ret = sdio_memcpy_toio(gInstance->func[func], addr, - ((uint8*)PKTDATA(sd->osh, pnext)), - pkt_len); - } else if (write) { - err_ret = sdio_memcpy_toio(gInstance->func[func], addr, - ((uint8*)PKTDATA(sd->osh, pnext)), - pkt_len); - } else if (fifo) { - err_ret = sdio_readsb(gInstance->func[func], - ((uint8*)PKTDATA(sd->osh, pnext)), - addr, - pkt_len); - } else { - err_ret = sdio_memcpy_fromio(gInstance->func[func], - ((uint8*)PKTDATA(sd->osh, pnext)), - addr, + if (!sd->use_rxchain || ttl_len <= sd->client_block_size[func]) { + blk_num = 0; + dma_len = 0; + } else { + blk_num = ttl_len / sd->client_block_size[func]; + dma_len = blk_num * sd->client_block_size[func]; + } + lft_len = ttl_len - dma_len; + + sd_trace(("%s: %s %dB to func%d:%08x, %d blks with DMA, %dB leftover\n", + __FUNCTION__, write ? "W" : "R", + ttl_len, func, addr, blk_num, lft_len)); + + if (0 != dma_len) { + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + /* Set up DMA descriptors */ + pprev = pkt; + for (pnext = pkt; + pnext && dma_len; + pnext = PKTNEXT(sd->osh, pnext)) { + pkt_len = PKTLEN(sd->osh, pnext); + + if (dma_len > pkt_len) + dma_len -= pkt_len; + else { + pkt_len = xfred_len = dma_len; + dma_len = 0; + pkt = pnext; + } + + sg_set_buf(&sd->sg_list[SGCount++], + (uint8*)PKTDATA(sd->osh, pnext), pkt_len); - } - if (err_ret) { - sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n", - __FUNCTION__, - (write) ? "TX" : "RX", - pnext, SGCount, addr, pkt_len, err_ret)); - } else { - sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n", - __FUNCTION__, - (write) ? "TX" : "RX", - pnext, SGCount, addr, pkt_len)); + if (SGCount >= SDIOH_SDMMC_MAX_SG_ENTRIES) { + sd_err(("%s: sg list entries exceed limit\n", + __FUNCTION__)); + return (SDIOH_API_RC_FAIL); + } } - if (!fifo) { - addr += pkt_len; - } - SGCount ++; + mmc_dat.sg = sd->sg_list; + mmc_dat.sg_len = SGCount; + mmc_dat.blksz = sd->client_block_size[func]; + mmc_dat.blocks = blk_num; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + + mmc_cmd.opcode = 53; /* SD_IO_RW_EXTENDED */ + mmc_cmd.arg = write ? 1<<31 : 0; + mmc_cmd.arg |= (func & 0x7) << 28; + mmc_cmd.arg |= 1<<27; + mmc_cmd.arg |= fifo ? 0 : 1<<26; + mmc_cmd.arg |= (addr & 0x1FFFF) << 9; + mmc_cmd.arg |= blk_num & 0x1FF; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + + sdio_claim_host(gInstance->func[func]); + mmc_set_data_timeout(&mmc_dat, gInstance->func[func]->card); + mmc_wait_for_req(gInstance->func[func]->card->host, &mmc_req); + sdio_release_host(gInstance->func[func]); + err_ret = mmc_cmd.error? mmc_cmd.error : mmc_dat.error; + if (0 != err_ret) { + sd_err(("%s:CMD53 %s failed with code %d\n", + __FUNCTION__, + write ? "write" : "read", + err_ret)); + sd_err(("%s:Disabling rxchain and fire it with PIO\n", + __FUNCTION__)); + sd->use_rxchain = FALSE; + pkt = pprev; + lft_len = ttl_len; + } else if (!fifo) { + addr = addr + ttl_len - lft_len - dma_len; + } } - /* Release host controller */ - sdio_release_host(gInstance->func[func]); + /* PIO mode */ + if (0 != lft_len) { + /* Claim host controller */ + sdio_claim_host(gInstance->func[func]); + for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) { + uint8 *buf = (uint8*)PKTDATA(sd->osh, pnext) + + xfred_len; + pkt_len = PKTLEN(sd->osh, pnext); + if (0 != xfred_len) { + pkt_len -= xfred_len; + xfred_len = 0; + } + pkt_len = (pkt_len + 3) & 0xFFFFFFFC; +#ifdef CONFIG_MMC_MSM7X00A + if ((pkt_len % 64) == 32) { + sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__)); + pkt_len += 32; + } +#endif /* CONFIG_MMC_MSM7X00A */ + + if ((write) && (!fifo)) + err_ret = sdio_memcpy_toio( + gInstance->func[func], + addr, buf, pkt_len); + else if (write) + err_ret = sdio_memcpy_toio( + gInstance->func[func], + addr, buf, pkt_len); + else if (fifo) + err_ret = sdio_readsb( + gInstance->func[func], + buf, addr, pkt_len); + else + err_ret = sdio_memcpy_fromio( + gInstance->func[func], + buf, addr, pkt_len); + + if (err_ret) + sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=%d\n", + __FUNCTION__, + (write) ? "TX" : "RX", + pnext, SGCount, addr, pkt_len, err_ret)); + else + sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n", + __FUNCTION__, + (write) ? "TX" : "RX", + pnext, SGCount, addr, pkt_len)); + + if (!fifo) + addr += pkt_len; + SGCount ++; + } + sdio_release_host(gInstance->func[func]); + } sd_trace(("%s: Exit\n", __FUNCTION__)); return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); @@ -1179,6 +1263,7 @@ static void IRQHandlerF2(struct sdio_func *func) sd = gInstance->sd; ASSERT(sd != NULL); + BCM_REFERENCE(sd); } #endif /* !defined(OOB_INTR_ONLY) */ @@ -1230,8 +1315,10 @@ sdioh_start(sdioh_info_t *si, int stage) 2.6.27. The implementation prior to that is buggy, and needs broadcom's patch for it */ - if ((ret = sdio_reset_comm(gInstance->func[0]->card))) + if ((ret = sdio_reset_comm(gInstance->func[0]->card))) { sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret)); + return ret; + } else { sd->num_funcs = 2; sd->sd_blockmode = TRUE; diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c index 0fa2329..a78faeb 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_sdmmc_linux.c,v 1.8.6.2 2011-02-01 18:38:36 Exp $ + * $Id: bcmsdh_sdmmc_linux.c 312783 2012-02-03 22:53:56Z $ */ #include <typedefs.h> @@ -55,13 +55,25 @@ #if !defined(SDIO_DEVICE_ID_BROADCOM_4319) #define SDIO_DEVICE_ID_BROADCOM_4319 0x4319 #endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4319) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4330) +#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4330) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4334) +#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4334) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4324) +#define SDIO_DEVICE_ID_BROADCOM_4324 0x4324 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4324) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_43239) +#define SDIO_DEVICE_ID_BROADCOM_43239 43239 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_43239) */ #include <bcmsdh_sdmmc.h> #include <dhd_dbg.h> #ifdef WL_CFG80211 -extern void wl_cfg80211_set_sdio_func(void *func); +extern void wl_cfg80211_set_parent_dev(void *dev); #endif extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd); @@ -89,7 +101,6 @@ PBCMSDH_SDMMC_INSTANCE gInstance; extern int bcmsdh_probe(struct device *dev); extern int bcmsdh_remove(struct device *dev); - extern volatile bool dhd_mmc_suspend; static int bcmsdh_sdmmc_probe(struct sdio_func *func, @@ -118,7 +129,7 @@ static int bcmsdh_sdmmc_probe(struct sdio_func *func, if (func->num == 2) { #ifdef WL_CFG80211 - wl_cfg80211_set_sdio_func(func); + wl_cfg80211_set_parent_dev(&func->dev); #endif sd_trace(("F2 found, calling bcmsdh_probe...\n")); ret = bcmsdh_probe(&func->dev); @@ -153,24 +164,48 @@ static const struct sdio_device_id bcmsdh_sdmmc_ids[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) }, { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) }, { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4324) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43239) }, { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE) }, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids); -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) static int bcmsdh_sdmmc_suspend(struct device *pdev) { struct sdio_func *func = dev_to_sdio_func(pdev); + mmc_pm_flag_t sdio_flags; + int ret; if (func->num != 2) return 0; + + sd_trace(("%s Enter\n", __FUNCTION__)); + if (dhd_os_check_wakelock(bcmsdh_get_drvdata())) return -EBUSY; + + sdio_flags = sdio_get_host_pm_caps(func); + + if (!(sdio_flags & MMC_PM_KEEP_POWER)) { + sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__)); + return -EINVAL; + } + + /* keep power while host suspended */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) { + sd_err(("%s: error while trying to keep power\n", __FUNCTION__)); + return ret; + } + #if defined(OOB_INTR_ONLY) bcmsdh_oob_intr_set(0); -#endif +#endif /* defined(OOB_INTR_ONLY) */ dhd_mmc_suspend = TRUE; smp_mb(); @@ -179,13 +214,16 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev) static int bcmsdh_sdmmc_resume(struct device *pdev) { +#if defined(OOB_INTR_ONLY) struct sdio_func *func = dev_to_sdio_func(pdev); - +#endif + sd_trace(("%s Enter\n", __FUNCTION__)); dhd_mmc_suspend = FALSE; #if defined(OOB_INTR_ONLY) if ((func->num == 2) && dhd_os_check_if_up(bcmsdh_get_drvdata())) bcmsdh_oob_intr_set(1); -#endif +#endif /* (OOB_INTR_ONLY) */ + smp_mb(); return 0; } @@ -194,18 +232,18 @@ static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = { .suspend = bcmsdh_sdmmc_suspend, .resume = bcmsdh_sdmmc_resume, }; -#endif +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ static struct sdio_driver bcmsdh_sdmmc_driver = { .probe = bcmsdh_sdmmc_probe, .remove = bcmsdh_sdmmc_remove, .name = "bcmsdh_sdmmc", .id_table = bcmsdh_sdmmc_ids, -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) .drv = { .pm = &bcmsdh_sdmmc_pm_ops, }, -#endif +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ }; struct sdos_info { diff --git a/drivers/net/wireless/bcmdhd/bcmutils.c b/drivers/net/wireless/bcmdhd/bcmutils.c index fbdd7cd..6b578e6 100644 --- a/drivers/net/wireless/bcmdhd/bcmutils.c +++ b/drivers/net/wireless/bcmdhd/bcmutils.c @@ -20,7 +20,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: bcmutils.c,v 1.277.2.18 2011-01-26 02:32:08 Exp $ + * $Id: bcmutils.c,v 1.277.2.18 2011-01-26 02:32:08 $ */ #include <typedefs.h> @@ -987,7 +987,6 @@ pktsetprio(void *pkt, bool update_vtag) return (rc | priority); } -#ifndef BCM_BOOTLOADER static char bcm_undeferrstr[32]; static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE; @@ -1009,7 +1008,6 @@ bcmerrorstr(int bcmerror) return bcmerrorstrtable[-bcmerror]; } -#endif /* !BCM_BOOTLOADER */ diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 84d1761..3b4dc7a 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd.h 290844 2011-10-20 08:54:39Z $ + * $Id: dhd.h 333052 2012-05-12 02:09:28Z $ */ /**************** @@ -51,6 +51,7 @@ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_HAS_WAKELOCK) #include <linux/wakelock.h> #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined (CONFIG_HAS_WAKELOCK) */ + /* The kernel threading is sdio-specific */ struct task_struct; struct sched_param; @@ -76,13 +77,24 @@ enum dhd_bus_state { /* Firmware requested operation mode */ #define STA_MASK 0x0001 -#define HOSTAPD_MASK 0x0002 +#define HOSTAPD_MASK 0x0002 #define WFD_MASK 0x0004 -#define SOFTAP_FW_MASK 0x0008 +#define SOFTAP_FW_MASK 0x0008 +#define P2P_GO_ENABLED 0x0010 +#define P2P_GC_ENABLED 0x0020 +#define CONCURENT_MASK 0x00F0 + +#define MANUFACTRING_FW "WLTEST" /* max sequential rxcntl timeouts to set HANG event */ #define MAX_CNTL_TIMEOUT 2 +#define DHD_SCAN_ACTIVE_TIME 40 /* ms : Embedded default Active setting from DHD Driver */ +#define DHD_SCAN_PASSIVE_TIME 130 /* ms: Embedded default Passive setting from DHD Driver */ + +#define DHD_BEACON_TIMEOUT_NORMAL 4 +#define DHD_BEACON_TIMEOUT_HIGH 10 + enum dhd_bus_wake_state { WAKE_LOCK_OFF, WAKE_LOCK_PRIV, @@ -115,7 +127,6 @@ typedef enum { DHD_IF_DELETING } dhd_if_state_t; - #if defined(CONFIG_DHD_USE_STATIC_BUF) uint8* dhd_os_prealloc(void *osh, int section, uint size); @@ -202,9 +213,11 @@ typedef struct dhd_pub { char eventmask[WL_EVENTING_MASK_LEN]; int op_mode; /* STA, HostAPD, WFD, SoftAP */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_HAS_WAKELOCK) - struct wake_lock wakelock[WAKE_LOCK_MAX]; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined (CONFIG_HAS_WAKELOCK) */ +/* Set this to 1 to use a seperate interface (p2p0) for p2p operations. + * For ICS MR1 releases it should be disable to be compatable with ICS MR1 Framework + * see target dhd-cdc-sdmmc-panda-cfg80211-icsmr1-gpl-debug in Makefile + */ + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 struct mutex wl_start_stop_lock; /* lock/unlock for Android start/stop */ struct mutex wl_softap_lock; /* lock/unlock for any SoftAP/STA settings */ @@ -286,7 +299,8 @@ void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags); extern int dhd_os_wake_lock(dhd_pub_t *pub); extern int dhd_os_wake_unlock(dhd_pub_t *pub); extern int dhd_os_wake_lock_timeout(dhd_pub_t *pub); -extern int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub, int val); +extern int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val); +extern int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val); inline static void MUTEX_LOCK_SOFTAP_SET_INIT(dhd_pub_t * dhdp) { @@ -312,8 +326,8 @@ inline static void MUTEX_UNLOCK_SOFTAP_SET(dhd_pub_t * dhdp) #define DHD_OS_WAKE_LOCK(pub) dhd_os_wake_lock(pub) #define DHD_OS_WAKE_UNLOCK(pub) dhd_os_wake_unlock(pub) #define DHD_OS_WAKE_LOCK_TIMEOUT(pub) dhd_os_wake_lock_timeout(pub) -#define DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(pub, val) dhd_os_wake_lock_timeout_enable(pub, val) - +#define DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(pub, val) dhd_os_wake_lock_rx_timeout_enable(pub, val) +#define DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(pub, val) dhd_os_wake_lock_ctrl_timeout_enable(pub, val) #define DHD_PACKET_TIMEOUT_MS 1000 #define DHD_EVENT_TIMEOUT_MS 1500 @@ -365,6 +379,11 @@ void dhd_osl_detach(osl_t *osh); * bus_hdrlen specifies required headroom for bus module header. */ extern dhd_pub_t *dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen); +#if defined(WLP2P) && defined(WL_CFG80211) +/* To allow attach/detach calls corresponding to p2p0 interface */ +extern int dhd_attach_p2p(dhd_pub_t *); +extern int dhd_detach_p2p(dhd_pub_t *); +#endif /* WLP2P && WL_CFG80211 */ extern int dhd_net_attach(dhd_pub_t *dhdp, int idx); /* Indication from bus module regarding removal/absence of dongle */ @@ -411,27 +430,37 @@ extern int dhd_custom_get_mac_address(unsigned char *buf); extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); +extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); + +#ifdef PNO_SUPPORT extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); extern int dhd_pno_clean(dhd_pub_t *dhd); extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_pno_set_ex(dhd_pub_t *dhd, wl_pfn_t* ssidnet, int nssid, + ushort pno_interval, int pno_repeat, int pno_expo_max, int pno_lost_time); extern int dhd_pno_get_status(dhd_pub_t *dhd); extern int dhd_dev_pno_reset(struct net_device *dev); extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_dev_pno_set_ex(struct net_device *dev, wl_pfn_t* ssidnet, int nssid, + ushort pno_interval, int pno_repeat, int pno_expo_max, int pno_lost_time); extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); extern int dhd_dev_get_pno_status(struct net_device *dev); -extern int dhd_get_dtim_skip(dhd_pub_t *dhd); -extern bool dhd_check_ap_wfd_mode_set(dhd_pub_t *dhd); -extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); +#endif /* PNO_SUPPORT */ #define DHD_UNICAST_FILTER_NUM 0 #define DHD_BROADCAST_FILTER_NUM 1 #define DHD_MULTICAST4_FILTER_NUM 2 #define DHD_MULTICAST6_FILTER_NUM 3 +#define DHD_MDNS_FILTER_NUM 4 +extern int dhd_os_set_packet_filter(dhd_pub_t *dhdp, int val); extern int net_os_set_packet_filter(struct net_device *dev, int val); extern int net_os_rxfilter_add_remove(struct net_device *dev, int val, int num); +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); +extern bool dhd_check_ap_wfd_mode_set(dhd_pub_t *dhd); + #ifdef DHD_DEBUG extern int write_to_file(dhd_pub_t *dhd, uint8 *buf, int size); #endif /* DHD_DEBUG */ @@ -453,7 +482,7 @@ extern int dhd_timeout_expired(dhd_timeout_t *tmo); extern int dhd_ifname2idx(struct dhd_info *dhd, char *name); extern int dhd_net2idx(struct dhd_info *dhd, struct net_device *net); -extern struct net_device * dhd_idx2net(struct dhd_pub *dhd_pub, int ifidx); +extern struct net_device * dhd_idx2net(void *pub, int ifidx); extern int wl_host_event(dhd_pub_t *dhd_pub, int *idx, void *pktdata, wl_event_msg_t *, void **data_ptr); extern void wl_event_to_host_order(wl_event_msg_t * evt); @@ -465,6 +494,7 @@ extern int dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uin extern struct dhd_cmn *dhd_common_init(osl_t *osh); extern void dhd_common_deinit(dhd_pub_t *dhd_pub, dhd_cmn_t *sa_cmn); +extern int dhd_do_driver_init(struct net_device *net); extern int dhd_add_if(struct dhd_info *dhd, int ifidx, void *handle, char *name, uint8 *mac_addr, uint32 flags, uint8 bssidx); extern void dhd_del_if(struct dhd_info *dhd, int ifidx); @@ -488,7 +518,8 @@ extern uint dhd_bus_status(dhd_pub_t *dhdp); extern int dhd_bus_start(dhd_pub_t *dhdp); extern int dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size); extern void dhd_print_buf(void *pbuf, int len, int bytes_per_line); -extern bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf); +extern bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval); +extern uint dhd_bus_chip_id(dhd_pub_t *dhdp); #if defined(KEEP_ALIVE) extern int dhd_keep_alive_onoff(dhd_pub_t *dhd); @@ -701,12 +732,6 @@ typedef struct dhd_pkttag { #define DHD_PKTTAG_DSTN(tag) ((dhd_pkttag_t*)(tag))->dstn_ether typedef int (*f_commitpkt_t)(void* ctx, void* p); -int dhd_wlfc_enable(dhd_pub_t *dhd); -int dhd_wlfc_interface_event(struct dhd_info *, uint8 action, uint8 ifid, uint8 iftype, uint8* ea); -int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data); -int dhd_wlfc_event(struct dhd_info *dhd); -int dhd_os_wlfc_block(dhd_pub_t *pub); -int dhd_os_wlfc_unblock(dhd_pub_t *pub); #ifdef PROP_TXSTATUS_DEBUG #define DHD_WLFC_CTRINC_MAC_CLOSE(entry) do { (entry)->closed_ct++; } while (0) diff --git a/drivers/net/wireless/bcmdhd/dhd_cdc.c b/drivers/net/wireless/bcmdhd/dhd_cdc.c index 8460992..f16d81c 100644 --- a/drivers/net/wireless/bcmdhd/dhd_cdc.c +++ b/drivers/net/wireless/bcmdhd/dhd_cdc.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_cdc.c,v 1.51.6.31 2011-02-09 14:31:43 Exp $ + * $Id: dhd_cdc.c 324280 2012-03-28 19:01:17Z $ * * BDC is like CDC, except it includes a header for data packets to convey * packet priority over the bus, and flags (e.g. to indicate checksum status @@ -78,6 +78,8 @@ typedef struct dhd_prot { unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN]; } dhd_prot_t; +extern int dhd_dbus_txdata(dhd_pub_t *dhdp, void *pktbuf); + static int dhdcdc_msg(dhd_pub_t *dhd) { @@ -2174,6 +2176,7 @@ dhd_wlfc_init(dhd_pub_t *dhd) WLFC_FLAGS_CREDIT_STATUS_SIGNALS | WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE : 0; + dhd->wlfc_state = NULL; /* try to enable/disable signaling by sending "tlv" iovar. if that fails, @@ -2463,7 +2466,7 @@ fail: #ifndef CONFIG_DHD_USE_STATIC_BUF if (cdc != NULL) MFREE(dhd->osh, cdc, sizeof(dhd_prot_t)); -#endif +#endif /* CONFIG_DHD_USE_STATIC_BUF */ return BCME_NOMEM; } @@ -2476,7 +2479,7 @@ dhd_prot_detach(dhd_pub_t *dhd) #endif #ifndef CONFIG_DHD_USE_STATIC_BUF MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t)); -#endif +#endif /* CONFIG_DHD_USE_STATIC_BUF */ dhd->prot = NULL; } @@ -2512,9 +2515,10 @@ dhd_prot_init(dhd_pub_t *dhd) ret = dhd_wlfc_init(dhd); #endif -#if !defined(WL_CFG80211) +#if defined(WL_CFG80211) + if (dhd_download_fw_on_driverload) +#endif /* defined(WL_CFG80211) */ ret = dhd_preinit_ioctls(dhd); -#endif /* WL_CFG80211 */ /* Always assumes wl for now */ dhd->iswl = TRUE; diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c new file mode 100644 index 0000000..bc1be94 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c @@ -0,0 +1,655 @@ +/* + * Linux cfg80211 driver - Dongle Host Driver (DHD) related + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + +#include <net/rtnetlink.h> + +#include <bcmutils.h> +#include <wldev_common.h> +#include <wl_cfg80211.h> +#include <dhd_cfg80211.h> +extern struct wl_priv *wlcfg_drv_priv; +static int dhd_dongle_up = FALSE; + +#include <dngl_stats.h> +#include <dhd.h> +#include <dhdioctl.h> +#include <wlioctl.h> +#include <dhd_cfg80211.h> + +static s32 wl_dongle_up(struct net_device *ndev, u32 up); + +/** + * Function implementations + */ + +s32 dhd_cfg80211_init(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_deinit(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_get_opmode(struct wl_priv *wl) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + return dhd->op_mode; +} + +s32 dhd_cfg80211_down(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + int bcn_timeout = DHD_BEACON_TIMEOUT_HIGH; + char iovbuf[30]; + + dhd->op_mode |= val; + WL_ERR(("Set : op_mode=%d\n", dhd->op_mode)); + +#ifdef ARP_OFFLOAD_SUPPORT + /* IF P2P is enabled, disable arpoe */ + dhd_arp_offload_set(dhd, 0); + dhd_arp_offload_enable(dhd, false); +#endif /* ARP_OFFLOAD_SUPPORT */ + + dhd_os_set_packet_filter(dhd, 0); + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + + return 0; +} + +s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + int bcn_timeout = DHD_BEACON_TIMEOUT_NORMAL; + char iovbuf[30]; + + dhd->op_mode &= ~CONCURENT_MASK; + WL_ERR(("Clean : op_mode=%d\n", dhd->op_mode)); + +#ifdef ARP_OFFLOAD_SUPPORT + /* IF P2P is disabled, enable arpoe back for STA mode. */ + dhd_arp_offload_set(dhd, dhd_arp_mode); + dhd_arp_offload_enable(dhd, true); +#endif /* ARP_OFFLOAD_SUPPORT */ + + dhd_os_set_packet_filter(dhd, 1); + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + return 0; +} + +static s32 wl_dongle_up(struct net_device *ndev, u32 up) +{ + s32 err = 0; + + err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true); + if (unlikely(err)) { + WL_ERR(("WLC_UP error (%d)\n", err)); + } + return err; +} + +s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock) +{ +#ifndef DHD_SDALIGN +#define DHD_SDALIGN 32 +#endif + struct net_device *ndev; + s32 err = 0; + + WL_TRACE(("In\n")); + if (dhd_dongle_up) { + WL_ERR(("Dongle is already up\n")); + return err; + } + + ndev = wl_to_prmry_ndev(wl); + + if (need_lock) + rtnl_lock(); + + err = wl_dongle_up(ndev, 0); + if (unlikely(err)) { + WL_ERR(("wl_dongle_up failed\n")); + goto default_conf_out; + } + dhd_dongle_up = true; + +default_conf_out: + if (need_lock) + rtnl_unlock(); + return err; + +} + + +/* TODO: clean up the BT-Coex code, it still have some legacy ioctl/iovar functions */ +#define COEX_DHCP + +#if defined(COEX_DHCP) + +/* use New SCO/eSCO smart YG suppression */ +#define BT_DHCP_eSCO_FIX +/* this flag boost wifi pkt priority to max, caution: -not fair to sco */ +#define BT_DHCP_USE_FLAGS +/* T1 start SCO/ESCo priority suppression */ +#define BT_DHCP_OPPR_WIN_TIME 2500 +/* T2 turn off SCO/SCO supperesion is (timeout) */ +#define BT_DHCP_FLAG_FORCE_TIME 5500 + +enum wl_cfg80211_btcoex_status { + BT_DHCP_IDLE, + BT_DHCP_START, + BT_DHCP_OPPR_WIN, + BT_DHCP_FLAG_FORCE_TIMEOUT +}; + +/* + * get named driver variable to uint register value and return error indication + * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, ®_value) + */ +static int +dev_wlc_intvar_get_reg(struct net_device *dev, char *name, + uint reg, int *retval) +{ + union { + char buf[WLC_IOCTL_SMLEN]; + int val; + } var; + int error; + + bcm_mkiovar(name, (char *)(®), sizeof(reg), + (char *)(&var), sizeof(var.buf)); + error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false); + + *retval = dtoh32(var.val); + return (error); +} + +static int +dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + char ioctlbuf_local[1024]; +#else + static char ioctlbuf_local[1024]; +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ + + bcm_mkiovar(name, buf, len, ioctlbuf_local, sizeof(ioctlbuf_local)); + + return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf_local, sizeof(ioctlbuf_local), true)); +} +/* +get named driver variable to uint register value and return error indication +calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value) +*/ +static int +dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val) +{ + char reg_addr[8]; + + memset(reg_addr, 0, sizeof(reg_addr)); + memcpy((char *)®_addr[0], (char *)addr, 4); + memcpy((char *)®_addr[4], (char *)val, 4); + + return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr))); +} + +static bool btcoex_is_sco_active(struct net_device *dev) +{ + int ioc_res = 0; + bool res = FALSE; + int sco_id_cnt = 0; + int param27; + int i; + + for (i = 0; i < 12; i++) { + + ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); + + WL_TRACE(("%s, sample[%d], btc params: 27:%x\n", + __FUNCTION__, i, param27)); + + if (ioc_res < 0) { + WL_ERR(("%s ioc read btc params error\n", __FUNCTION__)); + break; + } + + if ((param27 & 0x6) == 2) { /* count both sco & esco */ + sco_id_cnt++; + } + + if (sco_id_cnt > 2) { + WL_TRACE(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", + __FUNCTION__, sco_id_cnt, i)); + res = TRUE; + break; + } + + msleep(5); + } + + return res; +} + +#if defined(BT_DHCP_eSCO_FIX) +/* Enhanced BT COEX settings for eSCO compatibility during DHCP window */ +static int set_btc_esco_params(struct net_device *dev, bool trump_sco) +{ + static bool saved_status = FALSE; + + char buf_reg50va_dhcp_on[8] = + { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 }; + char buf_reg51va_dhcp_on[8] = + { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg64va_dhcp_on[8] = + { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg65va_dhcp_on[8] = + { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg71va_dhcp_on[8] = + { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + uint32 regaddr; + static uint32 saved_reg50; + static uint32 saved_reg51; + static uint32 saved_reg64; + static uint32 saved_reg65; + static uint32 saved_reg71; + + if (trump_sco) { + /* this should reduce eSCO agressive retransmit + * w/o breaking it + */ + + /* 1st save current */ + WL_TRACE(("Do new SCO/eSCO coex algo {save &" + "override}\n")); + if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { + saved_status = TRUE; + WL_TRACE(("%s saved bt_params[50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + __FUNCTION__, saved_reg50, saved_reg51, + saved_reg64, saved_reg65, saved_reg71)); + } else { + WL_ERR((":%s: save btc_params failed\n", + __FUNCTION__)); + saved_status = FALSE; + return -1; + } + + WL_TRACE(("override with [50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + *(u32 *)(buf_reg50va_dhcp_on+4), + *(u32 *)(buf_reg51va_dhcp_on+4), + *(u32 *)(buf_reg64va_dhcp_on+4), + *(u32 *)(buf_reg65va_dhcp_on+4), + *(u32 *)(buf_reg71va_dhcp_on+4))); + + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg50va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg51va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg64va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg65va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg71va_dhcp_on[0], 8); + + saved_status = TRUE; + } else if (saved_status) { + /* restore previously saved bt params */ + WL_TRACE(("Do new SCO/eSCO coex algo {save &" + "override}\n")); + + regaddr = 50; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg50); + regaddr = 51; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg51); + regaddr = 64; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg64); + regaddr = 65; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg65); + regaddr = 71; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg71); + + WL_TRACE(("restore bt_params[50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + saved_reg50, saved_reg51, saved_reg64, + saved_reg65, saved_reg71)); + + saved_status = FALSE; + } else { + WL_ERR((":%s att to restore not saved BTCOEX params\n", + __FUNCTION__)); + return -1; + } + return 0; +} +#endif /* BT_DHCP_eSCO_FIX */ + +static void +wl_cfg80211_bt_setflag(struct net_device *dev, bool set) +{ +#if defined(BT_DHCP_USE_FLAGS) + char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 }; + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; +#endif + + +#if defined(BT_DHCP_eSCO_FIX) + /* set = 1, save & turn on 0 - off & restore prev settings */ + set_btc_esco_params(dev, set); +#endif + +#if defined(BT_DHCP_USE_FLAGS) + WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set)); + if (set == TRUE) + /* Forcing bt_flag7 */ + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_dhcp_on[0], + sizeof(buf_flag7_dhcp_on)); + else + /* Restoring default bt flag7 */ + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], + sizeof(buf_flag7_default)); +#endif +} + +static void wl_cfg80211_bt_timerfunc(ulong data) +{ + struct btcoex_info *bt_local = (struct btcoex_info *)data; + WL_TRACE(("%s\n", __FUNCTION__)); + bt_local->timer_on = 0; + schedule_work(&bt_local->work); +} + +static void wl_cfg80211_bt_handler(struct work_struct *work) +{ + struct btcoex_info *btcx_inf; + + btcx_inf = container_of(work, struct btcoex_info, work); + + if (btcx_inf->timer_on) { + btcx_inf->timer_on = 0; + del_timer_sync(&btcx_inf->timer); + } + + switch (btcx_inf->bt_state) { + case BT_DHCP_START: + /* DHCP started + * provide OPPORTUNITY window to get DHCP address + */ + WL_TRACE(("%s bt_dhcp stm: started \n", + __FUNCTION__)); + btcx_inf->bt_state = BT_DHCP_OPPR_WIN; + mod_timer(&btcx_inf->timer, + jiffies + BT_DHCP_OPPR_WIN_TIME*HZ/1000); + btcx_inf->timer_on = 1; + break; + + case BT_DHCP_OPPR_WIN: + if (btcx_inf->dhcp_done) { + WL_TRACE(("%s DHCP Done before T1 expiration\n", + __FUNCTION__)); + goto btc_coex_idle; + } + + /* DHCP is not over yet, start lowering BT priority + * enforce btc_params + flags if necessary + */ + WL_TRACE(("%s DHCP T1:%d expired\n", __FUNCTION__, + BT_DHCP_OPPR_WIN_TIME)); + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE); + btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; + mod_timer(&btcx_inf->timer, + jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); + btcx_inf->timer_on = 1; + break; + + case BT_DHCP_FLAG_FORCE_TIMEOUT: + if (btcx_inf->dhcp_done) { + WL_TRACE(("%s DHCP Done before T2 expiration\n", + __FUNCTION__)); + } else { + /* Noo dhcp during T1+T2, restore BT priority */ + WL_TRACE(("%s DHCP wait interval T2:%d" + "msec expired\n", __FUNCTION__, + BT_DHCP_FLAG_FORCE_TIME)); + } + + /* Restoring default bt priority */ + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); +btc_coex_idle: + btcx_inf->bt_state = BT_DHCP_IDLE; + btcx_inf->timer_on = 0; + break; + + default: + WL_ERR(("%s error g_status=%d !!!\n", __FUNCTION__, + btcx_inf->bt_state)); + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); + btcx_inf->bt_state = BT_DHCP_IDLE; + btcx_inf->timer_on = 0; + break; + } + + net_os_wake_unlock(btcx_inf->dev); +} + +int wl_cfg80211_btcoex_init(struct wl_priv *wl) +{ + struct btcoex_info *btco_inf = NULL; + + btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL); + if (!btco_inf) + return -ENOMEM; + + btco_inf->bt_state = BT_DHCP_IDLE; + btco_inf->ts_dhcp_start = 0; + btco_inf->ts_dhcp_ok = 0; + /* Set up timer for BT */ + btco_inf->timer_ms = 10; + init_timer(&btco_inf->timer); + btco_inf->timer.data = (ulong)btco_inf; + btco_inf->timer.function = wl_cfg80211_bt_timerfunc; + + btco_inf->dev = wl->wdev->netdev; + + INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler); + + wl->btcoex_info = btco_inf; + return 0; +} + +void wl_cfg80211_btcoex_deinit(struct wl_priv *wl) +{ + if (!wl->btcoex_info) + return; + + if (!wl->btcoex_info->timer_on) { + wl->btcoex_info->timer_on = 0; + del_timer_sync(&wl->btcoex_info->timer); + } + + cancel_work_sync(&wl->btcoex_info->work); + + kfree(wl->btcoex_info); + wl->btcoex_info = NULL; +} + +int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) +{ + + struct wl_priv *wl = wlcfg_drv_priv; + char powermode_val = 0; + char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 }; + char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 }; + char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 }; + + uint32 regaddr; + static uint32 saved_reg66; + static uint32 saved_reg41; + static uint32 saved_reg68; + static bool saved_status = FALSE; + +#ifdef COEX_DHCP + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; + struct btcoex_info *btco_inf = wl->btcoex_info; +#endif /* COEX_DHCP */ + + /* Figure out powermode 1 or o command */ + strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1); + + if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { + + WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__)); + + /* Retrieve and saved orig regs value */ + if ((saved_status == FALSE) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { + saved_status = TRUE; + WL_TRACE(("Saved 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + + /* Disable PM mode during dhpc session */ + + /* Disable PM mode during dhpc session */ +#ifdef COEX_DHCP + /* Start BT timer only for SCO connection */ + if (btcoex_is_sco_active(dev)) { + /* btc_params 66 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg66va_dhcp_on[0], + sizeof(buf_reg66va_dhcp_on)); + /* btc_params 41 0x33 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg41va_dhcp_on[0], + sizeof(buf_reg41va_dhcp_on)); + /* btc_params 68 0x190 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg68va_dhcp_on[0], + sizeof(buf_reg68va_dhcp_on)); + saved_status = TRUE; + + btco_inf->bt_state = BT_DHCP_START; + btco_inf->timer_on = 1; + mod_timer(&btco_inf->timer, btco_inf->timer.expires); + WL_TRACE(("%s enable BT DHCP Timer\n", + __FUNCTION__)); + } +#endif /* COEX_DHCP */ + } + else if (saved_status == TRUE) { + WL_ERR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); + } + } + else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) { + + + /* Restoring PM mode */ + +#ifdef COEX_DHCP + /* Stop any bt timer because DHCP session is done */ + WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__)); + if (btco_inf->timer_on) { + btco_inf->timer_on = 0; + del_timer_sync(&btco_inf->timer); + + if (btco_inf->bt_state != BT_DHCP_IDLE) { + /* need to restore original btc flags & extra btc params */ + WL_TRACE(("%s bt->bt_state:%d\n", + __FUNCTION__, btco_inf->bt_state)); + /* wake up btcoex thread to restore btlags+params */ + schedule_work(&btco_inf->work); + } + } + + /* Restoring btc_flag paramter anyway */ + if (saved_status == TRUE) + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); +#endif /* COEX_DHCP */ + + /* Restore original values */ + if (saved_status == TRUE) { + regaddr = 66; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg66); + regaddr = 41; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg41); + regaddr = 68; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg68); + + WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + } + saved_status = FALSE; + + } + else { + WL_ERR(("%s Unkwown yet power setting, ignored\n", + __FUNCTION__)); + } + + snprintf(command, 3, "OK"); + + return (strlen("OK")); +} +#endif diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.h b/drivers/net/wireless/bcmdhd/dhd_cfg80211.h new file mode 100644 index 0000000..ced46db --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.h @@ -0,0 +1,45 @@ +/* + * Linux cfg80211 driver - Dongle Host Driver (DHD) related + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + + +#ifndef __DHD_CFG80211__ +#define __DHD_CFG80211__ + +#include <wl_cfg80211.h> +#include <wl_cfgp2p.h> + +s32 dhd_cfg80211_init(struct wl_priv *wl); +s32 dhd_cfg80211_deinit(struct wl_priv *wl); +s32 dhd_cfg80211_get_opmode(struct wl_priv *wl); +s32 dhd_cfg80211_down(struct wl_priv *wl); +s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val); +s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl); +s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock); + +int wl_cfg80211_btcoex_init(struct wl_priv *wl); +void wl_cfg80211_btcoex_deinit(struct wl_priv *wl); + +#endif /* __DHD_CFG80211__ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c index 9df70a0..d5af27f 100644 --- a/drivers/net/wireless/bcmdhd/dhd_common.c +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_common.c 290546 2011-10-19 01:55:21Z $ + * $Id: dhd_common.c 331276 2012-05-04 08:05:57Z $ */ #include <typedefs.h> #include <osl.h> @@ -134,7 +134,7 @@ enum { }; const bcm_iovar_t dhd_iovars[] = { - {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version) }, + {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version) }, #ifdef DHD_DEBUG {"msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, #endif /* DHD_DEBUG */ @@ -300,7 +300,7 @@ dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int le dhd_os_proto_block(dhd_pub); ret = dhd_prot_ioctl(dhd_pub, ifindex, ioc, buf, len); - if (!ret) + if (ret) dhd_os_check_hang(dhd_pub, ifindex, ret); dhd_os_proto_unblock(dhd_pub); @@ -336,6 +336,11 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch case IOV_SVAL(IOV_MSGLEVEL): dhd_msg_level = int_val; +#ifdef WL_CFG80211 + /* Enable DHD and WL logs in oneshot */ + if (dhd_msg_level & DHD_WL_VAL) + wl_cfg80211_enable_trace(dhd_msg_level); +#endif break; case IOV_GVAL(IOV_BCMERRORSTR): bcm_strncpy_s((char *)arg, len, bcmerrorstr(dhd_pub->bcmerror), BCME_STRLEN); @@ -857,6 +862,8 @@ wl_show_host_event(wl_event_msg_t *event, void *event_data) break; case WLC_E_SCAN_COMPLETE: + case WLC_E_ASSOC_REQ_IE: + case WLC_E_ASSOC_RESP_IE: case WLC_E_PMKID_CACHE: DHD_EVENT(("MACEVENT: %s\n", event_name)); break; @@ -989,6 +996,9 @@ wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, datalen = ntoh32_ua((void *)&event->datalen); evlen = datalen + sizeof(bcm_event_t); + DHD_TRACE(("RX: event_type:%d flags:%d status:%d reason:%d \n", + type, flags, status, reason)); + switch (type) { #ifdef PROP_TXSTATUS case WLC_E_FIFO_CREDIT_MAP: @@ -1716,11 +1726,12 @@ fail: /* * returns = TRUE if associated, FALSE if not associated + * third paramter retval can return error from error */ -bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf) +bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval) { char bssid[6], zbuf[6]; - int ret = -1; + int ret; bzero(bssid, 6); bzero(zbuf, 6); @@ -1728,6 +1739,9 @@ bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf) ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_BSSID, (char *)&bssid, ETHER_ADDR_LEN, FALSE, 0); DHD_TRACE((" %s WLC_GET_BSSID ioctl res = %d\n", __FUNCTION__, ret)); + if (retval) + *retval = ret; + if (ret == BCME_NOTASSOCIATED) { DHD_TRACE(("%s: not associated! res:%d\n", __FUNCTION__, ret)); } @@ -1749,7 +1763,6 @@ bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf) } } - /* Function to estimate possible DTIM_SKIP value */ int dhd_get_dtim_skip(dhd_pub_t *dhd) @@ -1764,7 +1777,7 @@ dhd_get_dtim_skip(dhd_pub_t *dhd) bcn_li_dtim = dhd->dtim_skip; /* Check if associated */ - if (dhd_is_associated(dhd, NULL) == FALSE) { + if (dhd_is_associated(dhd, NULL, NULL) == FALSE) { DHD_TRACE(("%s NOT assoc ret %d\n", __FUNCTION__, ret)); goto exit; } @@ -1807,10 +1820,24 @@ exit: bool dhd_check_ap_wfd_mode_set(dhd_pub_t *dhd) { #ifdef WL_CFG80211 +#ifndef WL_ENABLE_P2P_IF + /* To be back compatble with ICS MR1 release where p2p interface + * disable but wlan0 used for p2p + */ if (((dhd->op_mode & HOSTAPD_MASK) == HOSTAPD_MASK) || - ((dhd->op_mode & WFD_MASK) == WFD_MASK)) + ((dhd->op_mode & WFD_MASK) == WFD_MASK)) { return TRUE; + } else +#else + /* concurent mode with p2p interface for wfd and wlan0 for sta */ + if (((dhd->op_mode & P2P_GO_ENABLED) == P2P_GO_ENABLED) || + ((dhd->op_mode & P2P_GC_ENABLED) == P2P_GC_ENABLED)) { + DHD_ERROR(("%s P2P enabled for mode=%d\n", __FUNCTION__, dhd->op_mode)); + return TRUE; + } + else +#endif /* WL_ENABLE_P2P_IF */ #endif /* WL_CFG80211 */ return FALSE; } @@ -1857,15 +1884,18 @@ dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) return ret; } - if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) - return (ret); memset(iovbuf, 0, sizeof(iovbuf)); - if ((pfn_enabled) && (dhd_is_associated(dhd, NULL) == TRUE)) { +#ifndef WL_SCHED_SCAN + if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) + return (ret); + + if ((pfn_enabled) && (dhd_is_associated(dhd, NULL, NULL) == TRUE)) { DHD_ERROR(("%s pno is NOT enable : called in assoc mode , ignore\n", __FUNCTION__)); return ret; } +#endif /* !WL_SCHED_SCAN */ /* Enable/disable PNO */ if ((ret = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf))) > 0) { @@ -1904,9 +1934,10 @@ dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, err = -1; return err; } - +#ifndef WL_SCHED_SCAN if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) return (err); +#endif /* !WL_SCHED_SCAN */ /* Check for broadcast ssid */ for (k = 0; k < nssid; k++) { @@ -2000,6 +2031,123 @@ dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, } int +dhd_pno_set_ex(dhd_pub_t *dhd, wl_pfn_t* ssidnet, int nssid, ushort pno_interval, + int pno_repeat, int pno_expo_max, int pno_lost_time) +{ + int err = -1; + char iovbuf[128]; + int k, i; + wl_pfn_param_t pfn_param; + wl_pfn_t pfn_element; + uint len = 0; + + DHD_TRACE(("%s nssid=%d pno_interval=%d\n", __FUNCTION__, nssid, pno_interval)); + + if ((!dhd) && (!ssidnet)) { + DHD_ERROR(("%s error exit\n", __FUNCTION__)); + err = -1; + return err; + } + + if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) + return (err); + + /* Check for broadcast ssid */ + for (k = 0; k < nssid; k++) { + if (!ssidnet[k].ssid.SSID_len) { + DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", k)); + return err; + } + } +/* #define PNO_DUMP 1 */ +#ifdef PNO_DUMP + { + int j; + for (j = 0; j < nssid; j++) { + DHD_ERROR(("%d: scan for %s size =%d\n", j, + ssidnet[j].ssid.SSID, ssidnet[j].ssid.SSID_len)); + } + } +#endif /* PNO_DUMP */ + + /* clean up everything */ + if ((err = dhd_pno_clean(dhd)) < 0) { + DHD_ERROR(("%s failed error=%d\n", __FUNCTION__, err)); + return err; + } + memset(iovbuf, 0, sizeof(iovbuf)); + memset(&pfn_param, 0, sizeof(pfn_param)); + memset(&pfn_element, 0, sizeof(pfn_element)); + + /* set pfn parameters */ + pfn_param.version = htod32(PFN_VERSION); + pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT)); + + /* check and set extra pno params */ + if ((pno_repeat != 0) || (pno_expo_max != 0)) { + pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = (uchar) (pno_repeat); + pfn_param.exp = (uchar) (pno_expo_max); + } + + /* set up pno scan fr */ + if (pno_interval != 0) + pfn_param.scan_freq = htod32(pno_interval); + + if (pfn_param.scan_freq > PNO_SCAN_MAX_FW_SEC) { + DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW_SEC)); + return err; + } + if (pfn_param.scan_freq < PNO_SCAN_MIN_FW_SEC) { + DHD_ERROR(("%s pno freq less %d sec\n", __FUNCTION__, PNO_SCAN_MIN_FW_SEC)); + return err; + } + + /* network lost time */ + pfn_param.lost_network_timeout = htod32(pno_lost_time); + + len = bcm_mkiovar("pfn_set", (char *)&pfn_param, sizeof(pfn_param), iovbuf, sizeof(iovbuf)); + if ((err = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { + DHD_ERROR(("%s pfn_set failed for error=%d\n", + __FUNCTION__, err)); + return err; + } else { + DHD_TRACE(("%s pfn_set OK with PNO time=%d repeat=%d max_adjust=%d\n", + __FUNCTION__, pfn_param.scan_freq, + pfn_param.repeat, pfn_param.exp)); + } + + /* set all pfn ssid */ + for (i = 0; i < nssid; i++) { + pfn_element.flags = htod32(ssidnet[i].flags); + pfn_element.infra = htod32(ssidnet[i].infra); + pfn_element.auth = htod32(ssidnet[i].auth); + pfn_element.wpa_auth = htod32(ssidnet[i].wpa_auth); + pfn_element.wsec = htod32(ssidnet[i].wsec); + + memcpy((char *)pfn_element.ssid.SSID, ssidnet[i].ssid.SSID, ssidnet[i].ssid.SSID_len); + pfn_element.ssid.SSID_len = htod32(ssidnet[i].ssid.SSID_len); + + if ((len = + bcm_mkiovar("pfn_add", (char *)&pfn_element, + sizeof(pfn_element), iovbuf, sizeof(iovbuf))) > 0) { + if ((err = + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { + DHD_ERROR(("%s pfn_add failed with ssidnet[%d] error=%d\n", + __FUNCTION__, i, err)); + return err; + } else { + DHD_TRACE(("%s pfn_add OK with ssidnet[%d]\n", __FUNCTION__, i)); + } + } else { + DHD_ERROR(("%s bcm_mkiovar failed with ssidnet[%d]\n", __FUNCTION__, i)); + } + } + + return err; +} + +int dhd_pno_get_status(dhd_pub_t *dhd) { int ret = -1; @@ -2052,6 +2200,7 @@ int dhd_keep_alive_onoff(dhd_pub_t *dhd) return res; } #endif /* defined(KEEP_ALIVE) */ + /* Android ComboSCAN support */ /* diff --git a/drivers/net/wireless/bcmdhd/dhd_dbg.h b/drivers/net/wireless/bcmdhd/dhd_dbg.h index a195cbe..01be6a1 100644 --- a/drivers/net/wireless/bcmdhd/dhd_dbg.h +++ b/drivers/net/wireless/bcmdhd/dhd_dbg.h @@ -29,8 +29,8 @@ #if defined(DHD_DEBUG) -#define DHD_ERROR(args) do {if ((dhd_msg_level & DHD_ERROR_VAL) && (net_ratelimit())) \ - printf args;} while (0) +#define DHD_ERROR(args) do {if ((dhd_msg_level & DHD_ERROR_VAL) && (net_ratelimit())) \ + printf args;} while (0) #define DHD_TRACE(args) do {if (dhd_msg_level & DHD_TRACE_VAL) printf args;} while (0) #define DHD_INFO(args) do {if (dhd_msg_level & DHD_INFO_VAL) printf args;} while (0) #define DHD_DATA(args) do {if (dhd_msg_level & DHD_DATA_VAL) printf args;} while (0) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index d4cc56f..d8c1c1b 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_linux.c 291449 2011-10-22 12:16:26Z $ + * $Id: dhd_linux.c 333885 2012-05-18 00:39:03Z $ */ #include <typedefs.h> @@ -49,6 +49,7 @@ #include <epivers.h> #include <bcmutils.h> #include <bcmendian.h> +#include <bcmdevs.h> #include <proto/ethernet.h> #include <dngl_stats.h> @@ -109,6 +110,7 @@ extern bool ap_fw_loaded; #include <wl_android.h> #ifdef ARP_OFFLOAD_SUPPORT +void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add); static int dhd_device_event(struct notifier_block *this, unsigned long event, void *ptr); @@ -127,6 +129,9 @@ DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait); #if defined(OOB_INTR_ONLY) extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable); #endif /* defined(OOB_INTR_ONLY) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(struct work_struct *work); +#endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) MODULE_LICENSE("GPL v2"); #endif /* LinuxVer */ @@ -148,16 +153,15 @@ print_tainted() #endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */ /* Linux wireless extension support */ -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) #include <wl_iw.h> extern wl_iw_extra_params_t g_wl_iw_params; -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ -#if defined(CONFIG_HAS_EARLYSUSPEND) +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) #include <linux/earlysuspend.h> -extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len); -extern int dhd_get_dtim_skip(dhd_pub_t *dhd); #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); #ifdef PKT_FILTER_SUPPORT extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg); @@ -211,9 +215,9 @@ static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0; /* Local private structure (extension of pub) */ typedef struct dhd_info { -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) wl_iw_t iw; /* wireless extensions state (must be first) */ -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ dhd_pub_t pub; @@ -246,11 +250,15 @@ typedef struct dhd_info { bool dhd_tasklet_create; #endif /* DHDTHREAD */ tsk_ctl_t thr_sysioc_ctl; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + struct work_struct work_hang; +#endif /* Wakelocks */ #if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) struct wake_lock wl_wifi; /* Wifi wakelock */ struct wake_lock wl_rxwake; /* Wifi rx wakelock */ + struct wake_lock wl_ctrlwake; /* Wifi ctrl wakelock */ #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) @@ -258,10 +266,12 @@ typedef struct dhd_info { * calls and wifi_on or wifi_off */ struct mutex dhd_net_if_mutex; + struct mutex dhd_suspend_mutex; #endif spinlock_t wakelock_spinlock; int wakelock_counter; - int wakelock_timeout_enable; + int wakelock_rx_timeout_enable; + int wakelock_ctrl_timeout_enable; /* Thread to issue ioctl for multicast */ bool set_macaddress; @@ -270,9 +280,13 @@ typedef struct dhd_info { atomic_t pend_8021x_cnt; dhd_attach_states_t dhd_state; -#ifdef CONFIG_HAS_EARLYSUSPEND +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) struct early_suspend early_suspend; #endif /* CONFIG_HAS_EARLYSUSPEND */ + +#ifdef ARP_OFFLOAD_SUPPORT + u32 pend_ipaddr; +#endif /* ARP_OFFLOAD_SUPPORT */ } dhd_info_t; /* Definitions to provide path to the firmware and nvram @@ -281,6 +295,8 @@ typedef struct dhd_info { char firmware_path[MOD_PARAM_PATHLEN]; char nvram_path[MOD_PARAM_PATHLEN]; +int op_mode = 0; +module_param(op_mode, int, 0644); extern int wl_control_wl_start(struct net_device *dev); extern int net_os_send_hang_message(struct net_device *dev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) @@ -352,25 +368,6 @@ uint dhd_radio_up = 1; char iface_name[IFNAMSIZ] = {'\0'}; module_param_string(iface_name, iface_name, IFNAMSIZ, 0); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) -#define DAEMONIZE(a) daemonize(a); \ - allow_signal(SIGKILL); \ - allow_signal(SIGTERM); -#else /* Linux 2.4 (w/o preemption patch) */ -#define RAISE_RX_SOFTIRQ() \ - cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ) -#define DAEMONIZE(a) daemonize(); \ - do { if (a) \ - strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \ - } while (0); -#endif /* LINUX_VERSION_CODE */ - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) -#define BLOCKABLE() (!in_atomic()) -#else -#define BLOCKABLE() (!in_interrupt()) -#endif - /* The following are specific to the SDIO dongle */ /* IOCTL response timeout */ @@ -436,6 +433,11 @@ static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR ; static void dhd_net_if_lock_local(dhd_info_t *dhd); static void dhd_net_if_unlock_local(dhd_info_t *dhd); +static void dhd_suspend_lock(dhd_pub_t *dhdp); +static void dhd_suspend_unlock(dhd_pub_t *dhdp); +#if !defined(AP) && defined(WLP2P) && defined(WL_ENABLE_P2P_IF) +static u32 dhd_concurrent_fw(dhd_pub_t *dhd); +#endif #ifdef WLMEDIA_HTSF void htsf_update(dhd_info_t *dhd, void *data); @@ -454,9 +456,9 @@ int dhd_monitor_init(void *dhd_pub); int dhd_monitor_uninit(void); -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ static void dhd_dpc(ulong data); /* forward decl */ @@ -478,7 +480,7 @@ static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long actio { int ret = NOTIFY_DONE; -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) switch (action) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: @@ -498,7 +500,7 @@ static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long actio static struct notifier_block dhd_sleep_pm_notifier = { .notifier_call = dhd_sleep_pm_callback, - .priority = 0 + .priority = 10 }; extern int register_pm_notifier(struct notifier_block *nb); extern int unregister_pm_notifier(struct notifier_block *nb); @@ -510,7 +512,8 @@ static void dhd_set_packet_filter(int value, dhd_pub_t *dhd) DHD_TRACE(("%s: %d\n", __FUNCTION__, value)); /* 1 - Enable packet filter, only allow unicast packet to send up */ /* 0 - Disable packet filter */ - if (dhd_pkt_filter_enable) { + if (dhd_pkt_filter_enable && (!value || + (dhd_check_ap_wfd_mode_set(dhd) == FALSE))) { int i; for (i = 0; i < dhd->pktfilter_count; i++) { @@ -522,7 +525,6 @@ static void dhd_set_packet_filter(int value, dhd_pub_t *dhd) #endif } -#if defined(CONFIG_HAS_EARLYSUSPEND) static int dhd_set_suspend(int value, dhd_pub_t *dhd) { int power_mode = PM_MAX; @@ -534,70 +536,76 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd) DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n", __FUNCTION__, value, dhd->in_suspend)); + dhd_suspend_lock(dhd); if (dhd && dhd->up) { if (value && dhd->in_suspend) { - /* Kernel suspended */ - DHD_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__)); - - dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, - sizeof(power_mode), TRUE, 0); + /* Kernel suspended */ + DHD_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__)); - /* Enable packet filter, only allow unicast packet to send up */ - dhd_set_packet_filter(1, dhd); + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, + sizeof(power_mode), TRUE, 0); - /* If DTIM skip is set up as default, force it to wake - * each third DTIM for better power savings. Note that - * one side effect is a chance to miss BC/MC packet. - */ - bcn_li_dtim = dhd_get_dtim_skip(dhd); - bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, - 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + /* Enable packet filter, only allow unicast packet to send up */ + dhd_set_packet_filter(1, dhd); - /* Disable firmware roaming during suspend */ - bcm_mkiovar("roam_off", (char *)&roamvar, 4, - iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - } else { + /* If DTIM skip is set up as default, force it to wake + * each third DTIM for better power savings. Note that + * one side effect is a chance to miss BC/MC packet. + */ + bcn_li_dtim = dhd_get_dtim_skip(dhd); + bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, + 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + /* Disable firmware roaming during suspend */ + bcm_mkiovar("roam_off", (char *)&roamvar, 4, + iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + } else { - /* Kernel resumed */ - DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__)); + /* Kernel resumed */ + DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__)); - power_mode = PM_FAST; - dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, - sizeof(power_mode), TRUE, 0); + power_mode = PM_FAST; + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, + sizeof(power_mode), TRUE, 0); - /* disable pkt filter */ - dhd_set_packet_filter(0, dhd); + /* disable pkt filter */ + dhd_set_packet_filter(0, dhd); - /* restore pre-suspend setting for dtim_skip */ - bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip, - 4, iovbuf, sizeof(iovbuf)); + /* restore pre-suspend setting for dtim_skip */ + bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip, + 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - roamvar = dhd_roam_disable; - bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, - sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - } + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + roamvar = dhd_roam_disable; + bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, + sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + } } - + dhd_suspend_unlock(dhd); return 0; } -static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val) +static int dhd_suspend_resume_helper(struct dhd_info *dhd, int val, int force) { dhd_pub_t *dhdp = &dhd->pub; + int ret = 0; DHD_OS_WAKE_LOCK(dhdp); /* Set flag when early suspend was called */ dhdp->in_suspend = val; - if ((!dhdp->suspend_disable_flag) && (dhd_check_ap_wfd_mode_set(dhdp) == FALSE)) - dhd_set_suspend(val, dhdp); + if ((force || !dhdp->suspend_disable_flag) && + (dhd_check_ap_wfd_mode_set(dhdp) == FALSE)) { + ret = dhd_set_suspend(val, dhdp); + } DHD_OS_WAKE_UNLOCK(dhdp); + return ret; } +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) static void dhd_early_suspend(struct early_suspend *h) { struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); @@ -605,7 +613,7 @@ static void dhd_early_suspend(struct early_suspend *h) DHD_TRACE(("%s: enter\n", __FUNCTION__)); if (dhd) - dhd_suspend_resume_helper(dhd, 1); + dhd_suspend_resume_helper(dhd, 1, 0); } static void dhd_late_resume(struct early_suspend *h) @@ -615,7 +623,7 @@ static void dhd_late_resume(struct early_suspend *h) DHD_TRACE(("%s: enter\n", __FUNCTION__)); if (dhd) - dhd_suspend_resume_helper(dhd, 0); + dhd_suspend_resume_helper(dhd, 0, 0); } #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ @@ -663,16 +671,12 @@ dhd_timeout_expired(dhd_timeout_t *tmo) } else { wait_queue_head_t delay_wait; DECLARE_WAITQUEUE(wait, current); - int pending; init_waitqueue_head(&delay_wait); add_wait_queue(&delay_wait, &wait); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); - pending = signal_pending(current); remove_wait_queue(&delay_wait, &wait); set_current_state(TASK_RUNNING); - if (pending) - return 1; /* Interrupted */ } return 0; @@ -693,8 +697,9 @@ dhd_net2idx(dhd_info_t *dhd, struct net_device *net) return DHD_BAD_IF; } -struct net_device * dhd_idx2net(struct dhd_pub *dhd_pub, int ifidx) +struct net_device * dhd_idx2net(void *pub, int ifidx) { + struct dhd_pub *dhd_pub = (struct dhd_pub *)pub; struct dhd_info *dhd_info; if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS) @@ -922,6 +927,7 @@ _dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr) DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx))); } else { memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN); + memcpy(dhd->pub.mac.octet, addr, ETHER_ADDR_LEN); } return ret; @@ -941,8 +947,9 @@ dhd_op_if(dhd_if_t *ifp) unsigned long flags; #endif + if (!ifp || !ifp->info || !ifp->idx) + return; ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */ - dhd = ifp->info; DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state)); @@ -977,7 +984,7 @@ dhd_op_if(dhd_if_t *ifp) #ifdef WL_CFG80211 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx, - dhd_net_attach)) { + (void*)dhd_net_attach)) { ifp->state = DHD_IF_NONE; return; } @@ -1013,12 +1020,18 @@ dhd_op_if(dhd_if_t *ifp) DHD_TRACE(("\n%s: got 'DHD_IF_DEL' state\n", __FUNCTION__)); #ifdef WL_CFG80211 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { - wl_cfg80211_notify_ifdel(ifp->net); + wl_cfg80211_ifdel_ops(ifp->net); } #endif netif_stop_queue(ifp->net); unregister_netdev(ifp->net); - ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */ + ret = DHD_DEL_IF; + +#ifdef WL_CFG80211 + if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { + wl_cfg80211_notify_ifdel(); + } +#endif } break; case DHD_IF_DELETING: @@ -1032,10 +1045,8 @@ dhd_op_if(dhd_if_t *ifp) if (ret < 0) { ifp->set_multicast = FALSE; if (ifp->net) { -#ifdef WL_CFG80211 - wl_cfg80211_post_del((void*)(ifp->net)); -#endif free_netdev(ifp->net); + ifp->net = NULL; } dhd->iflist[ifp->idx] = NULL; #ifdef SOFTAP @@ -1137,7 +1148,7 @@ dhd_set_mac_address(struct net_device *dev, void *addr) if (ifidx == DHD_BAD_IF) return -1; - ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0); + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN); dhd->set_macaddress = TRUE; up(&dhd->thr_sysioc_ctl.sema); @@ -1155,7 +1166,7 @@ dhd_set_multicast_list(struct net_device *dev) if (ifidx == DHD_BAD_IF) return; - ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0); + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); dhd->iflist[ifidx]->set_multicast = TRUE; up(&dhd->thr_sysioc_ctl.sema); } @@ -1166,6 +1177,7 @@ dhd_os_wlfc_block(dhd_pub_t *pub) { dhd_info_t *di = (dhd_info_t *)(pub->info); ASSERT(di != NULL); + spin_lock_bh(&di->wlfc_spinlock); return 1; } @@ -1199,7 +1211,7 @@ dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) } /* Update multicast statistic */ - if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_ADDR_LEN) { + if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_HDR_LEN) { uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf); eh = (struct ether_header *)pktdata; @@ -1207,6 +1219,9 @@ dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) dhdp->tx_multicast++; if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X) atomic_inc(&dhd->pend_8021x_cnt); + } else { + PKTFREE(dhd->pub.osh, pktbuf, TRUE); + return BCME_ERROR; } /* Look into the packet and update the packet priority */ @@ -1282,11 +1297,13 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net) DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n", __FUNCTION__, dhd->pub.up, dhd->pub.busstate)); netif_stop_queue(net); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) /* Send Event when bus down detected during data session */ if (dhd->pub.busstate == DHD_BUS_DOWN) { DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__)); net_os_send_hang_message(net); } +#endif DHD_OS_WAKE_UNLOCK(&dhd->pub); return -ENODEV; } @@ -1400,7 +1417,8 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) int i; dhd_if_t *ifp; wl_event_msg_t event; - int tout = DHD_PACKET_TIMEOUT_MS; + int tout_rx = 0; + int tout_ctrl = 0; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -1417,7 +1435,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) PKTFREE(dhdp->osh, pktbuf, TRUE); continue; } - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) /* Dropping packets before registering net device to avoid kernel panic */ if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || !dhd->pub.up) { @@ -1426,6 +1444,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) PKTFREE(dhdp->osh, pktbuf, TRUE); continue; } +#endif pnext = PKTNEXT(dhdp->osh, pktbuf); PKTSETNEXT(wl->sh.osh, pktbuf, NULL); @@ -1450,6 +1469,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) */ ((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++; PKTFREE(dhdp->osh, pktbuf, TRUE); + DHD_TRACE(("RX: wlfc header \n")); continue; } #endif @@ -1501,10 +1521,18 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) &data); wl_event_to_host_order(&event); + if (!tout_ctrl) + tout_ctrl = DHD_PACKET_TIMEOUT_MS; if (event.event_type == WLC_E_BTA_HCI_EVENT) { dhd_bta_doevt(dhdp, data, event.datalen); } - tout = DHD_EVENT_TIMEOUT_MS; +#ifdef PNO_SUPPORT + if (event.event_type == WLC_E_PFN_NET_FOUND) { + tout_ctrl *= 2; + } +#endif /* PNO_SUPPORT */ + } else { + tout_rx = DHD_PACKET_TIMEOUT_MS; } ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]); @@ -1537,7 +1565,9 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ } } - DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(dhdp, tout); + + DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(dhdp, tout_rx); + DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhdp, tout_ctrl); } void @@ -1591,8 +1621,10 @@ dhd_get_stats(struct net_device *net) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ifidx = dhd_net2idx(dhd, net); - if (ifidx == DHD_BAD_IF) + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s: BAD_IF\n", __FUNCTION__)); return NULL; + } ifp = dhd->iflist[ifidx]; ASSERT(dhd && ifp); @@ -2003,6 +2035,7 @@ dhd_ethtool(dhd_info_t *dhd, void *uaddr) static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) if (!dhdp) return FALSE; if ((error == -ETIMEDOUT) || ((dhdp->busstate == DHD_BUS_DOWN) && @@ -2012,6 +2045,7 @@ static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) net_os_send_hang_message(net); return TRUE; } +#endif return FALSE; } @@ -2032,7 +2066,7 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) /* send to dongle only if we are not waiting for reload already */ if (dhd->pub.hang_was_sent) { DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__)); - DHD_OS_WAKE_LOCK_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT_MS); + DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT_MS); DHD_OS_WAKE_UNLOCK(&dhd->pub); return OSL_ERROR(BCME_DONGLE_DOWN); } @@ -2041,11 +2075,12 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd)); if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s: BAD IF\n", __FUNCTION__)); DHD_OS_WAKE_UNLOCK(&dhd->pub); return -1; } -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) /* linux wireless extensions */ if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { /* may recurse, do NOT lock */ @@ -2053,7 +2088,7 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) DHD_OS_WAKE_UNLOCK(&dhd->pub); return ret; } -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) if (cmd == SIOCETHTOOL) { @@ -2238,6 +2273,7 @@ dhd_cleanup_virt_ifaces(dhd_info_t *dhd) #endif for (i = 1; i < DHD_MAX_IFS; i++) { + dhd_net_if_lock_local(dhd); if (dhd->iflist[i]) { DHD_TRACE(("Deleting IF: %d \n", i)); if ((dhd->iflist[i]->state != DHD_IF_DEL) && @@ -2247,6 +2283,7 @@ dhd_cleanup_virt_ifaces(dhd_info_t *dhd) dhd_op_if(dhd->iflist[i]); } } + dhd_net_if_unlock_local(dhd); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) @@ -2261,10 +2298,10 @@ dhd_cleanup_virt_ifaces(dhd_info_t *dhd) static int dhd_stop(struct net_device *net) { - int ifidx; + int ifidx = 0; dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); DHD_OS_WAKE_LOCK(&dhd->pub); - DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_TRACE(("%s: Enter %p\n", __FUNCTION__, net)); if (dhd->pub.up == 0) { goto exit; } @@ -2272,7 +2309,7 @@ dhd_stop(struct net_device *net) #ifdef WL_CFG80211 if (ifidx == 0) { - wl_cfg80211_down(); + wl_cfg80211_down(NULL); /* * For CFG80211: Clean up all the left over virtual interfaces @@ -2295,14 +2332,15 @@ dhd_stop(struct net_device *net) /* Stop the protocol module */ dhd_prot_stop(&dhd->pub); + OLD_MOD_DEC_USE_COUNT; +exit: #if defined(WL_CFG80211) if (ifidx == 0 && !dhd_download_fw_on_driverload) wl_android_wifi_off(net); #endif dhd->pub.rxcnt_timeout = 0; dhd->pub.txcnt_timeout = 0; - OLD_MOD_DEC_USE_COUNT; -exit: + DHD_OS_WAKE_UNLOCK(&dhd->pub); return 0; } @@ -2328,14 +2366,18 @@ dhd_open(struct net_device *net) } dhd->pub.hang_was_sent = 0; - #if !defined(WL_CFG80211) /* * Force start if ifconfig_up gets called before START command * We keep WEXT's wl_control_wl_start to provide backward compatibility * This should be removed in the future */ - wl_control_wl_start(net); + ret = wl_control_wl_start(net); + if (ret != 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + ret = -1; + goto exit; + } #endif ifidx = dhd_net2idx(dhd, net); @@ -2357,12 +2399,17 @@ dhd_open(struct net_device *net) atomic_set(&dhd->pend_8021x_cnt, 0); #if defined(WL_CFG80211) DHD_ERROR(("\n%s\n", dhd_version)); - if (!dhd_download_fw_on_driverload) - wl_android_wifi_on(net); + if (!dhd_download_fw_on_driverload) { + ret = wl_android_wifi_on(net); + if (ret != 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + ret = -1; + goto exit; + } + } #endif /* defined(WL_CFG80211) */ if (dhd->pub.busstate != DHD_BUS_DATA) { - int ret; /* try to bring up bus */ if ((ret = dhd_bus_start(&dhd->pub)) != 0) { @@ -2385,7 +2432,7 @@ dhd_open(struct net_device *net) #endif /* TOE */ #if defined(WL_CFG80211) - if (unlikely(wl_cfg80211_up())) { + if (unlikely(wl_cfg80211_up(NULL))) { DHD_ERROR(("%s: failed to bring up cfg80211\n", __FUNCTION__)); ret = -1; goto exit; @@ -2403,10 +2450,39 @@ dhd_open(struct net_device *net) OLD_MOD_INC_USE_COUNT; exit: + if (ret) + dhd_stop(net); + DHD_OS_WAKE_UNLOCK(&dhd->pub); return ret; } +int dhd_do_driver_init(struct net_device *net) +{ + dhd_info_t *dhd = NULL; + + if (!net) { + DHD_ERROR(("Primary Interface not initialized \n")); + return -EINVAL; + } + + dhd = *(dhd_info_t **)netdev_priv(net); + + /* If driver is already initialized, do nothing + */ + if (dhd->pub.busstate == DHD_BUS_DATA) { + DHD_TRACE(("Driver already Inititalized. Nothing to do")); + return 0; + } + + if (dhd_open(net) < 0) { + DHD_ERROR(("Driver Init Failed \n")); + return -1; + } + + return 0; +} + osl_t * dhd_osl_attach(void *pdev, uint bustype) { @@ -2460,7 +2536,7 @@ dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name, ifp->state = DHD_IF_ADD; ifp->idx = ifidx; ifp->bssidx = bssidx; - ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0); + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); up(&dhd->thr_sysioc_ctl.sema); } else ifp->net = (struct net_device *)handle; @@ -2484,10 +2560,30 @@ dhd_del_if(dhd_info_t *dhd, int ifidx) ifp->state = DHD_IF_DEL; ifp->idx = ifidx; - ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0); + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); up(&dhd->thr_sysioc_ctl.sema); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) +static struct net_device_ops dhd_ops_pri = { + .ndo_open = dhd_open, + .ndo_stop = dhd_stop, + .ndo_get_stats = dhd_get_stats, + .ndo_do_ioctl = dhd_ioctl_entry, + .ndo_start_xmit = dhd_start_xmit, + .ndo_set_mac_address = dhd_set_mac_address, + .ndo_set_multicast_list = dhd_set_multicast_list, +}; + +static struct net_device_ops dhd_ops_virt = { + .ndo_get_stats = dhd_get_stats, + .ndo_do_ioctl = dhd_ioctl_entry, + .ndo_start_xmit = dhd_start_xmit, + .ndo_set_mac_address = dhd_set_mac_address, + .ndo_set_multicast_list = dhd_set_multicast_list, +}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */ + dhd_pub_t * dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) { @@ -2579,13 +2675,16 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) /* Initialize Wakelock stuff */ spin_lock_init(&dhd->wakelock_spinlock); dhd->wakelock_counter = 0; - dhd->wakelock_timeout_enable = 0; + dhd->wakelock_rx_timeout_enable = 0; + dhd->wakelock_ctrl_timeout_enable = 0; #ifdef CONFIG_HAS_WAKELOCK wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake"); wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake"); + wake_lock_init(&dhd->wl_ctrlwake, WAKE_LOCK_SUSPEND, "wlan_ctrl_wake"); #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) mutex_init(&dhd->dhd_net_if_mutex); + mutex_init(&dhd->dhd_suspend_mutex); #endif dhd_state |= DHD_ATTACH_STATE_WAKELOCKS_INIT; @@ -2606,7 +2705,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd_monitor_init(&dhd->pub); dhd_state |= DHD_ATTACH_STATE_CFG80211; #endif -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) /* Attach and link in the iw */ if (!(dhd_state & DHD_ATTACH_STATE_CFG80211)) { if (wl_iw_attach(net, (void *)&dhd->pub) != 0) { @@ -2615,7 +2714,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) } dhd_state |= DHD_ATTACH_STATE_WL_ATTACH; } -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ /* Set up the watchdog timer */ @@ -2661,7 +2760,9 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd->thr_sysioc_ctl.thr_pid = -1; } dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; - +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + INIT_WORK(&dhd->work_hang, dhd_hang_process); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ /* * Save the dhd_info into the priv */ @@ -2671,7 +2772,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) register_pm_notifier(&dhd_sleep_pm_notifier); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ -#ifdef CONFIG_HAS_EARLYSUSPEND +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20; dhd->early_suspend.suspend = dhd_early_suspend; dhd->early_suspend.resume = dhd_late_resume; @@ -2680,6 +2781,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) #endif #ifdef ARP_OFFLOAD_SUPPORT + dhd->pend_ipaddr = 0; register_inetaddr_notifier(&dhd_notifier); #endif /* ARP_OFFLOAD_SUPPORT */ @@ -2713,7 +2815,8 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_TRACE(("Enter %s:\n", __FUNCTION__)); #ifdef DHDTHREAD - dhd_os_sdlock(dhdp); + if (dhd->threads_only) + dhd_os_sdlock(dhdp); #endif /* DHDTHREAD */ /* try to download image and nvram to the dongle */ @@ -2726,14 +2829,16 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n", __FUNCTION__, fw_path, nv_path)); #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ return -1; } } if (dhd->pub.busstate != DHD_BUS_LOAD) { #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ return -ENETDOWN; } @@ -2747,7 +2852,8 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ return ret; } @@ -2763,7 +2869,8 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__)); #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ return -ENODEV; } @@ -2780,13 +2887,15 @@ dhd_bus_start(dhd_pub_t *dhdp) del_timer_sync(&dhd->timer); DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ return -ENODEV; } #ifdef DHDTHREAD - dhd_os_sdunlock(dhdp); + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); #endif /* DHDTHREAD */ #ifdef READ_MACADDR @@ -2801,45 +2910,93 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd_write_macaddr(dhd->pub.mac.octet); #endif +#ifdef ARP_OFFLOAD_SUPPORT + if (dhd->pend_ipaddr) { +#ifdef AOE_IP_ALIAS_SUPPORT + aoe_update_host_ipv4_table(&dhd->pub, dhd->pend_ipaddr, TRUE); +#endif /* AOE_IP_ALIAS_SUPPORT */ + dhd->pend_ipaddr = 0; + } +#endif /* ARP_OFFLOAD_SUPPORT */ + return 0; } +#if !defined(AP) && defined(WLP2P) && defined(WL_ENABLE_P2P_IF) +/* For Android ICS MR2 release, the concurrent mode is enabled by default and the firmware + * name would be fw_bcmdhd.bin. So we need to determine whether P2P is enabled in the STA + * firmware and accordingly enable concurrent mode (Apply P2P settings). SoftAP firmware + * would still be named as fw_bcmdhd_apsta. + */ +static u32 +dhd_concurrent_fw(dhd_pub_t *dhd) +{ + int ret = 0; + char buf[WLC_IOCTL_SMLEN]; + + if ((!op_mode) && (strstr(fw_path, "_p2p") == NULL) && + (strstr(fw_path, "_apsta") == NULL)) { + /* Given path is for the STA firmware. Check whether P2P support is present in + * the firmware. If so, set mode as P2P (concurrent support). + */ + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("p2p", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_ERROR(("%s: Get P2P failed (error=%d)\n", __FUNCTION__, ret)); + } else if (buf[0] == 1) { + DHD_TRACE(("%s: P2P is supported\n", __FUNCTION__)); + return 1; + } + } + return ret; +} +#endif + +/* + * dhd_preinit_ioctls makes special pre-setting in the firmware before radio turns on + * returns : 0 if all settings passed or negative value if anything failed +*/ int dhd_preinit_ioctls(dhd_pub_t *dhd) { int ret = 0; char eventmask[WL_EVENTING_MASK_LEN]; char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ - +#if !defined(WL_CFG80211) uint up = 0; +#endif /* defined(WL_CFG80211) */ uint power_mode = PM_FAST; uint32 dongle_align = DHD_SDALIGN; uint32 glom = 0; - uint bcn_timeout = 4; + uint bcn_timeout = DHD_BEACON_TIMEOUT_NORMAL; + uint retry_max = 3; #if defined(ARP_OFFLOAD_SUPPORT) int arpoe = 1; #endif - int scan_assoc_time = 40; +#if defined(KEEP_ALIVE) + int res; +#endif /* defined(KEEP_ALIVE) */ + int scan_assoc_time = DHD_SCAN_ACTIVE_TIME; int scan_unassoc_time = 40; - int scan_passive_time = 130; + int scan_passive_time = DHD_SCAN_PASSIVE_TIME; char buf[WLC_IOCTL_SMLEN]; char *ptr; uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ + uint16 chipID; #if defined(SOFTAP) uint dtim = 1; #endif #if (defined(AP) && !defined(WLP2P)) || (!defined(AP) && defined(WL_CFG80211)) uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */ #endif - #if defined(AP) || defined(WLP2P) uint32 apsta = 1; /* Enable APSTA mode */ #endif /* defined(AP) || defined(WLP2P) */ #ifdef GET_CUSTOM_MAC_ENABLE struct ether_addr ea_addr; #endif /* GET_CUSTOM_MAC_ENABLE */ - DHD_TRACE(("Enter %s\n", __FUNCTION__)); dhd->op_mode = 0; #ifdef GET_CUSTOM_MAC_ENABLE @@ -2849,9 +3006,10 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf)); ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); if (ret < 0) { - DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); + DHD_ERROR(("%s: can't set custom MAC address , error=%d\n", __FUNCTION__, ret)); return BCME_NOTUP; } + memcpy(dhd->mac.octet, ea_addr.octet, ETHER_ADDR_LEN); } else { #endif /* GET_CUSTOM_MAC_ENABLE */ /* Get the default device MAC address directly from firmware */ @@ -2869,7 +3027,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* GET_CUSTOM_MAC_ENABLE */ #ifdef SET_RANDOM_MAC_SOFTAP - if (strstr(fw_path, "_apsta") != NULL) { + if ((!op_mode && strstr(fw_path, "_apsta") != NULL) || (op_mode == HOSTAPD_MASK)) { uint rand_mac; srandom32((uint)jiffies); @@ -2891,26 +3049,65 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* SET_RANDOM_MAC_SOFTAP */ DHD_TRACE(("Firmware = %s\n", fw_path)); -#if !defined(AP) && defined(WLP2P) + +#if !defined(AP) && defined(WLP2P) /* Check if firmware with WFD support used */ - if (strstr(fw_path, "_p2p") != NULL) { +#if defined(WL_ENABLE_P2P_IF) + if ((ret = dhd_concurrent_fw(dhd)) < 0) { + DHD_ERROR(("%s error : firmware can't support p2p mode\n", __FUNCTION__)); + goto done; + } +#endif /* (WL_ENABLE_P2P_IF) */ + + if ((!op_mode && strstr(fw_path, "_p2p") != NULL) +#if defined(WL_ENABLE_P2P_IF) + || (op_mode == WFD_MASK) || (dhd_concurrent_fw(dhd) == 1) +#endif + ) { bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { - DHD_ERROR(("%s APSTA for WFD failed ret= %d\n", __FUNCTION__, ret)); + DHD_ERROR(("%s APSTA setting failed ret= %d\n", __FUNCTION__, ret)); } else { dhd->op_mode |= WFD_MASK; -#if defined(ARP_OFFLOAD_SUPPORT) - arpoe = 0; -#endif /* (ARP_OFFLOAD_SUPPORT) */ +#if !defined(WL_ENABLE_P2P_IF) + /* ICS back capability : disable any packet filtering for p2p only mode */ dhd_pkt_filter_enable = FALSE; +#endif /*!defined(WL_ENABLE_P2P_IF) */ } } #endif #if !defined(AP) && defined(WL_CFG80211) /* Check if firmware with HostAPD support used */ - if (strstr(fw_path, "_apsta") != NULL) { + if ((!op_mode && strstr(fw_path, "_apsta") != NULL) || (op_mode == HOSTAPD_MASK)) { + /* Disable A-band for HostAPD */ + uint band = WLC_BAND_2G; + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_BAND, (char *)&band, sizeof(band), + TRUE, 0)) < 0) { + DHD_ERROR(("%s:set band failed error (%d)\n", __FUNCTION__, ret)); + } + + /* Turn off wme if we are having only g ONLY firmware */ + bcm_mkiovar("nmode", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_ERROR(("%s:get nmode failed error (%d)\n", __FUNCTION__, ret)); + } + else { + DHD_TRACE(("%s:get nmode returned %d\n", __FUNCTION__,buf[0])); + } + if (buf[0] == 0) { + int wme = 0; + bcm_mkiovar("wme", (char *)&wme, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s set wme for HostAPD failed %d\n", __FUNCTION__, ret)); + } + else { + DHD_TRACE(("%s set wme succeeded for g ONLY firmware\n", __FUNCTION__)); + } + } /* Turn off MPC in AP mode */ bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, @@ -2921,16 +3118,20 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(ARP_OFFLOAD_SUPPORT) arpoe = 0; #endif /* (ARP_OFFLOAD_SUPPORT) */ + /* disable any filtering for SoftAP mode */ dhd_pkt_filter_enable = FALSE; } } #endif +#if !defined(WL_ENABLE_P2P_IF) + /* ICS mode setting for sta */ if ((dhd->op_mode != WFD_MASK) && (dhd->op_mode != HOSTAPD_MASK)) { /* STA only operation mode */ dhd->op_mode |= STA_MASK; dhd_pkt_filter_enable = TRUE; } +#endif /* !defined(WL_ENABLE_P2P_IF) */ DHD_ERROR(("Firmware up: op_mode=%d, " "Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", @@ -2958,9 +3159,13 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - /* disable glom option per default */ - bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + /* disable glom option for some chips */ + chipID = (uint16)dhd_bus_chip_id(dhd); + if ((chipID == BCM4330_CHIP_ID) || (chipID == BCM4329_CHIP_ID)) { + DHD_INFO(("%s disable glom for chipID=0x%X\n", __FUNCTION__, chipID)); + bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + } /* Setup timeout if Beacons are lost and roam is off to report link down */ bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); @@ -2968,6 +3173,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) /* Setup assoc_retry_max count to reconnect target AP in dongle */ bcm_mkiovar("assoc_retry_max", (char *)&retry_max, 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + #if defined(AP) && !defined(WLP2P) /* Turn off MPC in AP mode */ bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); @@ -2983,17 +3189,13 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif #if defined(KEEP_ALIVE) - { /* Set Keep Alive : be sure to use FW with -keepalive */ - int res; - #if defined(SOFTAP) if (ap_fw_loaded == FALSE) #endif if ((res = dhd_keep_alive_onoff(dhd)) < 0) DHD_ERROR(("%s set keeplive failed %d\n", __FUNCTION__, res)); - } #endif /* defined(KEEP_ALIVE) */ /* Read event_msgs mask */ @@ -3020,6 +3222,8 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) setbit(eventmask, WLC_E_LINK); setbit(eventmask, WLC_E_NDIS_LINK); setbit(eventmask, WLC_E_MIC_ERROR); + setbit(eventmask, WLC_E_ASSOC_REQ_IE); + setbit(eventmask, WLC_E_ASSOC_RESP_IE); setbit(eventmask, WLC_E_PMKID_CACHE); setbit(eventmask, WLC_E_TXFAIL); setbit(eventmask, WLC_E_JOIN_START); @@ -3074,12 +3278,14 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #ifdef PKT_FILTER_SUPPORT /* Setup defintions for pktfilter , enable in suspend */ - dhd->pktfilter_count = 4; + dhd->pktfilter_count = 5; /* Setup filter to allow only unicast */ dhd->pktfilter[0] = "100 0 0 0 0x01 0x00"; dhd->pktfilter[1] = NULL; dhd->pktfilter[2] = NULL; dhd->pktfilter[3] = NULL; + /* Add filter to pass multicastDNS packet and NOT filter out as Broadcast */ + dhd->pktfilter[4] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; #if defined(SOFTAP) if (ap_fw_loaded) { int i; @@ -3091,12 +3297,13 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* defined(SOFTAP) */ #endif /* PKT_FILTER_SUPPORT */ +#if !defined(WL_CFG80211) /* Force STA UP */ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0)) < 0) { DHD_ERROR(("%s Setting WL UP failed %d\n", __FUNCTION__, ret)); goto done; } - +#endif /* query for 'ver' to get version info from firmware */ memset(buf, 0, sizeof(buf)); ptr = buf; @@ -3107,8 +3314,15 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcmstrtok(&ptr, "\n", 0); /* Print fw version info */ DHD_ERROR(("Firmware version = %s\n", buf)); + DHD_BLOG(buf, strlen(buf) + 1); DHD_BLOG(dhd_version, strlen(dhd_version) + 1); + + /* Check and adjust IOCTL response timeout for Manufactring firmware */ + if (strstr(buf, MANUFACTRING_FW) != NULL) { + dhd_os_set_ioctl_resp_timeout(IOCTL_RESP_TIMEOUT * 10); + DHD_ERROR(("%s : adjust IOCTL response time for Manufactring Firmware\n", __FUNCTION__)); + } } done: @@ -3140,26 +3354,6 @@ dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, in return ret; } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) -static struct net_device_ops dhd_ops_pri = { - .ndo_open = dhd_open, - .ndo_stop = dhd_stop, - .ndo_get_stats = dhd_get_stats, - .ndo_do_ioctl = dhd_ioctl_entry, - .ndo_start_xmit = dhd_start_xmit, - .ndo_set_mac_address = dhd_set_mac_address, - .ndo_set_multicast_list = dhd_set_multicast_list, -}; - -static struct net_device_ops dhd_ops_virt = { - .ndo_get_stats = dhd_get_stats, - .ndo_do_ioctl = dhd_ioctl_entry, - .ndo_start_xmit = dhd_start_xmit, - .ndo_set_mac_address = dhd_set_mac_address, - .ndo_set_multicast_list = dhd_set_multicast_list, -}; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */ - int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx) { struct dhd_info *dhd = dhdp->info; @@ -3263,9 +3457,13 @@ static int dhd_device_event(struct notifier_block *this, DHD_ARPOE(("%s: [%s] Up IP: 0x%x\n", __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); - /* firmware not downloaded, do nothing */ - if (dhd->pub.busstate == DHD_BUS_DOWN) { - DHD_ERROR(("%s: bus is down, exit\n", __FUNCTION__)); + if (dhd->pub.busstate != DHD_BUS_DATA) { + DHD_ERROR(("%s: bus not ready, exit\n", __FUNCTION__)); + if (dhd->pend_ipaddr) { + DHD_ERROR(("%s: overwrite pending ipaddr: 0x%x\n", + __FUNCTION__, dhd->pend_ipaddr)); + } + dhd->pend_ipaddr = ifa->ifa_address; break; } @@ -3283,7 +3481,7 @@ static int dhd_device_event(struct notifier_block *this, case NETDEV_DOWN: DHD_ARPOE(("%s: [%s] Down IP: 0x%x\n", __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); - + dhd->pend_ipaddr = 0; #ifdef AOE_IP_ALIAS_SUPPORT if (!(ifa->ifa_label[strlen(ifa->ifa_label)-2] == 0x3a)) { DHD_ARPOE(("%s: primary interface is down, AOE clr all\n", @@ -3357,7 +3555,8 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) * portable hotspot. This will not work in simultaneous AP/STA mode, * nor with P2P. Need to set the Donlge's MAC address, and then use that. */ - if (ifidx > 0) { + if (!memcmp(temp_addr, dhd->iflist[0]->mac_addr, + ETHER_ADDR_LEN)) { DHD_ERROR(("%s interface [%s]: set locally administered bit in MAC\n", __func__, net->name)); temp_addr[0] |= 0x02; @@ -3369,14 +3568,14 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) net->ethtool_ops = &dhd_ethtool_ops; #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) #if WIRELESS_EXT < 19 net->get_wireless_stats = dhd_get_wireless_stats; #endif /* WIRELESS_EXT < 19 */ #if WIRELESS_EXT > 12 net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def; #endif /* WIRELESS_EXT > 12 */ -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net); @@ -3392,7 +3591,7 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) net->dev_addr[0], net->dev_addr[1], net->dev_addr[2], net->dev_addr[3], net->dev_addr[4], net->dev_addr[5]); -#if defined(SOFTAP) && defined(CONFIG_WIRELESS_EXT) && !defined(WL_CFG80211) +#if defined(SOFTAP) && defined(WL_WIRELESS_EXT) && !defined(WL_CFG80211) wl_iw_iscan_set_scan_broadcast_prep(net, 1); #endif @@ -3470,21 +3669,25 @@ void dhd_detach(dhd_pub_t *dhdp) unregister_inetaddr_notifier(&dhd_notifier); #endif /* ARP_OFFLOAD_SUPPORT */ -#if defined(CONFIG_HAS_EARLYSUSPEND) +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) if (dhd->dhd_state & DHD_ATTACH_STATE_EARLYSUSPEND_DONE) { if (dhd->early_suspend.suspend) unregister_early_suspend(&dhd->early_suspend); } #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ -#if defined(CONFIG_WIRELESS_EXT) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + cancel_work_sync(&dhd->work_hang); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + +#if defined(WL_WIRELESS_EXT) if (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) { /* Detatch and unlink in the iw */ wl_iw_detach(); } -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ - if (&dhd->thr_sysioc_ctl.thr_pid >= 0) { + if (dhd->thr_sysioc_ctl.thr_pid >= 0) { PROC_STOP(&dhd->thr_sysioc_ctl); } @@ -3494,13 +3697,15 @@ void dhd_detach(dhd_pub_t *dhdp) dhd_if_t *ifp; /* Cleanup virtual interfaces */ - for (i = 1; i < DHD_MAX_IFS; i++) + for (i = 1; i < DHD_MAX_IFS; i++) { + dhd_net_if_lock_local(dhd); if (dhd->iflist[i]) { dhd->iflist[i]->state = DHD_IF_DEL; dhd->iflist[i]->idx = i; dhd_op_if(dhd->iflist[i]); } - + dhd_net_if_unlock_local(dhd); + } /* delete primary interface 0 */ ifp = dhd->iflist[0]; ASSERT(ifp); @@ -3551,7 +3756,7 @@ void dhd_detach(dhd_pub_t *dhdp) #ifdef WL_CFG80211 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { - wl_cfg80211_detach(); + wl_cfg80211_detach(NULL); dhd_monitor_uninit(); } #endif @@ -3564,6 +3769,7 @@ void dhd_detach(dhd_pub_t *dhdp) #ifdef CONFIG_HAS_WAKELOCK wake_lock_destroy(&dhd->wl_wifi); wake_lock_destroy(&dhd->wl_rxwake); + wake_lock_destroy(&dhd->wl_ctrlwake); #endif } } @@ -3598,7 +3804,6 @@ dhd_module_cleanup(void) dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); } - static int __init dhd_module_init(void) { @@ -3645,26 +3850,26 @@ dhd_module_init(void) } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - /* - * Wait till MMC sdio_register_driver callback called and made driver attach. - * It's needed to make sync up exit from dhd insmod and - * Kernel MMC sdio device callback registration - */ + /* + * Wait till MMC sdio_register_driver callback called and made driver attach. + * It's needed to make sync up exit from dhd insmod and + * Kernel MMC sdio device callback registration + */ if (down_timeout(&dhd_registration_sem, msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) { - error = -EINVAL; + error = -ENODEV; DHD_ERROR(("%s: sdio_register_driver timeout\n", __FUNCTION__)); goto fail_2; - } + } #endif #if defined(WL_CFG80211) - error = wl_android_post_init(); -#endif + wl_android_post_init(); +#endif /* defined(WL_CFG80211) */ return error; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && 1 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) fail_2: dhd_bus_unregister(); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +#endif fail_1: #if defined(CONFIG_WIFI_CONTROL_FUNC) wl_android_wifictrl_func_del(); @@ -3676,7 +3881,11 @@ fail_1: return error; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) late_initcall(dhd_module_init); +#else +module_init(dhd_module_init); +#endif module_exit(dhd_module_cleanup); /* @@ -3724,7 +3933,6 @@ int dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) { dhd_info_t * dhd = (dhd_info_t *)(pub->info); - DECLARE_WAITQUEUE(wait, current); int timeout = dhd_ioctl_timeout_msec; /* Convert timeout in millsecond to jiffies */ @@ -3734,26 +3942,7 @@ dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) timeout = timeout * HZ / 1000; #endif - /* Wait until control frame is available */ - add_wait_queue(&dhd->ioctl_resp_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - /* Memory barrier to support multi-processing - * As the variable "condition", which points to dhd->rxlen (dhd_bus_rxctl[dhd_sdio.c]) - * Can be changed by another processor. - */ - smp_mb(); - while (!(*condition) && (!signal_pending(current) && timeout)) { - timeout = schedule_timeout(timeout); - smp_mb(); - } - - if (signal_pending(current)) - *pending = TRUE; - - set_current_state(TASK_RUNNING); - remove_wait_queue(&dhd->ioctl_resp_wait, &wait); - + timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout); return timeout; } @@ -3763,7 +3952,7 @@ dhd_os_ioctl_resp_wake(dhd_pub_t *pub) dhd_info_t *dhd = (dhd_info_t *)(pub->info); if (waitqueue_active(&dhd->ioctl_resp_wait)) { - wake_up_interruptible(&dhd->ioctl_resp_wait); + wake_up(&dhd->ioctl_resp_wait); } return 0; @@ -3930,7 +4119,7 @@ void dhd_os_prefree(void *osh, void *addr, uint size) } #endif /* defined(CONFIG_DHD_USE_STATIC_BUF) */ -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) struct iw_statistics * dhd_get_wireless_stats(struct net_device *dev) { @@ -3948,7 +4137,7 @@ dhd_get_wireless_stats(struct net_device *dev) else return NULL; } -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, @@ -3961,7 +4150,7 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, if (bcmerror != BCME_OK) return (bcmerror); -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) if (event->bsscfgidx == 0) { /* * Wireless ext is on primary interface only @@ -3974,10 +4163,16 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); } } -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ #ifdef WL_CFG80211 - + if ((ntoh32(event->event_type) == WLC_E_IF) && + (((dhd_if_event_t *)*data)->action == WLC_E_IF_ADD)) + /* If ADD_IF has been called directly by wl utility then we + * should not report this. In case if ADD_IF was called from + * CFG stack, then too this event need not be reported back + */ + return (BCME_OK); if ((wl_cfg80211_is_progress_ifchange() || wl_cfg80211_is_progress_ifadd()) && (*ifidx != 0)) { /* @@ -4106,8 +4301,13 @@ void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) struct dhd_info *dhdinfo = dhd->info; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + int timeout = msecs_to_jiffies(2000); +#else + int timeout = 2 * HZ; +#endif dhd_os_sdunlock(dhd); - wait_event_interruptible_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), HZ * 2); + wait_event_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), timeout); dhd_os_sdlock(dhd); #endif return; @@ -4118,7 +4318,7 @@ void dhd_wait_event_wakeup(dhd_pub_t *dhd) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) struct dhd_info *dhdinfo = dhd->info; if (waitqueue_active(&dhdinfo->ctrl_wait)) - wake_up_interruptible(&dhdinfo->ctrl_wait); + wake_up(&dhdinfo->ctrl_wait); #endif return; } @@ -4151,16 +4351,18 @@ int net_os_set_suspend_disable(struct net_device *dev, int val) return ret; } -int net_os_set_suspend(struct net_device *dev, int val) +int net_os_set_suspend(struct net_device *dev, int val, int force) { int ret = 0; -#if defined(CONFIG_HAS_EARLYSUSPEND) dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); if (dhd) { +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) ret = dhd_set_suspend(val, &dhd->pub); +#else + ret = dhd_suspend_resume_helper(dhd, val, force); +#endif } -#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ return ret; } @@ -4180,7 +4382,8 @@ int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) char *filterp = NULL; int ret = 0; - if (!dhd || (num == DHD_UNICAST_FILTER_NUM)) + if (!dhd || (num == DHD_UNICAST_FILTER_NUM) || + (num == DHD_MDNS_FILTER_NUM)) return ret; if (num >= dhd->pub.pktfilter_count) return -EINVAL; @@ -4203,9 +4406,8 @@ int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) return ret; } -int net_os_set_packet_filter(struct net_device *dev, int val) +int dhd_os_set_packet_filter(dhd_pub_t *dhdp, int val) { - dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); int ret = 0; /* Packet filtering is set only if we still in early-suspend and @@ -4213,22 +4415,29 @@ int net_os_set_packet_filter(struct net_device *dev, int val) * We can always turn it OFF in case of early-suspend, but we turn it * back ON only if suspend_disable_flag was not set */ - if (dhd && dhd->pub.up) { - if (dhd->pub.in_suspend) { - if (!val || (val && !dhd->pub.suspend_disable_flag)) - dhd_set_packet_filter(val, &dhd->pub); + if (dhdp && dhdp->up) { + if (dhdp->in_suspend) { + if (!val || (val && !dhdp->suspend_disable_flag)) + dhd_set_packet_filter(val, dhdp); } } return ret; + } +int net_os_set_packet_filter(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); -void + return dhd_os_set_packet_filter(&dhd->pub, val); +} + +int dhd_dev_init_ioctl(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - dhd_preinit_ioctls(&dhd->pub); + return dhd_preinit_ioctls(&dhd->pub); } #ifdef PNO_SUPPORT @@ -4262,6 +4471,17 @@ dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr, pno_repeat, pno_freq_expo_max)); } +/* Linux wrapper to call common dhd_pno_set_ex */ +int +dhd_dev_pno_set_ex(struct net_device *dev, wl_pfn_t* ssidnet, int nssid, + ushort pno_interval, int pno_repeat, int pno_expo_max, int pno_lost_time) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_set_ex(&dhd->pub, ssidnet, nssid, + pno_interval, pno_repeat, pno_expo_max, pno_lost_time)); +} + /* Linux wrapper to get pno status */ int dhd_dev_get_pno_status(struct net_device *dev) @@ -4273,6 +4493,28 @@ dhd_dev_get_pno_status(struct net_device *dev) #endif /* PNO_SUPPORT */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(struct work_struct *work) +{ + dhd_info_t *dhd; + struct net_device *dev; + + dhd = (dhd_info_t *)container_of(work, dhd_info_t, work_hang); + dev = dhd->iflist[0]->net; + + if (dev) { + rtnl_lock(); + dev_close(dev); + rtnl_unlock(); +#if defined(WL_WIRELESS_EXT) + wl_iw_send_priv_event(dev, "HANG"); +#endif +#if defined(WL_CFG80211) + wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); +#endif + } +} + int net_os_send_hang_message(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); @@ -4281,28 +4523,21 @@ int net_os_send_hang_message(struct net_device *dev) if (dhd) { if (!dhd->pub.hang_was_sent) { dhd->pub.hang_was_sent = 1; -#if defined(CONFIG_WIRELESS_EXT) - ret = wl_iw_send_priv_event(dev, "HANG"); -#endif -#if defined(WL_CFG80211) - ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); - dev_close(dev); - dev_open(dev); -#endif + schedule_work(&dhd->work_hang); } } return ret; } +#endif void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); if (dhd && dhd->pub.up) - memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); + memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); } - void dhd_net_if_lock(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); @@ -4331,6 +4566,24 @@ static void dhd_net_if_unlock_local(dhd_info_t *dhd) #endif } +static void dhd_suspend_lock(dhd_pub_t *pub) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + if (dhd) + mutex_lock(&dhd->dhd_suspend_mutex); +#endif +} + +static void dhd_suspend_unlock(dhd_pub_t *pub) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + if (dhd) + mutex_unlock(&dhd->dhd_suspend_mutex); +#endif +} + unsigned long dhd_os_spin_lock(dhd_pub_t *pub) { dhd_info_t *dhd = (dhd_info_t *)(pub->info); @@ -4423,13 +4676,18 @@ int dhd_os_wake_lock_timeout(dhd_pub_t *pub) if (dhd) { spin_lock_irqsave(&dhd->wakelock_spinlock, flags); - ret = dhd->wakelock_timeout_enable; + ret = dhd->wakelock_rx_timeout_enable > dhd->wakelock_ctrl_timeout_enable ? + dhd->wakelock_rx_timeout_enable : dhd->wakelock_ctrl_timeout_enable; #ifdef CONFIG_HAS_WAKELOCK - if (dhd->wakelock_timeout_enable) + if (dhd->wakelock_rx_timeout_enable) wake_lock_timeout(&dhd->wl_rxwake, - msecs_to_jiffies(dhd->wakelock_timeout_enable)); + msecs_to_jiffies(dhd->wakelock_rx_timeout_enable)); + if (dhd->wakelock_ctrl_timeout_enable) + wake_lock_timeout(&dhd->wl_ctrlwake, + msecs_to_jiffies(dhd->wakelock_ctrl_timeout_enable)); #endif - dhd->wakelock_timeout_enable = 0; + dhd->wakelock_rx_timeout_enable = 0; + dhd->wakelock_ctrl_timeout_enable = 0; spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); } return ret; @@ -4445,27 +4703,51 @@ int net_os_wake_lock_timeout(struct net_device *dev) return ret; } -int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub, int val) +int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (val > dhd->wakelock_rx_timeout_enable) + dhd->wakelock_rx_timeout_enable = val; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return 0; +} + +int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val) { dhd_info_t *dhd = (dhd_info_t *)(pub->info); unsigned long flags; if (dhd) { spin_lock_irqsave(&dhd->wakelock_spinlock, flags); - if (val > dhd->wakelock_timeout_enable) - dhd->wakelock_timeout_enable = val; + if (val > dhd->wakelock_ctrl_timeout_enable) + dhd->wakelock_ctrl_timeout_enable = val; spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); } return 0; } -int net_os_wake_lock_timeout_enable(struct net_device *dev, int val) +int net_os_wake_lock_rx_timeout_enable(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_lock_rx_timeout_enable(&dhd->pub, val); + return ret; +} + +int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); int ret = 0; if (dhd) - ret = dhd_os_wake_lock_timeout_enable(&dhd->pub, val); + ret = dhd_os_wake_lock_ctrl_timeout_enable(&dhd->pub, val); return ret; } @@ -4536,15 +4818,6 @@ int dhd_os_check_wakelock(void *dhdp) return 0; } -int dhd_os_check_if_up(void *dhdp) -{ - dhd_pub_t *pub = (dhd_pub_t *)dhdp; - - if (!pub) - return 0; - return pub->up; -} - int net_os_wake_unlock(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); @@ -4555,6 +4828,15 @@ int net_os_wake_unlock(struct net_device *dev) return ret; } +int dhd_os_check_if_up(void *dhdp) +{ + dhd_pub_t *pub = (dhd_pub_t *)dhdp; + + if (!pub) + return 0; + return pub->up; +} + int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) { int ifidx; @@ -4594,8 +4876,8 @@ extern int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t uint8 iftype, uint8* ea); extern int dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits); -int dhd_wlfc_interface_event(struct dhd_info *dhd, uint8 action, uint8 ifid, uint8 iftype, - uint8* ea) +int dhd_wlfc_interface_event(struct dhd_info *dhd, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) { if (dhd->pub.wlfc_state == NULL) return BCME_OK; diff --git a/drivers/net/wireless/bcmdhd/dhd_proto.h b/drivers/net/wireless/bcmdhd/dhd_proto.h index e0a54ad..bb1d736 100644 --- a/drivers/net/wireless/bcmdhd/dhd_proto.h +++ b/drivers/net/wireless/bcmdhd/dhd_proto.h @@ -34,7 +34,7 @@ #include <wlioctl.h> #ifndef IOCTL_RESP_TIMEOUT -#define IOCTL_RESP_TIMEOUT 20000 /* In milli second */ +#define IOCTL_RESP_TIMEOUT 2000 /* In milli second */ #endif /* diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index 1bdc7d5..4834ca2 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_sdio.c 288105 2011-10-06 01:58:02Z $ + * $Id: dhd_sdio.c 326662 2012-04-10 06:38:08Z $ */ #include <typedefs.h> @@ -801,7 +801,8 @@ dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok) #ifdef DHD_DEBUG if (dhd_console_ms == 0) #endif /* DHD_DEBUG */ - dhd_os_wd_timer(bus->dhd, 0); + if (bus->poll == 0) + dhd_os_wd_timer(bus->dhd, 0); break; } #ifdef DHD_DEBUG @@ -851,8 +852,10 @@ dhdsdio_bussleep(dhd_bus_t *bus, bool sleep) SBSDIO_FORCE_HW_CLKREQ_OFF, NULL); /* Isolate the bus */ - bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, - SBSDIO_DEVCTL_PADS_ISO, NULL); + if (bus->sih->chip != BCM4329_CHIP_ID && bus->sih->chip != BCM4319_CHIP_ID) { + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, + SBSDIO_DEVCTL_PADS_ISO, NULL); + } /* Change state */ bus->sleeping = TRUE; @@ -1366,9 +1369,7 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) /* Send from dpc */ bus->ctrl_frame_buf = frame; bus->ctrl_frame_len = len; - dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat); - if (bus->ctrl_frame_stat == FALSE) { DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__)); ret = 0; @@ -1453,7 +1454,7 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) { int timeleft; uint rxlen = 0; - bool pending; + bool pending = FALSE; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); @@ -1480,8 +1481,9 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) dhd_os_sdunlock(bus->dhd); #endif /* DHD_DEBUG */ } else if (pending == TRUE) { - DHD_CTL(("%s: canceled\n", __FUNCTION__)); - return -ERESTARTSYS; + /* signal pending */ + DHD_ERROR(("%s: signal pending\n", __FUNCTION__)); + return -EINTR; } else { DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__)); #ifdef DHD_DEBUG @@ -1520,7 +1522,7 @@ enum { #ifdef DHD_DEBUG IOV_CHECKDIED, IOV_SERIALCONS, -#endif +#endif /* DHD_DEBUG */ IOV_DOWNLOAD, IOV_SOCRAM_STATE, IOV_FORCEEVEN, @@ -2785,6 +2787,9 @@ dhdsdio_download_state(dhd_bus_t *bus, bool enter) uint retries; int bcmerror = 0; + if (!bus->sih) + return BCME_ERROR; + /* To enter download state, disable ARM and reset SOCRAM. * To exit download state, simply reset ARM (default is RAM boot). */ @@ -3058,6 +3063,13 @@ dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) bus->rxskip = FALSE; bus->tx_seq = bus->rx_seq = 0; + /* Set to a safe default. It gets updated when we + * receive a packet from the fw but when we reset, + * we need a safe default to be able to send the + * initial mac address. + */ + bus->tx_max = 4; + if (enforce_mutex) dhd_os_sdunlock(bus->dhd); } @@ -3112,9 +3124,9 @@ dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) dhd_timeout_start(&tmo, DHD_WAIT_F2RDY * 1000); ready = 0; - while (ready != enable && !dhd_timeout_expired(&tmo)) - ready = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY, NULL); - + do { + ready = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY, NULL); + } while (ready != enable && !dhd_timeout_expired(&tmo)); DHD_INFO(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n", __FUNCTION__, enable, ready, tmo.elapsed)); @@ -3565,7 +3577,7 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) if ((uint8)(txmax - bus->tx_seq) > 0x40) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); - txmax = bus->tx_seq; + txmax = bus->tx_max; } bus->tx_max = txmax; @@ -3766,6 +3778,14 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN; rxseq++, rxleft--) { +#ifdef DHDTHREAD + /* tx more to improve rx performance */ + if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && + pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) { + dhdsdio_sendfromq(bus, dhd_txbound); + } +#endif /* DHDTHREAD */ + /* Handle glomming separately */ if (bus->glom || bus->glomd) { uint8 cnt; @@ -3986,7 +4006,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) if ((uint8)(txmax - bus->tx_seq) > 0x40) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); - txmax = bus->tx_seq; + txmax = bus->tx_max; } bus->tx_max = txmax; @@ -4143,7 +4163,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) if ((uint8)(txmax - bus->tx_seq) > 0x40) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); - txmax = bus->tx_seq; + txmax = bus->tx_max; } bus->tx_max = txmax; @@ -4576,8 +4596,32 @@ clkwait: bcmsdh_intr_enable(sdh); } +#if defined(OOB_INTR_ONLY) && !defined(HW_OOB) + /* In case of SW-OOB(using edge trigger), + * Check interrupt status in the dongle again after enable irq on the host. + * and rechedule dpc if interrupt is pended in the dongle. + * There is a chance to miss OOB interrupt while irq is disabled on the host. + * No need to do this with HW-OOB(level trigger) + */ + R_SDREG(newstatus, ®s->intstatus, retries); + if (bcmsdh_regfail(bus->sdh)) + newstatus = 0; + if (newstatus & bus->hostintmask) { + bus->ipend = TRUE; + resched = TRUE; + } +#endif /* defined(OOB_INTR_ONLY) && !defined(HW_OOB) */ + if (TXCTLOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { int ret, i; + uint8* frame_seq = bus->ctrl_frame_buf + SDPCM_FRAMETAG_LEN; + + if (*frame_seq != bus->tx_seq) { + DHD_INFO(("%s IOCTL frame seq lag detected!" + " frm_seq:%d != bus->tx_seq:%d, corrected\n", + __FUNCTION__, *frame_seq, bus->tx_seq)); + *frame_seq = bus->tx_seq; + } ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, (uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len, @@ -5183,16 +5227,17 @@ dhdsdio_chipmatch(uint16 chipid) return TRUE; if (chipid == BCM4319_CHIP_ID) return TRUE; - if (chipid == BCM4336_CHIP_ID) - return TRUE; if (chipid == BCM4330_CHIP_ID) return TRUE; + if (chipid == BCM43239_CHIP_ID) + return TRUE; + if (chipid == BCM4336_CHIP_ID) + return TRUE; if (chipid == BCM43237_CHIP_ID) return TRUE; if (chipid == BCM43362_CHIP_ID) return TRUE; - if (chipid == BCM43239_CHIP_ID) - return TRUE; + return FALSE; } @@ -5369,6 +5414,7 @@ dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, if (ret == BCME_NOTUP) goto fail; } + /* Ok, have the per-port tell the stack we're open for business */ if (dhd_net_attach(bus->dhd, 0) != 0) { DHD_ERROR(("%s: Net attach failed!!\n", __FUNCTION__)); @@ -5545,8 +5591,10 @@ dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, return TRUE; fail: - if (bus->sih != NULL) + if (bus->sih != NULL) { si_detach(bus->sih); + bus->sih = NULL; + } return FALSE; } @@ -5779,6 +5827,7 @@ dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, bool r dhdsdio_clkctl(bus, CLK_NONE, FALSE); } si_detach(bus->sih); + bus->sih = NULL; if (bus->vars && bus->varsz) MFREE(osh, bus->vars, bus->varsz); bus->vars = NULL; @@ -6141,6 +6190,7 @@ dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf uint dhd_bus_chip(struct dhd_bus *bus) { + ASSERT(bus); ASSERT(bus->sih != NULL); return bus->sih->chip; } @@ -6148,6 +6198,7 @@ dhd_bus_chip(struct dhd_bus *bus) void * dhd_bus_pub(struct dhd_bus *bus) { + ASSERT(bus); return bus->dhd; } @@ -6194,7 +6245,6 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) bus->dhd->dongle_reset = TRUE; bus->dhd->up = FALSE; dhd_os_sdunlock(dhdp); - DHD_TRACE(("%s: WLAN OFF DONE\n", __FUNCTION__)); /* App can now remove power from device */ } else @@ -6265,6 +6315,14 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) return bcmerror; } +/* Get Chip ID version */ +uint dhd_bus_chip_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chip; +} + int dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size) { diff --git a/drivers/net/wireless/bcmdhd/dhd_wlfc.h b/drivers/net/wireless/bcmdhd/dhd_wlfc.h index 59d018b..c4d2518 100644 --- a/drivers/net/wireless/bcmdhd/dhd_wlfc.h +++ b/drivers/net/wireless/bcmdhd/dhd_wlfc.h @@ -18,7 +18,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. -* $Id: dhd_wlfc.h,v 1.1.8.1 2010-09-09 22:41:08 Exp $ +* $Id: dhd_wlfc.h 286994 2011-09-29 21:27:44Z $ * */ #ifndef __wlfc_host_driver_definitions_h__ @@ -201,6 +201,7 @@ typedef struct athost_wl_stat_counters { #define WLFC_FCMODE_IMPLIED_CREDIT 1 #define WLFC_FCMODE_EXPLICIT_CREDIT 2 +/* How long to defer borrowing in milliseconds */ #define WLFC_BORROW_DEFER_PERIOD_MS 100 /* Mask to represent available ACs (note: BC/MC is ignored */ @@ -261,6 +262,15 @@ typedef struct athost_wl_status_info { /* Timestamp to compute how long to defer borrowing for */ uint32 borrow_defer_timestamp; + } athost_wl_status_info_t; +int dhd_wlfc_enable(dhd_pub_t *dhd); +int dhd_wlfc_interface_event(struct dhd_info *, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea); +int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data); +int dhd_wlfc_event(struct dhd_info *dhd); +int dhd_os_wlfc_block(dhd_pub_t *pub); +int dhd_os_wlfc_unblock(dhd_pub_t *pub); + #endif /* __wlfc_host_driver_definitions_h__ */ diff --git a/drivers/net/wireless/bcmdhd/hndpmu.c b/drivers/net/wireless/bcmdhd/hndpmu.c index b9586e4..0e49334 100644 --- a/drivers/net/wireless/bcmdhd/hndpmu.c +++ b/drivers/net/wireless/bcmdhd/hndpmu.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndpmu.c,v 1.228.2.56 2011-02-11 22:49:07 Exp $ + * $Id: hndpmu.c,v 1.228.2.56 2011-02-11 22:49:07 $ */ #include <typedefs.h> @@ -93,26 +93,8 @@ static const sdiod_drive_str_t sdiod_drive_strength_tab4_1v8[] = { {0, 0x1} }; /* SDIO Drive Strength to sel value table for PMU Rev 11 (1.2v) */ -static const sdiod_drive_str_t sdiod_drive_strength_tab4_1v2[] = { - {16, 0x3}, - {13, 0x2}, - {11, 0x1}, - {8, 0x0}, - {6, 0x7}, - {4, 0x6}, - {2, 0x5}, - {0, 0x4} }; /* SDIO Drive Strength to sel value table for PMU Rev 11 (2.5v) */ -static const sdiod_drive_str_t sdiod_drive_strength_tab4_2v5[] = { - {80, 0x5}, - {65, 0x4}, - {55, 0x7}, - {40, 0x6}, - {30, 0x1}, - {20, 0x0}, - {10, 0x3}, - {0, 0x2} }; /* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ static const sdiod_drive_str_t sdiod_drive_strength_tab5_1v8[] = { @@ -125,14 +107,6 @@ static const sdiod_drive_str_t sdiod_drive_strength_tab5_1v8[] = { {0, 0x0} }; /* SDIO Drive Strength to sel value table for PMU Rev 13 (3.3v) */ -static const sdiod_drive_str_t sdiod_drive_strength_tab5_3v3[] = { - {12, 0x7}, - {10, 0x6}, - {8, 0x5}, - {6, 0x4}, - {4, 0x2}, - {2, 0x1}, - {0, 0x0} }; #define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) diff --git a/drivers/net/wireless/bcmdhd/include/Makefile b/drivers/net/wireless/bcmdhd/include/Makefile index c07266f..67c4906 100644 --- a/drivers/net/wireless/bcmdhd/include/Makefile +++ b/drivers/net/wireless/bcmdhd/include/Makefile @@ -10,7 +10,7 @@ # # Copyright 2005, Broadcom, Inc. # -# $Id: Makefile 241702 2011-02-19 00:41:03Z automrgr $ +# $Id: Makefile 241702 2011-02-19 00:41:03Z $ # SRCBASE := .. diff --git a/drivers/net/wireless/bcmdhd/include/aidmp.h b/drivers/net/wireless/bcmdhd/include/aidmp.h index 375df44..b993a03 100644 --- a/drivers/net/wireless/bcmdhd/include/aidmp.h +++ b/drivers/net/wireless/bcmdhd/include/aidmp.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: aidmp.h,v 13.4.14.1 2010-03-09 18:40:06 Exp $ + * $Id: aidmp.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmcdc.h b/drivers/net/wireless/bcmdhd/include/bcmcdc.h index ce45c50..77a20f8 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmcdc.h +++ b/drivers/net/wireless/bcmdhd/include/bcmcdc.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmcdc.h,v 13.25.10.3 2010-12-22 23:47:26 Exp $ + * $Id: bcmcdc.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _bcmcdc_h_ diff --git a/drivers/net/wireless/bcmdhd/include/bcmdefs.h b/drivers/net/wireless/bcmdhd/include/bcmdefs.h index da1fd5e..17cc0e9 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmdefs.h +++ b/drivers/net/wireless/bcmdhd/include/bcmdefs.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmdefs.h,v 13.68.2.8 2011-01-08 04:04:19 Exp $ + * $Id: bcmdefs.h 279282 2011-08-23 22:44:02Z $ */ @@ -30,12 +30,26 @@ + +#define BCM_REFERENCE(data) ((void)(data)) + + + #define bcmreclaimed 0 #define _data _data #define _fn _fn +#define BCMPREATTACHDATA(_data) _data +#define BCMPREATTACHFN(_fn) _fn #define _data _data #define _fn _fn #define _fn _fn +#define BCMNMIATTACHFN(_fn) _fn +#define BCMNMIATTACHDATA(_data) _data +#define BCMOVERLAY0DATA(_sym) _sym +#define BCMOVERLAY0FN(_fn) _fn +#define BCMOVERLAY1DATA(_sym) _sym +#define BCMOVERLAY1FN(_fn) _fn +#define BCMOVERLAYERRFN(_fn) _fn #define CONST const #define BCMFASTPATH @@ -43,9 +57,30 @@ #define _data _data +#define BCMROMDAT_NAME(_data) _data #define _fn _fn #define _fn _fn #define STATIC static +#define BCMROMDAT_ARYSIZ(data) ARRAYSIZE(data) +#define BCMROMDAT_SIZEOF(data) sizeof(data) +#define BCMROMDAT_APATCH(data) +#define BCMROMDAT_SPATCH(data) + + + +#define OVERLAY_INLINE +#define OSTATIC static +#define BCMOVERLAYDATA(_ovly, _sym) _sym +#define BCMOVERLAYFN(_ovly, _fn) _fn +#define BCMOVERLAYERRFN(_fn) _fn +#define BCMROMOVERLAYDATA(_ovly, _data) _data +#define BCMROMOVERLAYFN(_ovly, _fn) _fn +#define BCMATTACHOVERLAYDATA(_ovly, _sym) _sym +#define BCMATTACHOVERLAYFN(_ovly, _fn) _fn +#define BCMINITOVERLAYDATA(_ovly, _sym) _sym +#define BCMINITOVERLAYFN(_ovly, _fn) _fn +#define BCMUNINITOVERLAYFN(_ovly, _fn) _fn + #define SI_BUS 0 diff --git a/drivers/net/wireless/bcmdhd/include/bcmdevs.h b/drivers/net/wireless/bcmdhd/include/bcmdevs.h index 4f707c0..cdfc5fe 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmdevs.h +++ b/drivers/net/wireless/bcmdhd/include/bcmdevs.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmdevs.h,v 13.285.2.39 2011-02-04 05:03:16 Exp $ + * $Id: bcmdevs.h 332966 2012-05-11 22:40:21Z $ */ @@ -31,7 +31,16 @@ #define VENDOR_EPIGRAM 0xfeda #define VENDOR_BROADCOM 0x14e4 +#define VENDOR_3COM 0x10b7 +#define VENDOR_NETGEAR 0x1385 +#define VENDOR_DIAMOND 0x1092 +#define VENDOR_INTEL 0x8086 +#define VENDOR_DELL 0x1028 +#define VENDOR_HP 0x103c +#define VENDOR_HP_COMPAQ 0x0e11 +#define VENDOR_APPLE 0x106b #define VENDOR_SI_IMAGE 0x1095 +#define VENDOR_BUFFALO 0x1154 #define VENDOR_TI 0x104c #define VENDOR_RICOH 0x1180 #define VENDOR_JMICRON 0x197b @@ -54,9 +63,38 @@ #define BCM_DNGL_BL_PID_43239 0xbd1b #define BCM_DNGL_BDC_PID 0x0bdc #define BCM_DNGL_JTAG_PID 0x4a44 +#define BCM_DNGL_BL_PID_4324 0xbd1c + + +#define BCM_HWUSB_PID_43239 43239 + + +#define BCM4210_DEVICE_ID 0x1072 +#define BCM4230_DEVICE_ID 0x1086 +#define BCM4401_ENET_ID 0x170c +#define BCM3352_DEVICE_ID 0x3352 +#define BCM3360_DEVICE_ID 0x3360 +#define BCM4211_DEVICE_ID 0x4211 +#define BCM4231_DEVICE_ID 0x4231 +#define BCM4303_D11B_ID 0x4303 +#define BCM4311_D11G_ID 0x4311 +#define BCM4311_D11DUAL_ID 0x4312 +#define BCM4311_D11A_ID 0x4313 +#define BCM4328_D11DUAL_ID 0x4314 +#define BCM4328_D11G_ID 0x4315 +#define BCM4328_D11A_ID 0x4316 +#define BCM4318_D11G_ID 0x4318 +#define BCM4318_D11DUAL_ID 0x4319 +#define BCM4318_D11A_ID 0x431a #define BCM4325_D11DUAL_ID 0x431b #define BCM4325_D11G_ID 0x431c #define BCM4325_D11A_ID 0x431d +#define BCM4306_D11G_ID 0x4320 +#define BCM4306_D11A_ID 0x4321 +#define BCM4306_UART_ID 0x4322 +#define BCM4306_V90_ID 0x4323 +#define BCM4306_D11DUAL_ID 0x4324 +#define BCM4306_D11G_ID2 0x4325 #define BCM4321_D11N_ID 0x4328 #define BCM4321_D11N2G_ID 0x4329 #define BCM4321_D11N5G_ID 0x432a @@ -98,17 +136,58 @@ #define BCM43237_D11N5G_ID 0x4356 #define BCM43227_D11N2G_ID 0x4358 #define BCM43228_D11N_ID 0x4359 -#define BCM43228_D11N5G_ID 0x435a +#define BCM43228_D11N5G_ID 0x435a #define BCM43362_D11N_ID 0x4363 #define BCM43239_D11N_ID 0x4370 +#define BCM4324_D11N_ID 0x4374 +#define BCM43217_D11N2G_ID 0x43a9 +#define BCM43131_D11N2G_ID 0x43aa +#define BCM4314_D11N2G_ID 0x4364 +#define BCM43142_D11N2G_ID 0x4365 +#define BCMGPRS_UART_ID 0x4333 +#define BCMGPRS2_UART_ID 0x4344 +#define FPGA_JTAGM_ID 0x43f0 +#define BCM_JTAGM_ID 0x43f1 #define SDIOH_FPGA_ID 0x43f2 +#define BCM_SDIOH_ID 0x43f3 +#define SDIOD_FPGA_ID 0x43f4 #define SPIH_FPGA_ID 0x43f5 +#define BCM_SPIH_ID 0x43f6 +#define MIMO_FPGA_ID 0x43f8 +#define BCM_JTAGM2_ID 0x43f9 +#define SDHCI_FPGA_ID 0x43fa +#define BCM4402_ENET_ID 0x4402 +#define BCM4402_V90_ID 0x4403 +#define BCM4410_DEVICE_ID 0x4410 +#define BCM4412_DEVICE_ID 0x4412 +#define BCM4430_DEVICE_ID 0x4430 +#define BCM4432_DEVICE_ID 0x4432 +#define BCM4704_ENET_ID 0x4706 #define BCM4710_DEVICE_ID 0x4710 +#define BCM47XX_AUDIO_ID 0x4711 +#define BCM47XX_V90_ID 0x4712 +#define BCM47XX_ENET_ID 0x4713 +#define BCM47XX_EXT_ID 0x4714 +#define BCM47XX_GMAC_ID 0x4715 +#define BCM47XX_USBH_ID 0x4716 +#define BCM47XX_USBD_ID 0x4717 +#define BCM47XX_IPSEC_ID 0x4718 +#define BCM47XX_ROBO_ID 0x4719 +#define BCM47XX_USB20H_ID 0x471a +#define BCM47XX_USB20D_ID 0x471b +#define BCM47XX_ATA100_ID 0x471d +#define BCM47XX_SATAXOR_ID 0x471e +#define BCM47XX_GIGETH_ID 0x471f +#define BCM4712_MIPS_ID 0x4720 +#define BCM4716_DEVICE_ID 0x4722 +#define BCM47XX_SMBUS_EMU_ID 0x47fe +#define BCM47XX_XOR_EMU_ID 0x47ff +#define EPI41210_DEVICE_ID 0xa0fa +#define EPI41230_DEVICE_ID 0xa10e +#define JINVANI_SDIOH_ID 0x4743 #define BCM27XX_SDIOH_ID 0x2702 -#define PCIXX21_FLASHMEDIA0_ID 0x8033 -#define PCIXX21_SDIOH0_ID 0x8034 #define PCIXX21_FLASHMEDIA_ID 0x803b #define PCIXX21_SDIOH_ID 0x803c #define R5C822_SDIOH_ID 0x0822 @@ -121,11 +200,13 @@ #define BCM43112_CHIP_ID 43112 #define BCM4312_CHIP_ID 0x4312 #define BCM4313_CHIP_ID 0x4313 +#define BCM43131_CHIP_ID 43131 #define BCM4315_CHIP_ID 0x4315 #define BCM4318_CHIP_ID 0x4318 #define BCM4319_CHIP_ID 0x4319 #define BCM4320_CHIP_ID 0x4320 #define BCM4321_CHIP_ID 0x4321 +#define BCM43217_CHIP_ID 43217 #define BCM4322_CHIP_ID 0x4322 #define BCM43221_CHIP_ID 43221 #define BCM43222_CHIP_ID 43222 @@ -152,15 +233,28 @@ #define BCM4336_CHIP_ID 0x4336 #define BCM43362_CHIP_ID 43362 #define BCM4330_CHIP_ID 0x4330 +#define BCM6362_CHIP_ID 0x6362 +#define BCM4314_CHIP_ID 0x4314 +#define BCM43142_CHIP_ID 43142 +#define BCM4324_CHIP_ID 0x4324 + +#define BCM4342_CHIP_ID 4342 #define BCM4402_CHIP_ID 0x4402 #define BCM4704_CHIP_ID 0x4704 #define BCM4710_CHIP_ID 0x4710 #define BCM4712_CHIP_ID 0x4712 +#define BCM4716_CHIP_ID 0x4716 +#define BCM47162_CHIP_ID 47162 +#define BCM4748_CHIP_ID 0x4748 +#define BCM4749_CHIP_ID 0x4749 #define BCM4785_CHIP_ID 0x4785 #define BCM5350_CHIP_ID 0x5350 #define BCM5352_CHIP_ID 0x5352 #define BCM5354_CHIP_ID 0x5354 #define BCM5365_CHIP_ID 0x5365 +#define BCM5356_CHIP_ID 0x5356 +#define BCM5357_CHIP_ID 0x5357 +#define BCM53572_CHIP_ID 53572 #define BCM4303_PKG_ID 2 @@ -175,8 +269,479 @@ #define BCM4329_289PIN_PKG_ID 0 #define BCM4329_182PIN_PKG_ID 1 #define BCM5354E_PKG_ID 1 +#define BCM4716_PKG_ID 8 +#define BCM4717_PKG_ID 9 +#define BCM4718_PKG_ID 10 +#define BCM5356_PKG_NONMODE 1 +#define BCM5358U_PKG_ID 8 +#define BCM5358_PKG_ID 9 +#define BCM47186_PKG_ID 10 +#define BCM5357_PKG_ID 11 +#define BCM5356U_PKG_ID 12 +#define BCM53572_PKG_ID 8 +#define BCM47188_PKG_ID 9 +#define BCM4331TT_PKG_ID 8 +#define BCM4331TN_PKG_ID 9 +#define BCM4331TNA0_PKG_ID 0xb + + #define HDLSIM5350_PKG_ID 1 #define HDLSIM_PKG_ID 14 #define HWSIM_PKG_ID 15 +#define BCM43224_FAB_CSM 0x8 +#define BCM43224_FAB_SMIC 0xa +#define BCM4336_WLBGA_PKG_ID 0x8 +#define BCM4330_WLBGA_PKG_ID 0x0 +#define BCM4314PCIE_ARM_PKG_ID (8 | 0) +#define BCM4314SDIO_PKG_ID (8 | 1) +#define BCM4314PCIE_PKG_ID (8 | 2) +#define BCM4314SDIO_ARM_PKG_ID (8 | 3) +#define BCM4314SDIO_FPBGA_PKG_ID (8 | 4) +#define BCM4314DEV_PKG_ID (8 | 6) + +#define PCIXX21_FLASHMEDIA0_ID 0x8033 +#define PCIXX21_SDIOH0_ID 0x8034 + + +#define BFL_BTC2WIRE 0x00000001 +#define BFL_BTCOEX 0x00000001 +#define BFL_PACTRL 0x00000002 +#define BFL_AIRLINEMODE 0x00000004 +#define BFL_ADCDIV 0x00000008 +#define BFL_ENETROBO 0x00000010 +#define BFL_NOPLLDOWN 0x00000020 +#define BFL_CCKHIPWR 0x00000040 +#define BFL_ENETADM 0x00000080 +#define BFL_ENETVLAN 0x00000100 +#ifdef WLAFTERBURNER +#define BFL_AFTERBURNER 0x00000200 +#endif +#define BFL_NOPCI 0x00000400 +#define BFL_FEM 0x00000800 +#define BFL_EXTLNA 0x00001000 +#define BFL_HGPA 0x00002000 +#define BFL_BTC2WIRE_ALTGPIO 0x00004000 +#define BFL_ALTIQ 0x00008000 +#define BFL_NOPA 0x00010000 +#define BFL_RSSIINV 0x00020000 +#define BFL_PAREF 0x00040000 +#define BFL_3TSWITCH 0x00080000 +#define BFL_PHASESHIFT 0x00100000 +#define BFL_BUCKBOOST 0x00200000 +#define BFL_FEM_BT 0x00400000 +#define BFL_NOCBUCK 0x00800000 +#define BFL_CCKFAVOREVM 0x01000000 +#define BFL_PALDO 0x02000000 +#define BFL_LNLDO2_2P5 0x04000000 +#define BFL_FASTPWR 0x08000000 +#define BFL_UCPWRCTL_MININDX 0x08000000 +#define BFL_EXTLNA_5GHz 0x10000000 +#define BFL_TRSW_1by2 0x20000000 +#define BFL_LO_TRSW_R_5GHz 0x40000000 +#define BFL_ELNA_GAINDEF 0x80000000 +#define BFL_EXTLNA_TX 0x20000000 + + +#define BFL2_RXBB_INT_REG_DIS 0x00000001 +#define BFL2_APLL_WAR 0x00000002 +#define BFL2_TXPWRCTRL_EN 0x00000004 +#define BFL2_2X4_DIV 0x00000008 +#define BFL2_5G_PWRGAIN 0x00000010 +#define BFL2_PCIEWAR_OVR 0x00000020 +#define BFL2_CAESERS_BRD 0x00000040 +#define BFL2_BTC3WIRE 0x00000080 +#define BFL2_BTCLEGACY 0x00000080 +#define BFL2_SKWRKFEM_BRD 0x00000100 +#define BFL2_SPUR_WAR 0x00000200 +#define BFL2_GPLL_WAR 0x00000400 +#define BFL2_TRISTATE_LED 0x00000800 +#define BFL2_SINGLEANT_CCK 0x00001000 +#define BFL2_2G_SPUR_WAR 0x00002000 +#define BFL2_BPHY_ALL_TXCORES 0x00004000 +#define BFL2_FCC_BANDEDGE_WAR 0x00008000 +#define BFL2_GPLL_WAR2 0x00010000 +#define BFL2_IPALVLSHIFT_3P3 0x00020000 +#define BFL2_INTERNDET_TXIQCAL 0x00040000 +#define BFL2_XTALBUFOUTEN 0x00080000 +#define BFL2_ANAPACTRL_2G 0x00100000 +#define BFL2_ANAPACTRL_5G 0x00200000 +#define BFL2_ELNACTRL_TRSW_2G 0x00400000 +#define BFL2_BT_SHARE_ANT0 0x00800000 +#define BFL2_TEMPSENSE_HIGHER 0x01000000 +#define BFL2_BTC3WIREONLY 0x02000000 +#define BFL2_PWR_NOMINAL 0x04000000 +#define BFL2_EXTLNA_TX 0x08000000 + +#define BFL2_4313_RADIOREG 0x10000000 +#define BFL2_SECI_LOPWR_DIS 0x20000000 + + + +#define BOARD_GPIO_BTC3W_IN 0x850 +#define BOARD_GPIO_BTC3W_OUT 0x020 +#define BOARD_GPIO_BTCMOD_IN 0x010 +#define BOARD_GPIO_BTCMOD_OUT 0x020 +#define BOARD_GPIO_BTC_IN 0x080 +#define BOARD_GPIO_BTC_OUT 0x100 +#define BOARD_GPIO_PACTRL 0x200 +#define BOARD_GPIO_12 0x1000 +#define BOARD_GPIO_13 0x2000 +#define BOARD_GPIO_BTC4_IN 0x0800 +#define BOARD_GPIO_BTC4_BT 0x2000 +#define BOARD_GPIO_BTC4_STAT 0x4000 +#define BOARD_GPIO_BTC4_WLAN 0x8000 +#define BOARD_GPIO_1_WLAN_PWR 0x2 +#define BOARD_GPIO_4_WLAN_PWR 0x10 + +#define GPIO_BTC4W_OUT_4312 0x010 +#define GPIO_BTC4W_OUT_43224 0x020 +#define GPIO_BTC4W_OUT_43224_SHARED 0x0e0 +#define GPIO_BTC4W_OUT_43225 0x0e0 +#define GPIO_BTC4W_OUT_43421 0x020 +#define GPIO_BTC4W_OUT_4313 0x060 + +#define PCI_CFG_GPIO_SCS 0x10 +#define PCI_CFG_GPIO_HWRAD 0x20 +#define PCI_CFG_GPIO_XTAL 0x40 +#define PCI_CFG_GPIO_PLL 0x80 + + +#define PLL_DELAY 150 +#define FREF_DELAY 200 +#define MIN_SLOW_CLK 32 +#define XTAL_ON_DELAY 1000 + + +#define BU4710_BOARD 0x0400 +#define VSIM4710_BOARD 0x0401 +#define QT4710_BOARD 0x0402 + +#define BU4309_BOARD 0x040a +#define BCM94309CB_BOARD 0x040b +#define BCM94309MP_BOARD 0x040c +#define BCM4309AP_BOARD 0x040d + +#define BCM94302MP_BOARD 0x040e + +#define BU4306_BOARD 0x0416 +#define BCM94306CB_BOARD 0x0417 +#define BCM94306MP_BOARD 0x0418 + +#define BCM94710D_BOARD 0x041a +#define BCM94710R1_BOARD 0x041b +#define BCM94710R4_BOARD 0x041c +#define BCM94710AP_BOARD 0x041d + +#define BU2050_BOARD 0x041f + +#define BCM94306P50_BOARD 0x0420 + +#define BCM94309G_BOARD 0x0421 + +#define BU4704_BOARD 0x0423 +#define BU4702_BOARD 0x0424 + +#define BCM94306PC_BOARD 0x0425 + +#define MPSG4306_BOARD 0x0427 + +#define BCM94702MN_BOARD 0x0428 + + +#define BCM94702CPCI_BOARD 0x0429 + + +#define BCM95380RR_BOARD 0x042a + + +#define BCM94306CBSG_BOARD 0x042b + + +#define PCSG94306_BOARD 0x042d + + +#define BU4704SD_BOARD 0x042e + + +#define BCM94704AGR_BOARD 0x042f + + +#define BCM94308MP_BOARD 0x0430 + + +#define BCM94306GPRS_BOARD 0x0432 + + +#define BU5365_FPGA_BOARD 0x0433 + +#define BU4712_BOARD 0x0444 +#define BU4712SD_BOARD 0x045d +#define BU4712L_BOARD 0x045f + + +#define BCM94712AP_BOARD 0x0445 +#define BCM94712P_BOARD 0x0446 + + +#define BU4318_BOARD 0x0447 +#define CB4318_BOARD 0x0448 +#define MPG4318_BOARD 0x0449 +#define MP4318_BOARD 0x044a +#define SD4318_BOARD 0x044b + + +#define BCM94313BU_BOARD 0x050f +#define BCM94313HM_BOARD 0x0510 +#define BCM94313EPA_BOARD 0x0511 +#define BCM94313HMG_BOARD 0x051C + + +#define BCM96338_BOARD 0x6338 +#define BCM96348_BOARD 0x6348 +#define BCM96358_BOARD 0x6358 +#define BCM96368_BOARD 0x6368 + + +#define BCM94306P_BOARD 0x044c + + +#define BCM94303MP_BOARD 0x044e + + +#define BCM94306MPSGH_BOARD 0x044f + + +#define BCM94306MPM 0x0450 +#define BCM94306MPL 0x0453 + + +#define BCM94712AGR_BOARD 0x0451 + + +#define PC4303_BOARD 0x0454 + + +#define BCM95350K_BOARD 0x0455 + + +#define BCM95350R_BOARD 0x0456 + + +#define BCM94306MPLNA_BOARD 0x0457 + + +#define BU4320_BOARD 0x0458 +#define BU4320S_BOARD 0x0459 +#define BCM94320PH_BOARD 0x045a + + +#define BCM94306MPH_BOARD 0x045b + + +#define BCM94306PCIV_BOARD 0x045c + +#define BU4712SD_BOARD 0x045d + +#define BCM94320PFLSH_BOARD 0x045e + +#define BU4712L_BOARD 0x045f +#define BCM94712LGR_BOARD 0x0460 +#define BCM94320R_BOARD 0x0461 + +#define BU5352_BOARD 0x0462 + +#define BCM94318MPGH_BOARD 0x0463 + +#define BU4311_BOARD 0x0464 +#define BCM94311MC_BOARD 0x0465 +#define BCM94311MCAG_BOARD 0x0466 + +#define BCM95352GR_BOARD 0x0467 + + +#define BCM95351AGR_BOARD 0x0470 + + +#define BCM94704MPCB_BOARD 0x0472 + + +#define BU4785_BOARD 0x0478 + + +#define BU4321_BOARD 0x046b +#define BU4321E_BOARD 0x047c +#define MP4321_BOARD 0x046c +#define CB2_4321_BOARD 0x046d +#define CB2_4321_AG_BOARD 0x0066 +#define MC4321_BOARD 0x046e + + +#define BU4328_BOARD 0x0481 +#define BCM4328SDG_BOARD 0x0482 +#define BCM4328SDAG_BOARD 0x0483 +#define BCM4328UG_BOARD 0x0484 +#define BCM4328UAG_BOARD 0x0485 +#define BCM4328PC_BOARD 0x0486 +#define BCM4328CF_BOARD 0x0487 + + +#define BCM94325DEVBU_BOARD 0x0490 +#define BCM94325BGABU_BOARD 0x0491 + +#define BCM94325SDGWB_BOARD 0x0492 + +#define BCM94325SDGMDL_BOARD 0x04aa +#define BCM94325SDGMDL2_BOARD 0x04c6 +#define BCM94325SDGMDL3_BOARD 0x04c9 + +#define BCM94325SDABGWBA_BOARD 0x04e1 + + +#define BCM94322MC_SSID 0x04a4 +#define BCM94322USB_SSID 0x04a8 +#define BCM94322HM_SSID 0x04b0 +#define BCM94322USB2D_SSID 0x04bf + + +#define BCM4312MCGSG_BOARD 0x04b5 + + +#define BCM94315DEVBU_SSID 0x04c2 +#define BCM94315USBGP_SSID 0x04c7 +#define BCM94315BGABU_SSID 0x04ca +#define BCM94315USBGP41_SSID 0x04cb + + +#define BCM94319DEVBU_SSID 0X04e5 +#define BCM94319USB_SSID 0X04e6 +#define BCM94319SD_SSID 0X04e7 + + +#define BCM94716NR2_SSID 0x04cd + + +#define BCM94319DEVBU_SSID 0X04e5 +#define BCM94319USBNP4L_SSID 0X04e6 +#define BCM94319WLUSBN4L_SSID 0X04e7 +#define BCM94319SDG_SSID 0X04ea +#define BCM94319LCUSBSDN4L_SSID 0X04eb +#define BCM94319USBB_SSID 0x04ee +#define BCM94319LCSDN4L_SSID 0X0507 +#define BCM94319LSUSBN4L_SSID 0X0508 +#define BCM94319SDNA4L_SSID 0X0517 +#define BCM94319SDELNA4L_SSID 0X0518 +#define BCM94319SDELNA6L_SSID 0X0539 +#define BCM94319ARCADYAN_SSID 0X0546 +#define BCM94319WINDSOR_SSID 0x0561 +#define BCM94319MLAP_SSID 0x0562 +#define BCM94319SDNA_SSID 0x058b +#define BCM94319BHEMU3_SSID 0x0563 +#define BCM94319SDHMB_SSID 0x058c +#define BCM94319SDBREF_SSID 0x05a1 +#define BCM94319USBSDB_SSID 0x05a2 + + + +#define BCM94329AGB_SSID 0X04b9 +#define BCM94329TDKMDL1_SSID 0X04ba +#define BCM94329TDKMDL11_SSID 0X04fc +#define BCM94329OLYMPICN18_SSID 0X04fd +#define BCM94329OLYMPICN90_SSID 0X04fe +#define BCM94329OLYMPICN90U_SSID 0X050c +#define BCM94329OLYMPICN90M_SSID 0X050b +#define BCM94329AGBF_SSID 0X04ff +#define BCM94329OLYMPICX17_SSID 0X0504 +#define BCM94329OLYMPICX17M_SSID 0X050a +#define BCM94329OLYMPICX17U_SSID 0X0509 +#define BCM94329OLYMPICUNO_SSID 0X0564 +#define BCM94329MOTOROLA_SSID 0X0565 +#define BCM94329OLYMPICLOCO_SSID 0X0568 + +#define BCM94336SD_WLBGABU_SSID 0x0511 +#define BCM94336SD_WLBGAREF_SSID 0x0519 +#define BCM94336SDGP_SSID 0x0538 +#define BCM94336SDG_SSID 0x0519 +#define BCM94336SDGN_SSID 0x0538 +#define BCM94336SDGFC_SSID 0x056B + + +#define BCM94330SDG_SSID 0x0528 +#define BCM94330SD_FCBGABU_SSID 0x052e +#define BCM94330SD_WLBGABU_SSID 0x052f +#define BCM94330SD_FCBGA_SSID 0x0530 +#define BCM94330FCSDAGB_SSID 0x0532 +#define BCM94330OLYMPICAMG_SSID 0x0549 +#define BCM94330OLYMPICAMGEPA_SSID 0x054F +#define BCM94330OLYMPICUNO3_SSID 0x0551 +#define BCM94330WLSDAGB_SSID 0x0547 +#define BCM94330CSPSDAGBB_SSID 0x054A + + +#define BCM943224X21 0x056e +#define BCM943224X21_FCC 0x00d1 + + +#define BCM943228BU8_SSID 0x0540 +#define BCM943228BU9_SSID 0x0541 +#define BCM943228BU_SSID 0x0542 +#define BCM943227HM4L_SSID 0x0543 +#define BCM943227HMB_SSID 0x0544 +#define BCM943228HM4L_SSID 0x0545 +#define BCM943228SD_SSID 0x0573 + + +#define BCM943239MOD_SSID 0x05ac +#define BCM943239REF_SSID 0x05aa + + +#define BCM94331X19 0x00D6 +#define BCM94331PCIEBT3Ax_SSID 0x00E4 +#define BCM94331X12_2G_SSID 0x00EC +#define BCM94331X12_5G_SSID 0x00ED +#define BCM94331X29B 0x00EF +#define BCM94331BU_SSID 0x0523 +#define BCM94331S9BU_SSID 0x0524 +#define BCM94331MC_SSID 0x0525 +#define BCM94331MCI_SSID 0x0526 +#define BCM94331PCIEBT4_SSID 0x0527 +#define BCM94331HM_SSID 0x0574 +#define BCM94331PCIEDUAL_SSID 0x059B +#define BCM94331MCH5_SSID 0x05A9 +#define BCM94331PCIEDUALV2_SSID 0x05B7 +#define BCM94331CS_SSID 0x05C6 +#define BCM94331CSAX_SSID 0x00EF + + +#define BCM953572BU_SSID 0x058D +#define BCM953572NR2_SSID 0x058E +#define BCM947188NR2_SSID 0x058F +#define BCM953572SDRNR2_SSID 0x0590 + + +#define BCM943236OLYMPICSULLEY_SSID 0x594 +#define BCM943236PREPROTOBLU2O3_SSID 0x5b9 +#define BCM943236USBELNA_SSID 0x5f8 + + +#define GPIO_NUMPINS 32 + + +#define RDL_RAM_BASE_4319 0x60000000 +#define RDL_RAM_BASE_4329 0x60000000 +#define RDL_RAM_SIZE_4319 0x48000 +#define RDL_RAM_SIZE_4329 0x48000 +#define RDL_RAM_SIZE_43236 0x70000 +#define RDL_RAM_BASE_43236 0x60000000 +#define RDL_RAM_SIZE_4328 0x60000 +#define RDL_RAM_BASE_4328 0x80000000 +#define RDL_RAM_SIZE_4322 0x60000 +#define RDL_RAM_BASE_4322 0x60000000 + + +#define MUXENAB_UART 0x00000001 +#define MUXENAB_GPIO 0x00000002 +#define MUXENAB_ERCX 0x00000004 +#define MUXENAB_JTAG 0x00000008 +#define MUXENAB_HOST_WAKE 0x00000010 #endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmendian.h b/drivers/net/wireless/bcmdhd/include/bcmendian.h index 04b07ec..f3356a7 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmendian.h +++ b/drivers/net/wireless/bcmdhd/include/bcmendian.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmendian.h,v 1.36 2009-11-09 05:29:43 Exp $ + * $Id: bcmendian.h 277737 2011-08-16 17:54:59Z $ * * This file by default provides proper behavior on little-endian architectures. * On big-endian architectures, IL_BIGENDIAN should be defined. diff --git a/drivers/net/wireless/bcmdhd/include/bcmpcispi.h b/drivers/net/wireless/bcmdhd/include/bcmpcispi.h index fd148c5..51e0427 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmpcispi.h +++ b/drivers/net/wireless/bcmdhd/include/bcmpcispi.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmpcispi.h,v 13.15.112.1 2010-11-15 18:22:12 Exp $ + * $Id: bcmpcispi.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _BCM_PCI_SPI_H #define _BCM_PCI_SPI_H diff --git a/drivers/net/wireless/bcmdhd/include/bcmperf.h b/drivers/net/wireless/bcmdhd/include/bcmperf.h index a3985cf..a503edb 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmperf.h +++ b/drivers/net/wireless/bcmdhd/include/bcmperf.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmperf.h,v 13.5 2007-09-14 22:00:59 Exp $ + * $Id: bcmperf.h 277737 2011-08-16 17:54:59Z $ */ /* essai */ #ifndef _BCMPERF_H_ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdbus.h b/drivers/net/wireless/bcmdhd/include/bcmsdbus.h index 5fda5e9..21a58b4 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdbus.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdbus.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdbus.h,v 13.17.86.2 2010-12-23 01:13:20 Exp $ + * $Id: bcmsdbus.h 300017 2011-12-01 20:30:27Z $ */ #ifndef _sdio_api_h_ @@ -117,4 +117,12 @@ void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh); +extern SDIOH_API_RC sdioh_sleep(sdioh_info_t *si, bool enab); + +/* GPIO support */ +extern SDIOH_API_RC sdioh_gpio_init(sdioh_info_t *sd); +extern bool sdioh_gpioin(sdioh_info_t *sd, uint32 gpio); +extern SDIOH_API_RC sdioh_gpioouten(sdioh_info_t *sd, uint32 gpio); +extern SDIOH_API_RC sdioh_gpioout(sdioh_info_t *sd, uint32 gpio, bool enab); + #endif /* _sdio_api_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh.h b/drivers/net/wireless/bcmdhd/include/bcmsdh.h index 6131d8a..def3c02 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdh.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdh.h @@ -23,7 +23,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh.h,v 13.46.52.3 2010-10-19 00:41:44 Exp $ + * $Id: bcmsdh.h 300017 2011-12-01 20:30:27Z $ */ #ifndef _bcmsdh_h_ @@ -208,4 +208,12 @@ extern uint32 bcmsdh_cur_sbwad(void *sdh); extern void bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev); +extern int bcmsdh_sleep(void *sdh, bool enab); + +/* GPIO support */ +extern int bcmsdh_gpio_init(void *sd); +extern bool bcmsdh_gpioin(void *sd, uint32 gpio); +extern int bcmsdh_gpioouten(void *sd, uint32 gpio); +extern int bcmsdh_gpioout(void *sd, uint32 gpio, bool enab); + #endif /* _bcmsdh_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h index d188c4e..db8ea59 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_sdmmc.h,v 13.5.88.1 2010-12-23 01:13:20 Exp $ + * $Id: bcmsdh_sdmmc.h 314048 2012-02-09 20:31:56Z $ */ #ifndef __BCMSDH_SDMMC_H__ @@ -82,9 +82,10 @@ struct sdioh_info { uint8 num_funcs; /* Supported funcs on client */ uint32 com_cis_ptr; uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; - uint max_dma_len; - uint max_dma_descriptors; /* DMA Descriptors supported by this controller. */ -// SDDMA_DESCRIPTOR SGList[32]; /* Scatter/Gather DMA List */ + +#define SDIOH_SDMMC_MAX_SG_ENTRIES 32 + struct scatterlist sg_list[SDIOH_SDMMC_MAX_SG_ENTRIES]; + bool use_rxchain; }; /************************************************************ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h b/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h index ee29b5c..1b9d39f 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdpcm.h,v 13.4.90.2 2010-05-12 04:14:25 Exp $ + * $Id: bcmsdpcm.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _bcmsdpcm_h_ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdspi.h b/drivers/net/wireless/bcmdhd/include/bcmsdspi.h index 0bff355..a62bee4 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdspi.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdspi.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdspi.h,v 13.11.86.1 2010-11-15 18:14:56 Exp $ + * $Id: bcmsdspi.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _BCM_SD_SPI_H #define _BCM_SD_SPI_H diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdstd.h b/drivers/net/wireless/bcmdhd/include/bcmsdstd.h index 0f4c026..c738254 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmsdstd.h +++ b/drivers/net/wireless/bcmdhd/include/bcmsdstd.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdstd.h,v 13.21.2.6 2010-11-15 18:14:01 Exp $ + * $Id: bcmsdstd.h 324819 2012-03-30 12:15:19Z $ */ #ifndef _BCM_SD_STD_H #define _BCM_SD_STD_H @@ -78,6 +78,7 @@ extern void sdstd_osfree(sdioh_info_t *sd); #define SDIOH_CMD7_EXP_STATUS 0x00001E00 #define RETRIES_LARGE 100000 +#define sdstd_os_yield(sd) do {} while (0) #define RETRIES_SMALL 100 @@ -148,8 +149,8 @@ struct sdioh_info { bool got_hcint; /* local interrupt flag */ uint16 last_intrstatus; /* to cache intrstatus */ int host_UHSISupported; /* whether UHSI is supported for HC. */ - int card_UHSI_voltage_Supported; /* whether UHSI is supported for - * Card in terms of Voltage [1.8 or 3.3]. + int card_UHSI_voltage_Supported; /* whether UHSI is supported for + * Card in terms of Voltage [1.8 or 3.3]. */ int global_UHSI_Supp; /* type of UHSI support in both host and card. * HOST_SDR_UNSUPP: capabilities not supported/matched @@ -171,21 +172,6 @@ struct sdioh_info { #define USE_DMA(sd) ((bool)((sd->sd_dma_mode > 0) ? TRUE : FALSE)) -/* SDIO Host Control Register DMA Mode Definitions */ -#define SDIOH_SDMA_MODE 0 -#define SDIOH_ADMA1_MODE 1 -#define SDIOH_ADMA2_MODE 2 -#define SDIOH_ADMA2_64_MODE 3 - -#define ADMA2_ATTRIBUTE_VALID (1 << 0) /* ADMA Descriptor line valid */ -#define ADMA2_ATTRIBUTE_END (1 << 1) /* End of Descriptor */ -#define ADMA2_ATTRIBUTE_INT (1 << 2) /* Interrupt when line is done */ -#define ADMA2_ATTRIBUTE_ACT_NOP (0 << 4) /* Skip current line, go to next. */ -#define ADMA2_ATTRIBUTE_ACT_RSV (1 << 4) /* Same as NOP */ -#define ADMA1_ATTRIBUTE_ACT_SET (1 << 4) /* ADMA1 Only - set transfer length */ -#define ADMA2_ATTRIBUTE_ACT_TRAN (2 << 4) /* Transfer Data of one descriptor line. */ -#define ADMA2_ATTRIBUTE_ACT_LINK (3 << 4) /* Link Descriptor */ - /* States for Tuning and corr data */ #define TUNING_IDLE 0 #define TUNING_START 1 @@ -194,17 +180,9 @@ struct sdioh_info { #define DATA_TRANSFER_IDLE 0 #define DATA_TRANSFER_ONGOING 1 +#define CHECK_TUNING_PRE_DATA 1 +#define CHECK_TUNING_POST_DATA 2 -/* ADMA2 Descriptor Table Entry for 32-bit Address */ -typedef struct adma2_dscr_32b { - uint32 len_attr; - uint32 phys_addr; -} adma2_dscr_32b_t; - -/* ADMA1 Descriptor Table Entry */ -typedef struct adma1_dscr { - uint32 phys_addr_attr; -} adma1_dscr_t; /************************************************************ * Internal interfaces: per-port references into bcmsdstd.c @@ -252,9 +230,12 @@ extern int sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool yield, extern void sdstd_3_enable_retuning_int(sdioh_info_t *sd); extern void sdstd_3_disable_retuning_int(sdioh_info_t *sd); extern bool sdstd_3_is_retuning_int_set(sdioh_info_t *sd); +extern void sdstd_3_check_and_do_tuning(sdioh_info_t *sd, int tuning_param); extern bool sdstd_3_check_and_set_retuning(sdioh_info_t *sd); extern int sdstd_3_get_tune_state(sdioh_info_t *sd); +extern int sdstd_3_get_data_state(sdioh_info_t *sd); extern void sdstd_3_set_tune_state(sdioh_info_t *sd, int state); +extern void sdstd_3_set_data_state(sdioh_info_t *sd, int state); extern uint8 sdstd_3_get_tuning_exp(sdioh_info_t *sd); extern uint32 sdstd_3_get_uhsi_clkmode(sdioh_info_t *sd); extern int sdstd_3_clk_tuning(sdioh_info_t *sd, uint32 sd3ClkMode); diff --git a/drivers/net/wireless/bcmdhd/include/bcmspi.h b/drivers/net/wireless/bcmdhd/include/bcmspi.h index 0eb2a30..34a02d0 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmspi.h +++ b/drivers/net/wireless/bcmdhd/include/bcmspi.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmspi.h,v 13.5.112.1 2010-11-15 18:13:09 Exp $ + * $Id: bcmspi.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _BCM_SPI_H #define _BCM_SPI_H diff --git a/drivers/net/wireless/bcmdhd/include/bcmutils.h b/drivers/net/wireless/bcmdhd/include/bcmutils.h index 530036f..6849c26 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmutils.h +++ b/drivers/net/wireless/bcmdhd/include/bcmutils.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmutils.h,v 13.236.2.16 2011-01-26 00:45:06 Exp $ + * $Id: bcmutils.h 294991 2011-11-09 00:17:28Z $ */ @@ -221,7 +221,9 @@ extern uint16 pktpool_avail(pktpool_t *pktp); extern int pktpool_avail_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); extern int pktpool_empty_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); extern int pktpool_setmaxlen(pktpool_t *pktp, uint16 maxlen); +extern int pktpool_setmaxlen_strict(osl_t *osh, pktpool_t *pktp, uint16 maxlen); extern void pktpool_emptycb_disable(pktpool_t *pktp, bool disable); +extern bool pktpool_emptycb_disabled(pktpool_t *pktp); #define POOLPTR(pp) ((pktpool_t *)(pp)) #define pktpool_len(pp) (POOLPTR(pp)->len - 1) @@ -330,6 +332,8 @@ extern char *bcm_ip_ntoa(struct ipv4_addr *ia, char *buf); extern void bcm_mdelay(uint ms); +#define NVRAM_RECLAIM_CHECK(name) + extern char *getvar(char *vars, const char *name); extern int getintvar(char *vars, const char *name); extern int getintvararray(char *vars, const char *name, int index); @@ -534,9 +538,17 @@ extern int bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len); & ~((boundary) - 1)) #define ISPOWEROF2(x) ((((x)-1)&(x)) == 0) #define VALID_MASK(mask) !((mask) & ((mask) + 1)) + #ifndef OFFSETOF +#ifdef __ARMCC_VERSION + +#include <stddef.h> +#define OFFSETOF(type, member) offsetof(type, member) +#else #define OFFSETOF(type, member) ((uint)(uintptr)&((type *)0)->member) #endif +#endif + #ifndef ARRAYSIZE #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) #endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmwifi.h b/drivers/net/wireless/bcmdhd/include/bcmwifi.h index 45f3c03..e5207e9 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmwifi.h +++ b/drivers/net/wireless/bcmdhd/include/bcmwifi.h @@ -23,7 +23,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmwifi.h,v 1.29.6.3 2010-08-03 17:47:04 Exp $ + * $Id: bcmwifi.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/dhdioctl.h b/drivers/net/wireless/bcmdhd/include/dhdioctl.h index 9661dac..175ff85 100644 --- a/drivers/net/wireless/bcmdhd/include/dhdioctl.h +++ b/drivers/net/wireless/bcmdhd/include/dhdioctl.h @@ -25,7 +25,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhdioctl.h,v 13.11.10.1 2010-12-22 23:47:26 Exp $ + * $Id: dhdioctl.h 323572 2012-03-26 06:28:14Z $ */ #ifndef _dhdioctl_h_ @@ -87,6 +87,8 @@ enum { #define DHD_BTA_VAL 0x1000 #define DHD_ISCAN_VAL 0x2000 #define DHD_ARPOE_VAL 0x4000 +#define DHD_REORDER_VAL 0x8000 +#define DHD_WL_VAL 0x10000 #ifdef SDTEST /* For pktgen iovar */ diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h index fe71e16..5df25c1 100644 --- a/drivers/net/wireless/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -19,7 +19,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: epivers.h.in,v 13.32.4.1 2010-09-17 00:39:18 $ + * $Id: epivers.h.in 277737 2011-08-16 17:54:59Z $ * */ @@ -31,19 +31,19 @@ #define EPI_MINOR_VERSION 90 -#define EPI_RC_NUMBER 125 +#define EPI_RC_NUMBER 195 -#define EPI_INCREMENTAL_NUMBER 94 +#define EPI_INCREMENTAL_NUMBER 75 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 5, 90, 125, 94 +#define EPI_VERSION 5, 90, 195, 75 -#define EPI_VERSION_NUM 0x055a7d5e +#define EPI_VERSION_NUM 0x055ac34b -#define EPI_VERSION_DEV 5.90.125 +#define EPI_VERSION_DEV 5.90.195 -#define EPI_VERSION_STR "5.90.125.94.1" +#define EPI_VERSION_STR "5.90.195.75" #endif diff --git a/drivers/net/wireless/bcmdhd/include/hndpmu.h b/drivers/net/wireless/bcmdhd/include/hndpmu.h index 51c51b9..9bfc8c9 100644 --- a/drivers/net/wireless/bcmdhd/include/hndpmu.h +++ b/drivers/net/wireless/bcmdhd/include/hndpmu.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndpmu.h,v 13.35.8.5 2011-02-11 00:56:32 Exp $ + * $Id: hndpmu.h 335486 2012-05-28 09:47:55Z $ */ #ifndef _hndpmu_h_ @@ -31,4 +31,7 @@ extern void si_pmu_otp_power(si_t *sih, osl_t *osh, bool on); extern void si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength); +extern void si_pmu_set_otp_wr_volts(si_t *sih); +extern void si_pmu_set_otp_rd_volts(si_t *sih); + #endif /* _hndpmu_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h b/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h index 8b9615c..7d862c4 100644 --- a/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h +++ b/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndrte_armtrap.h,v 13.4.14.1 2011-02-05 00:04:30 Exp $ + * $Id: hndrte_armtrap.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _hndrte_armtrap_h diff --git a/drivers/net/wireless/bcmdhd/include/hndrte_cons.h b/drivers/net/wireless/bcmdhd/include/hndrte_cons.h index b9ede53..859ddc8 100644 --- a/drivers/net/wireless/bcmdhd/include/hndrte_cons.h +++ b/drivers/net/wireless/bcmdhd/include/hndrte_cons.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndrte_cons.h,v 13.4.10.4 2011-02-05 00:08:20 Exp $ + * $Id: hndrte_cons.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _HNDRTE_CONS_H diff --git a/drivers/net/wireless/bcmdhd/include/hndsoc.h b/drivers/net/wireless/bcmdhd/include/hndsoc.h index 4e26121..34f927c 100644 --- a/drivers/net/wireless/bcmdhd/include/hndsoc.h +++ b/drivers/net/wireless/bcmdhd/include/hndsoc.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: hndsoc.h,v 13.11 2009-12-03 23:52:31 Exp $ + * $Id: hndsoc.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _HNDSOC_H diff --git a/drivers/net/wireless/bcmdhd/include/htsf.h b/drivers/net/wireless/bcmdhd/include/htsf.h index 379fbbe..d875edb 100644 --- a/drivers/net/wireless/bcmdhd/include/htsf.h +++ b/drivers/net/wireless/bcmdhd/include/htsf.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: htsf.h,v 1.1.2.4 2011-01-21 08:27:03 Exp $ + * $Id: htsf.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _HTSF_H_ #define _HTSF_H_ diff --git a/drivers/net/wireless/bcmdhd/include/linux_osl.h b/drivers/net/wireless/bcmdhd/include/linux_osl.h index 38de298..830d351 100644 --- a/drivers/net/wireless/bcmdhd/include/linux_osl.h +++ b/drivers/net/wireless/bcmdhd/include/linux_osl.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: linux_osl.h,v 13.158.6.3 2010-12-22 23:47:26 Exp $ + * $Id: linux_osl.h 301794 2011-12-08 20:41:35Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/linuxver.h b/drivers/net/wireless/bcmdhd/include/linuxver.h index 96844db..54d88ee 100644 --- a/drivers/net/wireless/bcmdhd/include/linuxver.h +++ b/drivers/net/wireless/bcmdhd/include/linuxver.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: linuxver.h,v 13.53.2.2 2010-12-22 23:47:26 Exp $ + * $Id: linuxver.h 312264 2012-02-02 00:49:43Z $ */ @@ -482,7 +482,11 @@ typedef struct { #define DBG_THR(x) #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define SMP_RD_BARRIER_DEPENDS(x) smp_read_barrier_depends(x) +#else #define SMP_RD_BARRIER_DEPENDS(x) smp_rmb(x) +#endif #define PROC_START(thread_func, owner, tsk_ctl, flags) \ @@ -507,7 +511,24 @@ typedef struct { (tsk_ctl)->thr_pid = -1; \ } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define DAEMONIZE(a) daemonize(a); \ + allow_signal(SIGKILL); \ + allow_signal(SIGTERM); +#else /* Linux 2.4 (w/o preemption patch) */ +#define RAISE_RX_SOFTIRQ() \ + cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ) +#define DAEMONIZE(a) daemonize(); \ + do { if (a) \ + strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \ + } while (0); +#endif /* LINUX_VERSION_CODE */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define BLOCKABLE() (!in_atomic()) +#else +#define BLOCKABLE() (!in_interrupt()) +#endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) #define KILL_PROC(nr, sig) \ diff --git a/drivers/net/wireless/bcmdhd/include/miniopt.h b/drivers/net/wireless/bcmdhd/include/miniopt.h index f468420..77eace6 100644 --- a/drivers/net/wireless/bcmdhd/include/miniopt.h +++ b/drivers/net/wireless/bcmdhd/include/miniopt.h @@ -20,7 +20,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: miniopt.h,v 1.3 2009-01-15 00:06:54 Exp $ + * $Id: miniopt.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/msgtrace.h b/drivers/net/wireless/bcmdhd/include/msgtrace.h index 721d421..088f1e8 100644 --- a/drivers/net/wireless/bcmdhd/include/msgtrace.h +++ b/drivers/net/wireless/bcmdhd/include/msgtrace.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: msgtrace.h,v 1.4 2009-04-10 04:15:32 Exp $ + * $Id: msgtrace.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _MSGTRACE_H diff --git a/drivers/net/wireless/bcmdhd/include/osl.h b/drivers/net/wireless/bcmdhd/include/osl.h index 80248ee..b8cc256 100644 --- a/drivers/net/wireless/bcmdhd/include/osl.h +++ b/drivers/net/wireless/bcmdhd/include/osl.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: osl.h,v 13.44.96.1 2010-05-20 11:09:18 Exp $ + * $Id: osl.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/packed_section_end.h b/drivers/net/wireless/bcmdhd/include/packed_section_end.h index 5d4a876..71f8b2e 100644 --- a/drivers/net/wireless/bcmdhd/include/packed_section_end.h +++ b/drivers/net/wireless/bcmdhd/include/packed_section_end.h @@ -34,7 +34,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: packed_section_end.h,v 1.4 2008-12-09 23:43:22 Exp $ + * $Id: packed_section_end.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/packed_section_start.h b/drivers/net/wireless/bcmdhd/include/packed_section_start.h index da2fed6..afc2ba3 100644 --- a/drivers/net/wireless/bcmdhd/include/packed_section_start.h +++ b/drivers/net/wireless/bcmdhd/include/packed_section_start.h @@ -34,7 +34,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: packed_section_start.h,v 1.4.124.1 2010-09-17 00:47:03 Exp $ + * $Id: packed_section_start.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/pcicfg.h b/drivers/net/wireless/bcmdhd/include/pcicfg.h index fae063a..6619943 100644 --- a/drivers/net/wireless/bcmdhd/include/pcicfg.h +++ b/drivers/net/wireless/bcmdhd/include/pcicfg.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: pcicfg.h,v 1.50 2009-12-07 21:56:06 Exp $ + * $Id: pcicfg.h 277737 2011-08-16 17:54:59Z $ */ @@ -39,6 +39,19 @@ #define PCI_INT_MASK 0x94 #define PCIE_EXTCFG_OFFSET 0x100 +#define PCI_SPROM_CONTROL 0x88 +#define PCI_BAR1_CONTROL 0x8c +#define PCI_TO_SB_MB 0x98 +#define PCI_BACKPLANE_ADDR 0xa0 +#define PCI_BACKPLANE_DATA 0xa4 +#define PCI_CLK_CTL_ST 0xa8 +#define PCI_BAR0_WIN2 0xac +#define PCI_GPIO_IN 0xb0 +#define PCI_GPIO_OUT 0xb4 +#define PCI_GPIO_OUTEN 0xb8 + +#define PCI_BAR0_SHADOW_OFFSET (2 * 1024) +#define PCI_BAR0_SPROM_OFFSET (4 * 1024) #define PCI_BAR0_PCIREGS_OFFSET (6 * 1024) #define PCI_BAR0_PCISBR_OFFSET (4 * 1024) @@ -49,4 +62,17 @@ #define PCI_16KB0_CCREGS_OFFSET (12 * 1024) #define PCI_16KBB0_WINSZ (16 * 1024) + +#define PCI_16KB0_WIN2_OFFSET (4 * 1024) + + + +#define SPROM_SZ_MSK 0x02 +#define SPROM_LOCKED 0x08 +#define SPROM_BLANK 0x04 +#define SPROM_WRITEEN 0x10 +#define SPROM_BOOTROM_WE 0x20 +#define SPROM_BACKPLANE_EN 0x40 +#define SPROM_OTPIN_USE 0x80 + #endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11.h b/drivers/net/wireless/bcmdhd/include/proto/802.11.h index 2342cb3..fd69aac 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/802.11.h +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11.h @@ -21,7 +21,7 @@ * * Fundamental types and constants relating to 802.11 * - * $Id: 802.11.h,v 9.260.2.6 2010-12-15 21:41:14 Exp $ + * $Id: 802.11.h 304058 2011-12-21 00:39:12Z $ */ @@ -429,10 +429,26 @@ typedef struct dot11_obss_chanlist dot11_obss_chanlist_t; BWL_PRE_PACKED_STRUCT struct dot11_extcap_ie { uint8 id; uint8 len; - uint8 cap; + uint8 cap[1]; } BWL_POST_PACKED_STRUCT; typedef struct dot11_extcap_ie dot11_extcap_ie_t; #define DOT11_EXTCAP_LEN 1 +#define DOT11_EXTCAP_LEN_TDLS 5 + +BWL_PRE_PACKED_STRUCT struct dot11_extcap { + uint8 extcap[DOT11_EXTCAP_LEN_TDLS]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_extcap dot11_extcap_t; + + +#define TDLS_CAP_TDLS 37 +#define TDLS_CAP_PU_BUFFER_STA 28 +#define TDLS_CAP_PEER_PSM 20 +#define TDLS_CAP_CH_SW 30 +#define TDLS_CAP_PROH 38 +#define TDLS_CAP_CH_SW_PROH 39 + +#define TDLS_CAP_MAX_BIT 39 @@ -545,6 +561,9 @@ typedef struct dot11_ibss_dfs dot11_ibss_dfs_t; #define WME_SUBTYPE_IE 0 #define WME_SUBTYPE_PARAM_IE 1 #define WME_SUBTYPE_TSPEC 2 +#define WME_VERSION_LEN 1 +#define WME_PARAMETER_IE_LEN 24 + #define AC_BE 0 @@ -709,6 +728,15 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_MGMT_NOTIFICATION_LEN 4 +BWL_PRE_PACKED_STRUCT struct ti_ie { + uint8 ti_type; + uint32 ti_val; +} BWL_POST_PACKED_STRUCT; +typedef struct ti_ie ti_ie_t; +#define TI_TYPE_REASSOC_DEADLINE 1 +#define TI_TYPE_KEY_LIFETIME 2 + + #define WME_ADDTS_REQUEST 0 #define WME_ADDTS_RESPONSE 1 #define WME_DELTS_REQUEST 2 @@ -724,8 +752,7 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_OPEN_SYSTEM 0 #define DOT11_SHARED_KEY 1 -#define DOT11_OPEN_SHARED 2 -#define DOT11_FAST_BSS 3 +#define DOT11_FAST_BSS 2 #define DOT11_CHALLENGE_LEN 128 @@ -926,9 +953,18 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_RC_MAX 23 +#define DOT11_RC_TDLS_PEER_UNREACH 25 +#define DOT11_RC_TDLS_DOWN_UNSPECIFIED 26 + #define DOT11_SC_SUCCESS 0 #define DOT11_SC_FAILURE 1 +#define DOT11_SC_TDLS_WAKEUP_SCH_ALT 2 + +#define DOT11_SC_TDLS_WAKEUP_SCH_REJ 3 +#define DOT11_SC_TDLS_SEC_DISABLED 5 +#define DOT11_SC_LIFETIME_REJ 6 +#define DOT11_SC_NOT_SAME_BSS 7 #define DOT11_SC_CAP_MISMATCH 10 #define DOT11_SC_REASSOC_FAIL 11 #define DOT11_SC_ASSOC_FAIL 12 @@ -947,13 +983,22 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_SC_ASSOC_SHORTSLOT_REQUIRED 25 #define DOT11_SC_ASSOC_ERPBCC_REQUIRED 26 #define DOT11_SC_ASSOC_DSSOFDM_REQUIRED 27 - -#define DOT11_SC_DECLINED 37 -#define DOT11_SC_INVALID_PARAMS 38 -#define DOT11_SC_INVALID_AKMP 43 -#define DOT11_SC_INVALID_MDID 54 -#define DOT11_SC_INVALID_FTIE 55 - +#define DOT11_SC_ASSOC_R0KH_UNREACHABLE 28 +#define DOT11_SC_ASSOC_TRY_LATER 30 +#define DOT11_SC_ASSOC_MFP_VIOLATION 31 + +#define DOT11_SC_DECLINED 37 +#define DOT11_SC_INVALID_PARAMS 38 +#define DOT11_SC_INVALID_PAIRWISE_CIPHER 42 +#define DOT11_SC_INVALID_AKMP 43 +#define DOT11_SC_INVALID_RSNIE_CAP 45 +#define DOT11_SC_INVALID_PMKID 53 +#define DOT11_SC_INVALID_MDID 54 +#define DOT11_SC_INVALID_FTIE 55 + +#define DOT11_SC_UNEXP_MSG 70 +#define DOT11_SC_INVALID_SNONCE 71 +#define DOT11_SC_INVALID_RSNIE 72 #define DOT11_MNG_DS_PARAM_LEN 1 #define DOT11_MNG_IBSS_PARAM_LEN 2 @@ -1008,6 +1053,7 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_MNG_MDIE_ID 54 #define DOT11_MNG_FTIE_ID 55 #define DOT11_MNG_FT_TI_ID 56 +#define DOT11_MNG_RDE_ID 57 #define DOT11_MNG_REGCLASS_ID 59 #define DOT11_MNG_EXT_CSA_ID 60 #define DOT11_MNG_HT_ADD 61 @@ -1018,7 +1064,13 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_MNG_HT_BSS_COEXINFO_ID 72 #define DOT11_MNG_HT_BSS_CHANNEL_REPORT_ID 73 #define DOT11_MNG_HT_OBSS_ID 74 -#define DOT11_MNG_EXT_CAP 127 +#define DOT11_MNG_CHANNEL_USAGE 97 +#define DOT11_MNG_LINK_IDENTIFIER_ID 101 +#define DOT11_MNG_WAKEUP_SCHEDULE_ID 102 +#define DOT11_MNG_CHANNEL_SWITCH_TIMING_ID 104 +#define DOT11_MNG_PTI_CONTROL_ID 105 +#define DOT11_MNG_PU_BUFFER_STATUS_ID 106 +#define DOT11_MNG_EXT_CAP_ID 127 #define DOT11_MNG_WPA_ID 221 #define DOT11_MNG_PROPR_ID 221 @@ -1070,8 +1122,14 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_ACTION_CAT_RRM 5 #define DOT11_ACTION_CAT_FBT 6 #define DOT11_ACTION_CAT_HT 7 +#if defined(MFP) || defined(WLFBT) || defined(WLWNM) +#define DOT11_ACTION_CAT_SA_QUERY 8 +#define DOT11_ACTION_CAT_PDPA 9 #define DOT11_ACTION_CAT_BSSMGMT 10 #define DOT11_ACTION_NOTIFICATION 17 +#define DOT11_ACTION_CAT_VSP 126 +#endif +#define DOT11_ACTION_NOTIFICATION 17 #define DOT11_ACTION_CAT_VS 127 @@ -1107,6 +1165,121 @@ BWL_PRE_PACKED_STRUCT struct dot11_management_notification { #define DOT11_ADDBA_POLICY_DELAYED 0 #define DOT11_ADDBA_POLICY_IMMEDIATE 1 + +#define DOT11_FT_ACTION_FT_RESERVED 0 +#define DOT11_FT_ACTION_FT_REQ 1 +#define DOT11_FT_ACTION_FT_RES 2 +#define DOT11_FT_ACTION_FT_CON 3 +#define DOT11_FT_ACTION_FT_ACK 4 + + + +#define DOT11_WNM_ACTION_EVENT_REQ 0 +#define DOT11_WNM_ACTION_EVENT_REP 1 +#define DOT11_WNM_ACTION_DIAG_REQ 2 +#define DOT11_WNM_ACTION_DIAG_REP 3 +#define DOT11_WNM_ACTION_LOC_CFG_REQ 4 +#define DOT11_WNM_ACTION_LOC_RFG_RESP 5 +#define DOT11_WNM_ACTION_BSS_TRANS_QURY 6 +#define DOT11_WNM_ACTION_BSS_TRANS_REQ 7 +#define DOT11_WNM_ACTION_BSS_TRANS_RESP 8 +#define DOT11_WNM_ACTION_FMS_REQ 9 +#define DOT11_WNM_ACTION_FMS_RESP 10 +#define DOT11_WNM_ACTION_COL_INTRFRNCE_REQ 11 +#define DOT11_WNM_ACTION_COL_INTRFRNCE_REP 12 +#define DOT11_WNM_ACTION_TFS_REQ 13 +#define DOT11_WNM_ACTION_TFS_RESP 14 +#define DOT11_WNM_ACTION_TFS_NOTIFY 15 +#define DOT11_WNM_ACTION_WNM_SLEEP_REQ 16 +#define DOT11_WNM_ACTION_WNM_SLEEP_RESP 17 +#define DOT11_WNM_ACTION_TIM_BCAST_REQ 18 +#define DOT11_WNM_ACTION_TIM_BCAST_RESP 19 +#define DOT11_WNM_ACTION_QOS_TRFC_CAP_UPD 20 +#define DOT11_WNM_ACTION_CHAN_USAGE_REQ 21 +#define DOT11_WNM_ACTION_CHAN_USAGE_RESP 22 +#define DOT11_WNM_ACTION_DMS_REQ 23 +#define DOT11_WNM_ACTION_DMS_RESP 24 +#define DOT11_WNM_ACTION_TMNG_MEASUR_REQ 25 +#define DOT11_WNM_ACTION_NOTFCTN_REQ 26 +#define DOT11_WNM_ACTION_NOTFCTN_RES 27 + + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_query { + uint8 category; + uint8 action; + uint8 token; + uint8 reason; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_query dot11_bss_trans_query_t; +#define DOT11_BSS_TRANS_QUERY_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_req { + uint8 category; + uint8 action; + uint8 token; + uint8 reqmode; + uint16 disassoc_tmr; + uint8 validity_intrvl; + uint8 data[1]; + +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_req dot11_bss_trans_req_t; +#define DOT11_BSS_TRANS_REQ_LEN 7 + +#define DOT11_BSS_TERM_DUR_LEN 12 + + + +#define DOT11_BSS_TRNS_REQMODE_PREF_LIST_INCL 0x01 +#define DOT11_BSS_TRNS_REQMODE_ABRIDGED 0x02 +#define DOT11_BSS_TRNS_REQMODE_DISASSOC_IMMINENT 0x04 +#define DOT11_BSS_TRNS_REQMODE_BSS_TERM_INCL 0x08 +#define DOT11_BSS_TRNS_REQMODE_ESS_DISASSOC_IMNT 0x10 + + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_res { + uint8 category; + uint8 action; + uint8 token; + uint8 status; + uint8 term_delay; + uint8 data[1]; + +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_res dot11_bss_trans_res_t; +#define DOT11_BSS_TRANS_RES_LEN 5 + + +#define DOT11_BSS_TRNS_RES_STATUS_ACCEPT 0 +#define DOT11_BSS_TRNS_RES_STATUS_REJECT 1 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_INSUFF_BCN 2 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_INSUFF_CAP 3 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_TERM_UNDESIRED 4 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_TERM_DELAY_REQ 5 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_BSS_LIST_PROVIDED 6 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_NO_SUITABLE_BSS 7 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_LEAVING_ESS 8 + + + +#define DOT11_NBR_RPRT_BSSID_INFO_REACHABILTY 0x0003 +#define DOT11_NBR_RPRT_BSSID_INFO_SEC 0x0004 +#define DOT11_NBR_RPRT_BSSID_INFO_KEY_SCOPE 0x0008 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP 0x03f0 + +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_SPEC_MGMT 0x0010 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_QOS 0x0020 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_APSD 0x0040 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_RDIO_MSMT 0x0080 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_DEL_BA 0x0100 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_IMM_BA 0x0200 + + +#define DOT11_NBR_RPRT_SUBELEM_BSS_CANDDT_PREF_ID 3 BWL_PRE_PACKED_STRUCT struct dot11_addba_req { uint8 category; uint8 action; @@ -1145,6 +1318,48 @@ typedef struct dot11_delba dot11_delba_t; #define DOT11_DELBA_LEN 6 +#define SA_QUERY_REQUEST 0 +#define SA_QUERY_RESPONSE 1 + + + + +BWL_PRE_PACKED_STRUCT struct dot11_ft_req { + uint8 category; + uint8 action; + uint8 sta_addr[ETHER_ADDR_LEN]; + uint8 tgt_ap_addr[ETHER_ADDR_LEN]; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ft_req dot11_ft_req_t; +#define DOT11_FT_REQ_FIXED_LEN 14 + + +BWL_PRE_PACKED_STRUCT struct dot11_ft_res { + uint8 category; + uint8 action; + uint8 sta_addr[ETHER_ADDR_LEN]; + uint8 tgt_ap_addr[ETHER_ADDR_LEN]; + uint16 status; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ft_res dot11_ft_res_t; +#define DOT11_FT_RES_FIXED_LEN 16 + + +BWL_PRE_PACKED_STRUCT struct dot11_rde_ie { + uint8 id; + uint8 length; + uint8 rde_id; + uint8 rd_count; + uint16 status; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rde_ie dot11_rde_ie_t; + + +#define DOT11_MNG_RDE_IE_LEN sizeof(dot11_rde_ie_t) + + @@ -1166,6 +1381,28 @@ typedef struct dot11_rrm_cap_ie dot11_rrm_cap_ie_t; #define DOT11_RRM_CAP_AP_CHANREP 16 + +#define DOT11_EXT_CAP_LEN 4 +BWL_PRE_PACKED_STRUCT struct dot11_ext_cap_ie { + uint8 cap[DOT11_EXT_CAP_LEN]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ext_cap_ie dot11_ext_cap_ie_t; + + +#define DOT11_EXT_CAP_BSS_TRANSITION_MGMT 19 + + +#define DOT11_OP_CLASS_NONE 255 + +BWL_PRE_PACKED_STRUCT struct do11_ap_chrep { + uint8 id; + uint8 len; + uint8 reg; + uint8 chanlist[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct do11_ap_chrep dot11_ap_chrep_t; + + #define DOT11_RM_ACTION_RM_REQ 0 #define DOT11_RM_ACTION_RM_REP 1 #define DOT11_RM_ACTION_LM_REQ 2 @@ -1255,7 +1492,7 @@ typedef struct dot11_rmrep_bcn dot11_rmrep_bcn_t; #define DOT11_RMREQ_BCN_REPINFO_ID 1 #define DOT11_RMREQ_BCN_REPDET_ID 2 #define DOT11_RMREQ_BCN_REQUEST_ID 10 -#define DOT11_RMREQ_BCN_APCHREP_ID 51 +#define DOT11_RMREQ_BCN_APCHREP_ID DOT11_MNG_AP_CHREP_ID #define DOT11_RMREQ_BCN_REPDET_FIXED 0 @@ -1272,6 +1509,7 @@ BWL_PRE_PACKED_STRUCT struct dot11_rmrep_nbr { uint8 reg; uint8 channel; uint8 phytype; + uchar sub_elements[1]; } BWL_POST_PACKED_STRUCT; typedef struct dot11_rmrep_nbr dot11_rmrep_nbr_t; #define DOT11_RMREP_NBR_LEN 13 @@ -1660,6 +1898,9 @@ typedef struct dot11_obss_ie dot11_obss_ie_t; #define RSN_AKM_PSK 2 #define RSN_AKM_FBT_1X 3 #define RSN_AKM_FBT_PSK 4 +#define RSN_AKM_MFP_1X 5 +#define RSN_AKM_MFP_PSK 6 +#define RSN_AKM_TPK 7 #define DOT11_MAX_DEFAULT_KEYS 4 @@ -1724,6 +1965,66 @@ BWL_PRE_PACKED_STRUCT struct dot11_gtk_ie { } BWL_POST_PACKED_STRUCT; typedef struct dot11_gtk_ie dot11_gtk_ie_t; +#define BSSID_INVALID "\x00\x00\x00\x00\x00\x00" +#define BSSID_BROADCAST "\xFF\xFF\xFF\xFF\xFF\xFF" + + + +BWL_PRE_PACKED_STRUCT struct link_id_ie { + uint8 id; + uint8 len; + struct ether_addr bssid; + struct ether_addr tdls_init_mac; + struct ether_addr tdls_resp_mac; +} BWL_POST_PACKED_STRUCT; +typedef struct link_id_ie link_id_ie_t; +#define TDLS_LINK_ID_IE_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct wakeup_sch_ie { + uint8 id; + uint8 len; + uint32 offset; + uint32 interval; + uint32 awake_win_slots; + uint32 max_wake_win; + uint16 idle_cnt; +} BWL_POST_PACKED_STRUCT; +typedef struct wakeup_sch_ie wakeup_sch_ie_t; +#define TDLS_WAKEUP_SCH_IE_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct channel_switch_timing_ie { + uint8 id; + uint8 len; + uint16 switch_time; + uint16 switch_timeout; +} BWL_POST_PACKED_STRUCT; +typedef struct channel_switch_timing_ie channel_switch_timing_ie_t; +#define TDLS_CHANNEL_SWITCH_TIMING_IE_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct pti_control_ie { + uint8 id; + uint8 len; + uint8 tid; + uint16 seq_control; +} BWL_POST_PACKED_STRUCT; +typedef struct pti_control_ie pti_control_ie_t; +#define TDLS_PTI_CONTROL_IE_LEN 3 + + +BWL_PRE_PACKED_STRUCT struct pu_buffer_status_ie { + uint8 id; + uint8 len; + uint8 status; +} BWL_POST_PACKED_STRUCT; +typedef struct pu_buffer_status_ie pu_buffer_status_ie_t; +#define TDLS_PU_BUFFER_STATUS_IE_LEN 1 +#define TDLS_PU_BUFFER_STATUS_AC_BK 1 +#define TDLS_PU_BUFFER_STATUS_AC_BE 2 +#define TDLS_PU_BUFFER_STATUS_AC_VI 4 +#define TDLS_PU_BUFFER_STATUS_AC_VO 8 #include <packed_section_end.h> diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h b/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h index 4ccfab0..cbdd05e 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: 802.11_bta.h,v 9.2 2008-10-28 23:27:13 Exp $ + * $Id: 802.11_bta.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _802_11_BTA_H_ diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11e.h b/drivers/net/wireless/bcmdhd/include/proto/802.11e.h index ce8ad08..0e070a4 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/802.11e.h +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11e.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: 802.11e.h,v 1.6 2008-12-01 22:55:11 Exp $ + * $Id: 802.11e.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _802_11e_H_ diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.1d.h b/drivers/net/wireless/bcmdhd/include/proto/802.1d.h index cf20625..c7e07bd 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/802.1d.h +++ b/drivers/net/wireless/bcmdhd/include/proto/802.1d.h @@ -21,7 +21,7 @@ * * Fundamental types and constants relating to 802.1D * - * $Id: 802.1d.h,v 9.3 2007-04-10 21:33:06 Exp $ + * $Id: 802.1d.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h index 46fa4c9..0f75d3c 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmeth.h,v 9.12 2009-12-29 19:57:18 Exp $ + * $Id: bcmeth.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h index 30ec848..e8c2387 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h @@ -23,7 +23,7 @@ * * Dependencies: proto/bcmeth.h * - * $Id: bcmevent.h,v 9.64.2.9 2011-02-01 06:24:21 Exp $ + * $Id: bcmevent.h 288077 2011-10-06 00:08:47Z $ * */ @@ -182,7 +182,10 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event { #define WLC_E_PFN_SCAN_NONE 82 #define WLC_E_PFN_SCAN_ALLGONE 83 #define WLC_E_GTK_PLUMBED 84 -#define WLC_E_LAST 85 +#define WLC_E_ASSOC_REQ_IE 85 +#define WLC_E_ASSOC_RESP_IE 86 +#define WLC_E_LAST 87 + typedef struct { @@ -226,6 +229,8 @@ extern const int bcmevent_names_size; #define WLC_E_REASON_TSPEC_REJECTED 7 #define WLC_E_REASON_BETTER_AP 8 +#define WLC_E_REASON_REQUESTED_ROAM 11 + #define WLC_E_PRUNE_ENCR_MISMATCH 1 #define WLC_E_PRUNE_BCAST_BSSID 2 diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmip.h b/drivers/net/wireless/bcmdhd/include/proto/bcmip.h index 8a8f314..55eff24 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/bcmip.h +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmip.h @@ -21,7 +21,7 @@ * * Fundamental constants relating to IP Protocol * - * $Id: bcmip.h,v 9.19 2009-11-10 20:08:33 Exp $ + * $Id: bcmip.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h b/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h index 89c1181..91ab4fe 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h +++ b/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bt_amp_hci.h,v 9.14.8.2 2010-09-10 18:37:47 Exp $ + * $Id: bt_amp_hci.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _bt_amp_hci_h diff --git a/drivers/net/wireless/bcmdhd/include/proto/eapol.h b/drivers/net/wireless/bcmdhd/include/proto/eapol.h index 5781d13..92634c1 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/eapol.h +++ b/drivers/net/wireless/bcmdhd/include/proto/eapol.h @@ -7,7 +7,7 @@ * * Copyright (C) 2002 Broadcom Corporation * - * $Id: eapol.h,v 9.23.86.1 2010-09-02 18:09:39 Exp $ + * $Id: eapol.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _eapol_h_ diff --git a/drivers/net/wireless/bcmdhd/include/proto/ethernet.h b/drivers/net/wireless/bcmdhd/include/proto/ethernet.h index 6a6dd14..20865dc 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/ethernet.h +++ b/drivers/net/wireless/bcmdhd/include/proto/ethernet.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: ethernet.h,v 9.56 2009-10-15 22:54:58 Exp $ + * $Id: ethernet.h 285437 2011-09-21 22:16:56Z $ */ @@ -69,6 +69,7 @@ #define ETHER_TYPE_802_1X 0x888e #define ETHER_TYPE_802_1X_PREAUTH 0x88c7 #define ETHER_TYPE_WAI 0x88b4 +#define ETHER_TYPE_89_0D 0x890d diff --git a/drivers/net/wireless/bcmdhd/include/proto/p2p.h b/drivers/net/wireless/bcmdhd/include/proto/p2p.h index 4a0c9d1..d2bf3f2 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/p2p.h +++ b/drivers/net/wireless/bcmdhd/include/proto/p2p.h @@ -21,7 +21,7 @@ * * Fundamental types and constants relating to WFA P2P (aka WiFi Direct) * - * $Id: p2p.h,v 9.17.2.4 2010-12-15 21:41:21 Exp $ + * $Id: p2p.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _P2P_H_ diff --git a/drivers/net/wireless/bcmdhd/include/proto/sdspi.h b/drivers/net/wireless/bcmdhd/include/proto/sdspi.h index 7fe4fbc..7353ff0 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/sdspi.h +++ b/drivers/net/wireless/bcmdhd/include/proto/sdspi.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sdspi.h,v 9.2.120.1 2010-11-15 17:56:25 Exp $ + * $Id: sdspi.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _SD_SPI_H diff --git a/drivers/net/wireless/bcmdhd/include/proto/vlan.h b/drivers/net/wireless/bcmdhd/include/proto/vlan.h index 07fa7e4..27f0055 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/vlan.h +++ b/drivers/net/wireless/bcmdhd/include/proto/vlan.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: vlan.h,v 9.7 2009-03-13 01:11:50 Exp $ + * $Id: vlan.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/wpa.h b/drivers/net/wireless/bcmdhd/include/proto/wpa.h index 1ff06dc..7361cbf 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/wpa.h +++ b/drivers/net/wireless/bcmdhd/include/proto/wpa.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wpa.h,v 1.19 2009-07-13 08:29:58 Exp $ + * $Id: wpa.h 285437 2011-09-21 22:16:56Z $ */ @@ -114,6 +114,8 @@ typedef BWL_PRE_PACKED_STRUCT struct #define WPA_CIPHER_AES_OCB 3 #define WPA_CIPHER_AES_CCM 4 #define WPA_CIPHER_WEP_104 5 +#define WPA_CIPHER_BIP 6 +#define WPA_CIPHER_TPK 7 #define IS_WPA_CIPHER(cipher) ((cipher) == WPA_CIPHER_NONE || \ @@ -121,7 +123,9 @@ typedef BWL_PRE_PACKED_STRUCT struct (cipher) == WPA_CIPHER_WEP_104 || \ (cipher) == WPA_CIPHER_TKIP || \ (cipher) == WPA_CIPHER_AES_OCB || \ - (cipher) == WPA_CIPHER_AES_CCM) + (cipher) == WPA_CIPHER_AES_CCM || \ + (cipher) == WPA_CIPHER_TPK) + #define WPA_TKIP_CM_DETECT 60 @@ -149,7 +153,11 @@ typedef BWL_PRE_PACKED_STRUCT struct #define WPA_CAP_REPLAY_CNTR_MASK RSN_CAP_PTK_REPLAY_CNTR_MASK +#define WPA_CAP_PEER_KEY_ENABLE (0x1 << 1) + + #define WPA_CAP_LEN RSN_CAP_LEN +#define WPA_PMKID_CNT_LEN 2 #define WPA_CAP_WPA2_PREAUTH RSN_CAP_PREAUTH diff --git a/drivers/net/wireless/bcmdhd/include/sbchipc.h b/drivers/net/wireless/bcmdhd/include/sbchipc.h index cbd3749..aa4df44 100644 --- a/drivers/net/wireless/bcmdhd/include/sbchipc.h +++ b/drivers/net/wireless/bcmdhd/include/sbchipc.h @@ -5,7 +5,7 @@ * JTAG, 0/1/2 UARTs, clock frequency control, a watchdog interrupt timer, * GPIO interface, extbus, and support for serial and parallel flashes. * - * $Id: sbchipc.h,v 13.169.2.14 2011-02-10 23:43:55 Exp $ + * $Id: sbchipc.h 333924 2012-05-18 04:48:52Z $ * * Copyright (C) 1999-2011, Broadcom Corporation * @@ -41,6 +41,50 @@ #define PAD _XSTR(__LINE__) #endif +typedef struct eci_prerev35 { + uint32 eci_output; + uint32 eci_control; + uint32 eci_inputlo; + uint32 eci_inputmi; + uint32 eci_inputhi; + uint32 eci_inputintpolaritylo; + uint32 eci_inputintpolaritymi; + uint32 eci_inputintpolarityhi; + uint32 eci_intmasklo; + uint32 eci_intmaskmi; + uint32 eci_intmaskhi; + uint32 eci_eventlo; + uint32 eci_eventmi; + uint32 eci_eventhi; + uint32 eci_eventmasklo; + uint32 eci_eventmaskmi; + uint32 eci_eventmaskhi; + uint32 PAD[3]; +} eci_prerev35_t; + +typedef struct eci_rev35 { + uint32 eci_outputlo; + uint32 eci_outputhi; + uint32 eci_controllo; + uint32 eci_controlhi; + uint32 eci_inputlo; + uint32 eci_inputhi; + uint32 eci_inputintpolaritylo; + uint32 eci_inputintpolarityhi; + uint32 eci_intmasklo; + uint32 eci_intmaskhi; + uint32 eci_eventlo; + uint32 eci_eventhi; + uint32 eci_eventmasklo; + uint32 eci_eventmaskhi; + uint32 eci_auxtx; + uint32 eci_auxrx; + uint32 eci_datatag; + uint32 eci_uartescvalue; + uint32 eci_autobaudctr; + uint32 eci_uartfifolevel; +} eci_rev35_t; + typedef volatile struct { uint32 chipid; uint32 capabilities; @@ -153,10 +197,26 @@ typedef volatile struct { uint32 prog_waitcount; uint32 flash_config; uint32 flash_waitcount; - uint32 PAD[4]; - uint32 PAD[40]; + uint32 SECI_config; + uint32 SECI_status; + uint32 SECI_statusmask; + uint32 SECI_rxnibchanged; + + uint32 PAD[20]; + uint32 sromcontrol; + uint32 sromaddress; + uint32 sromdata; + uint32 PAD[9]; + uint32 seci_uart_data; + uint32 seci_uart_bauddiv; + uint32 seci_uart_fcr; + uint32 seci_uart_lcr; + uint32 seci_uart_mcr; + uint32 seci_uart_lsr; + uint32 seci_uart_msr; + uint32 seci_uart_baudadj; uint32 clk_ctl_st; uint32 hw_war; @@ -1332,9 +1392,27 @@ typedef volatile struct { #define CST43237_BOOT_FROM_INVALID 3 +#define RES43239_CBUCK_LPOM 0 +#define RES43239_CBUCK_BURST 1 +#define RES43239_CBUCK_LP_PWM 2 +#define RES43239_CBUCK_PWM 3 +#define RES43239_CLDO_PU 4 +#define RES43239_DIS_INT_RESET_PD 5 +#define RES43239_ILP_REQUEST 6 +#define RES43239_LNLDO_PU 7 +#define RES43239_LDO3P3_PU 8 #define RES43239_OTP_PU 9 +#define RES43239_XTAL_PU 10 +#define RES43239_ALP_AVAIL 11 +#define RES43239_RADIO_PU 12 #define RES43239_MACPHY_CLKAVAIL 23 #define RES43239_HT_AVAIL 24 +#define RES43239_XOLDO_PU 25 +#define RES43239_WL_XTAL_CTL_SEL 26 +#define RES43239_SR_CLK_STABLE 27 +#define RES43239_SR_SAVE_RESTORE 28 +#define RES43239_SR_PHY_PIC 29 +#define RES43239_SR_PHY_PWR_SW 30 #define CST43239_SPROM_MASK 0x00000002 @@ -1342,7 +1420,7 @@ typedef volatile struct { #define CST43239_RES_INIT_MODE_SHIFT 7 #define CST43239_RES_INIT_MODE_MASK 0x000001f0 #define CST43239_CHIPMODE_SDIOD(cs) ((cs) & (1 << 15)) -#define CST43239_CHIPMODE_USB20D(cs) ((cs) & !(1 << 15)) +#define CST43239_CHIPMODE_USB20D(cs) (~(cs) & (1 << 15)) #define CST43239_CHIPMODE_SDIO(cs) (((cs) & (1 << 0)) == 0) #define CST43239_CHIPMODE_GSPI(cs) (((cs) & (1 << 0)) == (1 << 0)) @@ -1350,6 +1428,40 @@ typedef volatile struct { #define CCTRL43239_XTAL_STRENGTH(ctl) ((ctl & 0x3F) << 12) +#define RES4331_REGULATOR 0 +#define RES4331_ILP_REQUEST 1 +#define RES4331_XTAL_PU 2 +#define RES4331_ALP_AVAIL 3 +#define RES4331_SI_PLL_ON 4 +#define RES4331_HT_SI_AVAIL 5 + + +#define CCTRL4331_BT_COEXIST (1<<0) +#define CCTRL4331_SECI (1<<1) +#define CCTRL4331_EXT_LNA_G (1<<2) +#define CCTRL4331_SPROM_GPIO13_15 (1<<3) +#define CCTRL4331_EXTPA_EN (1<<4) +#define CCTRL4331_GPIOCLK_ON_SPROMCS (1<<5) +#define CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6) +#define CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7) +#define CCTRL4331_OVR_PIPEAUXCLKEN (1<<8) +#define CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9) +#define CCTRL4331_PCIE_AUXCLKEN (1<<10) +#define CCTRL4331_PCIE_PIPE_PLLDOWN (1<<11) +#define CCTRL4331_EXTPA_EN2 (1<<12) +#define CCTRL4331_EXT_LNA_A (1<<13) +#define CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16) +#define CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17) +#define CCTRL4331_EXTPA_ANA_EN (1<<24) + + +#define CST4331_XTAL_FREQ 0x00000001 +#define CST4331_SPROM_OTP_SEL_MASK 0x00000006 +#define CST4331_SPROM_OTP_SEL_SHIFT 1 +#define CST4331_SPROM_PRESENT 0x00000002 +#define CST4331_OTP_PRESENT 0x00000004 +#define CST4331_LDO_RF 0x00000008 +#define CST4331_LDO_PAR 0x00000010 #define RES4315_CBUCK_LPOM 1 @@ -1547,6 +1659,9 @@ typedef volatile struct { #define CCTRL_4330_JTAG_DISABLE 0x00000008 +#define CCTRL_43239_GPIO_SEL 0x00000002 +#define CCTRL_43239_SDIO_HOST_WAKE 0x00000004 + #define RES4313_BB_PU_RSRC 0 #define RES4313_ILP_REQ_RSRC 1 #define RES4313_XTAL_PU_RSRC 2 @@ -1597,6 +1712,54 @@ typedef volatile struct { +#define SECI_MODE_UART 0x0 +#define SECI_MODE_SECI 0x1 +#define SECI_MODE_LEGACY_3WIRE_BT 0x2 +#define SECI_MODE_LEGACY_3WIRE_WLAN 0x3 +#define SECI_MODE_HALF_SECI 0x4 + +#define SECI_RESET (1 << 0) +#define SECI_RESET_BAR_UART (1 << 1) +#define SECI_ENAB_SECI_ECI (1 << 2) +#define SECI_ENAB_SECIOUT_DIS (1 << 3) +#define SECI_MODE_MASK 0x7 +#define SECI_MODE_SHIFT 4 +#define SECI_UPD_SECI (1 << 7) + + +#define CLKCTL_STS_SECI_CLK_REQ (1 << 8) +#define CLKCTL_STS_SECI_CLK_AVAIL (1 << 24) + +#define SECI_UART_MSR_CTS_STATE (1 << 0) +#define SECI_UART_MSR_RTS_STATE (1 << 1) +#define SECI_UART_SECI_IN_STATE (1 << 2) +#define SECI_UART_SECI_IN2_STATE (1 << 3) + + +#define SECI_UART_LCR_STOP_BITS (1 << 0) +#define SECI_UART_LCR_PARITY_EN (1 << 1) +#define SECI_UART_LCR_PARITY (1 << 2) +#define SECI_UART_LCR_RX_EN (1 << 3) +#define SECI_UART_LCR_LBRK_CTRL (1 << 4) +#define SECI_UART_LCR_TXO_EN (1 << 5) +#define SECI_UART_LCR_RTSO_EN (1 << 6) +#define SECI_UART_LCR_SLIPMODE_EN (1 << 7) +#define SECI_UART_LCR_RXCRC_CHK (1 << 8) +#define SECI_UART_LCR_TXCRC_INV (1 << 9) +#define SECI_UART_LCR_TXCRC_LSBF (1 << 10) +#define SECI_UART_LCR_TXCRC_EN (1 << 11) + +#define SECI_UART_MCR_TX_EN (1 << 0) +#define SECI_UART_MCR_PRTS (1 << 1) +#define SECI_UART_MCR_SWFLCTRL_EN (1 << 2) +#define SECI_UART_MCR_HIGHRATE_EN (1 << 3) +#define SECI_UART_MCR_LOOPBK_EN (1 << 4) +#define SECI_UART_MCR_AUTO_RTS (1 << 5) +#define SECI_UART_MCR_AUTO_TX_DIS (1 << 6) +#define SECI_UART_MCR_BAUD_ADJ_EN (1 << 7) +#define SECI_UART_MCR_XONOFF_RPT (1 << 9) + + #define ECI_BW_20 0x0 diff --git a/drivers/net/wireless/bcmdhd/include/sbconfig.h b/drivers/net/wireless/bcmdhd/include/sbconfig.h index 76f05ae..f45351a 100644 --- a/drivers/net/wireless/bcmdhd/include/sbconfig.h +++ b/drivers/net/wireless/bcmdhd/include/sbconfig.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbconfig.h,v 13.70 2008-03-28 19:17:04 Exp $ + * $Id: sbconfig.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/sbhnddma.h b/drivers/net/wireless/bcmdhd/include/sbhnddma.h index 05d0587..77c413f 100644 --- a/drivers/net/wireless/bcmdhd/include/sbhnddma.h +++ b/drivers/net/wireless/bcmdhd/include/sbhnddma.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbhnddma.h,v 13.20.2.3 2010-10-14 22:21:29 Exp $ + * $Id: sbhnddma.h 278779 2011-08-19 22:07:18Z $ */ @@ -217,7 +217,7 @@ typedef volatile struct { #define D64_XC_BL_SHIFT 18 -#define D64_XP_LD_MASK 0x00000fff +#define D64_XP_LD_MASK 0x00001fff #define D64_XS0_CD_MASK 0x00001fff @@ -260,7 +260,7 @@ typedef volatile struct { #define DMA_CTRL_USB_BOUNDRY4KB_WAR (1 << 4) -#define D64_RP_LD_MASK 0x00000fff +#define D64_RP_LD_MASK 0x00001fff #define D64_RS0_CD_MASK 0x00001fff diff --git a/drivers/net/wireless/bcmdhd/include/sbpcmcia.h b/drivers/net/wireless/bcmdhd/include/sbpcmcia.h index aba914b..d84f69a 100644 --- a/drivers/net/wireless/bcmdhd/include/sbpcmcia.h +++ b/drivers/net/wireless/bcmdhd/include/sbpcmcia.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbpcmcia.h,v 13.48.12.6 2010-11-04 09:39:42 Exp $ + * $Id: sbpcmcia.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/sbsdio.h b/drivers/net/wireless/bcmdhd/include/sbsdio.h index 4280d5b..7aaeb73 100644 --- a/drivers/net/wireless/bcmdhd/include/sbsdio.h +++ b/drivers/net/wireless/bcmdhd/include/sbsdio.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbsdio.h,v 13.34 2009-03-11 20:27:16 Exp $ + * $Id: sbsdio.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _SBSDIO_H diff --git a/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h b/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h index 107a8b0..e517648 100644 --- a/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h +++ b/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbsdpcmdev.h,v 13.38 2009-09-22 22:56:45 Exp $ + * $Id: sbsdpcmdev.h 282638 2011-09-08 21:18:10Z $ */ #ifndef _sbsdpcmdev_h_ diff --git a/drivers/net/wireless/bcmdhd/include/sbsocram.h b/drivers/net/wireless/bcmdhd/include/sbsocram.h index 1cba422..45c4dc2 100644 --- a/drivers/net/wireless/bcmdhd/include/sbsocram.h +++ b/drivers/net/wireless/bcmdhd/include/sbsocram.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sbsocram.h,v 13.15 2009-10-02 16:55:44 Exp $ + * $Id: sbsocram.h 277737 2011-08-16 17:54:59Z $ */ diff --git a/drivers/net/wireless/bcmdhd/include/sdio.h b/drivers/net/wireless/bcmdhd/include/sdio.h index ca93226..c8ac7b7 100644 --- a/drivers/net/wireless/bcmdhd/include/sdio.h +++ b/drivers/net/wireless/bcmdhd/include/sdio.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sdio.h,v 13.27.14.1 2010-09-07 13:37:45 Exp $ + * $Id: sdio.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _SDIO_H @@ -366,7 +366,7 @@ typedef volatile struct { * SDIO Commands and responses * * I/O only commands are: - * CMD0, CMD3, CMD5, CMD7, CMD15, CMD52, CMD53 + * CMD0, CMD3, CMD5, CMD7, CMD14, CMD15, CMD52, CMD53 * ------------------------------------------------ */ @@ -412,6 +412,7 @@ typedef volatile struct { #define CMD7_RCA_M BITFIELD_MASK(16) #define CMD7_RCA_S 16 + #define CMD14_RCA_M BITFIELD_MASK(16) #define CMD14_RCA_S 16 #define CMD14_SLEEP_M BITFIELD_MASK(1) diff --git a/drivers/net/wireless/bcmdhd/include/sdioh.h b/drivers/net/wireless/bcmdhd/include/sdioh.h index 3d37c7a..5f47d6f 100644 --- a/drivers/net/wireless/bcmdhd/include/sdioh.h +++ b/drivers/net/wireless/bcmdhd/include/sdioh.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sdioh.h,v 13.17.2.3 2011-01-08 05:28:21 Exp $ + * $Id: sdioh.h 300017 2011-12-01 20:30:27Z $ */ #ifndef _SDIOH_H @@ -68,6 +68,10 @@ #define SD_ADMA_SysAddr 0x58 #define SD_SlotInterruptStatus 0x0FC #define SD_HostControllerVersion 0x0FE +#define SD_GPIO_Reg 0x100 +#define SD_GPIO_OE 0x104 +#define SD_GPIO_Enable 0x108 + /* SD specific registers in PCI config space */ #define SD_SlotInfo 0x40 @@ -409,4 +413,30 @@ /* SD_SlotInterruptStatus: Offset 0x0FC , size = bytes */ /* SD_HostControllerVersion : Offset 0x0FE , size = bytes */ +/* SDIO Host Control Register DMA Mode Definitions */ +#define SDIOH_SDMA_MODE 0 +#define SDIOH_ADMA1_MODE 1 +#define SDIOH_ADMA2_MODE 2 +#define SDIOH_ADMA2_64_MODE 3 + +#define ADMA2_ATTRIBUTE_VALID (1 << 0) /* ADMA Descriptor line valid */ +#define ADMA2_ATTRIBUTE_END (1 << 1) /* End of Descriptor */ +#define ADMA2_ATTRIBUTE_INT (1 << 2) /* Interrupt when line is done */ +#define ADMA2_ATTRIBUTE_ACT_NOP (0 << 4) /* Skip current line, go to next. */ +#define ADMA2_ATTRIBUTE_ACT_RSV (1 << 4) /* Same as NOP */ +#define ADMA1_ATTRIBUTE_ACT_SET (1 << 4) /* ADMA1 Only - set transfer length */ +#define ADMA2_ATTRIBUTE_ACT_TRAN (2 << 4) /* Transfer Data of one descriptor line. */ +#define ADMA2_ATTRIBUTE_ACT_LINK (3 << 4) /* Link Descriptor */ + +/* ADMA2 Descriptor Table Entry for 32-bit Address */ +typedef struct adma2_dscr_32b { + uint32 len_attr; + uint32 phys_addr; +} adma2_dscr_32b_t; + +/* ADMA1 Descriptor Table Entry */ +typedef struct adma1_dscr { + uint32 phys_addr_attr; +} adma1_dscr_t; + #endif /* _SDIOH_H */ diff --git a/drivers/net/wireless/bcmdhd/include/sdiovar.h b/drivers/net/wireless/bcmdhd/include/sdiovar.h index 2c5bcf9..55a3d34 100644 --- a/drivers/net/wireless/bcmdhd/include/sdiovar.h +++ b/drivers/net/wireless/bcmdhd/include/sdiovar.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sdiovar.h,v 13.9 2009-12-08 22:30:15 Exp $ + * $Id: sdiovar.h 277737 2011-08-16 17:54:59Z $ */ #ifndef _sdiovar_h_ diff --git a/drivers/net/wireless/bcmdhd/include/siutils.h b/drivers/net/wireless/bcmdhd/include/siutils.h index c5a3383..4e7aeb7 100644 --- a/drivers/net/wireless/bcmdhd/include/siutils.h +++ b/drivers/net/wireless/bcmdhd/include/siutils.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: siutils.h,v 13.251.2.10 2011-02-04 05:06:32 Exp $ + * $Id: siutils.h 335486 2012-05-28 09:47:55Z $ */ @@ -60,6 +60,7 @@ struct si_pub { typedef const struct si_pub si_t; + #define SI_OSH NULL #define BADIDX (SI_MAXCORES + 1) @@ -213,8 +214,36 @@ extern int si_corepciid(si_t *sih, uint func, uint16 *pcivendor, uint16 *pcidevi #define si_eci(sih) 0 #define si_eci_init(sih) (0) #define si_eci_notify_bt(sih, type, val) (0) +#define si_seci(sih) 0 +static INLINE void * si_seci_init(si_t *sih, uint8 use_seci) {return NULL;} +#define si_seci_down(sih) do { } while (0) + + +extern bool si_is_otp_disabled(si_t *sih); +extern bool si_is_otp_powered(si_t *sih); +extern void si_otp_power(si_t *sih, bool on); +extern void si_set_otp_wr_volts(si_t *sih); +extern void si_set_otp_rd_volts(si_t *sih); + + +extern bool si_is_sprom_available(si_t *sih); +extern bool si_is_sprom_enabled(si_t *sih); +extern void si_sprom_enable(si_t *sih, bool enable); +extern int si_cis_source(si_t *sih); +#define CIS_DEFAULT 0 +#define CIS_SROM 1 +#define CIS_OTP 2 + + +#define DEFAULT_FAB 0x0 +#define CSM_FAB7 0x1 +#define TSMC_FAB12 0x2 +#define SMIC_FAB4 0x3 +extern int si_otp_fabid(si_t *sih, uint16 *fabid, bool rw); +extern uint16 si_fabid(si_t *sih); + extern int si_devpath(si_t *sih, char *path, int size); @@ -244,4 +273,5 @@ extern uint32 si_pcieserdesreg(si_t *sih, uint32 mdioslave, uint32 offset, uint3 char *si_getnvramflvar(si_t *sih, const char *name); + #endif diff --git a/drivers/net/wireless/bcmdhd/include/trxhdr.h b/drivers/net/wireless/bcmdhd/include/trxhdr.h index 397006a..b52fb15 100644 --- a/drivers/net/wireless/bcmdhd/include/trxhdr.h +++ b/drivers/net/wireless/bcmdhd/include/trxhdr.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: trxhdr.h,v 13.15.108.2 2010-11-15 17:57:30 Exp $ + * $Id: trxhdr.h 286295 2011-09-27 06:39:43Z $ */ #ifndef _TRX_HDR_H_ @@ -37,6 +37,7 @@ #define TRX_OVERLAYS 0x4 /* Contains an overlay header after the trx header */ #define TRX_MAX_OFFSET 3 /* Max number of individual files */ #define TRX_UNCOMP_IMAGE 0x20 /* Trx contains uncompressed rtecdc.bin image */ +#define TRX_ROMSIM_IMAGE 0x10 /* Trx contains ROM simulation image */ struct trx_header { uint32 magic; /* "HDR0" */ diff --git a/drivers/net/wireless/bcmdhd/include/typedefs.h b/drivers/net/wireless/bcmdhd/include/typedefs.h index 228b5dc..d0902fe 100644 --- a/drivers/net/wireless/bcmdhd/include/typedefs.h +++ b/drivers/net/wireless/bcmdhd/include/typedefs.h @@ -18,7 +18,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. - * $Id: typedefs.h,v 1.103.2.1 2010-05-11 18:19:28 Exp $ + * $Id: typedefs.h 290055 2011-10-15 21:26:26Z $ */ @@ -305,5 +305,8 @@ typedef float64 float_t; #define UNUSED_PARAMETER(x) (void)(x) +#define DISCARD_QUAL(ptr, type) ((type *)(uintptr)(ptr)) + + #include <bcmdefs.h> #endif diff --git a/drivers/net/wireless/bcmdhd/include/wlfc_proto.h b/drivers/net/wireless/bcmdhd/include/wlfc_proto.h index 7230d3b..d371051 100644 --- a/drivers/net/wireless/bcmdhd/include/wlfc_proto.h +++ b/drivers/net/wireless/bcmdhd/include/wlfc_proto.h @@ -18,7 +18,7 @@ * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. -* $Id: wlfc_proto.h,v 1.1.6.2 2010-05-08 01:30:41 Exp $ +* $Id: wlfc_proto.h 277737 2011-08-16 17:54:59Z $ * */ #ifndef __wlfc_proto_definitions_h__ diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h index 9357552..891d15c 100644 --- a/drivers/net/wireless/bcmdhd/include/wlioctl.h +++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wlioctl.h,v 1.767.2.38 2011-02-01 23:04:28 Exp $ + * $Id: wlioctl.h 331292 2012-05-04 09:04:23Z $ */ @@ -65,8 +65,8 @@ typedef struct wl_action_frame { typedef struct ssid_info { - uint8 ssid_len; - uint8 ssid[32]; + uint8 ssid_len; + uint8 ssid[32]; } ssid_info_t; typedef struct wl_af_params { @@ -191,6 +191,7 @@ typedef struct wlc_ssid { #define WL_SCANFLAGS_PROHIBITED 0x04 #define WL_SCAN_PARAMS_SSID_MAX 10 + typedef struct wl_scan_params { wlc_ssid_t ssid; struct ether_addr bssid; @@ -376,6 +377,8 @@ typedef struct { #define NRATE_SGI_SHIFT 23 #define NRATE_LDPC_CODING 0x00400000 #define NRATE_LDPC_SHIFT 22 +#define NRATE_BCMC_OVERRIDE 0x00200000 +#define NRATE_BCMC_SHIFT 21 #define NRATE_STF_SISO 0 #define NRATE_STF_CDD 1 @@ -555,7 +558,7 @@ typedef enum sup_auth_status { #define CRYPTO_ALGO_AES_OCB_MSDU 5 #define CRYPTO_ALGO_AES_OCB_MPDU 6 #define CRYPTO_ALGO_NALG 7 -#define CRYPTO_ALGO_PMK 12 +#define CRYPTO_ALGO_PMK 12 #define WSEC_GEN_MIC_ERROR 0x0001 #define WSEC_GEN_REPLAY 0x0002 @@ -617,9 +620,9 @@ typedef struct { #define WPA2_AUTH_PSK 0x0080 #define BRCM_AUTH_PSK 0x0100 #define BRCM_AUTH_DPT 0x0200 -#define WPA2_AUTH_MFP 0x1000 -#define WPA2_AUTH_TPK 0x2000 -#define WPA2_AUTH_FT 0x4000 +#define WPA2_AUTH_MFP 0x1000 +#define WPA2_AUTH_TPK 0x2000 +#define WPA2_AUTH_FT 0x4000 #define MAXPMKID 16 @@ -649,12 +652,12 @@ typedef struct wl_assoc_info { uint32 resp_len; uint32 flags; struct dot11_assoc_req req; - struct ether_addr reassoc_bssid; + struct ether_addr reassoc_bssid; struct dot11_assoc_resp resp; } wl_assoc_info_t; -#define WLC_ASSOC_REQ_IS_REASSOC 0x01 +#define WLC_ASSOC_REQ_IS_REASSOC 0x01 typedef struct { @@ -818,8 +821,14 @@ typedef struct wlc_iov_trx_s { #define WLC_IOCTL_MEDLEN 1536 #ifdef WLC_HIGH_ONLY #define WLC_SAMPLECOLLECT_MAXLEN 1024 +#define WLC_SAMPLECOLLECT_MAXLEN_LCN40 1024 +#else +#if defined(LCNCONF) || defined(LCN40CONF) +#define WLC_SAMPLECOLLECT_MAXLEN 8192 #else -#define WLC_SAMPLECOLLECT_MAXLEN 10240 +#define WLC_SAMPLECOLLECT_MAXLEN 10240 +#endif +#define WLC_SAMPLECOLLECT_MAXLEN_LCN40 8192 #endif @@ -851,6 +860,7 @@ typedef struct wlc_iov_trx_s { #define WLC_GET_SSID 25 #define WLC_SET_SSID 26 #define WLC_RESTART 27 +#define WLC_TERMINATED 28 #define WLC_GET_CHANNEL 29 #define WLC_SET_CHANNEL 30 @@ -1203,7 +1213,7 @@ typedef struct { #define WL_AUTH_OPEN_SYSTEM 0 #define WL_AUTH_SHARED_KEY 1 -#define WL_AUTH_OPEN_SHARED 2 +#define WL_AUTH_OPEN_SHARED 2 #define WL_RADIO_SW_DISABLE (1<<0) @@ -1280,18 +1290,17 @@ typedef struct wl_po { #define WL_CHAN_FREQ_RANGE_5GM 2 #define WL_CHAN_FREQ_RANGE_5GH 3 -#define WL_CHAN_FREQ_RANGE_5GLL_VER2 4 -#define WL_CHAN_FREQ_RANGE_5GLH_VER2 5 -#define WL_CHAN_FREQ_RANGE_5GML_VER2 6 -#define WL_CHAN_FREQ_RANGE_5GMH_VER2 7 -#define WL_CHAN_FREQ_RANGE_5GH_VER2 8 - #define WL_CHAN_FREQ_RANGE_5GLL_5BAND 4 #define WL_CHAN_FREQ_RANGE_5GLH_5BAND 5 #define WL_CHAN_FREQ_RANGE_5GML_5BAND 6 #define WL_CHAN_FREQ_RANGE_5GMH_5BAND 7 #define WL_CHAN_FREQ_RANGE_5GH_5BAND 8 +#define WL_CHAN_FREQ_RANGE_5G_BAND0 1 +#define WL_CHAN_FREQ_RANGE_5G_BAND1 2 +#define WL_CHAN_FREQ_RANGE_5G_BAND2 3 +#define WL_CHAN_FREQ_RANGE_5G_BAND3 4 + #define WLC_PHY_TYPE_A 0 #define WLC_PHY_TYPE_B 1 @@ -1363,7 +1372,7 @@ typedef struct wl_po { #define PM_MAX 1 #define PM_FAST 2 -#define LISTEN_INTERVAL 10 +#define LISTEN_INTERVAL 10 #define INTERFERE_OVRRIDE_OFF -1 #define INTERFERE_NONE 0 @@ -1408,7 +1417,7 @@ typedef struct wl_aci_args { #define TRIGGER_BADPLCP 0x10 #define TRIGGER_CRSGLITCH 0x20 #define WL_ACI_ARGS_LEGACY_LENGTH 16 -#define WL_SAMPLECOLLECT_T_VERSION 1 +#define WL_SAMPLECOLLECT_T_VERSION 2 typedef struct wl_samplecollect_args { uint8 coll_us; @@ -1416,7 +1425,7 @@ typedef struct wl_samplecollect_args { uint16 version; uint16 length; - uint8 trigger; + int8 trigger; uint16 timeout; uint16 mode; uint32 pre_dur; @@ -1426,6 +1435,11 @@ typedef struct wl_samplecollect_args { bool be_deaf; bool agc; bool filter; + + uint8 trigger_state; + uint8 module_sel1; + uint8 module_sel2; + uint16 nsamps; } wl_samplecollect_args_t; #define WL_SAMPLEDATA_HEADER_TYPE 1 @@ -1446,6 +1460,14 @@ typedef struct wl_sampledata { } wl_sampledata_t; +#define WL_CHAN_VALID_HW (1 << 0) +#define WL_CHAN_VALID_SW (1 << 1) +#define WL_CHAN_BAND_5G (1 << 2) +#define WL_CHAN_RADAR (1 << 3) +#define WL_CHAN_INACTIVE (1 << 4) +#define WL_CHAN_PASSIVE (1 << 5) +#define WL_CHAN_RESTRICTED (1 << 6) + #define WL_ERROR_VAL 0x00000001 #define WL_TRACE_VAL 0x00000002 @@ -1493,6 +1515,7 @@ typedef struct wl_sampledata { #define WL_P2P_VAL 0x00000200 #define WL_TXRX_VAL 0x00000400 #define WL_MCHAN_VAL 0x00000800 +#define WL_TDLS_VAL 0x00001000 #define WL_LED_NUMGPIO 16 @@ -1546,7 +1569,7 @@ typedef struct wl_sampledata { #define WL_JOIN_PREF_WPA 2 #define WL_JOIN_PREF_BAND 3 #define WL_JOIN_PREF_RSSI_DELTA 4 -#define WL_JOIN_PREF_TRANS_PREF 5 +#define WL_JOIN_PREF_TRANS_PREF 5 #define WLJP_BAND_ASSOC_PREF 255 @@ -1797,17 +1820,19 @@ struct wl_msglevel2 { }; typedef struct wl_mkeep_alive_pkt { - uint16 version; - uint16 length; + uint16 version; + uint16 length; uint32 period_msec; uint16 len_bytes; - uint8 keep_alive_id; + uint8 keep_alive_id; uint8 data[1]; } wl_mkeep_alive_pkt_t; -#define WL_MKEEP_ALIVE_VERSION 1 -#define WL_MKEEP_ALIVE_FIXED_LEN OFFSETOF(wl_mkeep_alive_pkt_t, data) -#define WL_MKEEP_ALIVE_PRECISION 500 +#define WL_MKEEP_ALIVE_VERSION 1 +#define WL_MKEEP_ALIVE_FIXED_LEN OFFSETOF(wl_mkeep_alive_pkt_t, data) +#define WL_MKEEP_ALIVE_PRECISION 500 + + #define WLC_ROAM_TRIGGER_DEFAULT 0 #define WLC_ROAM_TRIGGER_BANDWIDTH 1 @@ -1897,7 +1922,7 @@ typedef struct wl_pfn_param { uint8 mscan; uint8 repeat; uint8 exp; - int32 slow_freq; + int32 slow_freq; } wl_pfn_param_t; typedef struct wl_pfn_bssid { @@ -2016,8 +2041,31 @@ typedef struct wl_keep_alive_pkt { +#define MAX_WAKE_PACKET_BYTES 128 + + +typedef struct pm_wake_packet { + uint32 status; + uint32 pattern_id; + uint32 original_packet_size; + uint32 saved_packet_size; + uchar packet[MAX_WAKE_PACKET_BYTES]; +} pm_wake_packet_t; + + + +#define PKT_FILTER_MODE_FORWARD_ON_MATCH 1 + +#define PKT_FILTER_MODE_DISABLE 2 + +#define PKT_FILTER_MODE_PKT_CACHE_ON_MATCH 4 + +#define PKT_FILTER_MODE_PKT_FORWARD_OFF_DEFAULT 8 + + typedef enum wl_pkt_filter_type { - WL_PKT_FILTER_TYPE_PATTERN_MATCH + WL_PKT_FILTER_TYPE_PATTERN_MATCH, + WL_PKT_FILTER_TYPE_MAGIC_PATTERN_MATCH } wl_pkt_filter_type_t; #define WL_PKT_FILTER_TYPE wl_pkt_filter_type_t @@ -2550,6 +2598,12 @@ typedef struct wl_phycal_state { #define WL_PHYCAL_STAT_FIXED_LEN OFFSETOF(wl_phycal_state_t, phycal_core) #endif +#ifdef WLDSTA +typedef struct wl_dsta_if { + struct ether_addr addr; +} wl_dsta_if_t; +#endif + #ifdef WLP2P typedef struct wl_p2p_disc_st { diff --git a/drivers/net/wireless/bcmdhd/linux_osl.c b/drivers/net/wireless/bcmdhd/linux_osl.c index b3fcdd2..4ef7bf7 100644 --- a/drivers/net/wireless/bcmdhd/linux_osl.c +++ b/drivers/net/wireless/bcmdhd/linux_osl.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: linux_osl.c,v 1.168.2.7 2011-01-27 17:01:13 Exp $ + * $Id: linux_osl.c,v 1.168.2.7 2011-01-27 17:01:13 $ */ @@ -226,17 +226,16 @@ osl_attach(void *pdev, uint bustype, bool pkttag) } if (!bcm_static_skb) { - void *skb_buff_ptr = dhd_os_prealloc(osh, 4, 0); - - if (skb_buff_ptr) { - bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048); - bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *) * 16); - for (i = 0; i < STATIC_PKT_MAX_NUM * 2; i++) - bcm_static_skb->pkt_use[i] = 0; - sema_init(&bcm_static_skb->osl_pkt_sem, 1); - } else { - printk("can not alloc static skb buffers\n"); - } + int i; + void *skb_buff_ptr = 0; + bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048); + skb_buff_ptr = dhd_os_prealloc(osh, 4, 0); + + bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *) * 16); + for (i = 0; i < STATIC_PKT_MAX_NUM * 2; i++) + bcm_static_skb->pkt_use[i] = 0; + + sema_init(&bcm_static_skb->osl_pkt_sem, 1); } #endif diff --git a/drivers/net/wireless/bcmdhd/siutils.c b/drivers/net/wireless/bcmdhd/siutils.c index 22aa412..a655ac4 100644 --- a/drivers/net/wireless/bcmdhd/siutils.c +++ b/drivers/net/wireless/bcmdhd/siutils.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: siutils.c,v 1.813.2.36 2011-02-10 23:43:55 Exp $ + * $Id: siutils.c,v 1.813.2.36 2011-02-10 23:43:55 $ */ #include <typedefs.h> @@ -213,11 +213,11 @@ si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin, /* figure out bus/orignal core idx */ sii->pub.buscoretype = NODEV_CORE_ID; - sii->pub.buscorerev = NOREV; + sii->pub.buscorerev = (uint)NOREV; sii->pub.buscoreidx = BADIDX; pci = pcie = FALSE; - pcirev = pcierev = NOREV; + pcirev = pcierev = (uint)NOREV; pciidx = pcieidx = BADIDX; for (i = 0; i < sii->numcores; i++) { @@ -365,6 +365,19 @@ si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, return NULL; } +#if defined(HW_OOB) + if (CHIPID(sih->chip) == BCM43362_CHIP_ID) { + uint32 gpiocontrol, addr; + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, gpiocontrol); + gpiocontrol = bcmsdh_reg_read(sdh, addr, 4); + gpiocontrol |= 0x2; + bcmsdh_reg_write(sdh, addr, 4, gpiocontrol); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10005, 0xf, NULL); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10006, 0x0, NULL); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10007, 0x2, NULL); + } +#endif + if ((CHIPID(sih->chip) == BCM4329_CHIP_ID) && (sih->chiprev == 0) && (sih->chippkg != BCM4329_289PIN_PKG_ID)) { sih->chippkg = BCM4329_182PIN_PKG_ID; @@ -401,8 +414,9 @@ si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, } /* assume current core is CC */ - if ((sii->pub.ccrev == 0x25) && ((CHIPID(sih->chip) == BCM43236_CHIP_ID || + if ((sii->pub.ccrev == 0x25) && ((CHIPID(sih->chip) == BCM43234_CHIP_ID || CHIPID(sih->chip) == BCM43235_CHIP_ID || + CHIPID(sih->chip) == BCM43236_CHIP_ID || CHIPID(sih->chip) == BCM43238_CHIP_ID) && (CHIPREV(sii->pub.chiprev) == 0))) { @@ -1093,6 +1107,126 @@ si_watchdog_ms(si_t *sih, uint32 ms) +/* return the slow clock source - LPO, XTAL, or PCI */ +static uint +si_slowclk_src(si_info_t *sii) +{ + chipcregs_t *cc; + + ASSERT(SI_FAST(sii) || si_coreid(&sii->pub) == CC_CORE_ID); + + if (sii->pub.ccrev < 6) { + if ((BUSTYPE(sii->pub.bustype) == PCI_BUS) && + (OSL_PCI_READ_CONFIG(sii->osh, PCI_GPIO_OUT, sizeof(uint32)) & + PCI_CFG_GPIO_SCS)) + return (SCC_SS_PCI); + else + return (SCC_SS_XTAL); + } else if (sii->pub.ccrev < 10) { + cc = (chipcregs_t *)si_setcoreidx(&sii->pub, sii->curidx); + return (R_REG(sii->osh, &cc->slow_clk_ctl) & SCC_SS_MASK); + } else /* Insta-clock */ + return (SCC_SS_XTAL); +} + +/* return the ILP (slowclock) min or max frequency */ +static uint +si_slowclk_freq(si_info_t *sii, bool max_freq, chipcregs_t *cc) +{ + uint32 slowclk; + uint div; + + ASSERT(SI_FAST(sii) || si_coreid(&sii->pub) == CC_CORE_ID); + + /* shouldn't be here unless we've established the chip has dynamic clk control */ + ASSERT(R_REG(sii->osh, &cc->capabilities) & CC_CAP_PWR_CTL); + + slowclk = si_slowclk_src(sii); + if (sii->pub.ccrev < 6) { + if (slowclk == SCC_SS_PCI) + return (max_freq ? (PCIMAXFREQ / 64) : (PCIMINFREQ / 64)); + else + return (max_freq ? (XTALMAXFREQ / 32) : (XTALMINFREQ / 32)); + } else if (sii->pub.ccrev < 10) { + div = 4 * + (((R_REG(sii->osh, &cc->slow_clk_ctl) & SCC_CD_MASK) >> SCC_CD_SHIFT) + 1); + if (slowclk == SCC_SS_LPO) + return (max_freq ? LPOMAXFREQ : LPOMINFREQ); + else if (slowclk == SCC_SS_XTAL) + return (max_freq ? (XTALMAXFREQ / div) : (XTALMINFREQ / div)); + else if (slowclk == SCC_SS_PCI) + return (max_freq ? (PCIMAXFREQ / div) : (PCIMINFREQ / div)); + else + ASSERT(0); + } else { + /* Chipc rev 10 is InstaClock */ + div = R_REG(sii->osh, &cc->system_clk_ctl) >> SYCC_CD_SHIFT; + div = 4 * (div + 1); + return (max_freq ? XTALMAXFREQ : (XTALMINFREQ / div)); + } + return (0); +} + +static void +si_clkctl_setdelay(si_info_t *sii, void *chipcregs) +{ + chipcregs_t *cc = (chipcregs_t *)chipcregs; + uint slowmaxfreq, pll_delay, slowclk; + uint pll_on_delay, fref_sel_delay; + + pll_delay = PLL_DELAY; + + /* If the slow clock is not sourced by the xtal then add the xtal_on_delay + * since the xtal will also be powered down by dynamic clk control logic. + */ + + slowclk = si_slowclk_src(sii); + if (slowclk != SCC_SS_XTAL) + pll_delay += XTAL_ON_DELAY; + + /* Starting with 4318 it is ILP that is used for the delays */ + slowmaxfreq = si_slowclk_freq(sii, (sii->pub.ccrev >= 10) ? FALSE : TRUE, cc); + + pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000; + fref_sel_delay = ((slowmaxfreq * FREF_DELAY) + 999999) / 1000000; + + W_REG(sii->osh, &cc->pll_on_delay, pll_on_delay); + W_REG(sii->osh, &cc->fref_sel_delay, fref_sel_delay); +} + +/* initialize power control delay registers */ +void +si_clkctl_init(si_t *sih) +{ + si_info_t *sii; + uint origidx = 0; + chipcregs_t *cc; + bool fast; + + if (!CCCTL_ENAB(sih)) + return; + + sii = SI_INFO(sih); + fast = SI_FAST(sii); + if (!fast) { + origidx = sii->curidx; + if ((cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0)) == NULL) + return; + } else if ((cc = (chipcregs_t *)CCREGS_FAST(sii)) == NULL) + return; + ASSERT(cc != NULL); + + /* set all Instaclk chip ILP to 1 MHz */ + if (sih->ccrev >= 10) + SET_REG(sii->osh, &cc->system_clk_ctl, SYCC_CD_MASK, + (ILP_DIV_1MHZ << SYCC_CD_SHIFT)); + + si_clkctl_setdelay(sii, (void *)(uintptr)cc); + + if (!fast) + si_setcoreidx(sih, origidx); +} + /* change logical "focus" to the gpio core for optimized access */ void * si_gpiosetcore(si_t *sih) @@ -1718,3 +1852,64 @@ si_deviceremoved(si_t *sih) } return FALSE; } + +bool +si_is_sprom_available(si_t *sih) +{ + if (sih->ccrev >= 31) { + si_info_t *sii; + uint origidx; + chipcregs_t *cc; + uint32 sromctrl; + + if ((sih->cccaps & CC_CAP_SROM) == 0) + return FALSE; + + sii = SI_INFO(sih); + origidx = sii->curidx; + cc = si_setcoreidx(sih, SI_CC_IDX); + sromctrl = R_REG(sii->osh, &cc->sromcontrol); + si_setcoreidx(sih, origidx); + return (sromctrl & SRC_PRESENT); + } + + switch (CHIPID(sih->chip)) { + case BCM4312_CHIP_ID: + return ((sih->chipst & CST4312_SPROM_OTP_SEL_MASK) != CST4312_OTP_SEL); + case BCM4325_CHIP_ID: + return (sih->chipst & CST4325_SPROM_SEL) != 0; + case BCM4322_CHIP_ID: + case BCM43221_CHIP_ID: + case BCM43231_CHIP_ID: + case BCM43222_CHIP_ID: + case BCM43111_CHIP_ID: + case BCM43112_CHIP_ID: + case BCM4342_CHIP_ID: + { + uint32 spromotp; + spromotp = (sih->chipst & CST4322_SPROM_OTP_SEL_MASK) >> + CST4322_SPROM_OTP_SEL_SHIFT; + return (spromotp & CST4322_SPROM_PRESENT) != 0; + } + case BCM4329_CHIP_ID: + return (sih->chipst & CST4329_SPROM_SEL) != 0; + case BCM4315_CHIP_ID: + return (sih->chipst & CST4315_SPROM_SEL) != 0; + case BCM4319_CHIP_ID: + return (sih->chipst & CST4319_SPROM_SEL) != 0; + + case BCM4336_CHIP_ID: + case BCM43362_CHIP_ID: + return (sih->chipst & CST4336_SPROM_PRESENT) != 0; + + case BCM4330_CHIP_ID: + return (sih->chipst & CST4330_SPROM_PRESENT) != 0; + case BCM4313_CHIP_ID: + return (sih->chipst & CST4313_SPROM_PRESENT) != 0; + case BCM43239_CHIP_ID: + return ((sih->chipst & CST43239_SPROM_MASK) && + !(sih->chipst & CST43239_SFLASH_MASK)); + default: + return TRUE; + } +} diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index b680481..4fcdcd3 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -2,13 +2,13 @@ * Linux cfg80211 driver - Android related functions * * Copyright (C) 1999-2011, Broadcom Corporation - * + * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: - * + * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that @@ -16,7 +16,7 @@ * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. - * + * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. @@ -67,13 +67,16 @@ #define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP" #define CMD_BTCOEXMODE "BTCOEXMODE" #define CMD_SETSUSPENDOPT "SETSUSPENDOPT" +#define CMD_SETSUSPENDMODE "SETSUSPENDMODE" #define CMD_P2P_DEV_ADDR "P2P_DEV_ADDR" #define CMD_SETFWPATH "SETFWPATH" #define CMD_SETBAND "SETBAND" #define CMD_GETBAND "GETBAND" #define CMD_COUNTRY "COUNTRY" #define CMD_P2P_SET_NOA "P2P_SET_NOA" +#if !defined WL_ENABLE_P2P_IF #define CMD_P2P_GET_NOA "P2P_GET_NOA" +#endif #define CMD_P2P_SET_PS "P2P_SET_PS" #define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" @@ -112,7 +115,7 @@ typedef struct android_wifi_priv_cmd { */ void dhd_customer_gpio_wlan_ctrl(int onoff); uint dhd_dev_reset(struct net_device *dev, uint8 flag); -void dhd_dev_init_ioctl(struct net_device *dev); +int dhd_dev_init_ioctl(struct net_device *dev); #ifdef WL_CFG80211 int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command); @@ -202,7 +205,7 @@ static int wl_android_set_suspendopt(struct net_device *dev, char *command, int ret_now = net_os_set_suspend_disable(dev, suspend_flag); if (ret_now != suspend_flag) { - if (!(ret = net_os_set_suspend(dev, ret_now))) + if (!(ret = net_os_set_suspend(dev, ret_now, 1))) DHD_INFO(("%s: Suspend Flag %d -> %d\n", __FUNCTION__, ret_now, suspend_flag)); else @@ -211,6 +214,26 @@ static int wl_android_set_suspendopt(struct net_device *dev, char *command, int return ret; } +static int wl_android_set_suspendmode(struct net_device *dev, char *command, int total_len) +{ + int ret = 0; + +#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND) + int suspend_flag; + + suspend_flag = *(command + strlen(CMD_SETSUSPENDMODE) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + + if (!(ret = net_os_set_suspend(dev, suspend_flag, 0))) + DHD_INFO(("%s: Suspend Mode %d\n",__FUNCTION__,suspend_flag)); + else + DHD_ERROR(("%s: failed %d\n",__FUNCTION__,ret)); +#endif + return ret; +} + static int wl_android_get_band(struct net_device *dev, char *command, int total_len) { uint band; @@ -224,7 +247,7 @@ static int wl_android_get_band(struct net_device *dev, char *command, int total_ return bytes_written; } -#ifdef PNO_SUPPORT +#if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) { wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; @@ -330,7 +353,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t exit_proc: return res; } -#endif /* PNO_SUPPORT */ +#endif /* PNO_SUPPORT && !WL_SCHED_SCAN */ static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len) { @@ -364,8 +387,10 @@ int wl_android_wifi_on(struct net_device *dev) sdioh_start(NULL, 0); ret = dhd_dev_reset(dev, FALSE); sdioh_start(NULL, 1); - if (!ret) - dhd_dev_init_ioctl(dev); + if (!ret) { + if (dhd_dev_init_ioctl(dev) < 0) + ret = -EFAULT; + } g_wifi_on = 1; } dhd_net_if_unlock(dev); @@ -506,6 +531,9 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) { bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len); } + else if (strnicmp(command, CMD_SETSUSPENDMODE, strlen(CMD_SETSUSPENDMODE)) == 0) { + bytes_written = wl_android_set_suspendmode(net, command, priv_cmd.total_len); + } else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; bytes_written = wldev_set_band(net, band); @@ -517,7 +545,7 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) char *country_code = command + strlen(CMD_COUNTRY) + 1; bytes_written = wldev_set_country(net, country_code); } -#ifdef PNO_SUPPORT +#if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) { bytes_written = dhd_dev_pno_reset(net); } @@ -537,9 +565,11 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip, priv_cmd.total_len - skip); } +#if !defined WL_ENABLE_P2P_IF else if (strnicmp(command, CMD_P2P_GET_NOA, strlen(CMD_P2P_GET_NOA)) == 0) { bytes_written = wl_cfg80211_get_p2p_noa(net, command, priv_cmd.total_len); } +#endif else if (strnicmp(command, CMD_P2P_SET_PS, strlen(CMD_P2P_SET_PS)) == 0) { int skip = strlen(CMD_P2P_SET_PS) + 1; bytes_written = wl_cfg80211_set_p2p_ps(net, command + skip, @@ -591,7 +621,7 @@ int wl_android_init(void) { int ret = 0; - dhd_msg_level = DHD_ERROR_VAL; + dhd_msg_level |= DHD_ERROR_VAL; #ifdef ENABLE_INSMOD_NO_FW_LOAD dhd_download_fw_on_driverload = FALSE; #endif /* ENABLE_INSMOD_NO_FW_LOAD */ @@ -611,30 +641,14 @@ int wl_android_exit(void) return ret; } -int wl_android_post_init(void) +void wl_android_post_init(void) { - struct net_device *ndev; - int ret = 0; - char buf[IFNAMSIZ]; if (!dhd_download_fw_on_driverload) { /* Call customer gpio to turn off power with WL_REG_ON signal */ dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); g_wifi_on = 0; - } else { - memset(buf, 0, IFNAMSIZ); -#ifdef CUSTOMER_HW2 - snprintf(buf, IFNAMSIZ, "%s%d", iface_name, 0); -#else - snprintf(buf, IFNAMSIZ, "%s%d", "eth", 0); -#endif - if ((ndev = dev_get_by_name (&init_net, buf)) != NULL) { - dhd_dev_init_ioctl(ndev); - dev_put(ndev); - } } - return ret; } - /** * Functions for Android WiFi card detection */ @@ -678,7 +692,7 @@ void wl_android_wifictrl_func_del(void) } } -void *wl_android_prealloc(int section, unsigned long size) +void* wl_android_prealloc(int section, unsigned long size) { void *alloc_ptr = NULL; if (wifi_control_data && wifi_control_data->mem_prealloc) { @@ -791,7 +805,7 @@ static int wifi_remove(struct platform_device *pdev) static int wifi_suspend(struct platform_device *pdev, pm_message_t state) { DHD_TRACE(("##> %s\n", __FUNCTION__)); -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) bcmsdh_oob_intr_set(0); #endif return 0; @@ -800,7 +814,7 @@ static int wifi_suspend(struct platform_device *pdev, pm_message_t state) static int wifi_resume(struct platform_device *pdev) { DHD_TRACE(("##> %s\n", __FUNCTION__)); -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) if (dhd_os_check_if_up(bcmsdh_get_drvdata())) bcmsdh_oob_intr_set(1); #endif diff --git a/drivers/net/wireless/bcmdhd/wl_android.h b/drivers/net/wireless/bcmdhd/wl_android.h index 17373b7..3983306 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.h +++ b/drivers/net/wireless/bcmdhd/wl_android.h @@ -40,7 +40,7 @@ */ int wl_android_init(void); int wl_android_exit(void); -int wl_android_post_init(void); +void wl_android_post_init(void); int wl_android_wifi_on(struct net_device *dev); int wl_android_wifi_off(struct net_device *dev); int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd); diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 32241d5..bee7d55 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -2,13 +2,13 @@ * Linux cfg80211 driver * * Copyright (C) 1999-2011, Broadcom Corporation - * + * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: - * + * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that @@ -16,7 +16,7 @@ * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. - * + * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. @@ -41,10 +41,9 @@ #include <dhd.h> #include <dhdioctl.h> #include <wlioctl.h> +#include <dhd_cfg80211.h> #include <proto/ethernet.h> -#include <dngl_stats.h> -#include <dhd.h> #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/netdevice.h> @@ -54,54 +53,29 @@ #include <linux/ieee80211.h> #include <linux/wait.h> #include <net/cfg80211.h> - #include <net/rtnetlink.h> -#include <linux/mmc/sdio_func.h> -#include <linux/firmware.h> -#include <bcmsdbus.h> #include <wlioctl.h> #include <wldev_common.h> #include <wl_cfg80211.h> #include <wl_cfgp2p.h> -static struct sdio_func *cfg80211_sdio_func; -static struct wl_priv *wlcfg_drv_priv; +static struct device *cfg80211_parent_dev = NULL; +static int vsdb_supported = 0; +struct wl_priv *wlcfg_drv_priv = NULL; u32 wl_dbg_level = WL_DBG_ERR; -#define WL_4329_FW_FILE "brcm/bcm4329-fullmac-4-218-248-5.bin" -#define WL_4329_NVRAM_FILE "brcm/bcm4329-fullmac-4-218-248-5.txt" #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" #define MAX_WAIT_TIME 1500 -static s8 ioctlbuf[WLC_IOCTL_MAXLEN]; +#define WL_SCAN_ACTIVE_TIME 40 +#define WL_SCAN_PASSIVE_TIME 130 +#define WL_FRAME_LEN 300 +#define DNGL_FUNC(func, parameters) func parameters; #define COEX_DHCP -#if defined(COEX_DHCP) -#define BT_DHCP_eSCO_FIX /* use New SCO/eSCO smart YG - * suppression - */ -#define BT_DHCP_USE_FLAGS /* this flag boost wifi pkt priority - * to max, caution: -not fair to sco - */ -#define BT_DHCP_OPPR_WIN_TIME 2500 /* T1 start SCO/ESCo priority - * suppression - */ -#define BT_DHCP_FLAG_FORCE_TIME 5500 /* T2 turn off SCO/SCO supperesion - * is (timeout) - */ -enum wl_cfg80211_btcoex_status { - BT_DHCP_IDLE, - BT_DHCP_START, - BT_DHCP_OPPR_WIN, - BT_DHCP_FLAG_FORCE_TIMEOUT -}; - -static int wl_cfg80211_btcoex_init(struct wl_priv *wl); -static void wl_cfg80211_btcoex_deinit(struct wl_priv *wl); -#endif /* This is to override regulatory domains defined in cfg80211 module (reg.c) * By default world regulatory domain defined in reg.c puts the flags NL80211_RRF_PASSIVE_SCAN @@ -121,8 +95,10 @@ static const struct ieee80211_regdomain brcm_regdom = { REG_RULE(2467-10, 2472+10, 20, 6, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), - /* IEEE 802.11 channel 14 - Only JP enables - * this and for 802.11b only + /* + * IEEE 802.11 channel 14 - is for JP only, + * we need cfg80211 to allow it (reg_flags = 0); so that + * hostapd could request auto channel by sending down ch 14 */ REG_RULE(2484-10, 2484+10, 20, 6, 20, NL80211_RRF_PASSIVE_SCAN | @@ -224,7 +200,8 @@ static s32 wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_pmksa *pmksa); static s32 wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev); -static void wl_notify_escan_complete(struct wl_priv *wl, bool aborted); +static s32 wl_notify_escan_complete(struct wl_priv *wl, + struct net_device *ndev, bool aborted, bool fw_abort); /* * event & event Q handlers for cfg80211 interfaces */ @@ -242,6 +219,8 @@ static s32 wl_enq_event(struct wl_priv *wl, struct net_device *ndev, u32 type, const wl_event_msg_t *msg, void *data); static void wl_put_event(struct wl_event_q *e); static void wl_wakeup_event(struct wl_priv *wl); +static s32 wl_notify_connect_status_ap(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); static s32 wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); @@ -252,26 +231,25 @@ static s32 wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); static s32 wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data, bool completed); +static s32 wl_ibss_join_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, bool completed); static s32 wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); +#ifdef WL_SCHED_SCAN +static s32 +wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#endif /* WL_SCHED_SCAN */ +#ifdef PNO_SUPPORT +static s32 wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#endif /* PNO_SUPPORT */ /* - * register/deregister sdio function - */ -struct sdio_func *wl_cfg80211_get_sdio_func(void); -static void wl_clear_sdio_func(void); - -/* - * ioctl utilites + * register/deregister parent device */ -static s32 wl_dev_bufvar_get(struct net_device *dev, s8 *name, s8 *buf, - s32 buf_len); -static __used s32 wl_dev_bufvar_set(struct net_device *dev, s8 *name, - s8 *buf, s32 len); -static s32 wl_dev_intvar_set(struct net_device *dev, s8 *name, s32 val); -static s32 wl_dev_intvar_get(struct net_device *dev, s8 *name, - s32 *retval); +static void wl_cfg80211_clear_parent_dev(void); /* * cfg80211 set_wiphy_params utilities @@ -283,10 +261,10 @@ static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l); /* * wl profile utilities */ -static s32 wl_update_prof(struct wl_priv *wl, const wl_event_msg_t *e, - void *data, s32 item); -static void *wl_read_prof(struct wl_priv *wl, s32 item); -static void wl_init_prof(struct wl_priv *wl); +static s32 wl_update_prof(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, s32 item); +static void *wl_read_prof(struct wl_priv *wl, struct net_device *ndev, s32 item); +static void wl_init_prof(struct wl_priv *wl, struct net_device *ndev); /* * cfg80211 connect utilites @@ -314,14 +292,15 @@ static s32 wl_mrg_ie(struct wl_priv *wl, u8 *ie_stream, u16 ie_size); static s32 wl_cp_ie(struct wl_priv *wl, u8 *dst, u16 dst_size); static u32 wl_get_ielen(struct wl_priv *wl); -static s32 wl_mode_to_nl80211_iftype(s32 mode); -static struct wireless_dev *wl_alloc_wdev(struct device *sdiofunc_dev); +static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *dev); static void wl_free_wdev(struct wl_priv *wl); static s32 wl_inform_bss(struct wl_priv *wl); static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi); +static s32 wl_inform_ibss(struct wl_priv *wl, const u8 *bssid); static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev); +static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); static s32 wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, const u8 *mac_addr, @@ -347,42 +326,18 @@ static bool wl_is_ibssmode(struct wl_priv *wl, struct net_device *ndev); static __used bool wl_is_ibssstarter(struct wl_priv *wl); /* - * dongle up/down , default configuration utilities + * link up/down , default configuration utilities */ +static s32 __wl_cfg80211_up(struct wl_priv *wl); +static s32 __wl_cfg80211_down(struct wl_priv *wl); +static s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add); static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e); static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev); static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e); static void wl_link_up(struct wl_priv *wl); static void wl_link_down(struct wl_priv *wl); -static s32 wl_dongle_mode(struct wl_priv *wl, struct net_device *ndev, s32 iftype); -static s32 __wl_cfg80211_up(struct wl_priv *wl); -static s32 __wl_cfg80211_down(struct wl_priv *wl); -static s32 wl_dongle_probecap(struct wl_priv *wl); +static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype); static void wl_init_conf(struct wl_conf *conf); -static s32 wl_dongle_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add); - -/* - * dongle configuration utilities - */ -#ifndef EMBEDDED_PLATFORM -static s32 wl_dongle_country(struct net_device *ndev, u8 ccode); -static s32 wl_dongle_up(struct net_device *ndev, u32 up); -static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode); -static s32 wl_dongle_glom(struct net_device *ndev, u32 glom, - u32 dongle_align); -static s32 wl_dongle_roam(struct net_device *ndev, u32 roamvar, - u32 bcn_timeout); -static s32 wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, - s32 scan_unassoc_time); -static s32 wl_dongle_offload(struct net_device *ndev, s32 arpoe, - s32 arp_ol); -static s32 wl_pattern_atoh(s8 *src, s8 *dst); -static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode); -static s32 wl_update_wiphybands(struct wl_priv *wl); -#endif /* !EMBEDDED_PLATFORM */ -static __used void wl_dongle_poweron(struct wl_priv *wl); -static __used void wl_dongle_poweroff(struct wl_priv *wl); -static s32 wl_config_dongle(struct wl_priv *wl, bool need_lock); /* * iscan handler @@ -406,33 +361,20 @@ static s32 wl_iscan_inprogress(struct wl_priv *wl); static s32 wl_iscan_aborted(struct wl_priv *wl); /* - * fw/nvram downloading handler - */ -static void wl_init_fw(struct wl_fw_ctrl *fw); - -/* * find most significant bit set */ static __used u32 wl_find_msb(u16 bit16); /* - * update pmklist to dongle - */ -static __used s32 wl_update_pmklist(struct net_device *dev, - struct wl_pmk_list *pmk_list, s32 err); - -/* - * debufs support - */ -static int wl_debugfs_add_netdev_params(struct wl_priv *wl); -static void wl_debugfs_remove_netdev(struct wl_priv *wl); - -/* * rfkill support */ static int wl_setup_rfkill(struct wl_priv *wl, bool setup); static int wl_rfkill_set(void *data, bool blocked); +static wl_scan_params_t *wl_cfg80211_scan_alloc_params(int channel, + int nprobes, int *out_params_size); +static void get_primary_mac(struct wl_priv *wl, struct ether_addr *mac); + /* * Some external functions, TODO: move them to dhd_linux.h */ @@ -444,10 +386,10 @@ int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); #define CHECK_SYS_UP(wlpriv) \ do { \ - if (unlikely(!wl_get_drv_status(wlpriv, READY))) { \ - WL_INFO(("device is not ready : status (%d)\n", \ - (int)wlpriv->status)); \ - return -EIO; \ + struct net_device *ndev = wl_to_prmry_ndev(wlpriv); \ + if (unlikely(!wl_get_drv_status(wlpriv, READY, ndev))) { \ + WL_INFO(("device is not ready\n")); \ + return -EIO; \ } \ } while (0) @@ -589,7 +531,7 @@ static const u32 __wl_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, - WLAN_CIPHER_SUITE_AES_CMAC + WLAN_CIPHER_SUITE_AES_CMAC, }; /* There isn't a lot of sense in it, but you can transmit anything you like */ @@ -740,6 +682,43 @@ wl_validate_wps_ie(char *wps_ie, bool *pbc) } } +static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy) +{ + if (vsdb_supported) { + return wf_chspec_aton(WL_P2P_TEMP_CHAN); + } + else { + chanspec_t chspec; + int err = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *dev = wl_to_prmry_ndev(wl); + struct ether_addr bssid; + struct wl_bss_info *bss = NULL; + if ((err = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), false))) { + /* STA interface is not associated. So start the new interface on a temp + * channel . Later proper channel will be applied by the above framework + * via set_channel (cfg80211 API). + */ + WL_DBG(("Not associated. Return a temp channel. \n")); + return wf_chspec_aton(WL_P2P_TEMP_CHAN); + } + + + *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); + if ((err = wldev_ioctl(dev, WLC_GET_BSS_INFO, wl->extra_buf, + WL_EXTRA_BUF_MAX, false))) { + WL_ERR(("Failed to get associated bss info, use temp channel \n")); + chspec = wf_chspec_aton(WL_P2P_TEMP_CHAN); + } + else { + bss = (struct wl_bss_info *) (wl->extra_buf + 4); + chspec = bss->chanspec; + WL_DBG(("Valid BSS Found. chanspec:%d \n", bss->chanspec)); + } + return chspec; + } +} + static struct net_device* wl_cfg80211_add_monitor_if(char *name) { int ret = 0; @@ -758,15 +737,20 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, s32 err; s32 timeout = -1; s32 wlif_type = -1; - s32 index = 0; s32 mode = 0; +#if defined(WL_ENABLE_P2P_IF) + s32 dhd_mode = 0; +#endif /* (WL_ENABLE_P2P_IF) */ chanspec_t chspec; struct wl_priv *wl = wiphy_priv(wiphy); struct net_device *_ndev; - dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); - int (*net_attach)(dhd_pub_t *dhdp, int ifidx); + struct ether_addr primary_mac; + int (*net_attach)(void *dhdp, int ifidx); bool rollback_lock = false; + /* Use primary I/F for sending cmds down to firmware */ + _ndev = wl_to_prmry_ndev(wl); + WL_DBG(("if name: %s, type: %d\n", name, type)); switch (type) { case NL80211_IFTYPE_ADHOC: @@ -798,6 +782,8 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, WL_ERR(("name is NULL\n")); return NULL; } + if (wl->iface_cnt == IFACE_MAX_CNT) + return ERR_PTR(-ENOMEM); if (wl->p2p_supported && (wlif_type != -1)) { if (wl_get_p2p_status(wl, IF_DELETING)) { /* wait till IF_DEL is complete @@ -809,7 +795,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, } WL_INFO(("%s: Released the lock and wait till IF_DEL is complete\n", __func__)); - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, + timeout = wait_event_interruptible_timeout(wl->netif_change_event, (wl_get_p2p_status(wl, IF_DELETING) == false), msecs_to_jiffies(MAX_WAIT_TIME)); @@ -826,17 +812,25 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, return ERR_PTR(-EAGAIN); } } - if (!p2p_on(wl) && strstr(name, WL_P2P_INTERFACE_PREFIX)) { + if (wl->p2p && !wl->p2p->on && strstr(name, WL_P2P_INTERFACE_PREFIX)) { p2p_on(wl) = true; wl_cfgp2p_set_firm_p2p(wl); wl_cfgp2p_init_discovery(wl); + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, + &wl->p2p->dev_addr, &wl->p2p->int_addr); } + memset(wl->p2p->vir_ifname, 0, IFNAMSIZ); strncpy(wl->p2p->vir_ifname, name, IFNAMSIZ - 1); - wl_cfgp2p_generate_bss_mac(&dhd->mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); - /* Temporary use channel 11, in case GO will be changed with set_channel API */ - chspec = wf_chspec_aton(WL_P2P_TEMP_CHAN); + wldev_iovar_setint(_ndev, "mpc", 0); + wl_notify_escan_complete(wl, _ndev, true, true); + /* In concurrency case, STA may be already associated in a particular channel. + * so retrieve the current channel of primary interface and then start the virtual + * interface on that. + */ + chspec = wl_cfg80211_get_shared_freq(wiphy); /* For P2P mode, use P2P-specific driver features to create the * bss: "wl p2p_ifadd" @@ -849,7 +843,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, return ERR_PTR(-ENOMEM); } - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, + timeout = wait_event_interruptible_timeout(wl->netif_change_event, (wl_get_p2p_status(wl, IF_ADD) == false), msecs_to_jiffies(MAX_WAIT_TIME)); if (timeout > 0 && (!wl_get_p2p_status(wl, IF_ADD))) { @@ -863,28 +857,36 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, vwdev->wiphy = wl->wdev->wiphy; WL_INFO((" virtual interface(%s) is created memalloc done \n", wl->p2p->vir_ifname)); - index = alloc_idx_vwdev(wl); - wl->vwdev[index] = vwdev; - vwdev->iftype = - (wlif_type == WL_P2P_IF_CLIENT) ? NL80211_IFTYPE_STATION - : NL80211_IFTYPE_AP; + vwdev->iftype = type; _ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); _ndev->ieee80211_ptr = vwdev; SET_NETDEV_DEV(_ndev, wiphy_dev(vwdev->wiphy)); vwdev->netdev = _ndev; - wl_set_drv_status(wl, READY); + wl_set_drv_status(wl, READY, _ndev); wl->p2p->vif_created = true; - set_mode_by_netdev(wl, _ndev, mode); - WL_DBG((" virtual interface(%s) wl->wdev %p wl->wdev->netdev %p vwdev %p vwdev->netdev %p\n", - wl->p2p->vir_ifname, wl->wdev, wl->wdev->netdev, vwdev, vwdev->netdev)); + wl_set_mode_by_netdev(wl, _ndev, mode); net_attach = wl_to_p2p_bss_private(wl, P2PAPI_BSSCFG_CONNECTION); if (rtnl_is_locked()) { rtnl_unlock(); rollback_lock = true; } - if (net_attach && !net_attach(dhd, _ndev->ifindex)) { - WL_DBG((" virtual interface(%s) is " + if (net_attach && !net_attach(wl->pub, _ndev->ifindex)) { + wl_alloc_netinfo(wl, _ndev, vwdev, mode); + WL_ERR((" virtual interface(%s) is " "created net attach done\n", wl->p2p->vir_ifname)); +#if defined(WL_ENABLE_P2P_IF) + if (type == NL80211_IFTYPE_P2P_CLIENT) + dhd_mode = P2P_GC_ENABLED; + else if (type == NL80211_IFTYPE_P2P_GO) + dhd_mode = P2P_GO_ENABLED; + DNGL_FUNC(dhd_cfg80211_set_p2p_info, (wl, dhd_mode)); +#endif /* (WL_ENABLE_P2P_IF) */ + /* Start the P2P I/F with PM disabled. Enable PM from + * the framework + */ + if ((type == NL80211_IFTYPE_P2P_CLIENT) || ( + type == NL80211_IFTYPE_P2P_GO)) + vwdev->ps = NL80211_PS_DISABLED; } else { /* put back the rtnl_lock again */ if (rollback_lock) @@ -915,16 +917,28 @@ wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) s32 timeout = -1; s32 ret = 0; WL_DBG(("Enter\n")); + + if (wl->p2p_net == dev) { + /* Since there is no ifidx corresponding to p2p0, cmds to + * firmware should be routed through primary I/F + */ + dev = wl_to_prmry_ndev(wl); + } + if (wl->p2p_supported) { memcpy(p2p_mac.octet, wl->p2p->int_addr.octet, ETHER_ADDR_LEN); + + /* Clear GO_NEG_PHASE bit to take care of GO-NEG-FAIL cases + */ + WL_DBG(("P2P: GO_NEG_PHASE status cleared ")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); if (wl->p2p->vif_created) { - if (wl_get_drv_status(wl, SCANNING)) { - wl_cfg80211_scan_abort(wl, dev); + if (wl_get_drv_status(wl, SCANNING, dev)) { + wl_notify_escan_complete(wl, dev, true, true); } wldev_iovar_setint(dev, "mpc", 1); wl_set_p2p_status(wl, IF_DELETING); ret = wl_cfgp2p_ifdel(wl, &p2p_mac); - if (ret) { /* Firmware could not delete the interface so we will not get WLC_E_IF * event for cleaning the dhd virtual nw interace * So lets do it here. Failures from fw will ensure the application to do @@ -932,21 +946,22 @@ wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) * however we should cleanup the linux network virtual interfaces */ /* Request framework to RESET and clean up */ + if (ret) { struct net_device *ndev = wl_to_prmry_ndev(wl); WL_ERR(("Firmware returned an error (%d) from p2p_ifdel" - "HANG Notification sent to %s dev %p wdev %p ndev %p\n", ret, ndev->name, dev, wl->wdev, wl_to_prmry_ndev(wl))); + "HANG Notification sent to %s\n", ret, ndev->name)); wl_cfg80211_hang(ndev, WLAN_REASON_UNSPECIFIED); } - else { - WL_ERR(("Firmware success from p2p_ifdel dev %p wdev %p ndev %p", dev, wl->wdev, wl_to_prmry_ndev(wl))); - } /* Wait for any pending scan req to get aborted from the sysioc context */ - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, - (wl_get_p2p_status(wl, IF_DELETING) == false), + timeout = wait_event_interruptible_timeout(wl->netif_change_event, + (wl->p2p->vif_created == false), msecs_to_jiffies(MAX_WAIT_TIME)); - if (timeout > 0 && !wl_get_p2p_status(wl, IF_DELETING)) { + if (timeout > 0 && (wl->p2p->vif_created == false)) { WL_DBG(("IFDEL operation done\n")); +#if defined(WL_ENABLE_P2P_IF) + DNGL_FUNC(dhd_cfg80211_clean_p2p_info, (wl)); +#endif /* (WL_ENABLE_P2P_IF)) */ } else { WL_ERR(("IFDEL didn't complete properly\n")); } @@ -969,7 +984,8 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, s32 mode = 0; chanspec_t chspec; struct wl_priv *wl = wiphy_priv(wiphy); - WL_DBG(("Enter \n")); + + WL_DBG(("Enter type %d\n", type)); switch (type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_WDS: @@ -995,29 +1011,37 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, default: return -EINVAL; } - + WL_DBG(("%s : ap (%d), infra (%d), iftype: (%d)\n", ndev->name, ap, infra, type)); if (ap) { - set_mode_by_netdev(wl, ndev, mode); + wl_set_mode_by_netdev(wl, ndev, mode); if (wl->p2p_supported && wl->p2p->vif_created) { WL_DBG(("p2p_vif_created (%d) p2p_on (%d)\n", wl->p2p->vif_created, - p2p_on(wl))); - chspec = wf_chspec_aton(WL_P2P_TEMP_CHAN); - wlif_type = ap ? WL_P2P_IF_GO : WL_P2P_IF_CLIENT; + p2p_on(wl))); + wldev_iovar_setint(ndev, "mpc", 0); + wl_notify_escan_complete(wl, ndev, true, true); + + /* In concurrency case, STA may be already associated in a particular + * channel. so retrieve the current channel of primary interface and + * then start the virtual interface on that. + */ + chspec = wl_cfg80211_get_shared_freq(wiphy); + + wlif_type = WL_P2P_IF_GO; WL_ERR(("%s : ap (%d), infra (%d), iftype: (%d)\n", ndev->name, ap, infra, type)); wl_set_p2p_status(wl, IF_CHANGING); wl_clr_p2p_status(wl, IF_CHANGED); err = wl_cfgp2p_ifchange(wl, &wl->p2p->int_addr, htod32(wlif_type), chspec); - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, + timeout = wait_event_interruptible_timeout(wl->netif_change_event, (wl_get_p2p_status(wl, IF_CHANGED) == true), msecs_to_jiffies(MAX_WAIT_TIME)); - set_mode_by_netdev(wl, ndev, mode); + wl_set_mode_by_netdev(wl, ndev, mode); wl_clr_p2p_status(wl, IF_CHANGING); wl_clr_p2p_status(wl, IF_CHANGED); } else if (ndev == wl_to_prmry_ndev(wl) && - !wl_get_drv_status(wl, AP_CREATED)) { - wl_set_drv_status(wl, AP_CREATING); + !wl_get_drv_status(wl, AP_CREATED, ndev)) { + wl_set_drv_status(wl, AP_CREATING, ndev); if (!wl->ap_info && !(wl->ap_info = kzalloc(sizeof(struct ap_info), GFP_KERNEL))) { WL_ERR(("struct ap_saved_ie allocation failed\n")); @@ -1027,6 +1051,14 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, WL_ERR(("Cannot change the interface for GO or SOFTAP\n")); return -EINVAL; } + } else { + infra = htod32(infra); + err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(s32), true); + if (err) { + WL_ERR(("WLC_SET_INFRA error (%d)\n", err)); + return -EAGAIN; + } + wl_set_mode_by_netdev(wl, ndev, mode); } ndev->ieee80211_ptr->iftype = type; @@ -1035,10 +1067,11 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, s32 wl_cfg80211_notify_ifadd(struct net_device *ndev, s32 idx, s32 bssidx, -int (*_net_attach)(dhd_pub_t *dhdp, int ifidx)) + void* _net_attach) { struct wl_priv *wl = wlcfg_drv_priv; s32 ret = BCME_OK; + WL_DBG(("Enter")); if (!ndev) { WL_ERR(("net is NULL\n")); return 0; @@ -1054,7 +1087,7 @@ int (*_net_attach)(dhd_pub_t *dhdp, int ifidx)) ndev->ifindex = idx; wl_clr_p2p_status(wl, IF_ADD); - wake_up_interruptible(&wl->dongle_event_wait); + wake_up_interruptible(&wl->netif_change_event); } else { ret = BCME_NOTREADY; } @@ -1062,7 +1095,18 @@ int (*_net_attach)(dhd_pub_t *dhdp, int ifidx)) } s32 -wl_cfg80211_notify_ifdel(struct net_device *ndev) +wl_cfg80211_notify_ifdel(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + + WL_DBG(("Enter \n")); + wl_clr_p2p_status(wl, IF_DELETING); + + return 0; +} + +s32 +wl_cfg80211_ifdel_ops(struct net_device *ndev) { struct wl_priv *wl = wlcfg_drv_priv; bool rollback_lock = false; @@ -1075,7 +1119,8 @@ wl_cfg80211_notify_ifdel(struct net_device *ndev) if (p2p_is_on(wl) && wl->p2p->vif_created && wl_get_p2p_status(wl, IF_DELETING)) { - if (wl->scan_request) { + if (wl->scan_request && + (wl->escan_info.ndev == ndev)) { /* Abort any pending scan requests */ wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; if (!rtnl_is_locked()) { @@ -1083,7 +1128,7 @@ wl_cfg80211_notify_ifdel(struct net_device *ndev) rollback_lock = true; } WL_DBG(("ESCAN COMPLETED\n")); - wl_notify_escan_complete(wl, true); + wl_notify_escan_complete(wl, ndev, true, false); if (rollback_lock) rtnl_unlock(); } @@ -1097,25 +1142,13 @@ wl_cfg80211_notify_ifdel(struct net_device *ndev) wl->p2p->vif_created = false; wl_cfgp2p_clear_management_ie(wl, index); - wl_clr_p2p_status(wl, IF_DELETING); WL_DBG(("index : %d\n", index)); } - /* Wake up any waiting thread */ - wake_up_interruptible(&wl->dongle_event_wait); - return 0; -} + /* Wake up any waiting thread */ + wake_up_interruptible(&wl->netif_change_event); -s32 wl_cfg80211_post_del(void* ndev) -{ - int index; - struct wl_priv *wl = wlcfg_drv_priv; - index = get_idx_vwdev_by_netdev(wl, (struct net_device *)ndev); - WL_DBG(("index : %d\n", index)); - if (index >= 0) { - free_vwdev_by_index(wl, index); - } return 0; } @@ -1146,7 +1179,7 @@ wl_cfg80211_notify_ifchange(void) struct wl_priv *wl = wlcfg_drv_priv; if (wl_get_p2p_status(wl, IF_CHANGING)) { wl_set_p2p_status(wl, IF_CHANGED); - wake_up_interruptible(&wl->dongle_event_wait); + wake_up_interruptible(&wl->netif_change_event); } return 0; } @@ -1183,6 +1216,7 @@ static void wl_scan_prep(struct wl_scan_params *params, struct cfg80211_scan_req params->passive_time = htod32(params->passive_time); params->home_time = htod32(params->home_time); + /* if request is null just exit so it will be all channel broadcast scan */ if (!request) return; @@ -1215,7 +1249,7 @@ static void wl_scan_prep(struct wl_scan_params *params, struct cfg80211_scan_req params->channel_list[i] &= WL_CHANSPEC_CHAN_MASK; params->channel_list[i] |= chanspec; WL_SCAN(("Chan : %d, Channel spec: %x \n", - channel, params->channel_list[i])); + channel, params->channel_list[i])); params->channel_list[i] = htod16(params->channel_list[i]); } } else { @@ -1289,7 +1323,7 @@ wl_run_iscan(struct wl_iscan_ctrl *iscan, struct cfg80211_scan_request *request, goto done; } err = wldev_iovar_setbuf(iscan->dev, "iscan", params, params_size, - iscan->ioctl_buf, WLC_IOCTL_MEDLEN); + iscan->ioctl_buf, WLC_IOCTL_MEDLEN, NULL); if (unlikely(err)) { if (err == -EBUSY) { WL_ERR(("system busy : iscan canceled\n")); @@ -1327,6 +1361,25 @@ static s32 wl_do_iscan(struct wl_priv *wl, struct cfg80211_scan_request *request } static s32 +wl_get_valid_channels(struct net_device *ndev, u8 *valid_chan_list, s32 size) +{ + wl_uint32_list_t *list; + s32 err = BCME_OK; + if (valid_chan_list == NULL || size <= 0) + return -ENOMEM; + + memset(valid_chan_list, 0, size); + list = (wl_uint32_list_t *)(void *) valid_chan_list; + list->count = htod32(WL_NUMCHANNELS); + err = wldev_ioctl(ndev, WLC_GET_VALID_CHANNELS, valid_chan_list, size, false); + if (err != 0) { + WL_ERR(("get channels failed with %d\n", err)); + } + + return err; +} + +static s32 wl_run_escan(struct wl_priv *wl, struct net_device *ndev, struct cfg80211_scan_request *request, uint16 action) { @@ -1334,12 +1387,16 @@ wl_run_escan(struct wl_priv *wl, struct net_device *ndev, u32 n_channels; u32 n_ssids; s32 params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params)); - wl_escan_params_t *params; + wl_escan_params_t *params = NULL; struct cfg80211_scan_request *scan_request = wl->scan_request; + u8 chan_buf[sizeof(u32)*(WL_NUMCHANNELS + 1)]; u32 num_chans = 0; + s32 channel; + s32 n_valid_chan; s32 search_state = WL_P2P_DISC_ST_SCAN; - u32 i; + u32 i, j, n_nodfs = 0; u16 *default_chan_list = NULL; + wl_uint32_list_t *list; struct net_device *dev = NULL; WL_DBG(("Enter \n")); @@ -1379,13 +1436,15 @@ wl_run_escan(struct wl_priv *wl, struct net_device *ndev, goto exit; } err = wldev_iovar_setbuf(ndev, "escan", params, params_size, - wl->escan_ioctl_buf, WLC_IOCTL_MEDLEN); + wl->escan_ioctl_buf, WLC_IOCTL_MEDLEN, NULL); if (unlikely(err)) WL_ERR((" Escan set error (%d)\n", err)); kfree(params); } - else if (p2p_on(wl) && p2p_scan(wl)) { + else if (p2p_is_on(wl) && p2p_scan(wl)) { /* P2P SCAN TRIGGER */ + s32 _freq = 0; + n_nodfs = 0; if (scan_request && scan_request->n_channels) { num_chans = scan_request->n_channels; WL_SCAN((" chann number : %d\n", num_chans)); @@ -1396,11 +1455,26 @@ wl_run_escan(struct wl_priv *wl, struct net_device *ndev, err = -ENOMEM; goto exit; } - for (i = 0; i < num_chans; i++) - { - default_chan_list[i] = - ieee80211_frequency_to_channel( - scan_request->channels[i]->center_freq); + if (!wl_get_valid_channels(ndev, chan_buf, sizeof(chan_buf))) { + list = (wl_uint32_list_t *) chan_buf; + n_valid_chan = dtoh32(list->count); + for (i = 0; i < num_chans; i++) + { + _freq = scan_request->channels[i]->center_freq; + channel = ieee80211_frequency_to_channel(_freq); + /* remove DFS channels */ + if (channel < 52 || channel > 140) { + for (j = 0; j < n_valid_chan; j++) { + /* allows only supported channel on + * current reguatory + */ + if (channel == (dtoh32(list->element[j]))) + default_chan_list[n_nodfs++] = + channel; + } + } + + } } if (num_chans == 3 && ( (default_chan_list[0] == SOCIAL_CHAN_1) && @@ -1410,12 +1484,15 @@ wl_run_escan(struct wl_priv *wl, struct net_device *ndev, search_state = WL_P2P_DISC_ST_SEARCH; WL_INFO(("P2P SEARCH PHASE START \n")); } else if ((dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)) && - (get_mode_by_netdev(wl, dev) == WL_MODE_AP)) { + (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP)) { /* If you are already a GO, then do SEARCH only */ WL_INFO(("Already a GO. Do SEARCH Only")); search_state = WL_P2P_DISC_ST_SEARCH; + num_chans = n_nodfs; + } else { WL_INFO(("P2P SCAN STATE START \n")); + num_chans = n_nodfs; } } @@ -1440,7 +1517,7 @@ wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, s32 passive_scan; wl_scan_results_t *results; WL_SCAN(("Enter \n")); - + wl->escan_info.ndev = ndev; wl->escan_info.wiphy = wiphy; wl->escan_info.escan_state = WL_ESCAN_STATE_SCANING; passive_scan = wl->active_scan ? 0 : 1; @@ -1467,36 +1544,41 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, struct wl_priv *wl = wiphy_priv(wiphy); struct cfg80211_ssid *ssids; struct wl_scan_req *sr = wl_to_sr(wl); + struct ether_addr primary_mac; wpa_ie_fixed_t *wps_ie; s32 passive_scan; bool iscan_req; - bool escan_req; - bool spec_scan; + bool escan_req = false; bool p2p_ssid; s32 err = 0; s32 i; u32 wpsie_len = 0; u8 wpsie[IE_MAX_LEN]; + /* If scan req comes for p2p0, send it over primary I/F + * Scan results will be delivered corresponding to cfg80211_scan_request + */ + if (ndev == wl->p2p_net) { + ndev = wl_to_prmry_ndev(wl); + } + WL_DBG(("Enter wiphy (%p)\n", wiphy)); - if (unlikely(wl_get_drv_status(wl, SCANNING))) { - WL_ERR(("Scanning already : status (%d)\n", (int)wl->status)); + if (wl_get_drv_status_all(wl, SCANNING)) { + WL_ERR(("Scanning already\n")); return -EAGAIN; } - if (unlikely(wl_get_drv_status(wl, SCAN_ABORTING))) { - WL_ERR(("Scanning being aborted : status (%d)\n", - (int)wl->status)); + if (wl_get_drv_status(wl, SCAN_ABORTING, ndev)) { + WL_ERR(("Scanning being aborted\n")); return -EAGAIN; } if (request && request->n_ssids > WL_SCAN_PARAMS_SSID_MAX) { - WL_ERR(("n_ssids > WL_SCAN_PARAMS_SSID_MAX\n")); + WL_ERR(("request null or n_ssids > WL_SCAN_PARAMS_SSID_MAX\n")); return -EOPNOTSUPP; } /* Arm scan timeout timer */ mod_timer(&wl->scan_timeout, jiffies + WL_SCAN_TIMER_INTERVAL_MS * HZ / 1000); iscan_req = false; - spec_scan = false; if (request) { /* scan bss */ ssids = request->ssids; if (wl->iscan_on && (!ssids || !ssids->ssid_len || request->n_ssids != 1)) { @@ -1517,7 +1599,12 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, /* p2p on at the first time */ p2p_on(wl) = true; wl_cfgp2p_set_firm_p2p(wl); + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, + &wl->p2p->dev_addr, &wl->p2p->int_addr); } + wl_clr_p2p_status(wl, GO_NEG_PHASE); + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); p2p_scan(wl) = true; } } else { @@ -1556,10 +1643,13 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, } else { wpsie_len = 0; } - err = wl_cfgp2p_set_management_ie(wl, ndev, -1, - VNDR_IE_PRBREQ_FLAG, wpsie, wpsie_len); - if (unlikely(err)) { - goto scan_out; + if (wpsie_len > 0) { + err = wl_cfgp2p_set_management_ie(wl, + ndev, -1, VNDR_IE_PRBREQ_FLAG, + wpsie, wpsie_len); + if (unlikely(err)) { + goto scan_out; + } } } } @@ -1570,7 +1660,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, ssids = this_ssid; } wl->scan_request = request; - wl_set_drv_status(wl, SCANNING); + wl_set_drv_status(wl, SCANNING, ndev); if (iscan_req) { err = wl_do_iscan(wl, request); if (likely(!err)) @@ -1605,7 +1695,6 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, sr->ssid.SSID_len = htod32(sr->ssid.SSID_len); WL_SCAN(("Specific scan ssid=\"%s\" len=%d\n", sr->ssid.SSID, sr->ssid.SSID_len)); - spec_scan = true; } else { WL_SCAN(("Broadcast scan\n")); } @@ -1633,7 +1722,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, return 0; scan_out: - wl_clr_drv_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, ndev); wl->scan_request = NULL; return err; } @@ -1657,52 +1746,11 @@ wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, return err; } -static s32 wl_dev_intvar_set(struct net_device *dev, s8 *name, s32 val) -{ - s8 buf[WLC_IOCTL_SMLEN]; - u32 len; - s32 err = 0; - - val = htod32(val); - len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf)); - BUG_ON(unlikely(!len)); - - err = wldev_ioctl(dev, WLC_SET_VAR, buf, len, false); - if (unlikely(err)) { - WL_ERR(("error (%d)\n", err)); - } - - return err; -} - -static s32 -wl_dev_intvar_get(struct net_device *dev, s8 *name, s32 *retval) -{ - union { - s8 buf[WLC_IOCTL_SMLEN]; - s32 val; - } var; - u32 len; - u32 data_null; - s32 err = 0; - - len = bcm_mkiovar(name, (char *)(&data_null), 0, - (char *)(&var), sizeof(var.buf)); - BUG_ON(unlikely(!len)); - err = wldev_ioctl(dev, WLC_GET_VAR, &var, len, false); - if (unlikely(err)) { - WL_ERR(("error (%d)\n", err)); - } - *retval = dtoh32(var.val); - - return err; -} - static s32 wl_set_rts(struct net_device *dev, u32 rts_threshold) { s32 err = 0; - err = wl_dev_intvar_set(dev, "rtsthresh", rts_threshold); + err = wldev_iovar_setint(dev, "rtsthresh", rts_threshold); if (unlikely(err)) { WL_ERR(("Error (%d)\n", err)); return err; @@ -1714,7 +1762,7 @@ static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold) { s32 err = 0; - err = wl_dev_intvar_set(dev, "fragthresh", frag_threshold); + err = wldev_iovar_setint_bsscfg(dev, "fragthresh", frag_threshold, 0); if (unlikely(err)) { WL_ERR(("Error (%d)\n", err)); return err; @@ -1743,6 +1791,7 @@ static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) s32 err = 0; CHECK_SYS_UP(wl); + WL_DBG(("Enter\n")); if (changed & WIPHY_PARAM_RTS_THRESHOLD && (wl->conf->rts_threshold != wiphy->rts_threshold)) { wl->conf->rts_threshold = wiphy->rts_threshold; @@ -1772,7 +1821,6 @@ static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) return err; } } - return err; } @@ -1781,86 +1829,192 @@ wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params) { struct wl_priv *wl = wiphy_priv(wiphy); - struct cfg80211_bss *bss; - struct ieee80211_channel *chan; struct wl_join_params join_params; - struct cfg80211_ssid ssid; - s32 scan_retry = 0; + struct wlc_ssid ssid; + struct ether_addr bssid; + size_t join_params_size = 0; + s32 wsec = 0; + s32 bcnprd; s32 err = 0; - bool rollback_lock = false; WL_TRACE(("In\n")); CHECK_SYS_UP(wl); - if (params->bssid) { - WL_ERR(("Invalid bssid\n")); - return -EOPNOTSUPP; - } - bss = cfg80211_get_ibss(wiphy, NULL, params->ssid, params->ssid_len); - if (!bss) { - memcpy(ssid.ssid, params->ssid, params->ssid_len); - ssid.ssid_len = params->ssid_len; - do { - if (unlikely - (__wl_cfg80211_scan(wiphy, dev, NULL, &ssid) == - -EBUSY)) { - wl_delay(150); - } else { - break; - } - } while (++scan_retry < WL_SCAN_RETRY_MAX); - /* to allow scan_inform to propagate to cfg80211 plane */ - if (rtnl_is_locked()) { - rtnl_unlock(); - rollback_lock = true; - } - - /* wait 4 secons till scan done.... */ - schedule_timeout_interruptible(4 * HZ); - if (rollback_lock) - rtnl_lock(); - bss = cfg80211_get_ibss(wiphy, NULL, - params->ssid, params->ssid_len); - } - if (bss) { - wl->ibss_starter = false; - WL_DBG(("Found IBSS\n")); - } else { - wl->ibss_starter = true; - } - chan = params->channel; - if (chan) - wl->channel = ieee80211_frequency_to_channel(chan->center_freq); + /* - * Join with specific BSSID and cached SSID - * If SSID is zero join based on BSSID only + * Cancel ongoing scan to sync up with sme state machine of cfg80211. */ - memset(&join_params, 0, sizeof(join_params)); - memcpy((void *)join_params.ssid.SSID, (void *)params->ssid, - params->ssid_len); - join_params.ssid.SSID_len = htod32(params->ssid_len); + if (wl->scan_request) { + wl_notify_escan_complete(wl, dev, true, true); + } + /* Clean BSSID */ + bzero(&bssid, sizeof(bssid)); + wl_update_prof(wl, dev, NULL, (void *)&bssid, WL_PROF_BSSID); + + if (params->ssid) + WL_INFO(("SSID: %s\n", params->ssid)); + else { + WL_ERR(("SSID: NULL, Not supported\n")); + err = -EOPNOTSUPP; + goto CleanUp; + } + if (params->bssid) - memcpy(&join_params.params.bssid, params->bssid, - ETHER_ADDR_LEN); + WL_INFO(("BSSID: %02X:%02X:%02X:%02X:%02X:%02X\n", + params->bssid[0], params->bssid[1], params->bssid[2], + params->bssid[3], params->bssid[4], params->bssid[5])); + + if (params->channel) + WL_INFO(("channel: %d\n", params->channel->center_freq)); + + if (params->channel_fixed) + WL_INFO(("fixed channel required\n")); + + if (params->ie && params->ie_len) + WL_INFO(("ie len: %d\n", params->ie_len)); + + if (params->beacon_interval) + WL_INFO(("beacon interval: %d\n", params->beacon_interval)); + + if (params->basic_rates) + WL_INFO(("basic rates: %08X\n", params->basic_rates)); + + if (params->privacy) + WL_INFO(("privacy required\n")); + + wl_set_drv_status(wl, CONNECTING, dev); + + /* Configure Privacy for starter */ + if (params->privacy) + wsec |= WEP_ENABLED; + + err = wldev_iovar_setint(dev, "wsec", wsec); + if (err) { + WL_ERR(("wsec failed (%d)\n", err)); + goto CleanUp; + } + + err = wldev_iovar_setint(dev, "auth", WL_AUTH_OPEN_SYSTEM); + if (err) { + WL_ERR(("auth failed (%d)\n", err)); + goto CleanUp; + } + + err = wldev_iovar_setint(dev, "wpa_auth", 0); + if (err) { + WL_ERR(("wpa_auth failed (%d)\n", err)); + goto CleanUp; + } + + /* Configure Beacon Interval for starter */ + if (params->beacon_interval) + bcnprd = params->beacon_interval; else - memset(&join_params.params.bssid, 0, ETHER_ADDR_LEN); + bcnprd = 100; - err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, - sizeof(join_params), false); - if (unlikely(err)) { - WL_ERR(("Error (%d)\n", err)); - return err; + bcnprd = htod32(bcnprd); + err = wldev_ioctl(dev, WLC_SET_BCNPRD, &bcnprd, sizeof(bcnprd), true); + if (err) { + WL_ERR(("WLC_SET_BCNPRD failed (%d)\n", err)); + goto CleanUp; + } + + /* Configure required join parameter */ + memset(&join_params, 0, sizeof(struct wl_join_params)); + + /* SSID */ + memset(&ssid, 0, sizeof(struct wlc_ssid)); + ssid.SSID_len = MIN(params->ssid_len, 32); + join_params.ssid.SSID_len = htod32(ssid.SSID_len); + memcpy(ssid.SSID, params->ssid, ssid.SSID_len); + memcpy(join_params.ssid.SSID, params->ssid, ssid.SSID_len); + join_params_size = sizeof(join_params.ssid); + + wl_update_prof(wl, dev, NULL, &ssid, WL_PROF_SSID); + + /* BSSID */ + if (params->bssid) { + memcpy(&join_params.params.bssid, params->bssid, ETHER_ADDR_LEN); + join_params_size = sizeof(join_params.ssid) + + WL_ASSOC_PARAMS_FIXED_SIZE; + + wl_update_prof(wl, dev, NULL, params->bssid, WL_PROF_BSSID); + } else { + memcpy(&join_params.params.bssid, ðer_bcast, ETHER_ADDR_LEN); + } + + /* Channel */ + if (params->channel) { + u32 target_channel; + + target_channel = ieee80211_frequency_to_channel( + params->channel->center_freq); + if (params->channel_fixed) { + /* adding chanspec */ + wl_ch_to_chanspec(target_channel, + &join_params, &join_params_size); + } + + /* set channel for starter */ + target_channel = htod32(target_channel); + err = wldev_ioctl(dev, WLC_SET_CHANNEL, + &target_channel, sizeof(target_channel), true); + if (err) { + WL_ERR(("WLC_SET_CHANNEL failed (%d)\n", err)); + goto CleanUp; + } + } + + wl->ibss_starter = false; + + err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true); + if (err) { + WL_ERR(("WLC_SET_SSID failed (%d)\n", err)); + goto CleanUp; } + +CleanUp: + + if (err) + wl_clr_drv_status(wl, CONNECTING, dev); + + WL_TRACE(("Exit\n")); return err; } static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { struct wl_priv *wl = wiphy_priv(wiphy); + scb_val_t scbval; + bool act = false; s32 err = 0; + u8 *curbssid; + + WL_TRACE(("Enter\n")); CHECK_SYS_UP(wl); - wl_link_down(wl); + act = *(bool *) wl_read_prof(wl, dev, WL_PROF_ACT); + curbssid = wl_read_prof(wl, dev, WL_PROF_BSSID); + if (act) { + /* + * Cancel ongoing scan to sync up with sme state machine of cfg80211. + */ + if (wl->scan_request) { + wl_notify_escan_complete(wl, dev, true, true); + } + wl_set_drv_status(wl, DISCONNECTING, dev); + scbval.val = DOT11_RC_DISASSOC_LEAVING; + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); + if (unlikely(err)) { + wl_clr_drv_status(wl, DISCONNECTING, dev); + WL_ERR(("error (%d)\n", err)); + return err; + } + } + WL_TRACE(("Exit\n")); return err; } @@ -1889,7 +2043,7 @@ wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme) WL_ERR(("set wpa_auth failed (%d)\n", err)); return err; } - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); sec->wpa_versions = sme->crypto.wpa_versions; return err; } @@ -1904,21 +2058,21 @@ wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme) s32 bssidx = wl_cfgp2p_find_idx(wl, dev); switch (sme->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: - val = 0; + val = WL_AUTH_OPEN_SYSTEM; WL_DBG(("open system\n")); break; case NL80211_AUTHTYPE_SHARED_KEY: - val = 1; + val = WL_AUTH_SHARED_KEY; WL_DBG(("shared key\n")); break; case NL80211_AUTHTYPE_AUTOMATIC: - val = 2; + val = WL_AUTH_OPEN_SHARED; WL_DBG(("automatic\n")); break; case NL80211_AUTHTYPE_NETWORK_EAP: WL_DBG(("network eap\n")); default: - val = 2; + val = WL_AUTH_OPEN_SHARED; WL_ERR(("invalid auth type (%d)\n", sme->auth_type)); break; } @@ -1928,7 +2082,7 @@ wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme) WL_ERR(("set auth failed (%d)\n", err)); return err; } - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); sec->auth_type = sme->auth_type; return err; } @@ -1989,7 +2143,11 @@ wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme) WL_DBG(("pval (%d) gval (%d)\n", pval, gval)); if (is_wps_conn(sme)) { - err = wldev_iovar_setint_bsscfg(dev, "wsec", 4, bssidx); + if (sme->privacy) + err = wldev_iovar_setint_bsscfg(dev, "wsec", 4, bssidx); + else + /* WPS-2.0 allowes no security */ + err = wldev_iovar_setint_bsscfg(dev, "wsec", 0, bssidx); } else { WL_DBG((" NO, is_wps_conn, Set pval | gval to WSEC")); err = wldev_iovar_setint_bsscfg(dev, "wsec", @@ -2000,7 +2158,7 @@ wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme) return err; } - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; sec->cipher_group = sme->crypto.cipher_group; @@ -2017,7 +2175,7 @@ wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme) s32 bssidx = wl_cfgp2p_find_idx(wl, dev); if (sme->crypto.n_akm_suites) { - err = wl_dev_intvar_get(dev, "wpa_auth", &val); + err = wldev_iovar_getint(dev, "wpa_auth", &val); if (unlikely(err)) { WL_ERR(("could not get wpa_auth (%d)\n", err)); return err; @@ -2057,7 +2215,7 @@ wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme) return err; } } - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); sec->wpa_auth = sme->crypto.akm_suites[0]; return err; @@ -2076,13 +2234,14 @@ wl_set_set_sharedkey(struct net_device *dev, WL_DBG(("key len (%d)\n", sme->key_len)); if (sme->key_len) { - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); WL_DBG(("wpa_versions 0x%x cipher_pairwise 0x%x\n", sec->wpa_versions, sec->cipher_pairwise)); if (!(sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) && (sec->cipher_pairwise & (WLAN_CIPHER_SUITE_WEP40 | - WLAN_CIPHER_SUITE_WEP104))) { + WLAN_CIPHER_SUITE_WEP104))) + { memset(&key, 0, sizeof(key)); key.len = (u32) sme->key_len; key.index = (u32) sme->key_idx; @@ -2110,14 +2269,14 @@ wl_set_set_sharedkey(struct net_device *dev, WL_DBG(("key \"%s\"\n", key.data)); swap_key_from_BE(&key); err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), - ioctlbuf, sizeof(ioctlbuf), bssidx); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (unlikely(err)) { WL_ERR(("WLC_SET_KEY error (%d)\n", err)); return err; } - if (sec->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) { + if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { WL_DBG(("set auth_type to shared key\n")); - val = 1; /* shared key */ + val = WL_AUTH_SHARED_KEY; /* shared key */ err = wldev_iovar_setint_bsscfg(dev, "auth", val, bssidx); if (unlikely(err)) { WL_ERR(("set auth failed (%d)\n", err)); @@ -2156,15 +2315,15 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, * Cancel ongoing scan to sync up with sme state machine of cfg80211. */ if (wl->scan_request) { - wl_cfg80211_scan_abort(wl, dev); + wl_notify_escan_complete(wl, dev, true, true); } /* Clean BSSID */ bzero(&bssid, sizeof(bssid)); - wl_update_prof(wl, NULL, (void *)&bssid, WL_PROF_BSSID); + wl_update_prof(wl, dev, NULL, (void *)&bssid, WL_PROF_BSSID); if (IS_P2P_SSID(sme->ssid) && (dev != wl_to_prmry_ndev(wl))) { /* we only allow to connect using virtual interface in case of P2P */ - if (p2p_on(wl) && is_wps_conn(sme)) { + if (p2p_is_on(wl) && is_wps_conn(sme)) { WL_DBG(("ASSOC1 p2p index : %d sme->ie_len %d\n", wl_cfgp2p_find_idx(wl, dev), sme->ie_len)); /* Have to apply WPS IE + P2P IE in assoc req frame */ @@ -2175,7 +2334,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie_len); wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); - } else if (p2p_on(wl) && (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) { + } else if (p2p_is_on(wl) && (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) { /* This is the connect req after WPS is done [credentials exchanged] * currently identified with WPA_VERSION_2 . * Update the previously set IEs with @@ -2185,6 +2344,8 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, WL_DBG(("ASSOC2 p2p index : %d sme->ie_len %d\n", wl_cfgp2p_find_idx(wl, dev), sme->ie_len)); wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), + VNDR_IE_PRBREQ_FLAG, sme->ie, sme->ie_len); + wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); } @@ -2204,10 +2365,10 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, wpaie_len = (wpa_ie != NULL) ? wpa_ie->length : wpa2_ie->len; wpaie_len += WPA_RSN_IE_TAG_FIXED_LEN; wldev_iovar_setbuf(dev, "wpaie", wpaie, wpaie_len, - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); } else { wldev_iovar_setbuf(dev, "wpaie", NULL, 0, - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); } /* find the WPSIE */ @@ -2276,7 +2437,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, ext_join_params = (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL); if (ext_join_params == NULL) { err = -ENOMEM; - wl_clr_drv_status(wl, CONNECTING); + wl_clr_drv_status(wl, CONNECTING, dev); goto exit; } ext_join_params->ssid.SSID_len = min(sizeof(ext_join_params->ssid.SSID), sme->ssid_len); @@ -2285,12 +2446,13 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, /* Set up join scan parameters */ ext_join_params->scan.scan_type = -1; ext_join_params->scan.nprobes = 2; - /* increate dwell time to receive probe response - * from target AP at a noisy air + /* increate dwell time to receive probe response or detect Beacon + * from target AP at a noisy air only during connect command */ - ext_join_params->scan.active_time = 150; - ext_join_params->scan.passive_time = 300; + ext_join_params->scan.active_time = WL_SCAN_ACTIVE_TIME*3; + ext_join_params->scan.passive_time = WL_SCAN_PASSIVE_TIME*3; ext_join_params->scan.home_time = -1; + if (sme->bssid) memcpy(&ext_join_params->assoc.bssid, sme->bssid, ETH_ALEN); else @@ -2315,12 +2477,12 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, WL_INFO(("ssid \"%s\", len (%d)\n", ext_join_params->ssid.SSID, ext_join_params->ssid.SSID_len)); } - wl_set_drv_status(wl, CONNECTING); - err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size, ioctlbuf, - sizeof(ioctlbuf), wl_cfgp2p_find_idx(wl, dev)); + wl_set_drv_status(wl, CONNECTING, dev); + err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, wl_cfgp2p_find_idx(wl, dev), &wl->ioctl_buf_sync); kfree(ext_join_params); if (err) { - wl_clr_drv_status(wl, CONNECTING); + wl_clr_drv_status(wl, CONNECTING, dev); if (err == BCME_UNSUPPORTED) { WL_DBG(("join iovar is not supported\n")); goto set_ssid; @@ -2336,7 +2498,7 @@ set_ssid: join_params.ssid.SSID_len = min(sizeof(join_params.ssid.SSID), sme->ssid_len); memcpy(&join_params.ssid.SSID, sme->ssid, join_params.ssid.SSID_len); join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len); - wl_update_prof(wl, NULL, &join_params.ssid, WL_PROF_SSID); + wl_update_prof(wl, dev, NULL, &join_params.ssid, WL_PROF_SSID); if (sme->bssid) memcpy(&join_params.params.bssid, sme->bssid, ETH_ALEN); else @@ -2349,11 +2511,11 @@ set_ssid: WL_INFO(("ssid \"%s\", len (%d)\n", join_params.ssid.SSID, join_params.ssid.SSID_len)); } - wl_set_drv_status(wl, CONNECTING); + wl_set_drv_status(wl, CONNECTING, dev); err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true); if (err) { WL_ERR(("error (%d)\n", err)); - wl_clr_drv_status(wl, CONNECTING); + wl_clr_drv_status(wl, CONNECTING, dev); } exit: return err; @@ -2370,23 +2532,23 @@ wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u8 *curbssid; WL_ERR(("Reason %d\n", reason_code)); CHECK_SYS_UP(wl); - act = *(bool *) wl_read_prof(wl, WL_PROF_ACT); - curbssid = wl_read_prof(wl, WL_PROF_BSSID); - if (likely(act)) { + act = *(bool *) wl_read_prof(wl, dev, WL_PROF_ACT); + curbssid = wl_read_prof(wl, dev, WL_PROF_BSSID); + if (act) { /* * Cancel ongoing scan to sync up with sme state machine of cfg80211. */ if (wl->scan_request) { - wl_cfg80211_scan_abort(wl, dev); + wl_notify_escan_complete(wl, dev, true, true); } - wl_set_drv_status(wl, DISCONNECTING); + wl_set_drv_status(wl, DISCONNECTING, dev); scbval.val = reason_code; memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); scbval.val = htod32(scbval.val); err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true); if (unlikely(err)) { - wl_clr_drv_status(wl, DISCONNECTING); + wl_clr_drv_status(wl, DISCONNECTING, dev); WL_ERR(("error (%d)\n", err)); return err; } @@ -2436,7 +2598,7 @@ wl_cfg80211_set_tx_power(struct wiphy *wiphy, txpwrmw = 0xffff; else txpwrmw = (u16) dbm; - err = wl_dev_intvar_set(ndev, "qtxpower", + err = wldev_iovar_setint(ndev, "qtxpower", (s32) (bcm_mw_to_qdbm(txpwrmw))); if (unlikely(err)) { WL_ERR(("qtxpower error (%d)\n", err)); @@ -2456,7 +2618,7 @@ static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm) s32 err = 0; CHECK_SYS_UP(wl); - err = wl_dev_intvar_get(ndev, "qtxpower", &txpwrdbm); + err = wldev_iovar_getint(ndev, "qtxpower", &txpwrdbm); if (unlikely(err)) { WL_ERR(("error (%d)\n", err)); return err; @@ -2505,7 +2667,7 @@ wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, struct wl_wsec_key key; s32 err = 0; s32 bssidx = wl_cfgp2p_find_idx(wl, dev); - s32 mode = get_mode_by_netdev(wl, dev); + s32 mode = wl_get_mode_by_netdev(wl, dev); memset(&key, 0, sizeof(key)); key.index = (u32) key_idx; @@ -2517,8 +2679,8 @@ wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, if (key.len == 0) { /* key delete */ swap_key_from_BE(&key); - wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ioctlbuf, - sizeof(ioctlbuf), bssidx); + wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (unlikely(err)) { WL_ERR(("key delete error (%d)\n", err)); return err; @@ -2576,11 +2738,8 @@ wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, return -EINVAL; } swap_key_from_BE(&key); -#ifdef CONFIG_WIRELESS_EXT - dhd_wait_pend8021x(dev); -#endif - wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ioctlbuf, - sizeof(ioctlbuf), bssidx); + wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (unlikely(err)) { WL_ERR(("WLC_SET_KEY error (%d)\n", err)); return err; @@ -2601,7 +2760,7 @@ wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, u8 keybuf[8]; s32 bssidx = 0; struct wl_priv *wl = wiphy_priv(wiphy); - s32 mode = get_mode_by_netdev(wl, dev); + s32 mode = wl_get_mode_by_netdev(wl, dev); WL_DBG(("key index (%d)\n", key_idx)); CHECK_SYS_UP(wl); @@ -2662,8 +2821,8 @@ wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, /* Set the new key/index */ swap_key_from_BE(&key); - err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ioctlbuf, - sizeof(ioctlbuf), bssidx); + err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), wl->ioctl_buf, + WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (unlikely(err)) { WL_ERR(("WLC_SET_KEY error (%d)\n", err)); return err; @@ -2699,15 +2858,15 @@ wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, CHECK_SYS_UP(wl); memset(&key, 0, sizeof(key)); - key.index = (u32) key_idx; key.flags = WL_PRIMARY_KEY; key.algo = CRYPTO_ALGO_OFF; + key.index = (u32) key_idx; WL_DBG(("key index (%d)\n", key_idx)); /* Set the new key/index */ swap_key_from_BE(&key); - wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), ioctlbuf, - sizeof(ioctlbuf), bssidx); + wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), wl->ioctl_buf, + WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (unlikely(err)) { if (err == -EINVAL) { if (key.index >= DOT11_MAX_DEFAULT_KEYS) { @@ -2751,7 +2910,7 @@ wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, } switch (wsec & ~SES_OW_ENABLED) { case WEP_ENABLED: - sec = wl_read_prof(wl, WL_PROF_SEC); + sec = wl_read_prof(wl, dev, WL_PROF_SEC); if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { params.cipher = WLAN_CIPHER_SUITE_WEP40; WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); @@ -2801,15 +2960,15 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); CHECK_SYS_UP(wl); - if (get_mode_by_netdev(wl, dev) == WL_MODE_AP) { + if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP) { err = wldev_iovar_getbuf(dev, "sta_info", (struct ether_addr *)mac, - ETHER_ADDR_LEN, ioctlbuf, sizeof(ioctlbuf)); + ETHER_ADDR_LEN, wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (err < 0) { WL_ERR(("GET STA INFO failed, %d\n", err)); return err; } sinfo->filled = STATION_INFO_INACTIVE_TIME; - sta = (sta_info_t *)ioctlbuf; + sta = (sta_info_t *)wl->ioctl_buf; sta->len = dtoh16(sta->len); sta->cap = dtoh16(sta->cap); sta->flags = dtoh32(sta->flags); @@ -2825,50 +2984,48 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, bcm_ether_ntoa((const struct ether_addr *)mac, eabuf), sinfo->inactive_time, sta->idle * 1000)); #endif - } else if (get_mode_by_netdev(wl, dev) == WL_MODE_BSS) { - u8 *curmacp = wl_read_prof(wl, WL_PROF_BSSID); - - if (!wl_get_drv_status(wl, CONNECTED) || - (dhd_is_associated(dhd, NULL) == FALSE)) { - WL_ERR(("NOT assoc\n")); - err = -ENODEV; - goto get_station_err; - } - if (memcmp(mac, curmacp, ETHER_ADDR_LEN)) { - WL_ERR(("Wrong Mac address: "MACSTR" != "MACSTR"\n", - MAC2STR(mac), MAC2STR(curmacp))); - } - - /* Report the current tx rate */ - err = wldev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate), false); - if (err) { - WL_ERR(("Could not get rate (%d)\n", err)); - } else { - rate = dtoh32(rate); - sinfo->filled |= STATION_INFO_TX_BITRATE; - sinfo->txrate.legacy = rate * 5; - WL_DBG(("Rate %d Mbps\n", (rate / 2))); - } + } else if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_BSS) { + u8 *curmacp = wl_read_prof(wl, dev, WL_PROF_BSSID); + err = -ENODEV; + if (!wl_get_drv_status(wl, CONNECTED, dev) || + (dhd_is_associated(dhd, NULL, &err) == FALSE)) { + WL_ERR(("NOT assoc: %d\n", err)); + goto get_station_err; + } + if (memcmp(mac, curmacp, ETHER_ADDR_LEN)) { + WL_ERR(("Wrong Mac address: "MACSTR" != "MACSTR"\n", + MAC2STR(mac), MAC2STR(curmacp))); + } - memset(&scb_val, 0, sizeof(scb_val)); - scb_val.val = 0; - err = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, - sizeof(scb_val_t), false); - if (err) { - WL_ERR(("Could not get rssi (%d)\n", err)); - goto get_station_err; - } + /* Report the current tx rate */ + err = wldev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate), false); + if (err) { + WL_ERR(("Could not get rate (%d)\n", err)); + } else { + rate = dtoh32(rate); + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.legacy = rate * 5; + WL_DBG(("Rate %d Mbps\n", (rate / 2))); + } - rssi = dtoh32(scb_val.val); - sinfo->filled |= STATION_INFO_SIGNAL; - sinfo->signal = rssi; - WL_DBG(("RSSI %d dBm\n", rssi)); + memset(&scb_val, 0, sizeof(scb_val)); + scb_val.val = 0; + err = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, + sizeof(scb_val_t), false); + if (err) { + WL_ERR(("Could not get rssi (%d)\n", err)); + goto get_station_err; + } + rssi = dtoh32(scb_val.val); + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = rssi; + WL_DBG(("RSSI %d dBm\n", rssi)); get_station_err: - if (err) { + if (err && (err != -ETIMEDOUT) && (err != -EIO)) { /* Disconnect due to zero BSSID or error to get RSSI */ - WL_ERR(("force cfg80211_disconnected\n")); - wl_clr_drv_status(wl, CONNECTED); + WL_ERR(("force cfg80211_disconnected: %d\n", err)); + wl_clr_drv_status(wl, CONNECTED, dev); cfg80211_disconnected(dev, 0, NULL, 0, GFP_KERNEL); wl_link_down(wl); } @@ -2886,14 +3043,14 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, struct wl_priv *wl = wiphy_priv(wiphy); CHECK_SYS_UP(wl); - pm = enabled ? PM_FAST : PM_OFF; - /* Do not enable the power save after assoc if it is p2p interface */ - if (wl->p2p && wl->p2p->vif_created) { - WL_DBG(("Do not enable the power save for p2p interfaces even after assoc\n")); - pm = PM_OFF; + + WL_DBG(("Enter : power save %s\n", (enabled ? "enable" : "disable"))); + if (wl->p2p_net == dev) { + return err; } + + pm = enabled ? PM_FAST : PM_OFF; pm = htod32(pm); - WL_DBG(("power save %s\n", (pm ? "enabled" : "disabled"))); err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), true); if (unlikely(err)) { if (err == -ENODEV) @@ -2902,6 +3059,7 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, WL_ERR(("error (%d)\n", err)); return err; } + WL_DBG(("power save %s\n", (pm ? "enabled" : "disabled"))); return err; } @@ -2935,11 +3093,11 @@ static __used u32 wl_find_msb(u16 bit16) static s32 wl_cfg80211_resume(struct wiphy *wiphy) { struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *ndev = wl_to_prmry_ndev(wl); s32 err = 0; - if (unlikely(!wl_get_drv_status(wl, READY))) { - WL_INFO(("device is not ready : status (%d)\n", - (int)wl->status)); + if (unlikely(!wl_get_drv_status(wl, READY, ndev))) { + WL_INFO(("device is not ready\n")); return 0; } @@ -2956,33 +3114,38 @@ static s32 wl_cfg80211_suspend(struct wiphy *wiphy) { #ifdef DHD_CLEAR_ON_SUSPEND struct wl_priv *wl = wiphy_priv(wiphy); + struct net_info *iter, *next; struct net_device *ndev = wl_to_prmry_ndev(wl); unsigned long flags; - if (unlikely(!wl_get_drv_status(wl, READY))) { + if (unlikely(!wl_get_drv_status(wl, READY, ndev))) { WL_INFO(("device is not ready : status (%d)\n", (int)wl->status)); return 0; } - - wl_set_drv_status(wl, SCAN_ABORTING); + for_each_ndev(wl, iter, next) + wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); wl_term_iscan(wl); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); if (wl->scan_request) { cfg80211_scan_done(wl->scan_request, true); wl->scan_request = NULL; } - wl_clr_drv_status(wl, SCANNING); - wl_clr_drv_status(wl, SCAN_ABORTING); - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); - if (wl_get_drv_status(wl, CONNECTING)) { - wl_bss_connect_done(wl, ndev, NULL, NULL, false); + for_each_ndev(wl, iter, next) { + wl_clr_drv_status(wl, SCANNING, iter->ndev); + wl_clr_drv_status(wl, SCAN_ABORTING, iter->ndev); } -#endif + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + for_each_ndev(wl, iter, next) { + if (wl_get_drv_status(wl, CONNECTING, iter->ndev)) { + wl_bss_connect_done(wl, iter->ndev, NULL, NULL, false); + } + } +#endif /* DHD_CLEAR_ON_SUSPEND */ return 0; } -static __used s32 +static s32 wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, s32 err) { @@ -2990,10 +3153,13 @@ wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, struct wl_priv *wl = wlcfg_drv_priv; struct net_device *primary_dev = wl_to_prmry_ndev(wl); - /* Firmware is supporting pmk list only for STA interface i.e. primary interface - * Refer code wlc_bsscfg.c->wlc_bsscfg_sta_init - * Do we really need to support PMK cache in P2P in firmware? - */ + if (!pmk_list) { + printk("pmk_list is NULL\n"); + return -EINVAL; + } + /* pmk list is supported only for STA interface i.e. primary interface + * Refer code wlc_bsscfg.c->wlc_bsscfg_sta_init + */ if (primary_dev != dev) { WL_INFO(("Not supporting Flushing pmklist on virtual" " interfaces than primary interface\n")); @@ -3009,8 +3175,8 @@ wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, } } if (likely(!err)) { - err = wl_dev_bufvar_set(dev, "pmkid_info", (char *)pmk_list, - sizeof(*pmk_list)); + err = wldev_iovar_setbuf(dev, "pmkid_info", (char *)pmk_list, + sizeof(*pmk_list), wl->ioctl_buf, WLC_IOCTL_MAXLEN, NULL); } return err; @@ -3111,7 +3277,7 @@ wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev) } -wl_scan_params_t * +static wl_scan_params_t * wl_cfg80211_scan_alloc_params(int channel, int nprobes, int *out_params_size) { wl_scan_params_t *params; @@ -3143,47 +3309,12 @@ wl_cfg80211_scan_alloc_params(int channel, int nprobes, int *out_params_size) /* Our scan params have 1 channel and 0 ssids */ params->channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) | - (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); + (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); *out_params_size = params_size; /* rtn size to the caller */ return params; } -s32 -wl_cfg80211_scan_abort(struct wl_priv *wl, struct net_device *ndev) -{ - wl_scan_params_t *params = NULL; - s32 params_size = 0; - s32 err = BCME_OK; - unsigned long flags; - - WL_DBG(("Enter\n")); - - /* Our scan params only need space for 1 channel and 0 ssids */ - params = wl_cfg80211_scan_alloc_params(-1, 0, ¶ms_size); - if (params == NULL) { - WL_ERR(("scan params allocation failed \n")); - err = -ENOMEM; - } else { - /* Do a scan abort to stop the driver's scan engine */ - err = wldev_ioctl(ndev, WLC_SCAN, params, params_size, true); - if (err < 0) { - WL_ERR(("scan abort failed \n")); - } - } - del_timer_sync(&wl->scan_timeout); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); - if (wl->scan_request) { - cfg80211_scan_done(wl->scan_request, true); - wl->scan_request = NULL; - } - wl_clr_drv_status(wl, SCANNING); - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); - if (params) - kfree(params); - return err; -} - static s32 wl_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel * channel, @@ -3191,36 +3322,48 @@ wl_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, unsigned int duration, u64 *cookie) { s32 target_channel; + u32 id; + struct ether_addr primary_mac; + struct net_device *ndev = NULL; s32 err = BCME_OK; struct wl_priv *wl = wiphy_priv(wiphy); - dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); WL_DBG(("Enter, netdev_ifidx: %d \n", dev->ifindex)); - if (likely(wl_get_drv_status(wl, SCANNING))) { - wl_cfg80211_scan_abort(wl, dev); + + if (wl->p2p_net == dev) { + ndev = wl_to_prmry_ndev(wl); + } else { + ndev = dev; } + if (wl_get_drv_status(wl, SCANNING, ndev)) { + wl_notify_escan_complete(wl, ndev, true, true); + } target_channel = ieee80211_frequency_to_channel(channel->center_freq); memcpy(&wl->remain_on_chan, channel, sizeof(struct ieee80211_channel)); wl->remain_on_chan_type = channel_type; - wl->cache_cookie = *cookie; + id = ++wl->last_roc_id; + if (id == 0) + id = ++wl->last_roc_id; + *cookie = id; cfg80211_ready_on_channel(dev, *cookie, channel, channel_type, duration, GFP_KERNEL); - if (!p2p_on(wl)) { - wl_cfgp2p_generate_bss_mac(&dhd->mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); + if (wl->p2p && !wl->p2p->on) { + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); /* In case of p2p_listen command, supplicant send remain_on_channel * without turning on P2P */ p2p_on(wl) = true; - err = wl_cfgp2p_enable_discovery(wl, dev, NULL, 0); + err = wl_cfgp2p_enable_discovery(wl, ndev, NULL, 0); if (unlikely(err)) { goto exit; } } - if (p2p_on(wl)) + if (p2p_is_on(wl)) wl_cfgp2p_discover_listen(wl, target_channel, duration); @@ -3238,39 +3381,141 @@ wl_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, struct net_device *dev } static s32 -wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, +wl_cfg80211_send_pending_tx_act_frm(struct wl_priv *wl) +{ + wl_af_params_t *tx_act_frm; + struct net_device *dev = wl->afx_hdl->dev; + if (!p2p_is_on(wl)) + return -1; + + if (dev == wl->p2p_net) { + dev = wl_to_prmry_ndev(wl); + } + + tx_act_frm = wl->afx_hdl->pending_tx_act_frm; + WL_DBG(("Sending the action frame\n")); + wl->afx_hdl->pending_tx_act_frm = NULL; + if (tx_act_frm != NULL) { + /* Suspend P2P discovery's search-listen to prevent it from + * starting a scan or changing the channel. + */ + wl_clr_drv_status(wl, SENDING_ACT_FRM, wl->afx_hdl->dev); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + wl_notify_escan_complete(wl, dev, true, true); + wl_cfgp2p_discover_enable_search(wl, false); + tx_act_frm->channel = wl->afx_hdl->peer_chan; + wl->afx_hdl->ack_recv = (wl_cfgp2p_tx_action_frame(wl, dev, + tx_act_frm, wl->afx_hdl->bssidx)) ? false : true; + } + return 0; +} +static void +wl_cfg80211_afx_handler(struct work_struct *work) +{ + + struct afx_hdl *afx_instance; + struct wl_priv *wl = wlcfg_drv_priv; + afx_instance = container_of(work, struct afx_hdl, work); + if (afx_instance != NULL) { + wl_cfgp2p_act_frm_search(wl, wl->afx_hdl->dev, + wl->afx_hdl->bssidx, 0); + } +} + +static bool +wl_cfg80211_send_at_common_channel(struct wl_priv *wl, + struct net_device *dev, + wl_af_params_t *af_params) +{ + WL_DBG((" enter ) \n")); + /* initialize afx_hdl */ + wl->afx_hdl->pending_tx_act_frm = af_params; + wl->afx_hdl->bssidx = wl_cfgp2p_find_idx(wl, dev); + wl->afx_hdl->dev = dev; + wl->afx_hdl->retry = 0; + wl->afx_hdl->peer_chan = WL_INVALID; + wl->afx_hdl->ack_recv = false; + memcpy(wl->afx_hdl->pending_tx_dst_addr.octet, + af_params->action_frame.da.octet, + sizeof(wl->afx_hdl->pending_tx_dst_addr.octet)); + /* Loop to wait until we have sent the pending tx action frame or the + * pending action frame tx is cancelled. + */ + while ((wl->afx_hdl->retry < WL_CHANNEL_SYNC_RETRY) && + (wl->afx_hdl->peer_chan == WL_INVALID)) { + wl_set_drv_status(wl, SENDING_ACT_FRM, dev); + wl_set_drv_status(wl, SCANNING, dev); + WL_DBG(("Scheduling the action frame for sending.. retry %d\n", + wl->afx_hdl->retry)); + /* Do find_peer_for_action */ + schedule_work(&wl->afx_hdl->work); + wait_for_completion(&wl->act_frm_scan); + wl->afx_hdl->retry++; + } + if (wl->afx_hdl->peer_chan != WL_INVALID) + wl_cfg80211_send_pending_tx_act_frm(wl); + else { + WL_ERR(("Couldn't find the peer after %d retries\n", + wl->afx_hdl->retry)); + } + wl->afx_hdl->dev = NULL; + wl->afx_hdl->bssidx = WL_INVALID; + wl_clr_drv_status(wl, SENDING_ACT_FRM, dev); + if (wl->afx_hdl->ack_recv) + return true; /* ACK */ + else + return false; /* NO ACK */ +} + +static s32 +wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, struct ieee80211_channel *channel, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8* buf, size_t len, u64 *cookie) + const u8* buf, size_t len, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + bool no_cck, +#endif + u64 *cookie) { wl_action_frame_t *action_frame; wl_af_params_t *af_params; wifi_p2p_ie_t *p2p_ie; wpa_ie_fixed_t *wps_ie; + scb_val_t scb_val; const struct ieee80211_mgmt *mgmt; struct wl_priv *wl = wiphy_priv(wiphy); - dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + struct net_device *dev = NULL; s32 err = BCME_OK; s32 bssidx = 0; u32 p2pie_len = 0; u32 wpsie_len = 0; - u16 fc; + u32 id; + u32 retry = 0; bool ack = false; - wifi_p2p_pub_act_frame_t *act_frm; + wifi_p2p_pub_act_frame_t *act_frm = NULL; + wifi_p2p_action_frame_t *p2p_act_frm = NULL; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm = NULL; + s8 eabuf[ETHER_ADDR_STR_LEN]; + int retry_cnt = 0; + WL_DBG(("Enter \n")); + + if (ndev == wl->p2p_net) { + dev = wl_to_prmry_ndev(wl); + } else { + /* If TX req is for any valid ifidx. Use as is */ + dev = ndev; + } + /* find bssidx based on ndev */ bssidx = wl_cfgp2p_find_idx(wl, dev); - /* cookie generation */ - *cookie = (unsigned long) buf; - if (bssidx == -1) { WL_ERR(("Can not find the bssidx for dev( %p )\n", dev)); return -ENODEV; } - if (wl->p2p_supported && p2p_on(wl)) { - wl_cfgp2p_generate_bss_mac(&dhd->mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); + if (p2p_is_on(wl)) { /* Suspend P2P discovery search-listen to prevent it from changing the * channel. */ @@ -3279,31 +3524,27 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, return -EFAULT; } } - - mgmt = (const struct ieee80211_mgmt *) buf; - fc = mgmt->frame_control; - if (fc != IEEE80211_STYPE_ACTION) { - if (fc == IEEE80211_STYPE_PROBE_RESP) { + *cookie = 0; + id = wl->send_action_id++; + if (id == 0) + id = wl->send_action_id++; + *cookie = id; + mgmt = (const struct ieee80211_mgmt *)buf; + if (ieee80211_is_mgmt(mgmt->frame_control)) { + if (ieee80211_is_probe_resp(mgmt->frame_control)) { s32 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; s32 ie_len = len - ie_offset; if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)(buf + ie_offset), ie_len)) != NULL) { /* Total length of P2P Information Element */ p2pie_len = p2p_ie->len + sizeof(p2p_ie->len) + sizeof(p2p_ie->id); - /* Have to change p2p device address in dev_info attribute - * because Supplicant use primary eth0 address - */ - #ifdef ENABLE_DRIVER_CHANGE_IFADDR /* We are now doing this in supplicant */ - wl_cfg80211_change_ifaddr((u8 *)p2p_ie, - &wl->p2p_dev_addr, P2P_SEID_DEV_INFO); - #endif } if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)(buf + ie_offset), ie_len)) != NULL) { /* Order of Vendor IE is 1) WPS IE + * 2) P2P IE created by supplicant * So, it is ok to find start address of WPS IE - * to save IEs to firmware + * to save IEs */ wpsie_len = wps_ie->length + sizeof(wps_ie->length) + sizeof(wps_ie->tag); @@ -3311,18 +3552,42 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, VNDR_IE_PRBRSP_FLAG, (u8 *)wps_ie, wpsie_len + p2pie_len); } + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); + goto exit; + } else if (ieee80211_is_disassoc(mgmt->frame_control) || + ieee80211_is_deauth(mgmt->frame_control)) { + memcpy(scb_val.ea.octet, mgmt->da, ETH_ALEN); + scb_val.val = mgmt->u.disassoc.reason_code; + wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, + sizeof(scb_val_t), true); + WL_DBG(("Disconnect STA : %s scb_val.val %d\n", + bcm_ether_ntoa((const struct ether_addr *)mgmt->da, eabuf), + scb_val.val)); + /* Wait for the deauth event to come, supplicant will do the + * delete iface immediately and we will have problem in sending + * deauth frame if we delete the bss in firmware + */ + wl_delay(400); + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); + goto exit; + + } else if (ieee80211_is_action(mgmt->frame_control)) { + /* Abort the dwell time of any previous off-channel + * action frame that may be still in effect. Sending + * off-channel action frames relies on the driver's + * scan engine. If a previous off-channel action frame + * tx is still in progress (including the dwell time), + * then this new action frame will not be sent out. + */ + wl_notify_escan_complete(wl, dev, true, true); + } - cfg80211_mgmt_tx_status(dev, *cookie, buf, len, true, GFP_KERNEL); - goto exit; + } else { - /* Abort the dwell time of any previous off-channel action frame that may - * be still in effect. Sending off-channel action frames relies on the - * driver's scan engine. If a previous off-channel action frame tx is - * still in progress (including the dwell time), then this new action - * frame will not be sent out. - */ - wl_cfg80211_scan_abort(wl, dev); + WL_ERR(("Driver only allows MGMT packet type\n")); + goto exit; } + af_params = (wl_af_params_t *) kzalloc(WL_WIFI_AF_PARAMS_SIZE, GFP_KERNEL); if (af_params == NULL) @@ -3334,7 +3599,7 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, action_frame = &af_params->action_frame; /* Add the packet Id */ - action_frame->packetId = (u32) action_frame; + action_frame->packetId = *cookie; WL_DBG(("action frame %d\n", action_frame->packetId)); /* Add BSSID */ memcpy(&action_frame->da, &mgmt->da[0], ETHER_ADDR_LEN); @@ -3348,6 +3613,15 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, af_params->channel = ieee80211_frequency_to_channel(channel->center_freq); + if (channel->band == IEEE80211_BAND_5GHZ) { + WL_DBG(("5GHz channel %d", af_params->channel)); + err = wldev_ioctl(dev, WLC_SET_CHANNEL, + &af_params->channel, sizeof(af_params->channel), true); + if (err < 0) { + WL_ERR(("WLC_SET_CHANNEL error %d\n", err)); + } + } + /* Add the dwell time * Dwell time to stay off-channel to wait for a response action frame * after transmitting an GO Negotiation action frame @@ -3355,28 +3629,84 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, af_params->dwell_time = WL_DWELL_TIME; memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], action_frame->len); - - act_frm = (wifi_p2p_pub_act_frame_t *) (action_frame->data); - WL_DBG(("action_frame->len: %d chan %d category %d subtype %d\n", - action_frame->len, af_params->channel, - act_frm->category, act_frm->subtype)); - if (wl->p2p->vif_created) { - /* - * To make sure to send successfully action frame, we have to turn off mpc - */ - if ((act_frm->subtype == P2P_PAF_GON_REQ)|| - (act_frm->subtype == P2P_PAF_GON_RSP)) { + if (wl_cfgp2p_is_pub_action(action_frame->data, action_frame->len)) { + act_frm = (wifi_p2p_pub_act_frame_t *) (action_frame->data); + WL_DBG(("P2P PUB action_frame->len: %d chan %d category %d subtype %d\n", + action_frame->len, af_params->channel, + act_frm->category, act_frm->subtype)); + if (act_frm && ((act_frm->subtype == P2P_PAF_GON_REQ) || + (act_frm->subtype == P2P_PAF_GON_RSP) || + (act_frm->subtype == P2P_PAF_GON_CONF) || + (act_frm->subtype == P2P_PAF_PROVDIS_REQ))) { wldev_iovar_setint(dev, "mpc", 0); + } + + if (act_frm->subtype == P2P_PAF_GON_REQ) { + WL_DBG(("P2P: GO_NEG_PHASE status set \n")); + wl_set_p2p_status(wl, GO_NEG_PHASE); } else if (act_frm->subtype == P2P_PAF_GON_CONF) { - wldev_iovar_setint(dev, "mpc", 1); - } else if (act_frm->subtype == P2P_PAF_DEVDIS_REQ) { + /* If we reached till GO Neg confirmation + * reset the filter + */ + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); + } + + if (act_frm->subtype == P2P_PAF_GON_RSP) + retry_cnt = 1; + else retry_cnt = WL_ACT_FRAME_RETRY; + + if (act_frm && act_frm->subtype == P2P_PAF_DEVDIS_REQ) { af_params->dwell_time = WL_LONG_DWELL_TIME; + } else if (act_frm && + (act_frm->subtype == P2P_PAF_PROVDIS_REQ || + act_frm->subtype == P2P_PAF_PROVDIS_RSP || + act_frm->subtype == P2P_PAF_GON_RSP)) { + af_params->dwell_time = WL_MED_DWELL_TIME; } - } + } else if (wl_cfgp2p_is_p2p_action(action_frame->data, action_frame->len)) { + p2p_act_frm = (wifi_p2p_action_frame_t *) (action_frame->data); + WL_DBG(("P2P action_frame->len: %d chan %d category %d subtype %d\n", + action_frame->len, af_params->channel, + p2p_act_frm->category, p2p_act_frm->subtype)); + } else if (wl_cfgp2p_is_gas_action(action_frame->data, action_frame->len)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *) (action_frame->data); + WL_DBG(("Service Discovery action_frame->len: %d chan %d category %d action %d\n", + action_frame->len, af_params->channel, + sd_act_frm->category, sd_act_frm->action)); + + } + wl_cfgp2p_print_actframe(true, action_frame->data, action_frame->len); + /* + * To make sure to send successfully action frame, we have to turn off mpc + */ + if (IS_P2P_SOCIAL(af_params->channel) && + (IS_P2P_PUB_ACT_REQ(act_frm, &act_frm->elts[0], action_frame->len) || + IS_GAS_REQ(sd_act_frm, action_frame->len)) && + wl_to_p2p_bss_saved_ie(wl, P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie_len) { + /* channel offload require P2P IE for Probe request + * otherwise, we will use wl_cfgp2p_tx_action_frame directly. + * channel offload for action request frame + */ - ack = (wl_cfgp2p_tx_action_frame(wl, dev, af_params, bssidx)) ? false : true; - cfg80211_mgmt_tx_status(dev, *cookie, buf, len, ack, GFP_KERNEL); + /* channel offload for action request frame */ + ack = wl_cfg80211_send_at_common_channel(wl, dev, af_params); + } else { + ack = (wl_cfgp2p_tx_action_frame(wl, dev, af_params, bssidx)) ? false : true; + if (!ack) { + for (retry = 1; retry < retry_cnt; retry++) { + ack = (wl_cfgp2p_tx_action_frame(wl, dev, + af_params, bssidx)) ? false : true; + if (ack) + break; + } + } + } + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, ack, GFP_KERNEL); + if (act_frm && act_frm->subtype == P2P_PAF_GON_CONF) { + wldev_iovar_setint(dev, "mpc", 1); + } kfree(af_params); exit: return err; @@ -3430,8 +3760,21 @@ wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, { s32 channel; s32 err = BCME_OK; + struct wl_priv *wl = wiphy_priv(wiphy); + if (wl->p2p_net == dev) { + dev = wl_to_prmry_ndev(wl); + } channel = ieee80211_frequency_to_channel(chan->center_freq); + + if (wl_get_drv_status(wl, AP_CREATING, dev)) { + WL_TRACE(("<0> %s: as!!! in AP creating mode, save chan num:%d\n", + __FUNCTION__, channel)); + wl->hostapd_chan = channel; + if (channel == 14) + return err; /* hostapd requested ch auto-select, will be done later */ + } + WL_DBG(("netdev_ifidx(%d), chan_type(%d) target channel(%d) \n", dev->ifindex, channel_type, channel)); err = wldev_ioctl(dev, WLC_SET_CHANNEL, &channel, sizeof(channel), true); @@ -3446,8 +3789,7 @@ wl_validate_wpa2ie(struct net_device *dev, bcm_tlv_t *wpa2ie, s32 bssidx) { s32 len = 0; s32 err = BCME_OK; - u16 auth = 0; /* d11 open authentication */ - u16 count; + u16 auth = WL_AUTH_OPEN_SYSTEM; /* d11 open authentication */ u32 wsec; u32 pval = 0; u32 gval = 0; @@ -3485,7 +3827,7 @@ wl_validate_wpa2ie(struct net_device *dev, bcm_tlv_t *wpa2ie, s32 bssidx) len -= WPA_SUITE_LEN; /* check the unicast cipher */ ucast = (wpa_suite_ucast_t *)&mcast[1]; - count = ltoh16_ua(&ucast->count); + ltoh16_ua(&ucast->count); tmp = ucast->list[0].oui; switch (tmp[DOT11_OUI_LEN]) { case WPA_CIPHER_NONE: @@ -3508,7 +3850,7 @@ wl_validate_wpa2ie(struct net_device *dev, bcm_tlv_t *wpa2ie, s32 bssidx) wsec = (pval | gval | SES_OW_ENABLED); /* check the AKM */ mgmt = (wpa_suite_auth_key_mgmt_t *)&ucast->list[1]; - count = ltoh16_ua(&mgmt->count); + ltoh16_ua(&mgmt->count); tmp = (u8 *)&mgmt->list[0]; switch (tmp[DOT11_OUI_LEN]) { case RSN_AKM_NONE: @@ -3551,7 +3893,7 @@ wl_validate_wpaie(struct net_device *dev, wpa_ie_fixed_t *wpaie, s32 bssidx) wpa_suite_mcast_t *mcast; wpa_suite_ucast_t *ucast; wpa_suite_auth_key_mgmt_t *mgmt; - u16 auth = 0; /* d11 open authentication */ + u16 auth = WL_AUTH_OPEN_SYSTEM; /* d11 open authentication */ u16 count; s32 err = BCME_OK; s32 len = 0; @@ -3711,13 +4053,19 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, u16 p2pie_len = 0; u8 beacon_ie[IE_MAX_LEN]; s32 ie_offset = 0; - s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + s32 bssidx = 0; s32 infra = 1; s32 join_params_size = 0; s32 ap = 0; WL_DBG(("interval (%d) dtim_period (%d) head_len (%d) tail_len (%d)\n", info->interval, info->dtim_period, info->head_len, info->tail_len)); - if (wl->p2p_supported && p2p_on(wl) && + + if (wl->p2p_net == dev) { + dev = wl_to_prmry_ndev(wl); + } + + bssidx = wl_cfgp2p_find_idx(wl, dev); + if (p2p_is_on(wl) && (bssidx == wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION))) { memset(beacon_ie, 0, sizeof(beacon_ie)); @@ -3755,19 +4103,13 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)info->tail, info->tail_len)) != NULL) { /* Total length of P2P Information Element */ p2pie_len = p2p_ie->len + sizeof(p2p_ie->len) + sizeof(p2p_ie->id); - #ifdef ENABLE_DRIVER_CHANGE_IFADDR /* We are now doing this in supplicant */ - /* Have to change device address in dev_id attribute because Supplicant - * use primary eth0 address - */ - wl_cfg80211_change_ifaddr((u8 *)p2p_ie, &wl->p2p_dev_addr, P2P_SEID_DEV_ID); - #endif memcpy(&beacon_ie[wpsie_len], p2p_ie, p2pie_len); } else { WL_ERR(("No P2PIE in beacon \n")); } /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, beacon_ie, wpsie_len + p2pie_len); @@ -3790,17 +4132,18 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, goto exit; } err = wldev_iovar_setbuf_bsscfg(dev, "ssid", &wl->p2p->ssid, - sizeof(wl->p2p->ssid), ioctlbuf, sizeof(ioctlbuf), bssidx); + sizeof(wl->p2p->ssid), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + bssidx, &wl->ioctl_buf_sync); if (err < 0) { WL_ERR(("GO SSID setting error %d\n", err)); goto exit; } - if ((err = wl_cfgp2p_bss(dev, bssidx, 1)) < 0) { + if ((err = wl_cfgp2p_bss(wl, dev, bssidx, 1)) < 0) { WL_ERR(("GO Bring up error %d\n", err)); goto exit; } } - } else if (wl_get_drv_status(wl, AP_CREATING)) { + } else if (wl_get_drv_status(wl, AP_CREATING, dev)) { ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; ap = 1; /* find the SSID */ @@ -3818,6 +4161,26 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, WL_ERR(("setting AP mode failed %d \n", err)); return err; } + + /* if requested, do softap ch autoselect */ + if (wl->hostapd_chan == 14) { + int auto_chan; + if ((err = wldev_get_auto_channel(dev, &auto_chan)) != 0) { + WL_ERR(("softap: auto chan select failed," + " will use ch 6\n")); + auto_chan = 6; + } else { + printf("<0>softap: got auto ch:%d\n", auto_chan); + } + err = wldev_ioctl(dev, WLC_SET_CHANNEL, + &auto_chan, sizeof(auto_chan), true); + if (err < 0) { + WL_ERR(("softap: WLC_SET_CHANNEL error %d chip" + " may not be supporting this channel\n", err)); + return err; + } + } + /* find the RSN_IE */ if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, DOT11_MNG_RSN_ID)) != NULL) { @@ -3873,9 +4236,9 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, memcpy(beacon_ie, wps_ie, wpsie_len); wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, beacon_ie, wpsie_len); - wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); + wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); } else { WL_DBG(("No WPSIE in beacon \n")); } @@ -3906,11 +4269,11 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, /* create softap */ if ((err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true)) == 0) { - wl_clr_drv_status(wl, AP_CREATING); - wl_set_drv_status(wl, AP_CREATED); + wl_clr_drv_status(wl, AP_CREATING, dev); + wl_set_drv_status(wl, AP_CREATED, dev); } } - } else if (wl_get_drv_status(wl, AP_CREATED)) { + } else if (wl_get_drv_status(wl, AP_CREATED, dev)) { ap = 1; /* find the WPSIE */ if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)info->tail, info->tail_len)) != NULL) { @@ -3926,14 +4289,14 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, memcmp(wl->ap_info->wps_ie, wps_ie, wpsie_len)) { WL_DBG((" WPS IE is changed\n")); kfree(wl->ap_info->wps_ie); - wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); + wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); } else if (wl->ap_info->wps_ie == NULL) { WL_DBG((" WPS IE is added\n")); - wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); + wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); } /* find the RSN_IE */ if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, @@ -3999,12 +4362,12 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, } if (update_bss) { wl->ap_info->security_mode = true; - wl_cfgp2p_bss(dev, bssidx, 0); + wl_cfgp2p_bss(wl, dev, bssidx, 0); if (wl_validate_wpa2ie(dev, wpa2_ie, bssidx) < 0 || wl_validate_wpaie(dev, wpa_ie, bssidx) < 0) { return BCME_ERROR; } - wl_cfgp2p_bss(dev, bssidx, 1); + wl_cfgp2p_bss(wl, dev, bssidx, 1); } } } else { @@ -4017,6 +4380,108 @@ exit: return err; } +#ifdef WL_SCHED_SCAN +#define PNO_TIME 30 +#define PNO_REPEAT 4 +#define PNO_FREQ_EXPO_MAX 2 +int wl_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + ushort pno_time = PNO_TIME; + int pno_repeat = PNO_REPEAT; + int pno_freq_expo_max = PNO_FREQ_EXPO_MAX; + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + struct wl_priv *wl = wiphy_priv(wiphy); + struct cfg80211_ssid *ssid = NULL; + int ssid_count = 0; + int i; + int ret = 0; + + WL_DBG(("Enter n_match_sets:%d n_ssids:%d \n", + request->n_match_sets, request->n_ssids)); + WL_DBG(("ssids:%d pno_time:%d pno_repeat:%d pno_freq:%d \n", + request->n_ssids, pno_time, pno_repeat, pno_freq_expo_max)); + +#if defined(WL_ENABLE_P2P_IF) + /* While GO is operational, PNO is not supported */ + if (dhd_cfg80211_get_opmode(wl) & P2P_GO_ENABLED) { + WL_DBG(("PNO not enabled! op_mode: P2P GO")); + return -1; + } +#endif + + if (!request || !request->n_ssids || !request->n_match_sets) { + WL_ERR(("Invalid sched scan req!! n_ssids:%d \n", request->n_ssids)); + return -EINVAL; + } + + memset(&ssids_local, 0, sizeof(ssids_local)); + + if (request->n_match_sets > 0) { + for (i = 0; i < request->n_match_sets; i++) { + ssid = &request->match_sets[i].ssid; + memcpy(ssids_local[i].SSID, ssid->ssid, ssid->ssid_len); + ssids_local[i].SSID_len = ssid->ssid_len; + WL_DBG((">>> PNO filter set for ssid (%s) \n", ssid->ssid)); + ssid_count++; + } + } + + if (request->n_ssids > 0) { + for (i = 0; i < request->n_ssids; i++) { + /* Active scan req for ssids */ + WL_DBG((">>> Active scan req for ssid (%s) \n", request->ssids[i].ssid)); + + /* match_set ssids is a supert set of n_ssid list, so we need + * not add these set seperately + */ + } + } + + if (ssid_count) { + if ((ret = dhd_dev_pno_set(dev, ssids_local, request->n_match_sets, + pno_time, pno_repeat, pno_freq_expo_max)) < 0) { + WL_ERR(("PNO setup failed!! ret=%d \n", ret)); + return -EINVAL; + } + + /* Enable the PNO */ + if (dhd_dev_pno_enable(dev, 1) < 0) { + WL_ERR(("PNO enable failed!! ret=%d \n", ret)); + return -EINVAL; + } + wl->sched_scan_req = request; + } else { + return -EINVAL; + } + + return 0; +} + +int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + + WL_DBG(("Enter \n")); + + if (dhd_dev_pno_enable(dev, 0) < 0) + WL_ERR(("PNO disable failed")); + + if (dhd_dev_pno_reset(dev) < 0) + WL_ERR(("PNO reset failed")); + + if (wl->scan_request && wl->sched_scan_running) { + wl_notify_escan_complete(wl, dev, true, true); + } + + wl->sched_scan_req = NULL; + wl->sched_scan_running = FALSE; + + return 0; +} +#endif /* WL_SCHED_SCAN */ + static struct cfg80211_ops wl_cfg80211_ops = { .add_virtual_intf = wl_cfg80211_add_virtual_iface, .del_virtual_intf = wl_cfg80211_del_virtual_iface, @@ -4049,9 +4514,13 @@ static struct cfg80211_ops wl_cfg80211_ops = { .set_channel = wl_cfg80211_set_channel, .set_beacon = wl_cfg80211_add_set_beacon, .add_beacon = wl_cfg80211_add_set_beacon, +#ifdef WL_SCHED_SCAN + .sched_scan_start = wl_cfg80211_sched_scan_start, + .sched_scan_stop = wl_cfg80211_sched_scan_stop, +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) */ }; -static s32 wl_mode_to_nl80211_iftype(s32 mode) +s32 wl_mode_to_nl80211_iftype(s32 mode) { s32 err = 0; @@ -4069,33 +4538,33 @@ static s32 wl_mode_to_nl80211_iftype(s32 mode) return err; } -static struct wireless_dev *wl_alloc_wdev(struct device *sdiofunc_dev) +static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev) { - struct wireless_dev *wdev; s32 err = 0; - wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); - if (unlikely(!wdev)) { - WL_ERR(("Could not allocate wireless device\n")); - return ERR_PTR(-ENOMEM); - } wdev->wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct wl_priv)); if (unlikely(!wdev->wiphy)) { WL_ERR(("Couldn not allocate wiphy device\n")); err = -ENOMEM; - goto wiphy_new_out; + return err; } set_wiphy_dev(wdev->wiphy, sdiofunc_dev); wdev->wiphy->max_scan_ie_len = WL_SCAN_IE_LEN_MAX; /* Report how many SSIDs Driver can support per Scan request */ wdev->wiphy->max_scan_ssids = WL_SCAN_PARAMS_SSID_MAX; wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; +#ifdef WL_SCHED_SCAN + wdev->wiphy->max_sched_scan_ssids = MAX_PFN_LIST_COUNT; + wdev->wiphy->max_match_sets = MAX_PFN_LIST_COUNT; + wdev->wiphy->max_sched_scan_ie_len = WL_SCAN_IE_LEN_MAX; + wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +#endif /* WL_SCHED_SCAN */ wdev->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) - | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MONITOR); + BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MONITOR); wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; - wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; + /* wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; - set in runtime */ wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wdev->wiphy->cipher_suites = __wl_cipher_suites; wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); @@ -4112,7 +4581,9 @@ static struct wireless_dev *wl_alloc_wdev(struct device *sdiofunc_dev) WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS | #endif WIPHY_FLAG_4ADDR_STATION; - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; +#endif WL_DBG(("Registering custom regulatory)\n")); wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; wiphy_apply_custom_regulatory(wdev->wiphy, &brcm_regdom); @@ -4120,39 +4591,28 @@ static struct wireless_dev *wl_alloc_wdev(struct device *sdiofunc_dev) err = wiphy_register(wdev->wiphy); if (unlikely(err < 0)) { WL_ERR(("Couldn not register wiphy device (%d)\n", err)); - goto wiphy_register_out; + wiphy_free(wdev->wiphy); } - return wdev; - -wiphy_register_out: - wiphy_free(wdev->wiphy); - -wiphy_new_out: - kfree(wdev); - - return ERR_PTR(err); + return err; } static void wl_free_wdev(struct wl_priv *wl) { - int i; struct wireless_dev *wdev = wl->wdev; - - if (unlikely(!wdev)) { + struct wiphy *wiphy; + if (!wdev) { WL_ERR(("wdev is invalid\n")); return; } - - for (i = 0; i < VWDEV_CNT; i++) { - if ((wl->vwdev[i] != NULL)) { - kfree(wl->vwdev[i]); - wl->vwdev[i] = NULL; - } - } + wiphy = wdev->wiphy; wiphy_unregister(wdev->wiphy); wdev->wiphy->dev.parent = NULL; - wiphy_free(wdev->wiphy); - kfree(wdev); + + wl_delete_all_netinfo(wl); + wiphy_free(wiphy); + /* PLEASE do NOT call any function after wiphy_free, the driver's private structure "wl", + * which is the private part of wiphy, has been freed in wiphy_free !!!!!!!!!!! + */ } static s32 wl_inform_bss(struct wl_priv *wl) @@ -4206,6 +4666,11 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) band = wiphy->bands[IEEE80211_BAND_2GHZ]; else band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + kfree(notif_bss_info); + return -EINVAL; + } notif_bss_info->rssi = dtoh16(bi->RSSI); memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN); mgmt_type = wl->active_scan ? @@ -4232,8 +4697,13 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) freq = ieee80211_channel_to_frequency(notif_bss_info->channel, band->band); #endif channel = ieee80211_get_channel(wiphy, freq); + if (!channel) { + WL_ERR(("No valid channel")); + kfree(notif_bss_info); + return -EINVAL; + } - WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM" + WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM " "mgmt_type %d frame_len %d\n", bi->SSID, notif_bss_info->rssi, notif_bss_info->channel, mgmt->u.beacon.capab_info, &bi->BSSID, mgmt_type, @@ -4241,6 +4711,37 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) signal = notif_bss_info->rssi * 100; +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + if (wl->p2p && wl->p2p_net && wl->scan_request && + ((wl->scan_request->dev == wl->p2p_net) || + (wl->scan_request->dev == wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)))){ +#else + if (p2p_is_on(wl) && ( p2p_scan(wl) || + (wl->scan_request->dev == wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)))) { +#endif + /* find the P2PIE, if we do not find it, we will discard this frame */ + wifi_p2p_ie_t * p2p_ie; + if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)beacon_proberesp->variable, + wl_get_ielen(wl))) == NULL) { + WL_ERR(("Couldn't find P2PIE in probe response/beacon\n")); + kfree(notif_bss_info); + return err; + } + else if( wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_INFO) == NULL) + { + WL_DBG(("Couldn't find P2P_SEID_DEV_INFO in probe response/beacon\n")); + kfree(notif_bss_info); + return err; + } + } + if (!mgmt->u.probe_resp.timestamp) { + struct timeval tv; + + do_gettimeofday(&tv); + mgmt->u.probe_resp.timestamp = ((u64)tv.tv_sec * 1000000) + + tv.tv_usec; + } + cbss = cfg80211_inform_bss_frame(wiphy, channel, mgmt, le16_to_cpu(notif_bss_info->frame_len), signal, GFP_KERNEL); if (unlikely(!cbss)) { @@ -4255,21 +4756,129 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) return err; } +static s32 wl_inform_ibss(struct wl_priv *wl, const u8 *bssid) +{ + struct net_device *ndev = wl_to_prmry_ndev(wl); + struct wiphy *wiphy = wl_to_wiphy(wl); + struct wl_bss_info *bi = NULL; + struct ieee80211_channel *notify_channel; + struct ieee80211_supported_band *band; + struct cfg80211_bss *bss; + s32 err = 0; + u16 channel; + u32 freq; + u32 wsec = 0; + u16 notify_capability; + u16 notify_interval; + u8 *notify_ie; + size_t notify_ielen; + s32 notify_signal; + + WL_TRACE(("Enter\n")); + + if (wl->scan_request) { + wl_notify_escan_complete(wl, ndev, true, true); + } + + mutex_lock(&wl->usr_sync); + + *(u32 *)wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); + err = wldev_ioctl(ndev, WLC_GET_BSS_INFO, wl->extra_buf, + WL_EXTRA_BUF_MAX, false); + if (err) { + WL_ERR(("Failed to get bss info for IBSS\n")); + err = -EIO; + goto CleanUp; + } + bi = (struct wl_bss_info *)(wl->extra_buf + 4); + + if (memcmp(bssid, &bi->BSSID, ETHER_ADDR_LEN)) { + WL_ERR(("BSSID mismatch: Inform %02x:%02x:%02x:%02x:%02x:%02x," + "%02x:%02x:%02x:%02x:%02x:%02x\n", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], + bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2], + bi->BSSID.octet[3], bi->BSSID.octet[4], + bi->BSSID.octet[5])); + err = -EINVAL; + goto CleanUp; + } + + err = wldev_iovar_getint(ndev, "wsec", &wsec); + if (err) { + WL_ERR(("wsec failed: %d\n", err)); + err = -EIO; + goto CleanUp; + } + + channel = bi->ctl_ch ? bi->ctl_ch : + CHSPEC_CHANNEL(dtohchanspec(bi->chanspec)); + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + freq = ieee80211_channel_to_frequency(channel); + (void)band->band; +#else + freq = ieee80211_channel_to_frequency(channel, band->band); +#endif + notify_channel = ieee80211_get_channel(wiphy, freq); + + notify_capability = dtoh16(bi->capability); + notify_interval = dtoh16(bi->beacon_period); + notify_ie = (u8 *)bi + dtoh16(bi->ie_offset); + notify_ielen = dtoh32(bi->ie_length); + notify_signal = (int16)dtoh16(bi->RSSI) * 100; + + if (wl->p2p_supported) { + notify_capability |= DOT11_CAP_IBSS; + if (wsec) + notify_capability |= DOT11_CAP_PRIVACY; + } + + WL_DBG(("BSSID %02x:%02x:%02x:%02x:%02x:%02x", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5])); + WL_INFO(("channel: %d(%d)\n", channel, freq)); + WL_INFO(("capability: %X\n", notify_capability)); + WL_INFO(("beacon interval: %d ms\n", notify_interval)); + WL_INFO(("signal: %d dBm\n", notify_signal)); + WL_INFO(("ie_len: %d\n", notify_ielen)); + bss = cfg80211_inform_bss(wiphy, notify_channel, bssid, 0, + notify_capability, notify_interval, + notify_ie, notify_ielen, notify_signal, GFP_KERNEL); + if (!bss) { + WL_ERR(("cfg80211_inform_bss() Failed\n")); + err = -ENOMEM; + goto CleanUp; + } + + cfg80211_put_bss(bss); + err = 0; + +CleanUp: + + mutex_unlock(&wl->usr_sync); + + WL_TRACE(("Exit\n")); + return err; +} + static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev) { u32 event = ntoh32(e->event_type); u32 status = ntoh32(e->status); u16 flags = ntoh16(e->flags); - WL_DBG(("event %d, status %d\n", event, status)); + WL_DBG(("event %d, status %d flags %x\n", event, status, flags)); if (event == WLC_E_SET_SSID) { if (status == WLC_E_STATUS_SUCCESS) { - if (!wl_is_ibssmode(wl, ndev)) - return true; + return true; } } else if (event == WLC_E_LINK) { if (flags & WLC_EVENT_MSG_LINK) - return true; + if (!wl_is_ibssmode(wl, ndev)) + return true; } WL_DBG(("wl_is_linkup false\n")); @@ -4307,162 +4916,230 @@ static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e) return false; } +/* The mainline kernel >= 3.2.0 has support for indicating new/del station + * to AP/P2P GO via events. If this change is backported to kernel for which + * this driver is being built, then define WL_CFG80211_STA_EVENT. You + * should use this new/del sta event mechanism for BRCM supplicant >= 22. + */ static s32 -wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, +wl_notify_connect_status_ap(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data) { - bool act; - bool isfree = false; s32 err = 0; - s32 freq; - s32 channel; - u8 body[200]; u32 event = ntoh32(e->event_type); u32 reason = ntoh32(e->reason); u32 len = ntoh32(e->datalen); - u16 fc = 0; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) + bool isfree = false; u8 *mgmt_frame; u8 bsscfgidx = e->bsscfgidx; + s32 freq; + s32 channel; + u8 body[WL_FRAME_LEN]; + u16 fc = 0; struct ieee80211_supported_band *band; struct ether_addr da; struct ether_addr bssid; struct wiphy *wiphy = wl_to_wiphy(wl); channel_info_t ci; +#else + struct station_info sinfo; +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !WL_CFG80211_STA_EVENT */ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) memset(body, 0, sizeof(body)); memset(&bssid, 0, ETHER_ADDR_LEN); - WL_DBG(("Enter \n")); - - if (get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { - memcpy(body, data, len); - wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", - NULL, 0, ioctlbuf, sizeof(ioctlbuf), bsscfgidx); - memcpy(da.octet, ioctlbuf, ETHER_ADDR_LEN); - err = wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); - switch (event) { - case WLC_E_ASSOC_IND: - fc = FC_ASSOC_REQ; - break; - case WLC_E_REASSOC_IND: - fc = FC_REASSOC_REQ; - break; - case WLC_E_DISASSOC_IND: - fc = FC_DISASSOC; - break; - case WLC_E_DEAUTH_IND: - fc = FC_DISASSOC; - break; - case WLC_E_DEAUTH: - fc = FC_DISASSOC; - break; - default: - fc = 0; - goto exit; - } - if ((err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &ci, sizeof(ci), false))) - return err; + WL_DBG(("Enter event %d ndev %p\n", event, ndev)); + if (wl_get_mode_by_netdev(wl, ndev) == WL_INVALID) + return WL_INVALID; - channel = dtoh32(ci.hw_channel); - if (channel <= CH_MAX_2G_CHANNEL) - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - else - band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (len > WL_FRAME_LEN) { + WL_ERR(("Received frame length %d from dongle is greater than" + " allocated body buffer len %d", len, WL_FRAME_LEN)); + goto exit; + } + memcpy(body, data, len); + wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", + NULL, 0, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bsscfgidx, &wl->ioctl_buf_sync); + memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); + err = wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + switch (event) { + case WLC_E_ASSOC_IND: + fc = FC_ASSOC_REQ; + break; + case WLC_E_REASSOC_IND: + fc = FC_REASSOC_REQ; + break; + case WLC_E_DISASSOC_IND: + fc = FC_DISASSOC; + break; + case WLC_E_DEAUTH_IND: + fc = FC_DISASSOC; + break; + case WLC_E_DEAUTH: + fc = FC_DISASSOC; + break; + default: + fc = 0; + goto exit; + } + if ((err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &ci, sizeof(ci), false))) + return err; + channel = dtoh32(ci.hw_channel); + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + return -EINVAL; + } #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) - freq = ieee80211_channel_to_frequency(channel); + freq = ieee80211_channel_to_frequency(channel); #else - freq = ieee80211_channel_to_frequency(channel, band->band); + freq = ieee80211_channel_to_frequency(channel, band->band); #endif - err = wl_frame_get_mgmt(fc, &da, &e->addr, &bssid, + err = wl_frame_get_mgmt(fc, &da, &e->addr, &bssid, &mgmt_frame, &len, body); - if (err < 0) - goto exit; - isfree = true; + if (err < 0) + goto exit; + isfree = true; + + if (event == WLC_E_ASSOC_IND && reason == DOT11_SC_SUCCESS) { + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + } else if (event == WLC_E_DISASSOC_IND) { + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + } - if (event == WLC_E_ASSOC_IND && reason == DOT11_SC_SUCCESS) { - cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); - } else if (event == WLC_E_DISASSOC_IND) { - cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); - } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { - cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); +exit: + if (isfree) + kfree(mgmt_frame); + return err; +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) && !WL_CFG80211_STA_EVENT */ + sinfo.filled = 0; + if (((event == WLC_E_ASSOC_IND) || (event == WLC_E_REASSOC_IND)) && + reason == DOT11_SC_SUCCESS) { + sinfo.filled = STATION_INFO_ASSOC_REQ_IES; + if (!data) { + WL_ERR(("No IEs present in ASSOC/REASSOC_IND")); + return -EINVAL; } + sinfo.assoc_req_ies = data; + sinfo.assoc_req_ies_len = len; + cfg80211_new_sta(ndev, e->addr.octet, &sinfo, GFP_ATOMIC); + } else if (event == WLC_E_DISASSOC_IND) { + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); + } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); + } +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) && !WL_CFG80211_STA_EVENT */ + return err; +} + +static s32 +wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + bool act; + s32 err = 0; + u32 event = ntoh32(e->event_type); + u32 reason; + if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { + wl_notify_connect_status_ap(wl, ndev, e, data); } else { - WL_DBG(("wl_notify_connect_status : event %d status : %d \n", - ntoh32(e->event_type), ntoh32(e->status))); + WL_DBG(("wl_notify_connect_status : event %d status : %d ndev %p\n", + ntoh32(e->event_type), ntoh32(e->status), ndev)); + if((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DISASSOC_IND)) { + reason = ntoh32(e->reason); + wl->deauth_reason = reason; + WL_ERR(("Received %s event with reason code: %d\n", + (event == WLC_E_DEAUTH_IND)? + "WLC_E_DEAUTH_IND":"WLC_E_DISASSOC_IND", reason)); + } if (wl_is_linkup(wl, e, ndev)) { wl_link_up(wl); act = true; - wl_update_prof(wl, e, &act, WL_PROF_ACT); - wl_update_prof(wl, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + wl_update_prof(wl, ndev, e, &act, WL_PROF_ACT); + wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); + wl->deauth_reason = 0; if (wl_is_ibssmode(wl, ndev)) { - printk("cfg80211_ibss_joined\n"); - cfg80211_ibss_joined(ndev, (s8 *)&e->addr, - GFP_KERNEL); - WL_DBG(("joined in IBSS network\n")); + wl_ibss_join_done(wl, ndev, e, data, true); + WL_DBG(("wl_ibss_join_done succeeded\n")); } else { - if (!wl_get_drv_status(wl, DISCONNECTING)) { - printk("wl_bss_connect_done succeeded status=(0x%x)\n", - (int)wl->status); + if (!wl_get_drv_status(wl, DISCONNECTING, ndev)) { + printk("wl_bss_connect_done succeeded\n"); wl_bss_connect_done(wl, ndev, e, data, true); WL_DBG(("joined in BSS network \"%s\"\n", ((struct wlc_ssid *) - wl_read_prof(wl, WL_PROF_SSID))->SSID)); + wl_read_prof(wl, ndev, WL_PROF_SSID))->SSID)); } } - } else if (wl_is_linkdown(wl, e)) { if (wl->scan_request) { - del_timer_sync(&wl->scan_timeout); if (wl->escan_on) { - wl_notify_escan_complete(wl, true); - } else + wl_notify_escan_complete(wl, ndev, true, true); + } else { + del_timer_sync(&wl->scan_timeout); wl_iscan_aborted(wl); + } } - if (wl_get_drv_status(wl, CONNECTED)) { + if (wl_get_drv_status(wl, CONNECTED, ndev)) { scb_val_t scbval; - u8 *curbssid = wl_read_prof(wl, WL_PROF_BSSID); - printk("link down, call cfg80211_disconnected\n"); - wl_clr_drv_status(wl, CONNECTED); - /* To make sure disconnect, explictly send dissassoc - * for BSSID 00:00:00:00:00:00 issue - */ - scbval.val = WLAN_REASON_DEAUTH_LEAVING; - - memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); - scbval.val = htod32(scbval.val); - wldev_ioctl(ndev, WLC_DISASSOC, &scbval, - sizeof(scb_val_t), true); - cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); - wl_link_down(wl); - wl_init_prof(wl); - } else if (wl_get_drv_status(wl, CONNECTING)) { + u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + wl_clr_drv_status(wl, CONNECTED, ndev); + if (! wl_get_drv_status(wl, DISCONNECTING, ndev)) { + /* To make sure disconnect, explictly send dissassoc + * for BSSID 00:00:00:00:00:00 issue + */ + scbval.val = WLAN_REASON_DEAUTH_LEAVING; + + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + wldev_ioctl(ndev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); + WL_ERR(("link down, calling cfg80211_disconnected" + " with deauth_reason:%d\n", wl->deauth_reason)); + if (!wl_is_ibssmode(wl, ndev)) + cfg80211_disconnected(ndev, wl->deauth_reason, + NULL, 0, GFP_KERNEL); + wl_link_down(wl); + wl_init_prof(wl, ndev); + } + } + else if (wl_get_drv_status(wl, CONNECTING, ndev)) { printk("link down, during connecting\n"); - wl_bss_connect_done(wl, ndev, e, data, false); + if (wl_is_ibssmode(wl, ndev)) + wl_ibss_join_done(wl, ndev, e, data, false); + else + wl_bss_connect_done(wl, ndev, e, data, false); } - wl_clr_drv_status(wl, DISCONNECTING); + wl_clr_drv_status(wl, DISCONNECTING, ndev); } else if (wl_is_nonetwork(wl, e)) { printk("connect failed event=%d e->status 0x%x\n", event, (int)ntoh32(e->status)); /* Clean up any pending scan request */ if (wl->scan_request) { - del_timer_sync(&wl->scan_timeout); if (wl->escan_on) { - wl_notify_escan_complete(wl, true); - } else + wl_notify_escan_complete(wl, ndev, true, true); + } else { + del_timer_sync(&wl->scan_timeout); wl_iscan_aborted(wl); + } } - if (wl_get_drv_status(wl, CONNECTING)) + if (wl_get_drv_status(wl, CONNECTING, ndev)) wl_bss_connect_done(wl, ndev, e, data, false); } else { printk("%s nothing\n", __FUNCTION__); } } -exit: - if (isfree) - kfree(mgmt_frame); return err; } @@ -4476,47 +5153,14 @@ wl_notify_roaming_status(struct wl_priv *wl, struct net_device *ndev, u32 status = be32_to_cpu(e->status); WL_DBG(("Enter \n")); if (event == WLC_E_ROAM && status == WLC_E_STATUS_SUCCESS) { - if (wl_get_drv_status(wl, CONNECTED)) + if (wl_get_drv_status(wl, CONNECTED, ndev)) wl_bss_roaming_done(wl, ndev, e, data); else wl_bss_connect_done(wl, ndev, e, data, true); act = true; - wl_update_prof(wl, e, &act, WL_PROF_ACT); - wl_update_prof(wl, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); - } - return err; -} - -static __used s32 -wl_dev_bufvar_set(struct net_device *dev, s8 *name, s8 *buf, s32 len) -{ - struct wl_priv *wl = wlcfg_drv_priv; - u32 buflen; - - buflen = bcm_mkiovar(name, buf, len, wl->ioctl_buf, WL_IOCTL_LEN_MAX); - BUG_ON(unlikely(!buflen)); - - return wldev_ioctl(dev, WLC_SET_VAR, wl->ioctl_buf, buflen, true); -} - -static s32 -wl_dev_bufvar_get(struct net_device *dev, s8 *name, s8 *buf, - s32 buf_len) -{ - struct wl_priv *wl = wlcfg_drv_priv; - u32 len; - s32 err = 0; - - len = bcm_mkiovar(name, NULL, 0, wl->ioctl_buf, WL_IOCTL_LEN_MAX); - BUG_ON(unlikely(!len)); - err = wldev_ioctl(dev, WLC_GET_VAR, (void *)wl->ioctl_buf, - WL_IOCTL_LEN_MAX, false); - if (unlikely(err)) { - WL_ERR(("error (%d)\n", err)); - return err; + wl_update_prof(wl, ndev, e, &act, WL_PROF_ACT); + wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); } - memcpy(buf, wl->ioctl_buf, buf_len); - return err; } @@ -4527,8 +5171,8 @@ static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) s32 err = 0; WL_DBG(("Enter \n")); - err = wl_dev_bufvar_get(ndev, "assoc_info", wl->extra_buf, - WL_ASSOC_INFO_MAX); + err = wldev_iovar_getbuf(ndev, "assoc_info", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); if (unlikely(err)) { WL_ERR(("could not get assoc info (%d)\n", err)); return err; @@ -4546,8 +5190,8 @@ static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) bzero(conn_info->resp_ie, sizeof(conn_info->resp_ie)); } if (assoc_info.req_len) { - err = wl_dev_bufvar_get(ndev, "assoc_req_ies", wl->extra_buf, - WL_ASSOC_INFO_MAX); + err = wldev_iovar_getbuf(ndev, "assoc_req_ies", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); if (unlikely(err)) { WL_ERR(("could not get assoc req (%d)\n", err)); return err; @@ -4567,8 +5211,8 @@ static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) conn_info->req_ie_len = 0; } if (assoc_info.resp_len) { - err = wl_dev_bufvar_get(ndev, "assoc_resp_ies", wl->extra_buf, - WL_ASSOC_INFO_MAX); + err = wldev_iovar_getbuf(ndev, "assoc_resp_ies", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); if (unlikely(err)) { WL_ERR(("could not get assoc resp (%d)\n", err)); return err; @@ -4630,26 +5274,27 @@ static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev) struct wl_bss_info *bi; struct wlc_ssid *ssid; struct bcm_tlv *tim; - u16 beacon_interval; - u8 dtim_period; + s32 beacon_interval; + s32 dtim_period; size_t ie_len; u8 *ie; u8 *curbssid; s32 err = 0; struct wiphy *wiphy; + wiphy = wl_to_wiphy(wl); if (wl_is_ibssmode(wl, ndev)) return err; - ssid = (struct wlc_ssid *)wl_read_prof(wl, WL_PROF_SSID); - curbssid = wl_read_prof(wl, WL_PROF_BSSID); + ssid = (struct wlc_ssid *)wl_read_prof(wl, ndev, WL_PROF_SSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); bss = cfg80211_get_bss(wiphy, NULL, curbssid, ssid->SSID, ssid->SSID_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); mutex_lock(&wl->usr_sync); - if (unlikely(!bss)) { + if (!bss) { WL_DBG(("Could not find the AP\n")); *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); err = wldev_ioctl(ndev, WLC_GET_BSS_INFO, @@ -4685,7 +5330,7 @@ static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev) /* * active scan was done so we could not get dtim * information out of probe response. - * so we speficially query dtim information to dongle. + * so we speficially query dtim information. */ err = wldev_ioctl(ndev, WLC_GET_DTIMPRD, &dtim_period, sizeof(dtim_period), false); @@ -4695,8 +5340,8 @@ static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev) } } - wl_update_prof(wl, NULL, &beacon_interval, WL_PROF_BEACONINT); - wl_update_prof(wl, NULL, &dtim_period, WL_PROF_DTIMPERIOD); + wl_update_prof(wl, ndev, NULL, &beacon_interval, WL_PROF_BEACONINT); + wl_update_prof(wl, ndev, NULL, &dtim_period, WL_PROF_DTIMPERIOD); update_bss_info_out: mutex_unlock(&wl->usr_sync); @@ -4712,8 +5357,8 @@ wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, u8 *curbssid; wl_get_assoc_ies(wl, ndev); - wl_update_prof(wl, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); - curbssid = wl_read_prof(wl, WL_PROF_BSSID); + wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); wl_update_bss_info(wl, ndev); wl_update_pmklist(ndev, wl->pmk_list, err); cfg80211_roamed(ndev, @@ -4725,7 +5370,7 @@ wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); WL_DBG(("Report roaming result\n")); - wl_set_drv_status(wl, CONNECTED); + wl_set_drv_status(wl, CONNECTED, ndev); return err; } @@ -4736,20 +5381,21 @@ wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, { struct wl_connect_info *conn_info = wl_to_conn(wl); s32 err = 0; - u8 *curbssid = wl_read_prof(wl, WL_PROF_BSSID); + u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + WL_DBG((" enter\n")); if (wl->scan_request) { - wl_cfg80211_scan_abort(wl, ndev); + wl_notify_escan_complete(wl, ndev, true, true); } - if (wl_get_drv_status(wl, CONNECTING)) { - wl_clr_drv_status(wl, CONNECTING); + if (wl_get_drv_status(wl, CONNECTING, ndev)) { + wl_clr_drv_status(wl, CONNECTING, ndev); if (completed) { wl_get_assoc_ies(wl, ndev); - wl_update_prof(wl, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); - curbssid = wl_read_prof(wl, WL_PROF_BSSID); + wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); wl_update_bss_info(wl, ndev); wl_update_pmklist(ndev, wl->pmk_list, err); - wl_set_drv_status(wl, CONNECTED); + wl_set_drv_status(wl, CONNECTED, ndev); } cfg80211_connect_result(ndev, curbssid, @@ -4768,6 +5414,35 @@ wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, } static s32 +wl_ibss_join_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, bool completed) +{ + s32 err = 0; + + WL_TRACE(("Enter\n")); + + if (wl->scan_request) { + wl_notify_escan_complete(wl, ndev, true, true); + } + if (wl_get_drv_status(wl, CONNECTING, ndev)) { + wl_clr_drv_status(wl, CONNECTING, ndev); + if (completed) { + err = wl_inform_ibss(wl, (u8 *)&e->addr); + if (err) { + WL_ERR(("wl_inform_ibss() failed: %d\n", err)); + } + wl_set_drv_status(wl, CONNECTED, ndev); + + cfg80211_ibss_joined(ndev, (u8 *)&e->addr, GFP_KERNEL); + WL_DBG(("cfg80211_ibss_joined() called with valid BSSID\n")); + } + } + + WL_TRACE(("Exit\n")); + return err; +} + +static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data) { @@ -4787,6 +5462,28 @@ wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, return 0; } +#ifdef PNO_SUPPORT +static s32 +wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + WL_ERR((" PNO Event\n")); + + mutex_lock(&wl->usr_sync); +#ifndef WL_SCHED_SCAN + /* TODO: Use cfg80211_sched_scan_results(wiphy); */ + cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); +#else + /* If cfg80211 scheduled scan is supported, report the pno results via sched + * scan results + */ + wl_notify_sched_scan_results(wl, ndev, e, data); +#endif /* WL_SCHED_SCAN */ + mutex_unlock(&wl->usr_sync); + return 0; +} +#endif /* PNO_SUPPORT */ + static s32 wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data) @@ -4798,11 +5495,15 @@ wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, unsigned long flags; WL_DBG(("Enter \n")); + if (!wl_get_drv_status(wl, SCANNING, ndev)) { + WL_ERR(("scan is not ready \n")); + return err; + } if (wl->iscan_on && wl->iscan_kickstart) return wl_wakeup_iscan(wl_to_iscan(wl)); mutex_lock(&wl->usr_sync); - wl_clr_drv_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, ndev); err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &channel_inform, sizeof(channel_inform), false); if (unlikely(err)) { @@ -4833,13 +5534,13 @@ wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, scan_done_out: del_timer_sync(&wl->scan_timeout); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); if (wl->scan_request) { WL_DBG(("cfg80211_scan_done\n")); cfg80211_scan_done(wl->scan_request, false); wl->scan_request = NULL; } - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); mutex_unlock(&wl->usr_sync); return err; } @@ -4895,7 +5596,10 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, bool isfree = false; s32 err = 0; s32 freq; - wifi_p2p_pub_act_frame_t *act_frm; + struct net_device *dev = NULL; + wifi_p2p_pub_act_frame_t *act_frm = NULL; + wifi_p2p_action_frame_t *p2p_act_frm = NULL; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm = NULL; wl_event_rx_frame_data_t *rxframe = (wl_event_rx_frame_data_t*)data; u32 event = ntoh32(e->event_type); @@ -4905,10 +5609,27 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, u16 channel = ((ntoh16(rxframe->channel) & WL_CHANSPEC_CHAN_MASK)); memset(&bssid, 0, ETHER_ADDR_LEN); + + if (wl->p2p_net == ndev) { + dev = wl_to_prmry_ndev(wl); + } else { + dev = ndev; + } + if (channel <= CH_MAX_2G_CHANNEL) band = wiphy->bands[IEEE80211_BAND_2GHZ]; else band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + return -EINVAL; + } + + if ((event == WLC_E_P2P_PROBREQ_MSG) && + wl->p2p && wl_get_p2p_status(wl, GO_NEG_PHASE)) { + WL_DBG(("Filtering P2P probe_req while being in GO-Neg state\n")); + goto exit; + } #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) freq = ieee80211_channel_to_frequency(channel); @@ -4916,11 +5637,11 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, freq = ieee80211_channel_to_frequency(channel, band->band); #endif if (event == WLC_E_ACTION_FRAME_RX) { - wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", - NULL, 0, ioctlbuf, sizeof(ioctlbuf), bsscfgidx); + wldev_iovar_getbuf_bsscfg(dev, "cur_etheraddr", + NULL, 0, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bsscfgidx, &wl->ioctl_buf_sync); - wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); - memcpy(da.octet, ioctlbuf, ETHER_ADDR_LEN); + wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); err = wl_frame_get_mgmt(FC_ACTION, &da, &e->addr, &bssid, &mgmt_frame, &mgmt_frame_len, (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1)); @@ -4930,13 +5651,34 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, goto exit; } isfree = true; - act_frm = - (wifi_p2p_pub_act_frame_t *) (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + if (wl_cfgp2p_is_pub_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + act_frm = (wifi_p2p_pub_act_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + } else if (wl_cfgp2p_is_p2p_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + p2p_act_frm = (wifi_p2p_action_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + (void) p2p_act_frm; + } else if (wl_cfgp2p_is_gas_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + (void) sd_act_frm; + } + wl_cfgp2p_print_actframe(false, &mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN); /* * After complete GO Negotiation, roll back to mpc mode */ - if (act_frm->subtype == P2P_PAF_GON_CONF) { - wldev_iovar_setint(ndev, "mpc", 1); + if (act_frm && ((act_frm->subtype == P2P_PAF_GON_CONF) || + (act_frm->subtype == P2P_PAF_PROVDIS_RSP))) { + wldev_iovar_setint(dev, "mpc", 1); + } + + if (act_frm && (act_frm->subtype == P2P_PAF_GON_CONF)) { + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); } } else { mgmt_frame = (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1); @@ -4953,14 +5695,122 @@ exit: return 0; } +#ifdef WL_SCHED_SCAN +/* If target scan is not reliable, set the below define to "1" to do a + * full escan + */ +#define FULL_ESCAN_ON_PFN_NET_FOUND 0 +static s32 +wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + wl_pfn_net_info_t *netinfo, *pnetinfo; + struct cfg80211_scan_request request; + struct wiphy *wiphy = wl_to_wiphy(wl); + int err = 0; + struct cfg80211_ssid ssid[MAX_PFN_LIST_COUNT]; + struct ieee80211_channel *channel = NULL; + int channel_req = 0; + int band = 0; + struct wl_pfn_scanresults *pfn_result = (struct wl_pfn_scanresults *)data; + + WL_DBG(("Enter\n")); + + if (e->event_type == WLC_E_PFN_NET_LOST) { + WL_DBG(("PFN NET LOST event. Do Nothing \n")); + return 0; + } + WL_DBG(("PFN NET FOUND event. count:%d \n", pfn_result->count)); + if (pfn_result->count > 0) { + int i; + + memset(&request, 0x00, sizeof(struct cfg80211_scan_request)); + memset(&ssid, 0x00, sizeof(ssid)); + request.wiphy = wiphy; + + pnetinfo = (wl_pfn_net_info_t *)(data + sizeof(wl_pfn_scanresults_t) + - sizeof(wl_pfn_net_info_t)); + channel = (struct ieee80211_channel *)kzalloc( + (sizeof(struct ieee80211_channel) * MAX_PFN_LIST_COUNT), + GFP_KERNEL); + if (!channel) { + WL_ERR(("No memory")); + err = -ENOMEM; + goto out_err; + } + + for (i = 0; i < pfn_result->count; i++) { + netinfo = &pnetinfo[i]; + if (!netinfo) { + WL_ERR(("Invalid netinfo ptr. index:%d", i)); + err = -EINVAL; + goto out_err; + } + WL_DBG(("SSID:%s Channel:%d \n", + netinfo->pfnsubnet.SSID, netinfo->pfnsubnet.channel)); + /* PFN result doesn't have all the info which are required by the supplicant + * (For e.g IEs) Do a target Escan so that sched scan results are reported + * via wl_inform_single_bss in the required format. Escan does require the + * scan request in the form of cfg80211_scan_request. For timebeing, create + * cfg80211_scan_request one out of the received PNO event. + */ + memcpy(ssid[i].ssid, netinfo->pfnsubnet.SSID, + netinfo->pfnsubnet.SSID_len); + ssid[i].ssid_len = netinfo->pfnsubnet.SSID_len; + request.n_ssids++; + + channel_req = netinfo->pfnsubnet.channel; + band = (channel_req <= CH_MAX_2G_CHANNEL) ? NL80211_BAND_2GHZ + : NL80211_BAND_5GHZ; + channel[i].center_freq = ieee80211_channel_to_frequency(channel_req, band); + channel[i].band = band; + channel[i].flags |= IEEE80211_CHAN_NO_HT40; + request.channels[i] = &channel[i]; + request.n_channels++; + } + + /* assign parsed ssid array */ + if (request.n_ssids) + request.ssids = &ssid[0]; + + if (wl_get_drv_status_all(wl, SCANNING)) { + /* Abort any on-going scan */ + wl_notify_escan_complete(wl, ndev, true, true); + } + + if (wl_get_p2p_status(wl, DISCOVERY_ON)) { + err = wl_cfgp2p_discover_enable_search(wl, false); + if (unlikely(err)) { + wl_clr_drv_status(wl, SCANNING, ndev); + goto out_err; + } + } + + wl_set_drv_status(wl, SCANNING, ndev); +#if FULL_ESCAN_ON_PFN_NET_FOUND + err = wl_do_escan(wl, wiphy, ndev, NULL); +#else + err = wl_do_escan(wl, wiphy, ndev, &request); +#endif + if (err) { + wl_clr_drv_status(wl, SCANNING, ndev); + goto out_err; + } + wl->sched_scan_running = TRUE; + } + else { + WL_ERR(("FALSE PNO Event. (pfn_count == 0) \n")); + } +out_err: + if (channel) + kfree(channel); + return err; +} +#endif /* WL_SCHED_SCAN */ + static void wl_init_conf(struct wl_conf *conf) { - s32 i = 0; WL_DBG(("Enter \n")); - for (i = 0; i <= VWDEV_CNT; i++) { - conf->mode[i].type = -1; - conf->mode[i].ndev = NULL; - } conf->frag_threshold = (u32)-1; conf->rts_threshold = (u32)-1; conf->retry_short = (u32)-1; @@ -4968,13 +5818,14 @@ static void wl_init_conf(struct wl_conf *conf) conf->tx_power = -1; } -static void wl_init_prof(struct wl_priv *wl) +static void wl_init_prof(struct wl_priv *wl, struct net_device *ndev) { unsigned long flags; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); - memset(wl->profile, 0, sizeof(struct wl_profile)); - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + memset(profile, 0, sizeof(struct wl_profile)); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); } static void wl_init_event_handler(struct wl_priv *wl) @@ -4997,7 +5848,9 @@ static void wl_init_event_handler(struct wl_priv *wl) wl->evt_handler[WLC_E_P2P_DISC_LISTEN_COMPLETE] = wl_cfgp2p_listen_complete; wl->evt_handler[WLC_E_ACTION_FRAME_COMPLETE] = wl_cfgp2p_action_tx_complete; wl->evt_handler[WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE] = wl_cfgp2p_action_tx_complete; - +#ifdef PNO_SUPPORT + wl->evt_handler[WLC_E_PFN_NET_FOUND] = wl_notify_pfn_status; +#endif /* PNO_SUPPORT */ } static s32 wl_init_priv_mem(struct wl_priv *wl) @@ -5013,23 +5866,13 @@ static s32 wl_init_priv_mem(struct wl_priv *wl) WL_ERR(("wl_conf alloc failed\n")); goto init_priv_mem_out; } - wl->profile = (void *)kzalloc(sizeof(*wl->profile), GFP_KERNEL); - if (unlikely(!wl->profile)) { - WL_ERR(("wl_profile alloc failed\n")); - goto init_priv_mem_out; - } - wl->bss_info = (void *)kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); - if (unlikely(!wl->bss_info)) { - WL_ERR(("Bss information alloc failed\n")); - goto init_priv_mem_out; - } wl->scan_req_int = (void *)kzalloc(sizeof(*wl->scan_req_int), GFP_KERNEL); if (unlikely(!wl->scan_req_int)) { WL_ERR(("Scan req alloc failed\n")); goto init_priv_mem_out; } - wl->ioctl_buf = (void *)kzalloc(WL_IOCTL_LEN_MAX, GFP_KERNEL); + wl->ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL); if (unlikely(!wl->ioctl_buf)) { WL_ERR(("Ioctl buf alloc failed\n")); goto init_priv_mem_out; @@ -5049,11 +5892,6 @@ static s32 wl_init_priv_mem(struct wl_priv *wl) WL_ERR(("Iscan buf alloc failed\n")); goto init_priv_mem_out; } - wl->fw = (void *)kzalloc(sizeof(*wl->fw), GFP_KERNEL); - if (unlikely(!wl->fw)) { - WL_ERR(("fw object alloc failed\n")); - goto init_priv_mem_out; - } wl->pmk_list = (void *)kzalloc(sizeof(*wl->pmk_list), GFP_KERNEL); if (unlikely(!wl->pmk_list)) { WL_ERR(("pmk list alloc failed\n")); @@ -5064,6 +5902,14 @@ static s32 wl_init_priv_mem(struct wl_priv *wl) WL_ERR(("sta info alloc failed\n")); goto init_priv_mem_out; } + wl->afx_hdl = (void *)kzalloc(sizeof(*wl->afx_hdl), GFP_KERNEL); + if (unlikely(!wl->afx_hdl)) { + WL_ERR(("afx hdl alloc failed\n")); + goto init_priv_mem_out; + } else { + init_completion(&wl->act_frm_scan); + INIT_WORK(&wl->afx_hdl->work, wl_cfg80211_afx_handler); + } return 0; init_priv_mem_out: @@ -5076,12 +5922,8 @@ static void wl_deinit_priv_mem(struct wl_priv *wl) { kfree(wl->scan_results); wl->scan_results = NULL; - kfree(wl->bss_info); - wl->bss_info = NULL; kfree(wl->conf); wl->conf = NULL; - kfree(wl->profile); - wl->profile = NULL; kfree(wl->scan_req_int); wl->scan_req_int = NULL; kfree(wl->ioctl_buf); @@ -5092,12 +5934,16 @@ static void wl_deinit_priv_mem(struct wl_priv *wl) wl->extra_buf = NULL; kfree(wl->iscan); wl->iscan = NULL; - kfree(wl->fw); - wl->fw = NULL; kfree(wl->pmk_list); wl->pmk_list = NULL; kfree(wl->sta_info); wl->sta_info = NULL; + if (wl->afx_hdl) { + cancel_work_sync(&wl->afx_hdl->work); + kfree(wl->afx_hdl); + wl->afx_hdl = NULL; + } + if (wl->ap_info) { kfree(wl->ap_info->wpa_ie); kfree(wl->ap_info->rsn_ie); @@ -5112,7 +5958,8 @@ static s32 wl_create_event_handler(struct wl_priv *wl) int ret = 0; WL_DBG(("Enter \n")); - wl->event_tsk.thr_pid = DHD_PID_KT_INVALID; + /* Do not use DHD in cfg driver */ + wl->event_tsk.thr_pid = -1; PROC_START(wl_event_handler, wl, &wl->event_tsk, 0); if (wl->event_tsk.thr_pid < 0) ret = -ENOMEM; @@ -5142,21 +5989,22 @@ static void wl_term_iscan(struct wl_priv *wl) static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) { struct wl_priv *wl = iscan_to_wl(iscan); + struct net_device *ndev = wl_to_prmry_ndev(wl); unsigned long flags; WL_DBG(("Enter \n")); - if (unlikely(!wl_get_drv_status(wl, SCANNING))) { - wl_clr_drv_status(wl, SCANNING); + if (!wl_get_drv_status(wl, SCANNING, ndev)) { + wl_clr_drv_status(wl, SCANNING, ndev); WL_ERR(("Scan complete while device not scanning\n")); return; } - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); - wl_clr_drv_status(wl, SCANNING); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + wl_clr_drv_status(wl, SCANNING, ndev); if (likely(wl->scan_request)) { cfg80211_scan_done(wl->scan_request, aborted); wl->scan_request = NULL; } - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); wl->iscan_kickstart = false; } @@ -5192,7 +6040,7 @@ wl_get_iscan_results(struct wl_iscan_ctrl *iscan, u32 *status, list.results.buflen = htod32(WL_ISCAN_BUF_MAX); err = wldev_iovar_getbuf(iscan->dev, "iscanresults", &list, WL_ISCAN_RESULTS_FIXED_SIZE, iscan->scan_buf, - WL_ISCAN_BUF_MAX); + WL_ISCAN_BUF_MAX, NULL); if (unlikely(err)) { WL_ERR(("error (%d)\n", err)); return err; @@ -5265,13 +6113,11 @@ static s32 wl_iscan_aborted(struct wl_priv *wl) static s32 wl_iscan_thread(void *data) { - struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1 }; struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; struct wl_priv *wl = iscan_to_wl(iscan); u32 status; int err = 0; - sched_setscheduler(current, SCHED_FIFO, ¶m); allow_signal(SIGTERM); status = WL_SCAN_RESULTS_PARTIAL; while (likely(!down_interruptible(&iscan->sync))) { @@ -5306,7 +6152,7 @@ static void wl_scan_timeout(unsigned long data) if (wl->scan_request) { WL_ERR(("timer expired\n")); if (wl->escan_on) - wl_notify_escan_complete(wl, true); + wl_notify_escan_complete(wl, wl->escan_info.ndev, true, false); else wl_notify_iscan_complete(wl_to_iscan(wl), true); } @@ -5352,21 +6198,109 @@ static void wl_init_iscan_handler(struct wl_iscan_ctrl *iscan) iscan->iscan_handler[WL_SCAN_RESULTS_NO_MEM] = wl_iscan_aborted; } -static void wl_notify_escan_complete(struct wl_priv *wl, bool aborted) +static s32 +wl_cfg80211_netdev_notifier_call(struct notifier_block * nb, + unsigned long state, + void *ndev) +{ + struct net_device *dev = ndev; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wl_priv *wl = wlcfg_drv_priv; + + WL_DBG(("Enter \n")); + if (!wdev || !wl || dev == wl_to_prmry_ndev(wl)) + return NOTIFY_DONE; + switch (state) { + case NETDEV_UNREGISTER: + /* after calling list_del_rcu(&wdev->list) */ + wl_dealloc_netinfo(wl, ndev); + break; + case NETDEV_GOING_DOWN: + /* At NETDEV_DOWN state, wdev_cleanup_work work will be called. + * In front of door, the function checks + * whether current scan is working or not. + * If the scanning is still working, wdev_cleanup_work call WARN_ON and + * make the scan done forcibly. + */ + if (wl_get_drv_status(wl, SCANNING, dev)) { + if (wl->escan_on) { + wl_notify_escan_complete(wl, dev, true, true); + } + } + break; + } + return NOTIFY_DONE; +} +static struct notifier_block wl_cfg80211_netdev_notifier = { + .notifier_call = wl_cfg80211_netdev_notifier_call, +}; + +static s32 wl_notify_escan_complete(struct wl_priv *wl, + struct net_device *ndev, + bool aborted, bool fw_abort) { + wl_scan_params_t *params = NULL; + s32 params_size = 0; + s32 err = BCME_OK; unsigned long flags; + struct net_device *dev; WL_DBG(("Enter \n")); - wl_clr_drv_status(wl, SCANNING); - if (wl->p2p_supported && p2p_on(wl)) - wl_clr_p2p_status(wl, SCANNING); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + if (wl->scan_request) { + if (wl->scan_request->dev == wl->p2p_net) + dev = wl_to_prmry_ndev(wl); + else + dev = wl->scan_request->dev; + } + else { + WL_ERR(("wl->scan_request is NULL may be internal scan." + "doing scan_abort for ndev %p primary %p p2p_net %p", + ndev, wl_to_prmry_ndev(wl), wl->p2p_net)); + dev = ndev; + } + if (fw_abort && !in_atomic()) { + /* Our scan params only need space for 1 channel and 0 ssids */ + params = wl_cfg80211_scan_alloc_params(-1, 0, ¶ms_size); + if (params == NULL) { + WL_ERR(("scan params allocation failed \n")); + err = -ENOMEM; + } else { + /* Do a scan abort to stop the driver's scan engine */ + err = wldev_ioctl(dev, WLC_SCAN, params, params_size, true); + if (err < 0) { + WL_ERR(("scan abort failed \n")); + } + } + } + if (timer_pending(&wl->scan_timeout)) + del_timer_sync(&wl->scan_timeout); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + +#ifdef WL_SCHED_SCAN + if (wl->sched_scan_req && !wl->scan_request) { + WL_DBG((" REPORTING SCHED SCAN RESULTS \n")); + if (aborted) + cfg80211_sched_scan_stopped(wl->sched_scan_req->wiphy); + else + cfg80211_sched_scan_results(wl->sched_scan_req->wiphy); + wl->sched_scan_running = FALSE; + wl->sched_scan_req = NULL; + } +#endif /* WL_SCHED_SCAN */ + if (likely(wl->scan_request)) { cfg80211_scan_done(wl->scan_request, aborted); wl->scan_request = NULL; } - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + if (p2p_is_on(wl)) + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, dev); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + if (params) + kfree(params); + + return err; } static s32 wl_escan_handler(struct wl_priv *wl, @@ -5381,11 +6315,21 @@ static s32 wl_escan_handler(struct wl_priv *wl, wl_scan_results_t *list; u32 bi_length; u32 i; + WL_DBG((" enter event type : %d, status : %d \n", ntoh32(e->event_type), ntoh32(e->status))); - if (!wl->escan_on && - !wl_get_drv_status(wl, SCANNING)) { - WL_ERR(("escan is not ready \n")); + /* P2P SCAN is coming from primary interface */ + if (wl_get_p2p_status(wl, SCANNING)) { + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) + ndev = wl->afx_hdl->dev; + else + ndev = wl->escan_info.ndev; + + } + if (!ndev || !wl->escan_on || + !wl_get_drv_status(wl, SCANNING, ndev)) { + WL_ERR(("escan is not ready ndev %p wl->escan_on %d drv_status 0x%x\n", + ndev, wl->escan_on, wl_get_drv_status(wl, SCANNING, ndev))); return err; } @@ -5410,77 +6354,114 @@ static s32 wl_escan_handler(struct wl_priv *wl, WL_ERR(("Invalid bss_info length %d: ignoring\n", bi_length)); goto exit; } - list = (wl_scan_results_t *)wl->escan_info.escan_buf; - if (bi_length > ESCAN_BUF_SIZE - list->buflen) { - WL_ERR(("Buffer is too small: ignoring\n")); - goto exit; + + if (!(wl_to_wiphy(wl)->interface_modes & BIT(NL80211_IFTYPE_ADHOC))) { + if (dtoh16(bi->capability) & DOT11_CAP_IBSS) { + WL_DBG(("Ignoring IBSS result\n")); + goto exit; + } } -#define WLC_BSS_RSSI_ON_CHANNEL 0x0002 - for (i = 0; i < list->count; i++) { - bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) - : list->bss_info; - - if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) && - CHSPEC_BAND(bi->chanspec) == CHSPEC_BAND(bss->chanspec) && - bi->SSID_len == bss->SSID_len && - !bcmp(bi->SSID, bss->SSID, bi->SSID_len)) { - if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) == - (bi->flags & WLC_BSS_RSSI_ON_CHANNEL)) { - /* preserve max RSSI if the measurements are - * both on-channel or both off-channel - */ - bss->RSSI = MAX(bss->RSSI, bi->RSSI); - } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) && - (bi->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) { - /* preserve the on-channel rssi measurement - * if the new measurement is off channel - */ - bss->RSSI = bi->RSSI; - bss->flags |= WLC_BSS_RSSI_ON_CHANNEL; - } + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + if (!memcmp(bi->BSSID.octet, + wl->afx_hdl->pending_tx_dst_addr.octet, ETHER_ADDR_LEN)) { + s32 channel = CHSPEC_CHANNEL(dtohchanspec(bi->chanspec)); + WL_DBG(("ACTION FRAME SCAN : Peer found, channel : %d\n", channel)); + wl_clr_p2p_status(wl, SCANNING); + wl->afx_hdl->peer_chan = channel; + complete(&wl->act_frm_scan); + goto exit; + } + + } else { + list = (wl_scan_results_t *)wl->escan_info.escan_buf; + if (bi_length > ESCAN_BUF_SIZE - list->buflen) { + WL_ERR(("Buffer is too small: ignoring\n")); goto exit; } +#define WLC_BSS_RSSI_ON_CHANNEL 0x0002 + for (i = 0; i < list->count; i++) { + bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) + : list->bss_info; + + if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) && + CHSPEC_BAND(bi->chanspec) == CHSPEC_BAND(bss->chanspec) && + bi->SSID_len == bss->SSID_len && + !bcmp(bi->SSID, bss->SSID, bi->SSID_len)) { + if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) == + (bi->flags & WLC_BSS_RSSI_ON_CHANNEL)) { + /* preserve max RSSI if the measurements are + * both on-channel or both off-channel + */ + bss->RSSI = MAX(bss->RSSI, bi->RSSI); + } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) && + (bi->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) { + /* preserve the on-channel rssi measurement + * if the new measurement is off channel + */ + bss->RSSI = bi->RSSI; + bss->flags |= WLC_BSS_RSSI_ON_CHANNEL; + } + + goto exit; + } + } + memcpy(&(wl->escan_info.escan_buf[list->buflen]), bi, bi_length); + list->version = dtoh32(bi->version); + list->buflen += bi_length; + list->count++; + } - memcpy(&(wl->escan_info.escan_buf[list->buflen]), bi, bi_length); - list->version = dtoh32(bi->version); - list->buflen += bi_length; - list->count++; } else if (status == WLC_E_STATUS_SUCCESS) { wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - if (likely(wl->scan_request)) { + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { mutex_lock(&wl->usr_sync); - del_timer_sync(&wl->scan_timeout); WL_INFO(("ESCAN COMPLETED\n")); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); - wl_notify_escan_complete(wl, false); + wl_notify_escan_complete(wl, ndev, false, false); mutex_unlock(&wl->usr_sync); } } else if (status == WLC_E_STATUS_ABORT) { wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - if (likely(wl->scan_request)) { + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + wl_clr_p2p_status(wl, SCANNING); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { mutex_lock(&wl->usr_sync); - del_timer_sync(&wl->scan_timeout); WL_INFO(("ESCAN ABORTED\n")); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); - wl_notify_escan_complete(wl, true); + wl_notify_escan_complete(wl, ndev, true, false); mutex_unlock(&wl->usr_sync); } } else { WL_ERR(("unexpected Escan Event %d : abort\n", status)); wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - if (likely(wl->scan_request)) { + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { mutex_lock(&wl->usr_sync); - del_timer_sync(&wl->scan_timeout); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); - wl_notify_escan_complete(wl, true); + wl_notify_escan_complete(wl, ndev, true, false); mutex_unlock(&wl->usr_sync); } } @@ -5521,16 +6502,11 @@ static s32 wl_init_scan(struct wl_priv *wl) return err; } -static void wl_init_fw(struct wl_fw_ctrl *fw) -{ - fw->status = 0; -} - static s32 wl_init_priv(struct wl_priv *wl) { struct wiphy *wiphy = wl_to_wiphy(wl); + struct net_device *ndev = wl_to_prmry_ndev(wl); s32 err = 0; - s32 i = 0; wl->scan_request = NULL; wl->pwr_save = !!(wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT); @@ -5539,60 +6515,78 @@ static s32 wl_init_priv(struct wl_priv *wl) wl->roam_on = false; wl->iscan_kickstart = false; wl->active_scan = true; - wl->dongle_up = false; wl->rf_blocked = false; - - for (i = 0; i < VWDEV_CNT; i++) - wl->vwdev[i] = NULL; - - init_waitqueue_head(&wl->dongle_event_wait); + wl->deauth_reason = 0; + spin_lock_init(&wl->cfgdrv_lock); + mutex_init(&wl->ioctl_buf_sync); + init_waitqueue_head(&wl->netif_change_event); wl_init_eq(wl); err = wl_init_priv_mem(wl); - if (unlikely(err)) + if (err) return err; - if (unlikely(wl_create_event_handler(wl))) + if (wl_create_event_handler(wl)) return -ENOMEM; wl_init_event_handler(wl); mutex_init(&wl->usr_sync); err = wl_init_scan(wl); - if (unlikely(err)) + if (err) return err; - wl_init_fw(wl->fw); wl_init_conf(wl->conf); - wl_init_prof(wl); + wl_init_prof(wl, ndev); wl_link_down(wl); + DNGL_FUNC(dhd_cfg80211_init, (wl)); return err; } static void wl_deinit_priv(struct wl_priv *wl) { + DNGL_FUNC(dhd_cfg80211_deinit, (wl)); wl_destroy_event_handler(wl); - wl->dongle_up = false; /* dongle down */ wl_flush_eq(wl); wl_link_down(wl); del_timer_sync(&wl->scan_timeout); wl_term_iscan(wl); wl_deinit_priv_mem(wl); + unregister_netdevice_notifier(&wl_cfg80211_netdev_notifier); } -#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) -s32 wl_cfg80211_sysctl_export_devaddr(void *data) +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) +static s32 wl_cfg80211_attach_p2p(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + + WL_TRACE(("Enter \n")); + + if (wl_cfgp2p_register_ndev(wl) < 0) { + WL_ERR(("%s: P2P attach failed. \n", __func__)); + return -ENODEV; + } + + return 0; +} + +static s32 wl_cfg80211_detach_p2p(void) { - /* Export the p2p_dev_addr via sysctl interface - * so that wpa_supplicant can access it - */ - dhd_pub_t *dhd = (dhd_pub_t *)data; struct wl_priv *wl = wlcfg_drv_priv; + struct wireless_dev *wdev = wl->p2p_wdev; - wl_cfgp2p_generate_bss_mac(&dhd->mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); + WL_DBG(("Enter \n")); + if (!wdev || !wl) { + WL_ERR(("Invalid Ptr\n")); + return -EINVAL; + } - sprintf((char *)&wl_sysctl_macstring[0], MACSTR, MAC2STR(wl->p2p->dev_addr.octet)); - sprintf((char *)&wl_sysctl_macstring[1], MACSTR, MAC2STR(wl->p2p->int_addr.octet)); + wl_cfgp2p_unregister_ndev(wl); + + wl->p2p_wdev = NULL; + wl->p2p_net = NULL; + WL_DBG(("Freeing 0x%08x \n", (unsigned int)wdev)); + kfree(wdev); return 0; } -#endif /* CONFIG_SYSCTL */ +#endif /* defined(WLP2P) && defined(WL_ENABLE_P2P_IF) */ s32 wl_cfg80211_attach_post(struct net_device *ndev) { @@ -5604,75 +6598,107 @@ s32 wl_cfg80211_attach_post(struct net_device *ndev) return -ENODEV; } wl = wlcfg_drv_priv; - if (wl && !wl_get_drv_status(wl, READY)) { + if (wl && !wl_get_drv_status(wl, READY, ndev)) { if (wl->wdev && wl_cfgp2p_supported(wl, ndev)) { +#if !defined(WL_ENABLE_P2P_IF) wl->wdev->wiphy->interface_modes |= (BIT(NL80211_IFTYPE_P2P_CLIENT)| BIT(NL80211_IFTYPE_P2P_GO)); +#endif if ((err = wl_cfgp2p_init_priv(wl)) != 0) goto fail; -#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) - wl_cfg80211_sysctl_export_devaddr(wl->pub); -#endif + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + if (wl->p2p_net) { + /* Update MAC addr for p2p0 interface here. */ + memcpy(wl->p2p_net->dev_addr, ndev->dev_addr, ETH_ALEN); + wl->p2p_net->dev_addr[0] |= 0x02; + printk("%s: p2p_dev_addr="MACSTR "\n", + wl->p2p_net->name, MAC2STR(wl->p2p_net->dev_addr)); + } else { + WL_ERR(("p2p_net not yet populated." + " Couldn't update the MAC Address for p2p0 \n")); + return -ENODEV; + } +#endif /* defined(WLP2P) && (WL_ENABLE_P2P_IF) */ + wl->p2p_supported = true; } } else return -ENODEV; - - wl_set_drv_status(wl, READY); + wl_set_drv_status(wl, READY, ndev); fail: return err; } + s32 wl_cfg80211_attach(struct net_device *ndev, void *data) { struct wireless_dev *wdev; struct wl_priv *wl; s32 err = 0; + struct device *dev; WL_TRACE(("In\n")); - if (unlikely(!ndev)) { + if (!ndev) { WL_ERR(("ndev is invaild\n")); return -ENODEV; } - WL_DBG(("func %p\n", wl_cfg80211_get_sdio_func())); - wdev = wl_alloc_wdev(&wl_cfg80211_get_sdio_func()->dev); - if (unlikely(IS_ERR(wdev))) - return -ENOMEM; + WL_DBG(("func %p\n", wl_cfg80211_get_parent_dev())); + dev = wl_cfg80211_get_parent_dev(); + wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); + if (unlikely(!wdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return -ENOMEM; + } + err = wl_setup_wiphy(wdev, dev); + if (unlikely(err)) { + kfree(wdev); + return -ENOMEM; + } wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); wl = (struct wl_priv *)wiphy_priv(wdev->wiphy); wl->wdev = wdev; wl->pub = data; - + INIT_LIST_HEAD(&wl->net_list); ndev->ieee80211_ptr = wdev; SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); wdev->netdev = ndev; - + err = wl_alloc_netinfo(wl, ndev, wdev, WL_MODE_BSS); + if (err) { + WL_ERR(("Failed to alloc net_info (%d)\n", err)); + goto cfg80211_attach_out; + } err = wl_init_priv(wl); - if (unlikely(err)) { + if (err) { WL_ERR(("Failed to init iwm_priv (%d)\n", err)); goto cfg80211_attach_out; } err = wl_setup_rfkill(wl, TRUE); - if (unlikely(err)) { + if (err) { WL_ERR(("Failed to setup rfkill %d\n", err)); goto cfg80211_attach_out; } - -#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) - if (!(wl_sysctl_hdr = register_sysctl_table(wl_sysctl_table))) { - WL_ERR(("%s: sysctl register failed!! \n", __func__)); + err = register_netdevice_notifier(&wl_cfg80211_netdev_notifier); + if (err) { + WL_ERR(("Failed to register notifierl %d\n", err)); goto cfg80211_attach_out; } -#endif #if defined(COEX_DHCP) if (wl_cfg80211_btcoex_init(wl)) goto cfg80211_attach_out; -#endif /* COEX_DHCP */ +#endif wlcfg_drv_priv = wl; + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + err = wl_cfg80211_attach_p2p(); + if (err) + goto cfg80211_attach_out; +#endif + return err; cfg80211_attach_out: @@ -5681,7 +6707,7 @@ cfg80211_attach_out: return err; } -void wl_cfg80211_detach(void) +void wl_cfg80211_detach(void *para) { struct wl_priv *wl; @@ -5691,19 +6717,21 @@ void wl_cfg80211_detach(void) #if defined(COEX_DHCP) wl_cfg80211_btcoex_deinit(wl); -#endif /* COEX_DHCP */ +#endif -#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) - if (wl_sysctl_hdr) - unregister_sysctl_table(wl_sysctl_hdr); +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + wl_cfg80211_detach_p2p(); #endif wl_setup_rfkill(wl, FALSE); if (wl->p2p_supported) wl_cfgp2p_deinit_priv(wl); wl_deinit_priv(wl); wlcfg_drv_priv = NULL; - wl_clear_sdio_func(); + wl_cfg80211_clear_parent_dev(); wl_free_wdev(wl); + /* PLEASE do NOT call any function after wl_free_wdev, the driver's private structure "wl", + * which is the private part of wiphy, has been freed in wl_free_wdev !!!!!!!!!!! + */ } static void wl_wakeup_event(struct wl_priv *wl) @@ -5714,6 +6742,42 @@ static void wl_wakeup_event(struct wl_priv *wl) } } +static int wl_is_p2p_event(struct wl_event_q *e) +{ + switch (e->etype) { + /* We have to seperate out the P2P events received + * on primary interface so that it can be send up + * via p2p0 interface. + */ + case WLC_E_P2P_PROBREQ_MSG: + case WLC_E_P2P_DISC_LISTEN_COMPLETE: + case WLC_E_ACTION_FRAME_RX: + case WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE: + case WLC_E_ACTION_FRAME_COMPLETE: + + if (e->emsg.ifidx != 0) { + WL_TRACE(("P2P Event on Virtual I/F (ifidx:%d) \n", + e->emsg.ifidx)); + /* We are only bothered about the P2P events received + * on primary interface. For rest of them return false + * so that it is sent over the interface corresponding + * to the ifidx. + */ + return FALSE; + } else { + WL_TRACE(("P2P Event on Primary I/F (ifidx:%d)." + " Sent it to p2p0 \n", e->emsg.ifidx)); + return TRUE; + } + break; + + default: + WL_TRACE(("NON-P2P Event %d on ifidx (ifidx:%d) \n", + e->etype, e->emsg.ifidx)); + return FALSE; + } +} + static s32 wl_event_handler(void *data) { struct net_device *netdev; @@ -5722,6 +6786,7 @@ static s32 wl_event_handler(void *data) tsk_ctl_t *tsk = (tsk_ctl_t *)data; wl = (struct wl_priv *)tsk->parent; + DAEMONIZE("dhd_cfg80211_event"); complete(&tsk->completed); while (down_interruptible (&tsk->sema) == 0) { @@ -5730,7 +6795,15 @@ static s32 wl_event_handler(void *data) break; while ((e = wl_deq_event(wl))) { WL_DBG(("event type (%d), if idx: %d\n", e->etype, e->emsg.ifidx)); - netdev = dhd_idx2net((struct dhd_pub *)(wl->pub), e->emsg.ifidx); + /* All P2P device address related events comes on primary interface since + * there is no corresponding bsscfg for P2P interface. Map it to p2p0 + * interface. + */ + if ((wl_is_p2p_event(e) == TRUE) && (wl->p2p_net)) { + netdev = wl->p2p_net; + } else { + netdev = dhd_idx2net((struct dhd_pub *)(wl->pub), e->emsg.ifidx); + } if (!netdev) netdev = wl_to_prmry_ndev(wl); if (e->etype < WLC_E_LAST && wl->evt_handler[e->etype]) { @@ -5742,7 +6815,7 @@ static s32 wl_event_handler(void *data) } DHD_OS_WAKE_UNLOCK(wl->pub); } - WL_DBG(("%s was terminated\n", __func__)); + WL_ERR(("%s was terminated\n", __func__)); complete_and_exit(&tsk->completed, 0); return 0; } @@ -5759,9 +6832,6 @@ wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t * e, void *data) WL_DBG(("event_type (%d):" "WLC_E_" "%s\n", event_type, estr)); #endif /* (WL_DBG_LEVEL > 0) */ - if (event_type == WLC_E_PFN_NET_FOUND) - WL_ERR((" PNO Event\n")); - if (likely(!wl_enq_event(wl, ndev, event_type, e, data))) wl_wakeup_event(wl); } @@ -5846,22 +6916,7 @@ static void wl_put_event(struct wl_event_q *e) kfree(e); } -void wl_cfg80211_set_sdio_func(void *func) -{ - cfg80211_sdio_func = (struct sdio_func *)func; -} - -static void wl_clear_sdio_func(void) -{ - cfg80211_sdio_func = NULL; -} - -struct sdio_func *wl_cfg80211_get_sdio_func(void) -{ - return cfg80211_sdio_func; -} - -static s32 wl_dongle_mode(struct wl_priv *wl, struct net_device *ndev, s32 iftype) +static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype) { s32 infra = 0; s32 err = 0; @@ -5898,11 +6953,12 @@ static s32 wl_dongle_mode(struct wl_priv *wl, struct net_device *ndev, s32 iftyp return err; } - set_mode_by_netdev(wl, ndev, mode); + wl_set_mode_by_netdev(wl, ndev, mode); return 0; } -static s32 wl_dongle_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add) + +static s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add) { s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; @@ -5915,7 +6971,7 @@ static s32 wl_dongle_add_remove_eventmsg(struct net_device *ndev, u16 event, boo err = wldev_ioctl(ndev, WLC_GET_VAR, iovbuf, sizeof(iovbuf), false); if (unlikely(err)) { WL_ERR(("Get event_msgs error (%d)\n", err)); - goto dongle_eventmsg_out; + goto eventmsg_out; } memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN); if (add) { @@ -5928,389 +6984,230 @@ static s32 wl_dongle_add_remove_eventmsg(struct net_device *ndev, u16 event, boo err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); if (unlikely(err)) { WL_ERR(("Set event_msgs error (%d)\n", err)); - goto dongle_eventmsg_out; + goto eventmsg_out; } -dongle_eventmsg_out: - return err; - -} - - -#ifndef EMBEDDED_PLATFORM -static s32 wl_dongle_country(struct net_device *ndev, u8 ccode) -{ - - s32 err = 0; - +eventmsg_out: return err; -} - -static s32 wl_dongle_up(struct net_device *ndev, u32 up) -{ - s32 err = 0; - err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true); - if (unlikely(err)) { - WL_ERR(("WLC_UP error (%d)\n", err)); - } - return err; } -static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode) +static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) { - s32 err = 0; - - WL_TRACE(("In\n")); - err = wldev_ioctl(ndev, WLC_SET_PM, &power_mode, sizeof(power_mode), true); - if (unlikely(err)) { - WL_ERR(("WLC_SET_PM error (%d)\n", err)); + struct net_device *dev = wl_to_prmry_ndev(wl); + struct ieee80211_channel *band_chan_arr = NULL; + wl_uint32_list_t *list; + u32 i, j, index, n_2g, n_5g, band, channel, array_size; + u32 *n_cnt = NULL; + chanspec_t c = 0; + s32 err = BCME_OK; + bool update; + bool ht40_allowed; + u8 *pbuf = NULL; + +#define LOCAL_BUF_LEN 1024 + pbuf = kzalloc(LOCAL_BUF_LEN, GFP_KERNEL); + if (pbuf == NULL) { + WL_ERR(("failed to allocate local buf\n")); + return -ENOMEM; } - return err; -} - -static s32 -wl_dongle_glom(struct net_device *ndev, u32 glom, u32 dongle_align) -{ - s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; - s32 err = 0; + list = (wl_uint32_list_t *)(void *)pbuf; + list->count = htod32(WL_NUMCHANSPECS); - /* Match Host and Dongle rx alignment */ - bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, - sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (unlikely(err)) { - WL_ERR(("txglomalign error (%d)\n", err)); - goto dongle_glom_out; - } - /* disable glom option per default */ - bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (unlikely(err)) { - WL_ERR(("txglom error (%d)\n", err)); - goto dongle_glom_out; + err = wldev_iovar_getbuf_bsscfg(dev, "chanspecs", NULL, + 0, pbuf, LOCAL_BUF_LEN, 0, &wl->ioctl_buf_sync); + if (err != 0) { + WL_ERR(("get chanspecs failed with %d\n", err)); + kfree(pbuf); + return err; } -dongle_glom_out: - return err; -} - -static s32 -wl_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout) -{ - s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; - - s32 err = 0; - - /* Setup timeout if Beacons are lost and roam is off to report link down */ - if (roamvar) { - bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, - sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (unlikely(err)) { - WL_ERR(("bcn_timeout error (%d)\n", err)); - goto dongle_rom_out; +#undef LOCAL_BUF_LEN + + band = array_size = n_2g = n_5g = 0; + for (i = 0; i < dtoh32(list->count); i++) { + index = 0; + update = FALSE; + ht40_allowed = FALSE; + c = (chanspec_t)dtoh32(list->element[i]); + channel = CHSPEC_CHANNEL(c); + if (CHSPEC_IS40(c)) { + if (CHSPEC_SB_UPPER(c)) + channel += CH_10MHZ_APART; + else + channel -= CH_10MHZ_APART; } - } - /* Enable/Disable built-in roaming to allow supplicant to take care of roaming */ - bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (unlikely(err)) { - WL_ERR(("roam_off error (%d)\n", err)); - goto dongle_rom_out; - } -dongle_rom_out: - return err; -} -static s32 -wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, - s32 scan_unassoc_time) -{ - s32 err = 0; - - err = wldev_ioctl(ndev, WLC_SET_SCAN_CHANNEL_TIME, &scan_assoc_time, - sizeof(scan_assoc_time), true); - if (err) { - if (err == -EOPNOTSUPP) { - WL_INFO(("Scan assoc time is not supported\n")); - } else { - WL_ERR(("Scan assoc time error (%d)\n", err)); - } - goto dongle_scantime_out; - } - err = wldev_ioctl(ndev, WLC_SET_SCAN_UNASSOC_TIME, &scan_unassoc_time, - sizeof(scan_unassoc_time), true); - if (err) { - if (err == -EOPNOTSUPP) { - WL_INFO(("Scan unassoc time is not supported\n")); - } else { - WL_ERR(("Scan unassoc time error (%d)\n", err)); + if (CHSPEC_IS2G(c) && channel <= CH_MAX_2G_CHANNEL) { + band_chan_arr = __wl_2ghz_channels; + array_size = ARRAYSIZE(__wl_2ghz_channels); + n_cnt = &n_2g; + band = IEEE80211_BAND_2GHZ; + ht40_allowed = (bw_cap == WLC_N_BW_40ALL) ? TRUE : FALSE; + } else if (CHSPEC_IS5G(c) && channel > CH_MAX_2G_CHANNEL) { + band_chan_arr = __wl_5ghz_a_channels; + array_size = ARRAYSIZE(__wl_5ghz_a_channels); + n_cnt = &n_5g; + band = IEEE80211_BAND_5GHZ; + ht40_allowed = (bw_cap == WLC_N_BW_20ALL) ? FALSE : TRUE; } - goto dongle_scantime_out; - } - -dongle_scantime_out: - return err; -} - -static s32 -wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol) -{ - /* Room for "event_msgs" + '\0' + bitvec */ - s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; - - s32 err = 0; - /* Set ARP offload */ - bcm_mkiovar("arpoe", (char *)&arpoe, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (err) { - if (err == -EOPNOTSUPP) - WL_INFO(("arpoe is not supported\n")); - else - WL_ERR(("arpoe error (%d)\n", err)); + for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { + if (band_chan_arr[j].hw_value == channel) { + update = TRUE; + break; + } + } - goto dongle_offload_out; - } - bcm_mkiovar("arp_ol", (char *)&arp_ol, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (err) { - if (err == -EOPNOTSUPP) - WL_INFO(("arp_ol is not supported\n")); + if (update) + index = j; else - WL_ERR(("arp_ol error (%d)\n", err)); - - goto dongle_offload_out; - } - -dongle_offload_out: - return err; -} - -static s32 wl_pattern_atoh(s8 *src, s8 *dst) -{ - int i; - if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) { - WL_ERR(("Mask invalid format. Needs to start with 0x\n")); - return -1; - } - src = src + 2; /* Skip past 0x */ - if (strlen(src) % 2 != 0) { - WL_ERR(("Mask invalid format. Needs to be of even length\n")); - return -1; - } - for (i = 0; *src != '\0'; i++) { - char num[3]; - strncpy(num, src, 2); - num[2] = '\0'; - dst[i] = (u8) simple_strtoul(num, NULL, 16); - src += 2; - } - return i; -} - -static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode) -{ - /* Room for "event_msgs" + '\0' + bitvec */ - s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; - - const s8 *str; - struct wl_pkt_filter pkt_filter; - struct wl_pkt_filter *pkt_filterp; - s32 buf_len; - s32 str_len; - u32 mask_size; - u32 pattern_size; - s8 buf[256]; - s32 err = 0; - - /* add a default packet filter pattern */ - str = "pkt_filter_add"; - str_len = strlen(str); - strncpy(buf, str, str_len); - buf[str_len] = '\0'; - buf_len = str_len + 1; - - pkt_filterp = (struct wl_pkt_filter *)(buf + str_len + 1); - - /* Parse packet filter id. */ - pkt_filter.id = htod32(100); - - /* Parse filter polarity. */ - pkt_filter.negate_match = htod32(0); - - /* Parse filter type. */ - pkt_filter.type = htod32(0); - - /* Parse pattern filter offset. */ - pkt_filter.u.pattern.offset = htod32(0); - - /* Parse pattern filter mask. */ - mask_size = htod32(wl_pattern_atoh("0xff", - (char *)pkt_filterp->u.pattern. - mask_and_pattern)); + index = *n_cnt; - /* Parse pattern filter pattern. */ - pattern_size = htod32(wl_pattern_atoh("0x00", - (char *)&pkt_filterp->u.pattern.mask_and_pattern[mask_size])); - - if (mask_size != pattern_size) { - WL_ERR(("Mask and pattern not the same size\n")); - err = -EINVAL; - goto dongle_filter_out; - } - - pkt_filter.u.pattern.size_bytes = mask_size; - buf_len += WL_PKT_FILTER_FIXED_LEN; - buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size); - - /* Keep-alive attributes are set in local - * variable (keep_alive_pkt), and - * then memcpy'ed into buffer (keep_alive_pktp) since there is no - * guarantee that the buffer is properly aligned. - */ - memcpy((char *)pkt_filterp, &pkt_filter, - WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN); + if (index < array_size) { +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + band_chan_arr[index].center_freq = + ieee80211_channel_to_frequency(channel); +#else + band_chan_arr[index].center_freq = + ieee80211_channel_to_frequency(channel, band); +#endif + band_chan_arr[index].hw_value = channel; + + if (CHSPEC_IS40(c) && ht40_allowed) { + u32 ht40_flag = band_chan_arr[index].flags & IEEE80211_CHAN_NO_HT40; + if (CHSPEC_SB_UPPER(c)) { + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags &= ~IEEE80211_CHAN_NO_HT40; + band_chan_arr[index].flags |= IEEE80211_CHAN_NO_HT40PLUS; + } else { + band_chan_arr[index].flags &= ~IEEE80211_CHAN_NO_HT40; + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags |= IEEE80211_CHAN_NO_HT40MINUS; + } + } else { + band_chan_arr[index].flags = IEEE80211_CHAN_NO_HT40; + if (band == IEEE80211_BAND_2GHZ) + channel |= WL_CHANSPEC_BAND_2G; + else + channel |= WL_CHANSPEC_BAND_5G; + err = wldev_iovar_getint(dev, "per_chan_info", &channel); + if (!err) { + if (channel & WL_CHAN_RADAR) { + band_chan_arr[index].flags |= IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS; + } + if (channel & WL_CHAN_PASSIVE) { + band_chan_arr[index].flags |= IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS; + } + } + } - err = wldev_ioctl(ndev, WLC_SET_VAR, buf, buf_len, true); - if (err) { - if (err == -EOPNOTSUPP) { - WL_INFO(("filter not supported\n")); - } else { - WL_ERR(("filter (%d)\n", err)); + if (!update) + (*n_cnt)++; } - goto dongle_filter_out; } - /* set mode to allow pattern */ - bcm_mkiovar("pkt_filter_mode", (char *)&filter_mode, 4, iovbuf, - sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); - if (err) { - if (err == -EOPNOTSUPP) { - WL_INFO(("filter_mode not supported\n")); - } else { - WL_ERR(("filter_mode (%d)\n", err)); - } - goto dongle_filter_out; - } + __wl_band_2ghz.n_channels = n_2g; + __wl_band_5ghz_a.n_channels = n_5g; -dongle_filter_out: + kfree(pbuf); return err; } -#endif /* !EMBEDDED_PLATFORM */ -s32 wl_config_dongle(struct wl_priv *wl, bool need_lock) +s32 wl_update_wiphybands(struct wl_priv *wl) { -#ifndef DHD_SDALIGN -#define DHD_SDALIGN 32 -#endif - struct net_device *ndev; - struct wireless_dev *wdev; + struct wiphy *wiphy; + struct net_device *dev; + u32 bandlist[3]; + u32 nband = 0; + u32 i = 0; s32 err = 0; + int nmode = 0; + int bw_cap = 0; + int index = 0; - WL_TRACE(("In\n")); - if (wl->dongle_up) { - WL_ERR(("Dongle is already up\n")); - return err; - } + WL_DBG(("Entry")); - ndev = wl_to_prmry_ndev(wl); - wdev = ndev->ieee80211_ptr; - if (need_lock) - rtnl_lock(); -#ifndef EMBEDDED_PLATFORM - err = wl_dongle_up(ndev, 0); - if (unlikely(err)) { - WL_ERR(("wl_dongle_up failed\n")); - goto default_conf_out; - } - err = wl_dongle_country(ndev, 0); - if (unlikely(err)) { - WL_ERR(("wl_dongle_country failed\n")); - goto default_conf_out; - } - err = wl_dongle_power(ndev, PM_FAST); - if (unlikely(err)) { - WL_ERR(("wl_dongle_power failed\n")); - goto default_conf_out; - } - err = wl_dongle_glom(ndev, 0, DHD_SDALIGN); - if (unlikely(err)) { - WL_ERR(("wl_dongle_glom failed\n")); - goto default_conf_out; - } - err = wl_dongle_roam(ndev, (wl->roam_on ? 0 : 1), 3); - if (unlikely(err)) { - WL_ERR(("wl_dongle_roam failed\n")); - goto default_conf_out; - } - wl_dongle_scantime(ndev, 40, 80); - wl_dongle_offload(ndev, 1, 0xf); - wl_dongle_filter(ndev, 1); -#endif /* !EMBEDDED_PLATFORM */ + if (wl == NULL) + wl = wlcfg_drv_priv; + dev = wl_to_prmry_ndev(wl); - err = wl_dongle_mode(wl, ndev, wdev->iftype); - if (unlikely(err && err != -EINPROGRESS)) { - WL_ERR(("wl_dongle_mode failed\n")); - goto default_conf_out; - } - err = wl_dongle_probecap(wl); + memset(bandlist, 0, sizeof(bandlist)); + err = wldev_ioctl(dev, WLC_GET_BANDLIST, bandlist, + sizeof(bandlist), false); if (unlikely(err)) { - WL_ERR(("wl_dongle_probecap failed\n")); - goto default_conf_out; + WL_ERR(("error read bandlist (%d)\n", err)); + return err; } + wiphy = wl_to_wiphy(wl); + nband = bandlist[0]; + wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; + wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - /* -EINPROGRESS: Call commit handler */ - -default_conf_out: - if (need_lock) - rtnl_unlock(); - - wl->dongle_up = true; - - return err; - -} - -static s32 wl_update_wiphybands(struct wl_priv *wl) -{ - struct wiphy *wiphy; - s8 phylist_buf[128]; - s8 *phy; - s32 err = 0; - - err = wldev_ioctl(wl_to_prmry_ndev(wl), WLC_GET_PHYLIST, phylist_buf, - sizeof(phylist_buf), false); + err = wldev_iovar_getint(dev, "nmode", &nmode); if (unlikely(err)) { - WL_ERR(("error (%d)\n", err)); - return err; + WL_ERR(("error reading nmode (%d)\n", err)); + } else { + /* For nmodeonly check bw cap */ + err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap); + if (unlikely(err)) { + WL_ERR(("error get mimo_bw_cap (%d)\n", err)); + } } - phy = phylist_buf; - for (; *phy; phy++) { - if (*phy == 'a' || *phy == 'n') { - wiphy = wl_to_wiphy(wl); + + for (i = 1; i <= nband && i < sizeof(bandlist)/sizeof(u32); i++) { + index = -1; + if (bandlist[i] == WLC_BAND_5G) { wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; + index = IEEE80211_BAND_5GHZ; + } else if (bandlist[i] == WLC_BAND_2G) { + wiphy->bands[IEEE80211_BAND_2GHZ] = + &__wl_band_2ghz; + index = IEEE80211_BAND_2GHZ; + } + if ((index >= 0) && nmode) { + wiphy->bands[index]->ht_cap.cap = + IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_DSSSCCK40 | + IEEE80211_HT_CAP_MAX_AMSDU; + wiphy->bands[index]->ht_cap.ht_supported = TRUE; + wiphy->bands[index]->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + wiphy->bands[index]->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; + } + + if ((index >= 0) && bw_cap) { + wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; } } + + wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); return err; } static s32 __wl_cfg80211_up(struct wl_priv *wl) { s32 err = 0; + struct net_device *ndev = wl_to_prmry_ndev(wl); + struct wireless_dev *wdev = ndev->ieee80211_ptr; - WL_TRACE(("In\n")); - wl_debugfs_add_netdev_params(wl); + WL_DBG(("In\n")); - err = wl_config_dongle(wl, false); + err = dhd_config_dongle(wl, false); if (unlikely(err)) return err; - dhd_monitor_init(wl->pub); - wl_invoke_iscan(wl); - wl_set_drv_status(wl, READY); + + err = wl_config_ifmode(wl, ndev, wdev->iftype); + if (unlikely(err && err != -EINPROGRESS)) { + WL_ERR(("wl_config_ifmode failed\n")); + } + err = wl_update_wiphybands(wl); + if (unlikely(err)) { + WL_ERR(("wl_update_wiphybands failed\n")); + } + + err = dhd_monitor_init(wl->pub); + err = wl_invoke_iscan(wl); + wl_set_drv_status(wl, READY, ndev); return err; } @@ -6318,52 +7215,65 @@ static s32 __wl_cfg80211_down(struct wl_priv *wl) { s32 err = 0; unsigned long flags; + struct net_info *iter, *next; + struct net_device *ndev = wl_to_prmry_ndev(wl); +#ifdef WL_ENABLE_P2P_IF + struct wiphy *wiphy = wl_to_prmry_ndev(wl)->ieee80211_ptr->wiphy; + struct net_device *p2p_net = wl->p2p_net; +#endif - WL_TRACE(("In\n")); + WL_DBG(("In\n")); /* Check if cfg80211 interface is already down */ - if (!wl_get_drv_status(wl, READY)) + if (!wl_get_drv_status(wl, READY, ndev)) return err; /* it is even not ready */ - - wl_set_drv_status(wl, SCAN_ABORTING); + for_each_ndev(wl, iter, next) + wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); wl_term_iscan(wl); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); if (wl->scan_request) { cfg80211_scan_done(wl->scan_request, true); wl->scan_request = NULL; } - wl_clr_drv_status(wl, READY); - wl_clr_drv_status(wl, SCANNING); - wl_clr_drv_status(wl, SCAN_ABORTING); - wl_clr_drv_status(wl, CONNECTING); - wl_clr_drv_status(wl, CONNECTED); - wl_clr_drv_status(wl, DISCONNECTING); - if (wl_get_drv_status(wl, AP_CREATED)) { - wl_clr_drv_status(wl, AP_CREATED); - wl_clr_drv_status(wl, AP_CREATING); + for_each_ndev(wl, iter, next) { + wl_clr_drv_status(wl, READY, iter->ndev); + wl_clr_drv_status(wl, SCANNING, iter->ndev); + wl_clr_drv_status(wl, SCAN_ABORTING, iter->ndev); + wl_clr_drv_status(wl, CONNECTING, iter->ndev); + wl_clr_drv_status(wl, CONNECTED, iter->ndev); + wl_clr_drv_status(wl, DISCONNECTING, iter->ndev); + wl_clr_drv_status(wl, AP_CREATED, iter->ndev); + wl_clr_drv_status(wl, AP_CREATING, iter->ndev); } wl_to_prmry_ndev(wl)->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); - - wl->dongle_up = false; +#ifdef WL_ENABLE_P2P_IF + wiphy->interface_modes = (wiphy->interface_modes) + & (~(BIT(NL80211_IFTYPE_P2P_CLIENT)| + BIT(NL80211_IFTYPE_P2P_GO))); + if ((p2p_net) && (p2p_net->flags & IFF_UP)) { + /* p2p0 interface is still UP. Bring it down */ + p2p_net->flags &= ~IFF_UP; + } +#endif /* WL_ENABLE_P2P_IF */ + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + + DNGL_FUNC(dhd_cfg80211_down, (wl)); wl_flush_eq(wl); wl_link_down(wl); if (wl->p2p_supported) wl_cfgp2p_down(wl); dhd_monitor_uninit(); - wl_debugfs_remove_netdev(wl); - return err; } -s32 wl_cfg80211_up(void) +s32 wl_cfg80211_up(void *para) { struct wl_priv *wl; s32 err = 0; - WL_TRACE(("In\n")); + WL_DBG(("In\n")); wl = wlcfg_drv_priv; mutex_lock(&wl->usr_sync); wl_cfg80211_attach_post(wl_to_prmry_ndev(wl)); @@ -6371,17 +7281,16 @@ s32 wl_cfg80211_up(void) if (err) WL_ERR(("__wl_cfg80211_up failed\n")); mutex_unlock(&wl->usr_sync); - return err; } -/* Private Event to Supplicant with indication that FW hangs */ +/* Private Event to Supplicant with indication that chip hangs */ int wl_cfg80211_hang(struct net_device *dev, u16 reason) { struct wl_priv *wl; wl = wlcfg_drv_priv; - WL_ERR(("In : FW crash Eventing\n")); + WL_ERR(("In : chip crash eventing\n")); cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL); if (wl != NULL) { wl_link_down(wl); @@ -6389,12 +7298,12 @@ int wl_cfg80211_hang(struct net_device *dev, u16 reason) return 0; } -s32 wl_cfg80211_down(void) +s32 wl_cfg80211_down(void *para) { struct wl_priv *wl; s32 err = 0; - WL_TRACE(("In\n")); + WL_DBG(("In\n")); wl = wlcfg_drv_priv; mutex_lock(&wl->usr_sync); err = __wl_cfg80211_down(wl); @@ -6403,84 +7312,79 @@ s32 wl_cfg80211_down(void) return err; } -static s32 wl_dongle_probecap(struct wl_priv *wl) -{ - s32 err = 0; - - err = wl_update_wiphybands(wl); - if (unlikely(err)) - return err; - - return err; -} - -static void *wl_read_prof(struct wl_priv *wl, s32 item) +static void *wl_read_prof(struct wl_priv *wl, struct net_device *ndev, s32 item) { unsigned long flags; void *rptr = NULL; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + if (!profile) + return NULL; + spin_lock_irqsave(&wl->cfgdrv_lock, flags); switch (item) { case WL_PROF_SEC: - rptr = &wl->profile->sec; + rptr = &profile->sec; break; case WL_PROF_ACT: - rptr = &wl->profile->active; + rptr = &profile->active; break; case WL_PROF_BSSID: - rptr = &wl->profile->bssid; + rptr = profile->bssid; break; case WL_PROF_SSID: - rptr = &wl->profile->ssid; + rptr = &profile->ssid; break; } - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); if (!rptr) WL_ERR(("invalid item (%d)\n", item)); return rptr; } static s32 -wl_update_prof(struct wl_priv *wl, const wl_event_msg_t *e, void *data, - s32 item) +wl_update_prof(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, s32 item) { s32 err = 0; struct wlc_ssid *ssid; unsigned long flags; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); - flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + if (!profile) + return WL_INVALID; + spin_lock_irqsave(&wl->cfgdrv_lock, flags); switch (item) { case WL_PROF_SSID: ssid = (wlc_ssid_t *) data; - memset(wl->profile->ssid.SSID, 0, - sizeof(wl->profile->ssid.SSID)); - memcpy(wl->profile->ssid.SSID, ssid->SSID, ssid->SSID_len); - wl->profile->ssid.SSID_len = ssid->SSID_len; + memset(profile->ssid.SSID, 0, + sizeof(profile->ssid.SSID)); + memcpy(profile->ssid.SSID, ssid->SSID, ssid->SSID_len); + profile->ssid.SSID_len = ssid->SSID_len; break; case WL_PROF_BSSID: if (data) - memcpy(wl->profile->bssid, data, ETHER_ADDR_LEN); + memcpy(profile->bssid, data, ETHER_ADDR_LEN); else - memset(wl->profile->bssid, 0, ETHER_ADDR_LEN); + memset(profile->bssid, 0, ETHER_ADDR_LEN); break; case WL_PROF_SEC: - memcpy(&wl->profile->sec, data, sizeof(wl->profile->sec)); + memcpy(&profile->sec, data, sizeof(profile->sec)); break; case WL_PROF_ACT: - wl->profile->active = *(bool *)data; + profile->active = *(bool *)data; break; case WL_PROF_BEACONINT: - wl->profile->beacon_interval = *(u16 *)data; + profile->beacon_interval = *(u16 *)data; break; case WL_PROF_DTIMPERIOD: - wl->profile->dtim_period = *(u8 *)data; + profile->dtim_period = *(u8 *)data; break; default: WL_ERR(("unsupported item (%d)\n", item)); err = -EOPNOTSUPP; break; } - dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); return err; } @@ -6497,7 +7401,7 @@ void wl_cfg80211_dbg_level(u32 level) static bool wl_is_ibssmode(struct wl_priv *wl, struct net_device *ndev) { - return get_mode_by_netdev(wl, ndev) == WL_MODE_IBSS; + return wl_get_mode_by_netdev(wl, ndev) == WL_MODE_IBSS; } static __used bool wl_is_ibssstarter(struct wl_priv *wl) @@ -6600,122 +7504,35 @@ static void wl_init_eq_lock(struct wl_priv *wl) static void wl_delay(u32 ms) { - if (ms < 1000 / HZ) { - cond_resched(); + if (in_atomic() || ms < 1000 / HZ) { mdelay(ms); } else { msleep(ms); } } -s32 wl_cfg80211_read_fw(s8 *buf, u32 size) -{ - const struct firmware *fw_entry; - struct wl_priv *wl; - - wl = wlcfg_drv_priv; - - fw_entry = wl->fw->fw_entry; - - if (fw_entry->size < wl->fw->ptr + size) - size = fw_entry->size - wl->fw->ptr; - - memcpy(buf, &fw_entry->data[wl->fw->ptr], size); - wl->fw->ptr += size; - return size; -} - -void wl_cfg80211_release_fw(void) -{ - struct wl_priv *wl; - - wl = wlcfg_drv_priv; - release_firmware(wl->fw->fw_entry); - wl->fw->ptr = 0; -} - -void *wl_cfg80211_request_fw(s8 *file_name) -{ - struct wl_priv *wl; - const struct firmware *fw_entry = NULL; - s32 err = 0; - - WL_TRACE(("In\n")); - WL_DBG(("file name : \"%s\"\n", file_name)); - wl = wlcfg_drv_priv; - - if (!test_bit(WL_FW_LOADING_DONE, &wl->fw->status)) { - err = request_firmware(&wl->fw->fw_entry, file_name, - &wl_cfg80211_get_sdio_func()->dev); - if (unlikely(err)) { - WL_ERR(("Could not download fw (%d)\n", err)); - goto req_fw_out; - } - set_bit(WL_FW_LOADING_DONE, &wl->fw->status); - fw_entry = wl->fw->fw_entry; - if (fw_entry) { - WL_DBG(("fw size (%zd), data (%p)\n", fw_entry->size, - fw_entry->data)); - } - } else if (!test_bit(WL_NVRAM_LOADING_DONE, &wl->fw->status)) { - err = request_firmware(&wl->fw->fw_entry, file_name, - &wl_cfg80211_get_sdio_func()->dev); - if (unlikely(err)) { - WL_ERR(("Could not download nvram (%d)\n", err)); - goto req_fw_out; - } - set_bit(WL_NVRAM_LOADING_DONE, &wl->fw->status); - fw_entry = wl->fw->fw_entry; - if (fw_entry) { - WL_DBG(("nvram size (%zd), data (%p)\n", fw_entry->size, - fw_entry->data)); - } - } else { - WL_DBG(("Downloading already done. Nothing to do more\n")); - err = -EPERM; - } - -req_fw_out: - if (unlikely(err)) { - return NULL; - } - wl->fw->ptr = 0; - return (void *)fw_entry->data; -} - -s8 *wl_cfg80211_get_fwname(void) -{ - struct wl_priv *wl; - - wl = wlcfg_drv_priv; - strcpy(wl->fw->fw_name, WL_4329_FW_FILE); - return wl->fw->fw_name; -} - -s8 *wl_cfg80211_get_nvramname(void) -{ - struct wl_priv *wl; - - wl = wlcfg_drv_priv; - strcpy(wl->fw->nvram_name, WL_4329_NVRAM_FILE); - return wl->fw->nvram_name; -} - s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) { - struct wl_priv *wl; - dhd_pub_t *dhd_pub; + struct wl_priv *wl = wlcfg_drv_priv; struct ether_addr p2pif_addr; + struct ether_addr primary_mac; - wl = wlcfg_drv_priv; - dhd_pub = (dhd_pub_t *)wl->pub; - wl_cfgp2p_generate_bss_mac(&dhd_pub->mac, p2pdev_addr, &p2pif_addr); + if (!wl->p2p) + return -1; + if (!p2p_is_on(wl)) { + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, p2pdev_addr, &p2pif_addr); + } else { + memcpy(p2pdev_addr->octet, + wl->p2p->dev_addr.octet, ETHER_ADDR_LEN); + } return 0; } s32 wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) { struct wl_priv *wl; + wl = wlcfg_drv_priv; return wl_cfgp2p_set_p2p_noa(wl, net, buf, len); @@ -6749,8 +7566,8 @@ s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, if (wl->p2p && wl->p2p->vif_created) { ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION); - } else if (wl_get_drv_status(wl, AP_CREATING) || - wl_get_drv_status(wl, AP_CREATED)) { + } else if (wl_get_drv_status(wl, AP_CREATING, net) || + wl_get_drv_status(wl, AP_CREATED, net)) { ndev = net; bssidx = 0; } @@ -6773,69 +7590,6 @@ s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, return ret; } -static __used void wl_dongle_poweron(struct wl_priv *wl) -{ - WL_DBG(("Enter \n")); - dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); - -#if defined(BCMLXSDMMC) - sdioh_start(NULL, 0); -#endif -#if defined(BCMLXSDMMC) - sdioh_start(NULL, 1); -#endif - wl_cfg80211_resume(wl_to_wiphy(wl)); -} - -static __used void wl_dongle_poweroff(struct wl_priv *wl) -{ - WL_DBG(("Enter \n")); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) - wl_cfg80211_suspend(wl_to_wiphy(wl), NULL); -#else - wl_cfg80211_suspend(wl_to_wiphy(wl)); -#endif - -#if defined(BCMLXSDMMC) - sdioh_stop(NULL); -#endif - /* clean up dtim_skip setting */ - dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); -} - -static int wl_debugfs_add_netdev_params(struct wl_priv *wl) -{ - char buf[10+IFNAMSIZ]; - struct dentry *fd; - s32 err = 0; - - WL_TRACE(("In\n")); - sprintf(buf, "netdev:%s", wl_to_prmry_ndev(wl)->name); - wl->debugfsdir = debugfs_create_dir(buf, wl_to_wiphy(wl)->debugfsdir); - - fd = debugfs_create_u16("beacon_int", S_IRUGO, wl->debugfsdir, - (u16 *)&wl->profile->beacon_interval); - if (!fd) { - err = -ENOMEM; - goto err_out; - } - - fd = debugfs_create_u8("dtim_period", S_IRUGO, wl->debugfsdir, - (u8 *)&wl->profile->dtim_period); - if (!fd) { - err = -ENOMEM; - goto err_out; - } - -err_out: - return err; -} - -static void wl_debugfs_remove_netdev(struct wl_priv *wl) -{ - WL_DBG(("Enter \n")); -} - static const struct rfkill_ops wl_rfkill_ops = { .set_block = wl_rfkill_set }; @@ -6864,7 +7618,7 @@ static int wl_setup_rfkill(struct wl_priv *wl, bool setup) return -EINVAL; if (setup) { wl->rfkill = rfkill_alloc("brcmfmac-wifi", - &wl_cfg80211_get_sdio_func()->dev, + wl_cfg80211_get_parent_dev(), RFKILL_TYPE_WLAN, &wl_rfkill_ops, (void *)wl); if (!wl->rfkill) { @@ -6890,471 +7644,42 @@ err_out: return err; } -#if defined(COEX_DHCP) -/* - * get named driver variable to uint register value and return error indication - * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, ®_value) - */ -static int -dev_wlc_intvar_get_reg(struct net_device *dev, char *name, - uint reg, int *retval) -{ - union { - char buf[WLC_IOCTL_SMLEN]; - int val; - } var; - int error; - - bcm_mkiovar(name, (char *)(®), sizeof(reg), - (char *)(&var), sizeof(var.buf)); - error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false); - - *retval = dtoh32(var.val); - return (error); -} - -static int -dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len) -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) - char ioctlbuf[1024]; -#else - static char ioctlbuf[1024]; -#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ - - bcm_mkiovar(name, buf, len, ioctlbuf, sizeof(ioctlbuf)); - - return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf, sizeof(ioctlbuf), true)); -} -/* -get named driver variable to uint register value and return error indication -calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value) -*/ -static int -dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val) -{ - char reg_addr[8]; - - memset(reg_addr, 0, sizeof(reg_addr)); - memcpy((char *)®_addr[0], (char *)addr, 4); - memcpy((char *)®_addr[4], (char *)val, 4); - - return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr))); -} - -static bool btcoex_is_sco_active(struct net_device *dev) -{ - int ioc_res = 0; - bool res = FALSE; - int sco_id_cnt = 0; - int param27; - int i; - - for (i = 0; i < 12; i++) { - - ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); - - WL_TRACE(("%s, sample[%d], btc params: 27:%x\n", - __FUNCTION__, i, param27)); - - if (ioc_res < 0) { - WL_ERR(("%s ioc read btc params error\n", __FUNCTION__)); - break; - } - - if ((param27 & 0x6) == 2) { /* count both sco & esco */ - sco_id_cnt++; - } - - if (sco_id_cnt > 2) { - WL_TRACE(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", - __FUNCTION__, sco_id_cnt, i)); - res = TRUE; - break; - } - - msleep(5); - } - - return res; -} - -#if defined(BT_DHCP_eSCO_FIX) -/* Enhanced BT COEX settings for eSCO compatibility during DHCP window */ -static int set_btc_esco_params(struct net_device *dev, bool trump_sco) +struct device *wl_cfg80211_get_parent_dev(void) { - static bool saved_status = FALSE; - - char buf_reg50va_dhcp_on[8] = - { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 }; - char buf_reg51va_dhcp_on[8] = - { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; - char buf_reg64va_dhcp_on[8] = - { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; - char buf_reg65va_dhcp_on[8] = - { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; - char buf_reg71va_dhcp_on[8] = - { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; - uint32 regaddr; - static uint32 saved_reg50; - static uint32 saved_reg51; - static uint32 saved_reg64; - static uint32 saved_reg65; - static uint32 saved_reg71; - - if (trump_sco) { - /* this should reduce eSCO agressive retransmit - * w/o breaking it - */ - - /* 1st save current */ - WL_TRACE(("Do new SCO/eSCO coex algo {save &" - "override}\n")); - if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { - saved_status = TRUE; - WL_TRACE(("%s saved bt_params[50,51,64,65,71]:" - "0x%x 0x%x 0x%x 0x%x 0x%x\n", - __FUNCTION__, saved_reg50, saved_reg51, - saved_reg64, saved_reg65, saved_reg71)); - } else { - WL_ERR((":%s: save btc_params failed\n", - __FUNCTION__)); - saved_status = FALSE; - return -1; - } - - WL_TRACE(("override with [50,51,64,65,71]:" - "0x%x 0x%x 0x%x 0x%x 0x%x\n", - *(u32 *)(buf_reg50va_dhcp_on+4), - *(u32 *)(buf_reg51va_dhcp_on+4), - *(u32 *)(buf_reg64va_dhcp_on+4), - *(u32 *)(buf_reg65va_dhcp_on+4), - *(u32 *)(buf_reg71va_dhcp_on+4))); - - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg50va_dhcp_on[0], 8); - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg51va_dhcp_on[0], 8); - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg64va_dhcp_on[0], 8); - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg65va_dhcp_on[0], 8); - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg71va_dhcp_on[0], 8); - - saved_status = TRUE; - } else if (saved_status) { - /* restore previously saved bt params */ - WL_TRACE(("Do new SCO/eSCO coex algo {save &" - "override}\n")); - - regaddr = 50; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg50); - regaddr = 51; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg51); - regaddr = 64; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg64); - regaddr = 65; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg65); - regaddr = 71; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg71); - - WL_TRACE(("restore bt_params[50,51,64,65,71]:" - "0x%x 0x%x 0x%x 0x%x 0x%x\n", - saved_reg50, saved_reg51, saved_reg64, - saved_reg65, saved_reg71)); - - saved_status = FALSE; - } else { - WL_ERR((":%s att to restore not saved BTCOEX params\n", - __FUNCTION__)); - return -1; - } - return 0; + return cfg80211_parent_dev; } -#endif /* BT_DHCP_eSCO_FIX */ -static void -wl_cfg80211_bt_setflag(struct net_device *dev, bool set) +void wl_cfg80211_set_parent_dev(void *dev) { -#if defined(BT_DHCP_USE_FLAGS) - char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 }; - char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; -#endif - -#if defined(BT_DHCP_eSCO_FIX) - /* set = 1, save & turn on 0 - off & restore prev settings */ - set_btc_esco_params(dev, set); -#endif - -#if defined(BT_DHCP_USE_FLAGS) - WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set)); - if (set == TRUE) - /* Forcing bt_flag7 */ - dev_wlc_bufvar_set(dev, "btc_flags", - (char *)&buf_flag7_dhcp_on[0], - sizeof(buf_flag7_dhcp_on)); - else - /* Restoring default bt flag7 */ - dev_wlc_bufvar_set(dev, "btc_flags", - (char *)&buf_flag7_default[0], - sizeof(buf_flag7_default)); -#endif + cfg80211_parent_dev = dev; } -static void wl_cfg80211_bt_timerfunc(ulong data) +static void wl_cfg80211_clear_parent_dev(void) { - struct btcoex_info *bt_local = (struct btcoex_info *)data; - WL_TRACE(("%s\n", __FUNCTION__)); - bt_local->timer_on = 0; - schedule_work(&bt_local->work); + cfg80211_parent_dev = NULL; } -static void wl_cfg80211_bt_handler(struct work_struct *work) +static void get_primary_mac(struct wl_priv *wl, struct ether_addr *mac) { - struct btcoex_info *btcx_inf; - - btcx_inf = container_of(work, struct btcoex_info, work); - - if (btcx_inf->timer_on) { - btcx_inf->timer_on = 0; - del_timer_sync(&btcx_inf->timer); - } - - switch (btcx_inf->bt_state) { - case BT_DHCP_START: - /* DHCP started - * provide OPPORTUNITY window to get DHCP address - */ - WL_TRACE(("%s bt_dhcp stm: started \n", - __FUNCTION__)); - btcx_inf->bt_state = BT_DHCP_OPPR_WIN; - mod_timer(&btcx_inf->timer, - jiffies + BT_DHCP_OPPR_WIN_TIME*HZ/1000); - btcx_inf->timer_on = 1; - break; - - case BT_DHCP_OPPR_WIN: - if (btcx_inf->dhcp_done) { - WL_TRACE(("%s DHCP Done before T1 expiration\n", - __FUNCTION__)); - goto btc_coex_idle; - } - - /* DHCP is not over yet, start lowering BT priority - * enforce btc_params + flags if necessary - */ - WL_TRACE(("%s DHCP T1:%d expired\n", __FUNCTION__, - BT_DHCP_OPPR_WIN_TIME)); - if (btcx_inf->dev) - wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE); - btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; - mod_timer(&btcx_inf->timer, - jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); - btcx_inf->timer_on = 1; - break; - - case BT_DHCP_FLAG_FORCE_TIMEOUT: - if (btcx_inf->dhcp_done) { - WL_TRACE(("%s DHCP Done before T2 expiration\n", - __FUNCTION__)); - } else { - /* Noo dhcp during T1+T2, restore BT priority */ - WL_TRACE(("%s DHCP wait interval T2:%d" - "msec expired\n", __FUNCTION__, - BT_DHCP_FLAG_FORCE_TIME)); - } - - /* Restoring default bt priority */ - if (btcx_inf->dev) - wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); -btc_coex_idle: - btcx_inf->bt_state = BT_DHCP_IDLE; - btcx_inf->timer_on = 0; - break; - - default: - WL_ERR(("%s error g_status=%d !!!\n", __FUNCTION__, - btcx_inf->bt_state)); - if (btcx_inf->dev) - wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); - btcx_inf->bt_state = BT_DHCP_IDLE; - btcx_inf->timer_on = 0; - break; - } - - net_os_wake_unlock(btcx_inf->dev); + wldev_iovar_getbuf_bsscfg(wl_to_prmry_ndev(wl), "cur_etheraddr", NULL, + 0, wl->ioctl_buf, WLC_IOCTL_MAXLEN, 0, &wl->ioctl_buf_sync); + memcpy(mac->octet, wl->ioctl_buf, ETHER_ADDR_LEN); } -static int wl_cfg80211_btcoex_init(struct wl_priv *wl) +int wl_cfg80211_do_driver_init(struct net_device *net) { - struct btcoex_info *btco_inf = NULL; - - btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL); - if (!btco_inf) - return -ENOMEM; - - btco_inf->bt_state = BT_DHCP_IDLE; - btco_inf->ts_dhcp_start = 0; - btco_inf->ts_dhcp_ok = 0; - /* Set up timer for BT */ - btco_inf->timer_ms = 10; - init_timer(&btco_inf->timer); - btco_inf->timer.data = (ulong)btco_inf; - btco_inf->timer.function = wl_cfg80211_bt_timerfunc; + struct wl_priv *wl = *(struct wl_priv **)netdev_priv(net); - btco_inf->dev = wl->wdev->netdev; + if (!wl || !wl->wdev) + return -EINVAL; - INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler); + if (dhd_do_driver_init(wl->wdev->netdev) < 0) + return -1; - wl->btcoex_info = btco_inf; return 0; } -static void -wl_cfg80211_btcoex_deinit(struct wl_priv *wl) +void wl_cfg80211_enable_trace(int level) { - if (!wl->btcoex_info) - return; - - if (!wl->btcoex_info->timer_on) { - wl->btcoex_info->timer_on = 0; - del_timer_sync(&wl->btcoex_info->timer); - } - - cancel_work_sync(&wl->btcoex_info->work); - - kfree(wl->btcoex_info); - wl->btcoex_info = NULL; -} -#endif /* COEX_DHCP */ - -int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) -{ - char powermode_val = 0; - char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 }; - char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 }; - char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 }; - - uint32 regaddr; - static uint32 saved_reg66; - static uint32 saved_reg41; - static uint32 saved_reg68; - static bool saved_status = FALSE; - -#ifdef COEX_DHCP - char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; - struct btcoex_info *btco_inf = wlcfg_drv_priv->btcoex_info; -#endif /* COEX_DHCP */ - - /* Figure out powermode 1 or o command */ - strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1); - - if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { - - WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__)); - - /* Retrieve and saved orig regs value */ - if ((saved_status == FALSE) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && - (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { - saved_status = TRUE; - WL_TRACE(("Saved 0x%x 0x%x 0x%x\n", - saved_reg66, saved_reg41, saved_reg68)); - - /* Disable PM mode during dhpc session */ - - /* Disable PM mode during dhpc session */ -#ifdef COEX_DHCP - /* Start BT timer only for SCO connection */ - if (btcoex_is_sco_active(dev)) { - /* btc_params 66 */ - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg66va_dhcp_on[0], - sizeof(buf_reg66va_dhcp_on)); - /* btc_params 41 0x33 */ - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg41va_dhcp_on[0], - sizeof(buf_reg41va_dhcp_on)); - /* btc_params 68 0x190 */ - dev_wlc_bufvar_set(dev, "btc_params", - (char *)&buf_reg68va_dhcp_on[0], - sizeof(buf_reg68va_dhcp_on)); - saved_status = TRUE; - - btco_inf->bt_state = BT_DHCP_START; - btco_inf->timer_on = 1; - mod_timer(&btco_inf->timer, btco_inf->timer.expires); - WL_TRACE(("%s enable BT DHCP Timer\n", - __FUNCTION__)); - } -#endif /* COEX_DHCP */ - } - else if (saved_status == TRUE) { - WL_ERR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); - } - } - else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) { - - - /* Restoring PM mode */ - -#ifdef COEX_DHCP - /* Stop any bt timer because DHCP session is done */ - WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__)); - if (btco_inf->timer_on) { - btco_inf->timer_on = 0; - del_timer_sync(&btco_inf->timer); - - if (btco_inf->bt_state != BT_DHCP_IDLE) { - /* need to restore original btc flags & extra btc params */ - WL_TRACE(("%s bt->bt_state:%d\n", - __FUNCTION__, btco_inf->bt_state)); - /* wake up btcoex thread to restore btlags+params */ - schedule_work(&btco_inf->work); - } - } - - /* Restoring btc_flag paramter anyway */ - if (saved_status == TRUE) - dev_wlc_bufvar_set(dev, "btc_flags", - (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); -#endif /* COEX_DHCP */ - - /* Restore original values */ - if (saved_status == TRUE) { - regaddr = 66; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg66); - regaddr = 41; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg41); - regaddr = 68; - dev_wlc_intvar_set_reg(dev, "btc_params", - (char *)®addr, (char *)&saved_reg68); - - WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", - saved_reg66, saved_reg41, saved_reg68)); - } - saved_status = FALSE; - - } - else { - WL_ERR(("%s Unkwown yet power setting, ignored\n", - __FUNCTION__)); - } - - snprintf(command, 3, "OK"); - - return (strlen("OK")); + wl_dbg_level |= WL_DBG_DBG; } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 232db1b..37c8e58 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -2,13 +2,13 @@ * Linux cfg80211 driver * * Copyright (C) 1999-2011, Broadcom Corporation - * + * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: - * + * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that @@ -16,7 +16,7 @@ * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. - * + * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. @@ -68,6 +68,9 @@ do { \ printk args; \ } \ } while (0) +#ifdef WL_INFO +#undef WL_INFO +#endif #define WL_INFO(args) \ do { \ if (wl_dbg_level & WL_DBG_INFO) { \ @@ -75,6 +78,9 @@ do { \ printk args; \ } \ } while (0) +#ifdef WL_SCAN +#undef WL_SCAN +#endif #define WL_SCAN(args) \ do { \ if (wl_dbg_level & WL_DBG_SCAN) { \ @@ -82,6 +88,9 @@ do { \ printk args; \ } \ } while (0) +#ifdef WL_TRACE +#undef WL_TRACE +#endif #define WL_TRACE(args) \ do { \ if (wl_dbg_level & WL_DBG_TRACE) { \ @@ -102,39 +111,38 @@ do { \ #endif /* (WL_DBG_LEVEL > 0) */ -#define WL_SCAN_RETRY_MAX 3 /* used for ibss scan */ -#define WL_NUM_PMKIDS_MAX MAXPMKID /* will be used - * for 2.6.33 kernel - * or later - */ -#define WL_SCAN_BUF_MAX (1024 * 8) -#define WL_TLV_INFO_MAX 1024 +#define WL_SCAN_RETRY_MAX 3 +#define WL_NUM_PMKIDS_MAX MAXPMKID +#define WL_SCAN_BUF_MAX (1024 * 8) +#define WL_TLV_INFO_MAX 1024 #define WL_SCAN_IE_LEN_MAX 2048 -#define WL_BSS_INFO_MAX 2048 -#define WL_ASSOC_INFO_MAX 512 /* - * needs to grab assoc info from dongle to - * report it to cfg80211 through "connect" - * event - */ +#define WL_BSS_INFO_MAX 2048 +#define WL_ASSOC_INFO_MAX 512 #define WL_IOCTL_LEN_MAX 1024 #define WL_EXTRA_BUF_MAX 2048 -#define WL_ISCAN_BUF_MAX 2048 /* - * the buf lengh can be WLC_IOCTL_MAXLEN (8K) - * to reduce iteration - */ +#define WL_ISCAN_BUF_MAX 2048 #define WL_ISCAN_TIMER_INTERVAL_MS 3000 #define WL_SCAN_ERSULTS_LAST (WL_SCAN_RESULTS_NO_MEM+1) -#define WL_AP_MAX 256 /* virtually unlimitted as long - * as kernel memory allows - */ +#define WL_AP_MAX 256 #define WL_FILE_NAME_MAX 256 -#define WL_DWELL_TIME 200 -#define WL_LONG_DWELL_TIME 1000 -#define VWDEV_CNT 3 +#define WL_DWELL_TIME 200 +#define WL_MED_DWELL_TIME 400 +#define WL_LONG_DWELL_TIME 1000 +#define IFACE_MAX_CNT 2 #define WL_SCAN_TIMER_INTERVAL_MS 8000 /* Scan timeout */ +#define WL_CHANNEL_SYNC_RETRY 3 +#define WL_ACT_FRAME_RETRY 4 + +#define WL_INVALID -1 + + +/* Bring down SCB Timeout to 20secs from 60secs default */ +#ifndef WL_SCB_TIMEOUT +#define WL_SCB_TIMEOUT 20 +#endif -/* dongle status */ +/* driver status */ enum wl_status { WL_STATUS_READY = 0, WL_STATUS_SCANNING, @@ -143,7 +151,8 @@ enum wl_status { WL_STATUS_CONNECTED, WL_STATUS_DISCONNECTING, WL_STATUS_AP_CREATING, - WL_STATUS_AP_CREATED + WL_STATUS_AP_CREATED, + WL_STATUS_SENDING_ACT_FRM }; /* wi-fi mode */ @@ -153,7 +162,7 @@ enum wl_mode { WL_MODE_AP }; -/* dongle profile list */ +/* driver profile list */ enum wl_prof_list { WL_PROF_MODE, WL_PROF_SSID, @@ -166,7 +175,7 @@ enum wl_prof_list { WL_PROF_DTIMPERIOD }; -/* dongle iscan state */ +/* driver iscan state */ enum wl_iscan_state { WL_ISCAN_STATE_IDLE, WL_ISCAN_STATE_SCANING @@ -196,12 +205,8 @@ struct beacon_proberesp { u8 variable[0]; } __attribute__ ((packed)); -/* dongle configuration */ +/* driver configuration */ struct wl_conf { - struct net_mode { - struct net_device *ndev; - s32 type; - } mode [VWDEV_CNT + 1]; /* adhoc , infrastructure or ap */ u32 frag_threshold; u32 rts_threshold; u32 retry_short; @@ -259,22 +264,30 @@ struct wl_ibss { u8 channel; }; -/* dongle profile */ +/* wl driver profile */ struct wl_profile { u32 mode; + s32 band; struct wlc_ssid ssid; + struct wl_security sec; + struct wl_ibss ibss; u8 bssid[ETHER_ADDR_LEN]; u16 beacon_interval; u8 dtim_period; - struct wl_security sec; - struct wl_ibss ibss; - s32 band; bool active; }; +struct net_info { + struct net_device *ndev; + struct wireless_dev *wdev; + struct wl_profile profile; + s32 mode; + unsigned long sme_state; + struct list_head list; /* list of all net_info structure */ +}; typedef s32(*ISCAN_HANDLER) (struct wl_priv *wl); -/* dongle iscan controller */ +/* iscan controller */ struct wl_iscan_ctrl { struct net_device *dev; struct timer_list timer; @@ -323,9 +336,10 @@ struct wl_pmk_list { #define ESCAN_BUF_SIZE (64 * 1024) struct escan_info { - u32 escan_state; - u8 escan_buf[ESCAN_BUF_SIZE]; - struct wiphy *wiphy; + u32 escan_state; + u8 escan_buf[ESCAN_BUF_SIZE]; + struct wiphy *wiphy; + struct net_device *ndev; }; struct ap_info { @@ -341,14 +355,14 @@ struct ap_info { }; struct btcoex_info { struct timer_list timer; - uint32 timer_ms; - uint32 timer_on; - uint32 ts_dhcp_start; /* ms ts ecord time stats */ - uint32 ts_dhcp_ok; /* ms ts ecord time stats */ - bool dhcp_done; /* flag, indicates that host done with - * dhcp before t1/t2 expiration - */ - int bt_state; + u32 timer_ms; + u32 timer_on; + u32 ts_dhcp_start; /* ms ts ecord time stats */ + u32 ts_dhcp_ok; /* ms ts ecord time stats */ + bool dhcp_done; /* flag, indicates that host done with + * dhcp before t1/t2 expiration + */ + s32 bt_state; struct work_struct work; struct net_device *dev; }; @@ -360,40 +374,50 @@ struct sta_info { u32 probe_req_ie_len; u32 assoc_req_ie_len; }; -/* dongle private data of cfg80211 interface */ + +struct afx_hdl { + wl_af_params_t *pending_tx_act_frm; + struct ether_addr pending_tx_dst_addr; + struct net_device *dev; + struct work_struct work; + u32 bssidx; + u32 retry; + s32 peer_chan; + bool ack_recv; +}; + +/* private data of cfg80211 interface */ struct wl_priv { struct wireless_dev *wdev; /* representing wl cfg80211 device */ - struct wireless_dev *vwdev[VWDEV_CNT]; - struct wl_conf *conf; /* dongle configuration */ + + struct wireless_dev *p2p_wdev; /* representing wl cfg80211 device for P2P */ + struct net_device *p2p_net; /* reference to p2p0 interface */ + + struct wl_conf *conf; struct cfg80211_scan_request *scan_request; /* scan request object */ EVENT_HANDLER evt_handler[WLC_E_LAST]; struct list_head eq_list; /* used for event queue */ + struct list_head net_list; /* used for struct net_info */ spinlock_t eq_lock; /* for event queue synchronization */ - struct mutex usr_sync; /* maily for dongle up/down synchronization */ + spinlock_t cfgdrv_lock; /* to protect scan status (and others if needed) */ + struct completion act_frm_scan; + struct mutex usr_sync; /* maily for up/down synchronization */ struct wl_scan_results *bss_list; struct wl_scan_results *scan_results; /* scan request object for internal purpose */ struct wl_scan_req *scan_req_int; - - /* bss information for cfg80211 layer */ - struct wl_cfg80211_bss_info *bss_info; /* information element object for internal purpose */ struct wl_ie ie; - - /* for synchronization of main event thread */ - struct wl_profile *profile; /* holding dongle profile */ struct wl_iscan_ctrl *iscan; /* iscan controller */ /* association information container */ struct wl_connect_info conn_info; - /* control firwmare and nvram paramter downloading */ - struct wl_fw_ctrl *fw; struct wl_pmk_list *pmk_list; /* wpa2 pmk list */ tsk_ctl_t event_tsk; /* task of main event handler thread */ - unsigned long status; /* current dongle status */ void *pub; + u32 iface_cnt; u32 channel; /* current channel */ bool iscan_on; /* iscan on/off switch */ bool iscan_kickstart; /* indicate iscan already started */ @@ -403,12 +427,12 @@ struct wl_priv { bool ibss_starter; /* indicates this sta is ibss starter */ bool link_up; /* link/connection up flag */ - /* indicate whether dongle to support power save mode */ + /* indicate whether chip to support power save mode */ bool pwr_save; - bool dongle_up; /* indicate whether dongle up or not */ - bool roam_on; /* on/off switch for dongle self-roaming */ + bool roam_on; /* on/off switch for self-roaming */ bool scan_tried; /* indicates if first scan attempted */ - u8 *ioctl_buf; /* ioctl buffer */ + u8 *ioctl_buf; /* ioctl buffer */ + struct mutex ioctl_buf_sync; u8 *escan_ioctl_buf; u8 *extra_buf; /* maily to grab assoc information */ struct dentry *debugfsdir; @@ -416,134 +440,224 @@ struct wl_priv { bool rf_blocked; struct ieee80211_channel remain_on_chan; enum nl80211_channel_type remain_on_chan_type; - u64 cache_cookie; - wait_queue_head_t dongle_event_wait; + u64 send_action_id; + u64 last_roc_id; + wait_queue_head_t netif_change_event; + struct afx_hdl *afx_hdl; struct ap_info *ap_info; struct sta_info *sta_info; struct p2p_info *p2p; bool p2p_supported; struct btcoex_info *btcoex_info; struct timer_list scan_timeout; /* Timer for catch scan event timeout */ +#ifdef WL_SCHED_SCAN + struct cfg80211_sched_scan_request *sched_scan_req; /* scheduled scan req */ +#endif /* WL_SCHED_SCAN */ + bool sched_scan_running; /* scheduled scan req status */ + u16 hostapd_chan; /* remember chan requested by framework for hostapd */ + u16 deauth_reason; /* Place holder to save deauth/disassoc reasons */ }; -#define wl_to_wiphy(w) (w->wdev->wiphy) -#define wl_to_prmry_ndev(w) (w->wdev->netdev) -#define ndev_to_wl(n) (wdev_to_wl(n->ieee80211_ptr)) -#define wl_to_sr(w) (w->scan_req_int) -#define wl_to_ie(w) (&w->ie) -#define iscan_to_wl(i) ((struct wl_priv *)(i->data)) -#define wl_to_iscan(w) (w->iscan) -#define wl_to_conn(w) (&w->conn_info) -#define wiphy_from_scan(w) (w->escan_info.wiphy) -#define wl_get_drv_status(wl, stat) (test_bit(WL_STATUS_ ## stat, &(wl)->status)) -#define wl_set_drv_status(wl, stat) (set_bit(WL_STATUS_ ## stat, &(wl)->status)) -#define wl_clr_drv_status(wl, stat) (clear_bit(WL_STATUS_ ## stat, &(wl)->status)) -#define wl_chg_drv_status(wl, stat) (change_bit(WL_STATUS_ ## stat, &(wl)->status)) - static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss) { return bss = bss ? (struct wl_bss_info *)((uintptr) bss + dtoh32(bss->length)) : list->bss_info; } -static inline s32 alloc_idx_vwdev(struct wl_priv *wl) + +static inline s32 +wl_alloc_netinfo(struct wl_priv *wl, struct net_device *ndev, + struct wireless_dev * wdev, s32 mode) { - s32 i = 0; - for (i = 0; i < VWDEV_CNT; i++) { - if (wl->vwdev[i] == NULL) - return i; + struct net_info *_net_info; + s32 err = 0; + if (wl->iface_cnt == IFACE_MAX_CNT) + return -ENOMEM; + _net_info = kzalloc(sizeof(struct net_info), GFP_KERNEL); + if (!_net_info) + err = -ENOMEM; + else { + _net_info->mode = mode; + _net_info->ndev = ndev; + _net_info->wdev = wdev; + wl->iface_cnt++; + list_add(&_net_info->list, &wl->net_list); } - WL_ERR((" *********alloc_idx_vwdev failed (%d) \n", -1)); - return -1; + return err; } -static inline s32 get_idx_vwdev_by_netdev(struct wl_priv *wl, struct net_device *ndev) +static inline void +wl_dealloc_netinfo(struct wl_priv *wl, struct net_device *ndev) { - s32 i = 0; - for (i = 0; i < VWDEV_CNT; i++) { - if ((wl->vwdev[i] != NULL) && (wl->vwdev[i]->netdev == ndev)) - return i; + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) { + list_del(&_net_info->list); + wl->iface_cnt--; + if (_net_info->wdev) { + kfree(_net_info->wdev); + ndev->ieee80211_ptr = NULL; + } + kfree(_net_info); + } } - WL_ERR((" *********get_idx_vwdev_by_netdev failed (%d) \n", -1)); - return -1; } -static inline s32 get_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev) +static inline void +wl_delete_all_netinfo(struct wl_priv *wl) { - s32 i = 0; - for (i = 0; i <= VWDEV_CNT; i++) { - if (wl->conf->mode[i].ndev != NULL && (wl->conf->mode[i].ndev == ndev)) - return wl->conf->mode[i].type; + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + list_del(&_net_info->list); + if (_net_info->wdev) + kfree(_net_info->wdev); + kfree(_net_info); } - return -1; + wl->iface_cnt = 0; } -static inline void set_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev, s32 type) + +static inline bool +wl_get_status_all(struct wl_priv *wl, s32 status) + { - s32 i = 0; - for (i = 0; i <= VWDEV_CNT; i++) { - if (type == -1) { - /* free the info of netdev */ - if (wl->conf->mode[i].ndev == ndev) { - wl->conf->mode[i].ndev = NULL; - wl->conf->mode[i].type = -1; - break; - } + struct net_info *_net_info, *next; + u32 cnt = 0; + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (_net_info->ndev && + test_bit(status, &_net_info->sme_state)) + cnt++; + } + return cnt? true: false; +} - } else { - if ((wl->conf->mode[i].ndev != NULL)&& - (wl->conf->mode[i].ndev == ndev)) { - /* update type of ndev */ - wl->conf->mode[i].type = type; - break; - } - else if ((wl->conf->mode[i].ndev == NULL)&& - (wl->conf->mode[i].type == -1)) { - wl->conf->mode[i].ndev = ndev; - wl->conf->mode[i].type = type; - break; +static inline void +wl_set_status_by_netdev(struct wl_priv *wl, s32 status, + struct net_device *ndev, u32 op) +{ + + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) { + switch (op) { + case 1: + set_bit(status, &_net_info->sme_state); + break; + case 2: + clear_bit(status, &_net_info->sme_state); + break; + case 4: + change_bit(status, &_net_info->sme_state); + break; } } + + } +} + +static inline u32 +wl_get_status_by_netdev(struct wl_priv *wl, s32 status, + struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return test_bit(status, &_net_info->sme_state); + } + return 0; +} + +static inline s32 +wl_get_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return _net_info->mode; + } + return -1; +} + +static inline void +wl_set_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev, + s32 mode) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + _net_info->mode = mode; } } -#define free_vwdev_by_index(wl, __i) do { \ - if (wl->vwdev[__i] != NULL) \ - kfree(wl->vwdev[__i]); \ - wl->vwdev[__i] = NULL; \ - } while (0) + +static inline struct wl_profile * +wl_get_profile_by_netdev(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return &_net_info->profile; + } + return NULL; +} +#define wl_to_wiphy(w) (w->wdev->wiphy) +#define wl_to_prmry_ndev(w) (w->wdev->netdev) +#define ndev_to_wl(n) (wdev_to_wl(n->ieee80211_ptr)) +#define wl_to_sr(w) (w->scan_req_int) +#define wl_to_ie(w) (&w->ie) +#define iscan_to_wl(i) ((struct wl_priv *)(i->data)) +#define wl_to_iscan(w) (w->iscan) +#define wl_to_conn(w) (&w->conn_info) +#define wiphy_from_scan(w) (w->escan_info.wiphy) +#define wl_get_drv_status_all(wl, stat) \ + (wl_get_status_all(wl, WL_STATUS_ ## stat)) +#define wl_get_drv_status(wl, stat, ndev) \ + (wl_get_status_by_netdev(wl, WL_STATUS_ ## stat, ndev)) +#define wl_set_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 1)) +#define wl_clr_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 2)) +#define wl_chg_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 4)) #define for_each_bss(list, bss, __i) \ for (__i = 0; __i < list->count && __i < WL_AP_MAX; __i++, bss = next_bss(list, bss)) +#define for_each_ndev(wl, iter, next) \ + list_for_each_entry_safe(iter, next, &wl->net_list, list) + + /* In case of WPS from wpa_supplicant, pairwise siute and group suite is 0. * In addtion to that, wpa_version is WPA_VERSION_1 */ #define is_wps_conn(_sme) \ - ((_sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) && \ + ((wl_cfgp2p_find_wpsie((u8 *)_sme->ie, _sme->ie_len) != NULL) && \ (!_sme->crypto.n_ciphers_pairwise) && \ (!_sme->crypto.cipher_group)) extern s32 wl_cfg80211_attach(struct net_device *ndev, void *data); extern s32 wl_cfg80211_attach_post(struct net_device *ndev); -extern void wl_cfg80211_detach(void); -/* event handler from dongle */ +extern void wl_cfg80211_detach(void *para); + extern void wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t *e, void *data); -extern void wl_cfg80211_set_sdio_func(void *func); /* set sdio function info */ -extern struct sdio_func *wl_cfg80211_get_sdio_func(void); /* set sdio function info */ -extern s32 wl_cfg80211_up(void); /* dongle up */ -extern s32 wl_cfg80211_down(void); /* dongle down */ -extern s32 wl_cfg80211_notify_ifadd(struct net_device *net, s32 idx, s32 bssidx, -int (*_net_attach)(dhd_pub_t *dhdp, int ifidx)); -extern s32 wl_cfg80211_notify_ifdel(struct net_device *ndev); -extern s32 wl_cfg80211_post_del(void *ndev); +void wl_cfg80211_set_parent_dev(void *dev); +struct device *wl_cfg80211_get_parent_dev(void); + +extern s32 wl_cfg80211_up(void *para); +extern s32 wl_cfg80211_down(void *para); +extern s32 wl_cfg80211_notify_ifadd(struct net_device *ndev, s32 idx, s32 bssidx, + void* _net_attach); +extern s32 wl_cfg80211_ifdel_ops(struct net_device *net); +extern s32 wl_cfg80211_notify_ifdel(void); extern s32 wl_cfg80211_is_progress_ifadd(void); extern s32 wl_cfg80211_is_progress_ifchange(void); extern s32 wl_cfg80211_is_progress_ifadd(void); extern s32 wl_cfg80211_notify_ifchange(void); extern void wl_cfg80211_dbg_level(u32 level); -extern void *wl_cfg80211_request_fw(s8 *file_name); -extern s32 wl_cfg80211_read_fw(s8 *buf, u32 size); -extern void wl_cfg80211_release_fw(void); -extern s8 *wl_cfg80211_get_fwname(void); -extern s8 *wl_cfg80211_get_nvramname(void); extern s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); extern s32 wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len); extern s32 wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len); @@ -551,11 +665,9 @@ extern s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len enum wl_management_type type); extern s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len); extern int wl_cfg80211_hang(struct net_device *dev, u16 reason); - -/* do scan abort */ -extern s32 -wl_cfg80211_scan_abort(struct wl_priv *wl, struct net_device *ndev); - -extern s32 -wl_cfg80211_if_is_group_owner(void); +extern s32 wl_mode_to_nl80211_iftype(s32 mode); +int wl_cfg80211_do_driver_init(struct net_device *net); +void wl_cfg80211_enable_trace(int level); +extern s32 wl_update_wiphybands(struct wl_priv *wl); +extern s32 wl_cfg80211_if_is_group_owner(void); #endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index 4ee6557..a902666 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -30,6 +30,7 @@ #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/netdevice.h> +#include <linux/etherdevice.h> #include <linux/types.h> #include <linux/string.h> #include <linux/timer.h> @@ -39,26 +40,201 @@ #include <bcmutils.h> #include <bcmendian.h> #include <proto/ethernet.h> -#include <dngl_stats.h> -#include <dhd.h> -#include <dhdioctl.h> -#include <wlioctl.h> #include <wl_cfg80211.h> #include <wl_cfgp2p.h> #include <wldev_common.h> +#include <wl_android.h> - -static s8 ioctlbuf[WLC_IOCTL_MAXLEN]; static s8 scanparambuf[WLC_IOCTL_SMLEN]; -static s8 *smbuf = ioctlbuf; static bool wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type); static s32 -wl_cfgp2p_vndr_ie(struct net_device *ndev, s32 bssidx, s32 pktflag, +wl_cfgp2p_vndr_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete); + +static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev); +static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd); +static int wl_cfgp2p_if_open(struct net_device *net); +static int wl_cfgp2p_if_stop(struct net_device *net); +static s32 wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, + bool notify); + +static const struct net_device_ops wl_cfgp2p_if_ops = { + .ndo_open = wl_cfgp2p_if_open, + .ndo_stop = wl_cfgp2p_if_stop, + .ndo_do_ioctl = wl_cfgp2p_do_ioctl, + .ndo_start_xmit = wl_cfgp2p_start_xmit, +}; + +bool wl_cfgp2p_is_pub_action(void *frame, u32 frame_len) +{ + wifi_p2p_pub_act_frame_t *pact_frm; + + if (frame == NULL) + return false; + pact_frm = (wifi_p2p_pub_act_frame_t *)frame; + if (frame_len < sizeof(wifi_p2p_pub_act_frame_t) -1) + return false; + + if (pact_frm->category == P2P_PUB_AF_CATEGORY && + pact_frm->action == P2P_PUB_AF_ACTION && + pact_frm->oui_type == P2P_VER && + memcmp(pact_frm->oui, P2P_OUI, sizeof(pact_frm->oui)) == 0) { + return true; + } + + return false; +} + +bool wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len) +{ + wifi_p2p_action_frame_t *act_frm; + + if (frame == NULL) + return false; + act_frm = (wifi_p2p_action_frame_t *)frame; + if (frame_len < sizeof(wifi_p2p_action_frame_t) -1) + return false; + + if (act_frm->category == P2P_AF_CATEGORY && + act_frm->type == P2P_VER && + memcmp(act_frm->OUI, P2P_OUI, DOT11_OUI_LEN) == 0) { + return true; + } + + return false; +} + +bool wl_cfgp2p_is_gas_action(void *frame, u32 frame_len) +{ + + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm; + + if (frame == NULL) + return false; + + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame; + if (frame_len < sizeof(wifi_p2psd_gas_pub_act_frame_t) - 1) + return false; + if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) + return false; + + if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP) + return true; + else + return false; + +} + +void wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len) +{ + wifi_p2p_pub_act_frame_t *pact_frm; + wifi_p2p_action_frame_t *act_frm; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm; + if (!frame || frame_len <= 2) + return; + + if (wl_cfgp2p_is_pub_action(frame, frame_len)) { + pact_frm = (wifi_p2p_pub_act_frame_t *)frame; + switch (pact_frm->subtype) { + case P2P_PAF_GON_REQ: + CFGP2P_DBG(("%s P2P Group Owner Negotiation Req Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_GON_RSP: + CFGP2P_DBG(("%s P2P Group Owner Negotiation Rsp Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_GON_CONF: + CFGP2P_DBG(("%s P2P Group Owner Negotiation Confirm Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_INVITE_REQ: + CFGP2P_DBG(("%s P2P Invitation Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_INVITE_RSP: + CFGP2P_DBG(("%s P2P Invitation Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_DEVDIS_REQ: + CFGP2P_DBG(("%s P2P Device Discoverability Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_DEVDIS_RSP: + CFGP2P_DBG(("%s P2P Device Discoverability Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_PROVDIS_REQ: + CFGP2P_DBG(("%s P2P Provision Discovery Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_PROVDIS_RSP: + CFGP2P_DBG(("%s P2P Provision Discovery Response Frame\n", + (tx)? "TX": "RX")); + break; + default: + CFGP2P_DBG(("%s Unknown P2P Public Action Frame\n", + (tx)? "TX": "RX")); + + } + + } else if (wl_cfgp2p_is_p2p_action(frame, frame_len)) { + act_frm = (wifi_p2p_action_frame_t *)frame; + switch (act_frm->subtype) { + case P2P_AF_NOTICE_OF_ABSENCE: + CFGP2P_DBG(("%s P2P Notice of Absence Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_PRESENCE_REQ: + CFGP2P_DBG(("%s P2P Presence Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_PRESENCE_RSP: + CFGP2P_DBG(("%s P2P Presence Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_GO_DISC_REQ: + CFGP2P_DBG(("%s P2P Discoverability Request Frame\n", + (tx)? "TX": "RX")); + break; + default: + CFGP2P_DBG(("%s Unknown P2P Action Frame\n", + (tx)? "TX": "RX")); + } + + } else if (wl_cfgp2p_is_gas_action(frame, frame_len)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame; + switch (sd_act_frm->action) { + case P2PSD_ACTION_ID_GAS_IREQ: + CFGP2P_DBG(("%s P2P GAS Initial Request\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_IRESP: + CFGP2P_DBG(("%s P2P GAS Initial Response\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_CREQ: + CFGP2P_DBG(("%s P2P GAS Comback Request\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_CRESP: + CFGP2P_DBG(("%s P2P GAS Comback Response\n", + (tx)? "TX" : "RX")); + break; + default: + CFGP2P_DBG(("%s Unknown P2P GAS Frame\n", + (tx)? "TX" : "RX")); + } + } +} + /* * Initialize variables related to P2P * @@ -99,7 +275,6 @@ wl_cfgp2p_init_priv(struct wl_priv *wl) wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = NULL; wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = 0; - spin_lock_init(&wl->p2p->timer_lock); return BCME_OK; } @@ -110,6 +285,8 @@ wl_cfgp2p_init_priv(struct wl_priv *wl) void wl_cfgp2p_deinit_priv(struct wl_priv *wl) { + CFGP2P_DBG(("In\n")); + if (wl->p2p) { kfree(wl->p2p); wl->p2p = NULL; @@ -142,9 +319,9 @@ wl_cfgp2p_set_firm_p2p(struct wl_priv *wl) * firmware for P2P device address */ ret = wldev_iovar_setbuf_bsscfg(ndev, "p2p_da_override", &null_eth_addr, - sizeof(null_eth_addr), ioctlbuf, sizeof(ioctlbuf), 0); + sizeof(null_eth_addr), wl->ioctl_buf, WLC_IOCTL_MAXLEN, 0, &wl->ioctl_buf_sync); if (ret && ret != BCME_UNSUPPORTED) { - CFGP2P_ERR(("failed to update device address\n")); + CFGP2P_ERR(("failed to update device address ret %d\n", ret)); } return ret; } @@ -163,19 +340,29 @@ wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, wl_p2p_if_t ifreq; s32 err; struct net_device *ndev = wl_to_prmry_ndev(wl); + u32 scb_timeout = WL_SCB_TIMEOUT; ifreq.type = if_type; ifreq.chspec = chspec; memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); - CFGP2P_INFO(("---wl p2p_ifadd %02x:%02x:%02x:%02x:%02x:%02x %s %u\n", + CFGP2P_DBG(("---wl p2p_ifadd %02x:%02x:%02x:%02x:%02x:%02x %s %u\n", ifreq.addr.octet[0], ifreq.addr.octet[1], ifreq.addr.octet[2], ifreq.addr.octet[3], ifreq.addr.octet[4], ifreq.addr.octet[5], (if_type == WL_P2P_IF_GO) ? "go" : "client", (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); err = wldev_iovar_setbuf(ndev, "p2p_ifadd", &ifreq, sizeof(ifreq), - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + + if (unlikely(err < 0)) { + printk("'wl p2p_ifadd' error %d\n", err); + } else if (if_type == WL_P2P_IF_GO) { + err = wldev_ioctl(ndev, WLC_SET_SCB_TIMEOUT, &scb_timeout, sizeof(u32), true); + if (unlikely(err < 0)) + printk("'wl scb_timeout' error %d\n", err); + } + return err; } @@ -194,7 +381,7 @@ wl_cfgp2p_ifdel(struct wl_priv *wl, struct ether_addr *mac) netdev->ifindex, mac->octet[0], mac->octet[1], mac->octet[2], mac->octet[3], mac->octet[4], mac->octet[5])); ret = wldev_iovar_setbuf(netdev, "p2p_ifdel", mac, sizeof(*mac), - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (unlikely(ret < 0)) { printk("'wl p2p_ifdel' error %d\n", ret); } @@ -212,6 +399,7 @@ wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, { wl_p2p_if_t ifreq; s32 err; + u32 scb_timeout = WL_SCB_TIMEOUT; struct net_device *netdev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); ifreq.type = if_type; @@ -225,10 +413,14 @@ wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); err = wldev_iovar_setbuf(netdev, "p2p_ifupd", &ifreq, sizeof(ifreq), - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (unlikely(err < 0)) { printk("'wl p2p_ifupd' error %d\n", err); + } else if (if_type == WL_P2P_IF_GO) { + err = wldev_ioctl(netdev, WLC_SET_SCB_TIMEOUT, &scb_timeout, sizeof(u32), true); + if (unlikely(err < 0)) + printk("'wl scb_timeout' error %d\n", err); } return err; } @@ -251,8 +443,8 @@ wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index) mac->octet[0], mac->octet[1], mac->octet[2], mac->octet[3], mac->octet[4], mac->octet[5])); - ret = wldev_iovar_getbuf_bsscfg(dev, "p2p_if", mac, sizeof(*mac), - getbuf, sizeof(getbuf), wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY)); + ret = wldev_iovar_getbuf_bsscfg(dev, "p2p_if", mac, sizeof(*mac), getbuf, + sizeof(getbuf), wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY), NULL); if (ret == 0) { memcpy(index, getbuf, sizeof(index)); @@ -262,7 +454,7 @@ wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index) return ret; } -s32 +static s32 wl_cfgp2p_set_discovery(struct wl_priv *wl, s32 on) { s32 ret = BCME_OK; @@ -311,13 +503,14 @@ wl_cfgp2p_set_p2p_mode(struct wl_priv *wl, u8 mode, u32 channel, u16 listen_ms, discovery_mode.chspec = CH20MHZ_CHSPEC(channel); discovery_mode.dwell = listen_ms; ret = wldev_iovar_setbuf_bsscfg(dev, "p2p_state", &discovery_mode, - sizeof(discovery_mode), ioctlbuf, sizeof(ioctlbuf), bssidx); + sizeof(discovery_mode), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + bssidx, &wl->ioctl_buf_sync); return ret; } /* Get the index of the P2P Discovery BSS */ -s32 +static s32 wl_cfgp2p_get_disc_idx(struct wl_priv *wl, s32 *index) { s32 ret; @@ -364,7 +557,7 @@ wl_cfgp2p_init_discovery(struct wl_priv *wl) /* Set the initial discovery state to SCAN */ ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, - wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); if (unlikely(ret != 0)) { CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n")); @@ -381,7 +574,7 @@ wl_cfgp2p_init_discovery(struct wl_priv *wl) * @wl : wl_private data * Returns 0 if succes */ -s32 +static s32 wl_cfgp2p_deinit_discovery(struct wl_priv *wl) { s32 ret = BCME_OK; @@ -419,7 +612,8 @@ wl_cfgp2p_deinit_discovery(struct wl_priv *wl) * Returns 0 if success. */ s32 -wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, const u8 *ie, u32 ie_len) +wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, + const u8 *ie, u32 ie_len) { s32 ret = BCME_OK; if (wl_get_p2p_status(wl, DISCOVERY_ON)) { @@ -511,10 +705,10 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, wl_escan_params_t *eparams; wlc_ssid_t ssid; /* Scan parameters */ -#define P2PAPI_SCAN_NPROBES 4 -#define P2PAPI_SCAN_DWELL_TIME_MS 80 -#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 100 -#define P2PAPI_SCAN_HOME_TIME_MS 10 +#define P2PAPI_SCAN_NPROBES 1 +#define P2PAPI_SCAN_DWELL_TIME_MS 50 +#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40 +#define P2PAPI_SCAN_HOME_TIME_MS 60 struct net_device *pri_dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); wl_set_p2p_status(wl, SCANNING); /* Allocate scan params which need space for 3 channels and 0 ssids */ @@ -530,7 +724,7 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, return -1; } memset(memblk, 0, memsize); - memset(ioctlbuf, 0, sizeof(ioctlbuf)); + memset(wl->ioctl_buf, 0, WLC_IOCTL_MAXLEN); if (search_state == WL_P2P_DISC_ST_SEARCH) { /* * If we in SEARCH STATE, we don't need to set SSID explictly @@ -569,7 +763,7 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, eparams->params.nprobes = htod32(P2PAPI_SCAN_NPROBES); eparams->params.home_time = htod32(P2PAPI_SCAN_HOME_TIME_MS); - if (wl_get_drv_status(wl, CONNECTED)) + if (wl_get_drv_status_all(wl, CONNECTED)) eparams->params.active_time = htod32(-1); else if (num_chans == 3) eparams->params.active_time = htod32(P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS); @@ -595,9 +789,54 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, CFGP2P_INFO(("\n")); ret = wldev_iovar_setbuf_bsscfg(pri_dev, "p2p_scan", - memblk, memsize, smbuf, sizeof(ioctlbuf), bssidx); + memblk, memsize, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); return ret; } + +/* search function to reach at common channel to send action frame + * Parameters: + * @wl : wl_private data + * @ndev : net device for bssidx + * @bssidx : bssidx for BSS + * Returns 0 if success. + */ +s32 +wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, + s32 bssidx, s32 channel) +{ + s32 ret = 0; + u32 chan_cnt = 0; + u16 *default_chan_list = NULL; + if (!p2p_is_on(wl)) + return -BCME_ERROR; + CFGP2P_ERR((" Enter\n")); + if (bssidx == P2PAPI_BSSCFG_PRIMARY) + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + if (channel) + chan_cnt = 1; + else + chan_cnt = SOCIAL_CHAN_CNT; + default_chan_list = kzalloc(chan_cnt * sizeof(*default_chan_list), GFP_KERNEL); + if (default_chan_list == NULL) { + CFGP2P_ERR(("channel list allocation failed \n")); + ret = -ENOMEM; + goto exit; + } + if (channel) { + default_chan_list[0] = channel; + } else { + default_chan_list[0] = SOCIAL_CHAN_1; + default_chan_list[1] = SOCIAL_CHAN_2; + default_chan_list[2] = SOCIAL_CHAN_3; + } + ret = wl_cfgp2p_escan(wl, ndev, true, SOCIAL_CHAN_CNT, + default_chan_list, WL_P2P_DISC_ST_SEARCH, + WL_SCAN_ACTION_START, bssidx); + kfree(default_chan_list); +exit: + return ret; +} + /* Check whether pointed-to IE looks like WPA. */ #define wl_cfgp2p_is_wpa_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ (const uint8 *)WPS_OUI, WPS_OUI_LEN, WPA_OUI_TYPE) @@ -635,7 +874,7 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss u8 delete = 0; #define IE_TYPE(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie) #define IE_TYPE_LEN(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie_len) - if (wl->p2p_supported && p2p_on(wl) && bssidx != -1) { + if (p2p_is_on(wl) && bssidx != -1) { if (bssidx == P2PAPI_BSSCFG_PRIMARY) bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); switch (pktflag) { @@ -670,7 +909,7 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss CFGP2P_ERR(("not suitable type\n")); return -1; } - } else if (get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { + } else if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { switch (pktflag) { case VNDR_IE_PRBRSP_FLAG : mgmt_ie_buf = wl->ap_info->probe_res_ie; @@ -689,7 +928,7 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss return -1; } bssidx = 0; - } else if (bssidx == -1 && get_mode_by_netdev(wl, ndev) == WL_MODE_BSS) { + } else if (bssidx == -1 && wl_get_mode_by_netdev(wl, ndev) == WL_MODE_BSS) { switch (pktflag) { case VNDR_IE_PRBREQ_FLAG : mgmt_ie_buf = wl->sta_info->probe_req_ie; @@ -735,8 +974,9 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss CFGP2P_INFO(("DELELED ID : %d, Len : %d , OUI :" "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], ie_buf[pos+1], ie_buf[pos+2])); - ret = wl_cfgp2p_vndr_ie(ndev, bssidx, pktflag, ie_buf+pos, - VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, ie_len-3, delete); + ret = wl_cfgp2p_vndr_ie(wl, ndev, bssidx, pktflag, + ie_buf+pos, VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, + ie_len-3, delete); } pos += ie_len; } @@ -760,8 +1000,9 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss CFGP2P_INFO(("ADDED ID : %d, Len : %d , OUI :" "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], ie_buf[pos+1], ie_buf[pos+2])); - ret = wl_cfgp2p_vndr_ie(ndev, bssidx, pktflag, ie_buf+pos, - VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, ie_len-3, delete); + ret = wl_cfgp2p_vndr_ie(wl, ndev, bssidx, pktflag, + ie_buf+pos, VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, + ie_len-3, delete); } pos += ie_len; } @@ -867,7 +1108,7 @@ wl_cfgp2p_find_p2pie(u8 *parse, u32 len) } static s32 -wl_cfgp2p_vndr_ie(struct net_device *ndev, s32 bssidx, s32 pktflag, +wl_cfgp2p_vndr_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete) { s32 err = BCME_OK; @@ -909,7 +1150,7 @@ wl_cfgp2p_vndr_ie(struct net_device *ndev, s32 bssidx, s32 pktflag, memcpy(ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui, oui, 3); memcpy(ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data, data, data_len); err = wldev_iovar_setbuf_bsscfg(ndev, "vndr_ie", ie_setbuf, buf_len, - ioctlbuf, sizeof(ioctlbuf), bssidx); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); CFGP2P_INFO(("vndr_ie iovar returns %d\n", err)); kfree(ie_setbuf); @@ -957,14 +1198,17 @@ wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, s32 ret = BCME_OK; CFGP2P_DBG((" Enter\n")); + + /* If p2p_info is de-initialized, do nothing */ + if (!wl->p2p) + return ret; + if (wl_get_p2p_status(wl, LISTEN_EXPIRED) == 0) { wl_set_p2p_status(wl, LISTEN_EXPIRED); if (timer_pending(&wl->p2p->listen_timer)) { - spin_lock_bh(&wl->p2p->timer_lock); del_timer_sync(&wl->p2p->listen_timer); - spin_unlock_bh(&wl->p2p->timer_lock); } - cfg80211_remain_on_channel_expired(ndev, wl->cache_cookie, &wl->remain_on_chan, + cfg80211_remain_on_channel_expired(ndev, wl->last_roc_id, &wl->remain_on_chan, wl->remain_on_chan_type, GFP_KERNEL); } else wl_clr_p2p_status(wl, LISTEN_EXPIRED); @@ -990,6 +1234,33 @@ wl_cfgp2p_listen_expired(unsigned long data) } /* + * Routine for cancelling the P2P LISTEN + */ +static s32 +wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, + bool notify) +{ + WL_DBG(("Enter \n")); + + /* Irrespective of whether timer is running or not, reset + * the LISTEN state. + */ + wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + + if (timer_pending(&wl->p2p->listen_timer)) { + del_timer_sync(&wl->p2p->listen_timer); + + if (notify) + cfg80211_remain_on_channel_expired(ndev, wl->last_roc_id, + &wl->remain_on_chan, wl->remain_on_chan_type, GFP_KERNEL); + } + + + return 0; +} + +/* * Do a P2P Listen on the given channel for the given duration. * A listen consists of sitting idle and responding to P2P probe requests * with a P2P probe response. @@ -1093,10 +1364,10 @@ wl_cfgp2p_action_tx_complete(struct wl_priv *wl, struct net_device *ndev, wl_set_p2p_status(wl, ACTION_TX_NOACK); CFGP2P_ERR(("WLC_E_ACTION_FRAME_COMPLETE : NO ACK\n")); } - wake_up_interruptible(&wl->dongle_event_wait); } else { CFGP2P_INFO((" WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE is received," "status : %d\n", status)); + wake_up_interruptible(&wl->netif_change_event); } return ret; } @@ -1126,15 +1397,15 @@ wl_cfgp2p_tx_action_frame(struct wl_priv *wl, struct net_device *dev, if (bssidx == P2PAPI_BSSCFG_PRIMARY) bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); - ret = wldev_iovar_setbuf_bsscfg(dev, "actframe", - af_params, sizeof(*af_params), ioctlbuf, sizeof(ioctlbuf), bssidx); + ret = wldev_iovar_setbuf_bsscfg(dev, "actframe", af_params, sizeof(*af_params), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); if (ret < 0) { CFGP2P_ERR((" sending action frame is failed\n")); goto exit; } - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, + timeout = wait_event_interruptible_timeout(wl->netif_change_event, (wl_get_p2p_status(wl, ACTION_TX_COMPLETED) || wl_get_p2p_status(wl, ACTION_TX_NOACK)), msecs_to_jiffies(MAX_WAIT_TIME)); @@ -1243,7 +1514,7 @@ wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx) /* Check if the BSS is up */ *(int*)getbuf = -1; result = wldev_iovar_getbuf_bsscfg(ndev, "bss", &bsscfg_idx, - sizeof(bsscfg_idx), getbuf, sizeof(getbuf), 0); + sizeof(bsscfg_idx), getbuf, sizeof(getbuf), 0, NULL); if (result != 0) { CFGP2P_ERR(("'wl bss -C %d' failed: %d\n", bsscfg_idx, result)); CFGP2P_ERR(("NOTE: this ioctl error is normal " @@ -1260,7 +1531,7 @@ wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx) /* Bring up or down a BSS */ s32 -wl_cfgp2p_bss(struct net_device *ndev, s32 bsscfg_idx, s32 up) +wl_cfgp2p_bss(struct wl_priv *wl, struct net_device *ndev, s32 bsscfg_idx, s32 up) { s32 ret = BCME_OK; s32 val = up ? 1 : 0; @@ -1274,7 +1545,7 @@ wl_cfgp2p_bss(struct net_device *ndev, s32 bsscfg_idx, s32 up) bss_setbuf.val = htod32(val); CFGP2P_INFO(("---wl bss -C %d %s\n", bsscfg_idx, up ? "up" : "down")); ret = wldev_iovar_setbuf(ndev, "bss", &bss_setbuf, sizeof(bss_setbuf), - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (ret != 0) { CFGP2P_ERR(("'bss %d' failed with %d\n", up, ret)); @@ -1308,13 +1579,16 @@ wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev) s32 wl_cfgp2p_down(struct wl_priv *wl) { - if (timer_pending(&wl->p2p->listen_timer)) - del_timer_sync(&wl->p2p->listen_timer); + + wl_cfgp2p_cancel_listen(wl, + wl->p2p_net ? wl->p2p_net : wl_to_prmry_ndev(wl), TRUE); + wl_cfgp2p_deinit_priv(wl); return 0; } -s32 wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) +s32 +wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) { s32 ret = -1; int count, start, duration; @@ -1330,7 +1604,7 @@ s32 wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf sscanf(buf, "%d %d %d", &count, &start, &duration); CFGP2P_DBG(("set_p2p_noa count %d start %d duration %d\n", - count, start, duration)); + count, start, duration)); if (count != -1) wl->p2p->noa.desc[0].count = count; @@ -1374,7 +1648,8 @@ s32 wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf dongle_noa.desc[0].interval = htod32(wl->p2p->noa.desc[0].interval*1000); ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), - "p2p_noa", &dongle_noa, sizeof(dongle_noa), ioctlbuf, sizeof(ioctlbuf)); + "p2p_noa", &dongle_noa, sizeof(dongle_noa), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + &wl->ioctl_buf_sync); if (ret < 0) { CFGP2P_ERR(("fw set p2p_noa failed %d\n", ret)); @@ -1386,7 +1661,8 @@ s32 wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf return ret; } -s32 wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int buf_len) +s32 +wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int buf_len) { wifi_p2p_noa_desc_t *noa_desc; int len = 0, i; @@ -1428,7 +1704,8 @@ s32 wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf return len * 2; } -s32 wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) +s32 +wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) { int ps, ctw; int ret = -1; @@ -1446,7 +1723,7 @@ s32 wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, wl->p2p->ops.ops = ps; ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), "p2p_ops", &wl->p2p->ops, sizeof(wl->p2p->ops), - ioctlbuf, sizeof(ioctlbuf)); + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (ret < 0) { CFGP2P_ERR(("fw set p2p_ops failed %d\n", ret)); } @@ -1467,3 +1744,257 @@ s32 wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, } return ret; } + +u8 * +wl_cfgp2p_retreive_p2pattrib(void *buf, u8 element_id) +{ + wifi_p2p_ie_t *ie = NULL; + u16 len = 0; + u8 *subel; + u8 subelt_id; + u16 subelt_len; + + if (!buf) { + WL_ERR(("P2P IE not present")); + return 0; + } + + ie = (wifi_p2p_ie_t*) buf; + len = ie->len; + + /* Point subel to the P2P IE's subelt field. + * Subtract the preceding fields (id, len, OUI, oui_type) from the length. + */ + subel = ie->subelts; + len -= 4; /* exclude OUI + OUI_TYPE */ + + while (len >= 3) { + /* attribute id */ + subelt_id = *subel; + subel += 1; + len -= 1; + + /* 2-byte little endian */ + subelt_len = *subel++; + subelt_len |= *subel++ << 8; + + len -= 2; + len -= subelt_len; /* for the remaining subelt fields */ + + if (subelt_id == element_id) { + /* This will point to start of subelement attrib after + * attribute id & len + */ + return subel; + } + + /* Go to next subelement */ + subel += subelt_len; + } + + /* Not Found */ + return NULL; +} + +#define P2P_GROUP_CAPAB_GO_BIT 0x01 +u8 * +wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length) +{ + wifi_p2p_ie_t * p2p_ie = NULL; + u8 *capability = NULL; + bool p2p_go = 0; + u8 *ptr = NULL; + + if (!(p2p_ie = wl_cfgp2p_find_p2pie(((u8 *) bi) + bi->ie_offset, bi->ie_length))) { + WL_ERR(("P2P IE not found")); + return NULL; + } + + if (!(capability = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_P2P_INFO))) { + WL_ERR(("P2P Capability attribute not found")); + return NULL; + } + + /* Check Group capability for Group Owner bit */ + p2p_go = capability[1] & P2P_GROUP_CAPAB_GO_BIT; + if (!p2p_go) { + return bi->BSSID.octet; + } + + /* In probe responses, DEVICE INFO attribute will be present */ + if (!(ptr = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_INFO))) { + /* If DEVICE_INFO is not found, this might be a beacon frame. + * check for DEVICE_ID in the beacon frame. + */ + ptr = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_ID); + } + + if (!ptr) + WL_ERR((" Both DEVICE_ID & DEVICE_INFO attribute not present in P2P IE ")); + + return ptr; +} + +s32 +wl_cfgp2p_register_ndev(struct wl_priv *wl) +{ + int ret = 0; + struct net_device* net = NULL; + struct wireless_dev *wdev; + uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x33, 0x22, 0x11 }; + + /* Allocate etherdev, including space for private structure */ + if (!(net = alloc_etherdev(sizeof(wl)))) { + CFGP2P_ERR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); + goto fail; + } + + strcpy(net->name, "p2p%d"); + net->name[IFNAMSIZ - 1] = '\0'; + + /* Copy the reference to wl_priv */ + memcpy((void *)netdev_priv(net), &wl, sizeof(wl)); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + ASSERT(!net->open); + net->do_ioctl = wl_cfgp2p_do_ioctl; + net->hard_start_xmit = wl_cfgp2p_start_xmit; + net->open = wl_cfgp2p_if_open; + net->stop = wl_cfgp2p_if_stop; +#else + ASSERT(!net->netdev_ops); + net->netdev_ops = &wl_cfgp2p_if_ops; +#endif + + /* Register with a dummy MAC addr */ + memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); + + wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); + if (unlikely(!wdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return -ENOMEM; + } + + wdev->wiphy = wl->wdev->wiphy; + + wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); + + net->ieee80211_ptr = wdev; + + SET_NETDEV_DEV(net, wiphy_dev(wdev->wiphy)); + + /* Associate p2p0 network interface with new wdev */ + wdev->netdev = net; + + /* store p2p net ptr for further reference. Note that iflist won't have this + * entry as there corresponding firmware interface is a "Hidden" interface. + */ + if (wl->p2p_net) { + CFGP2P_ERR(("p2p_net defined already.\n")); + return -EINVAL; + } else { + wl->p2p_wdev = wdev; + wl->p2p_net = net; + } + + ret = register_netdev(net); + if (ret) { + CFGP2P_ERR((" register_netdevice failed (%d)\n", ret)); + goto fail; + } + + printk("%s: P2P Interface Registered\n", net->name); + + return ret; +fail: + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + net->open = NULL; +#else + net->netdev_ops = NULL; +#endif + + if (net) { + unregister_netdev(net); + free_netdev(net); + } + + return -ENODEV; +} + +s32 +wl_cfgp2p_unregister_ndev(struct wl_priv *wl) +{ + + if (!wl || !wl->p2p_net) { + CFGP2P_ERR(("Invalid Ptr\n")); + return -EINVAL; + } + + unregister_netdev(wl->p2p_net); + free_netdev(wl->p2p_net); + + return 0; +} + +static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + CFGP2P_DBG(("(%s) is not used for data operations. Droping the packet. \n", ndev->name)); + return 0; +} + +static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd) +{ + int ret = 0; + struct wl_priv *wl = *(struct wl_priv **)netdev_priv(net); + struct net_device *ndev = wl_to_prmry_ndev(wl); + + /* There is no ifidx corresponding to p2p0 in our firmware. So we should + * not Handle any IOCTL cmds on p2p0 other than ANDROID PRIVATE CMDs. + * For Android PRIV CMD handling map it to primary I/F + */ + if (cmd == SIOCDEVPRIVATE+1) { + ret = wl_android_priv_cmd(ndev, ifr, cmd); + + } else { + CFGP2P_ERR(("%s: IOCTL req 0x%x on p2p0 I/F. Ignoring. \n", + __FUNCTION__, cmd)); + return -1; + } + + return ret; +} + +static int wl_cfgp2p_if_open(struct net_device *net) +{ + struct wireless_dev *wdev = net->ieee80211_ptr; + + if (!wdev) + return -EINVAL; + + /* If suppose F/W download (ifconfig wlan0 up) hasn't been done by now, + * do it here. This will make sure that in concurrent mode, supplicant + * is not dependent on a particular order of interface initialization. + * i.e you may give wpa_supp -iwlan0 -N -ip2p0 or wpa_supp -ip2p0 -N + * -iwlan0. + */ + wl_cfg80211_do_driver_init(net); + + wdev->wiphy->interface_modes |= (BIT(NL80211_IFTYPE_P2P_CLIENT) + | BIT(NL80211_IFTYPE_P2P_GO)); + + return 0; +} + +static int wl_cfgp2p_if_stop(struct net_device *net) +{ + struct wireless_dev *wdev = net->ieee80211_ptr; + + if (!wdev) + return -EINVAL; + + wdev->wiphy->interface_modes = (wdev->wiphy->interface_modes) + & (~(BIT(NL80211_IFTYPE_P2P_CLIENT)| + BIT(NL80211_IFTYPE_P2P_GO))); + return 0; +} diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h index 5a69168..05323ed 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h @@ -42,7 +42,7 @@ typedef enum { P2PAPI_BSSCFG_MAX } p2p_bsscfg_type_t; -#define IE_MAX_LEN 300 +#define IE_MAX_LEN 512 /* Structure to hold all saved P2P and WPS IEs for a BSSCFG */ struct p2p_saved_ie { u8 p2p_probe_req_ie[IE_MAX_LEN]; @@ -77,7 +77,6 @@ struct p2p_info { wl_p2p_sched_t noa; wl_p2p_ops_t ops; wlc_ssid_t ssid; - spinlock_t timer_lock; }; /* dongle status */ @@ -92,7 +91,8 @@ enum wl_cfgp2p_status { WLP2P_STATUS_LISTEN_EXPIRED, WLP2P_STATUS_ACTION_TX_COMPLETED, WLP2P_STATUS_ACTION_TX_NOACK, - WLP2P_STATUS_SCANNING + WLP2P_STATUS_SCANNING, + WLP2P_STATUS_GO_NEG_PHASE }; @@ -101,13 +101,13 @@ enum wl_cfgp2p_status { #define wl_to_p2p_bss_saved_ie(w, type) ((wl)->p2p->bss_idx[type].saved_ie) #define wl_to_p2p_bss_private(w, type) ((wl)->p2p->bss_idx[type].private_data) #define wl_to_p2p_bss(wl, type) ((wl)->p2p->bss_idx[type]) -#define wl_get_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : test_bit(WLP2P_STATUS_ ## stat, \ +#define wl_get_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:test_bit(WLP2P_STATUS_ ## stat, \ &(wl)->p2p->status)) -#define wl_set_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? : set_bit(WLP2P_STATUS_ ## stat, \ +#define wl_set_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:set_bit(WLP2P_STATUS_ ## stat, \ &(wl)->p2p->status)) -#define wl_clr_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? : clear_bit(WLP2P_STATUS_ ## stat, \ +#define wl_clr_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:clear_bit(WLP2P_STATUS_ ## stat, \ &(wl)->p2p->status)) -#define wl_chg_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? : change_bit(WLP2P_STATUS_ ## stat, \ +#define wl_chg_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:change_bit(WLP2P_STATUS_ ## stat, \ &(wl)->p2p->status)) #define p2p_on(wl) ((wl)->p2p->on) #define p2p_scan(wl) ((wl)->p2p->scan) @@ -140,7 +140,14 @@ enum wl_cfgp2p_status { } \ } while (0) - +extern bool +wl_cfgp2p_is_pub_action(void *frame, u32 frame_len); +extern bool +wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len); +extern bool +wl_cfgp2p_is_gas_action(void *frame, u32 frame_len); +extern void +wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len); extern s32 wl_cfgp2p_init_priv(struct wl_priv *wl); extern void @@ -172,6 +179,10 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, u32 num_ u16 *channels, s32 search_state, u16 action, u32 bssidx); +extern s32 +wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, + s32 bssidx, s32 channel); + extern wpa_ie_fixed_t * wl_cfgp2p_find_wpaie(u8 *parse, u32 len); @@ -217,7 +228,7 @@ extern bool wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx); extern s32 -wl_cfgp2p_bss(struct net_device *ndev, s32 bsscfg_idx, s32 up); +wl_cfgp2p_bss(struct wl_priv *wl, struct net_device *ndev, s32 bsscfg_idx, s32 up); extern s32 @@ -235,13 +246,38 @@ wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, in extern s32 wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); +extern u8 * +wl_cfgp2p_retreive_p2pattrib(void *buf, u8 element_id); + +extern u8 * +wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length); + +extern s32 +wl_cfgp2p_register_ndev(struct wl_priv *wl); + +extern s32 +wl_cfgp2p_unregister_ndev(struct wl_priv *wl); + /* WiFi Direct */ #define SOCIAL_CHAN_1 1 #define SOCIAL_CHAN_2 6 #define SOCIAL_CHAN_3 11 +#define SOCIAL_CHAN_CNT 3 #define WL_P2P_WILDCARD_SSID "DIRECT-" #define WL_P2P_WILDCARD_SSID_LEN 7 #define WL_P2P_INTERFACE_PREFIX "p2p" #define WL_P2P_TEMP_CHAN "11" + +/* If the provision discovery is for JOIN operations, then we need not do an internal scan to find GO */ +#define IS_PROV_DISC_WITHOUT_GROUP_ID(p2p_ie, len) (wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_GROUP_ID) == NULL ) + +#define IS_GAS_REQ(frame, len) (wl_cfgp2p_is_gas_action(frame, len) && \ + ((frame->action == P2PSD_ACTION_ID_GAS_IREQ) || \ + (frame->action == P2PSD_ACTION_ID_GAS_CREQ))) +#define IS_P2P_PUB_ACT_REQ(frame, p2p_ie, len) (wl_cfgp2p_is_pub_action(frame, len) && \ + ((frame->subtype == P2P_PAF_GON_REQ) || \ + (frame->subtype == P2P_PAF_INVITE_REQ) || \ + ((frame->subtype == P2P_PAF_PROVDIS_REQ) && IS_PROV_DISC_WITHOUT_GROUP_ID(p2p_ie, len)))) +#define IS_P2P_SOCIAL(ch) ((ch == SOCIAL_CHAN_1) || (ch == SOCIAL_CHAN_2) || (ch == SOCIAL_CHAN_3)) #define IS_P2P_SSID(ssid) (memcmp(ssid, WL_P2P_WILDCARD_SSID, WL_P2P_WILDCARD_SSID_LEN) == 0) #endif /* _wl_cfgp2p_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wl_iw.c b/drivers/net/wireless/bcmdhd/wl_iw.c index 32f7b57..095dc86 100644 --- a/drivers/net/wireless/bcmdhd/wl_iw.c +++ b/drivers/net/wireless/bcmdhd/wl_iw.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wl_iw.c,v 1.132.2.18 2011-02-05 01:44:47 Exp $ + * $Id: wl_iw.c,v 1.132.2.18 2011-02-05 01:44:47 $ */ #include <wlioctl.h> @@ -148,7 +148,7 @@ static struct mutex wl_softap_lock; #include <bcmsdbus.h> extern void dhd_customer_gpio_wlan_ctrl(int onoff); extern uint dhd_dev_reset(struct net_device *dev, uint8 flag); -extern void dhd_dev_init_ioctl(struct net_device *dev); +extern int dhd_dev_init_ioctl(struct net_device *dev); uint wl_msg_level = WL_ERROR_VAL; @@ -162,11 +162,8 @@ uint wl_msg_level = WL_ERROR_VAL; #define htodchanspec(i) i #define dtohchanspec(i) i -#ifdef CONFIG_WIRELESS_EXT - extern struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); extern int dhd_wait_pend8021x(struct net_device *dev); -#endif #if WIRELESS_EXT < 19 #define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) @@ -1122,7 +1119,7 @@ wl_iw_set_btcoex_dhcp( } static int -wl_iw_set_suspend( +wl_iw_set_suspend_opt( struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, @@ -1133,16 +1130,15 @@ char *extra int ret_now; int ret = 0; - suspend_flag = *(extra + strlen(SETSUSPEND_CMD) + 1) - '0'; + suspend_flag = *(extra + strlen(SETSUSPENDOPT_CMD) + 1) - '0'; if (suspend_flag != 0) suspend_flag = 1; ret_now = net_os_set_suspend_disable(dev, suspend_flag); - if (ret_now != suspend_flag) { - if (!(ret = net_os_set_suspend(dev, ret_now))) + if (!(ret = net_os_set_suspend(dev, ret_now, 1))) WL_ERROR(("%s: Suspend Flag %d -> %d\n", __FUNCTION__, ret_now, suspend_flag)); else @@ -1153,6 +1149,32 @@ char *extra } static int +wl_iw_set_suspend_mode( +struct net_device *dev, +struct iw_request_info *info, +union iwreq_data *wrqu, +char *extra +) +{ + int ret = 0; + +#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND) + int suspend_flag; + + suspend_flag = *(extra + strlen(SETSUSPENDMODE_CMD) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + + if (!(ret = net_os_set_suspend(dev, suspend_flag, 0))) + WL_ERROR(("%s: Suspend Mode %d\n",__FUNCTION__,suspend_flag)); + else + WL_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); +#endif + return ret; +} + +static int wl_format_ssid(char* ssid_buf, uint8* ssid, int ssid_len) { int i, c; @@ -1564,6 +1586,63 @@ exit_proc: net_os_wake_unlock(dev); return res; } + +static int +wl_iw_set_pno_setadd( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int ret = -1; + char *tmp_ptr; + int size, tmp_size; + + net_os_wake_lock(dev); + WL_ERROR(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + goto exit_proc; + } + + if (wrqu->data.length <= strlen(PNOSETADD_SET_CMD) + sizeof(cmd_tlv_t)) { + WL_ERROR(("%s argument=%d less than %d\n", __FUNCTION__, + wrqu->data.length, (int)(strlen(PNOSETADD_SET_CMD) + sizeof(cmd_tlv_t)))); + goto exit_proc; + } + + + bcopy(PNOSETUP_SET_CMD, extra, strlen(PNOSETUP_SET_CMD)); + + tmp_ptr = extra + strlen(PNOSETUP_SET_CMD); + size = wrqu->data.length - strlen(PNOSETUP_SET_CMD); + tmp_size = size; + + while (*tmp_ptr && tmp_size > 0) { + if ((*tmp_ptr == 'S') && (size - tmp_size) >= sizeof(cmd_tlv_t)) { + *(tmp_ptr + 1) = ((*(tmp_ptr + 1) - '0') << 4) + (*(tmp_ptr + 2) - '0'); + memmove(tmp_ptr + 2, tmp_ptr + 3, tmp_size - 3); + tmp_size -= 2 + *(tmp_ptr + 1); + tmp_ptr += 2 + *(tmp_ptr + 1); + size--; + } else { + tmp_ptr++; + tmp_size--; + } + } + + wrqu->data.length = strlen(PNOSETUP_SET_CMD) + size; + ret = wl_iw_set_pno_set(dev, info, wrqu, extra); + +exit_proc: + net_os_wake_unlock(dev); + return ret; + +} #endif static int @@ -1626,7 +1705,7 @@ wl_iw_send_priv_event( strcpy(extra, flag); wrqu.data.length = strlen(extra); wireless_send_event(dev, cmd, &wrqu, extra); - net_os_wake_lock_timeout_enable(dev, DHD_EVENT_TIMEOUT_MS); + net_os_wake_lock_ctrl_timeout_enable(dev, DHD_EVENT_TIMEOUT_MS); WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra)); return 0; @@ -2567,6 +2646,8 @@ wl_iw_get_range( IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP); IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE); + IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCREQIE); + IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCRESPIE); IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND); #endif @@ -5473,7 +5554,15 @@ wl_iw_set_wpaauth( switch (paramid) { case IW_AUTH_WPA_VERSION: - iw->wpaversion = paramval; + if (paramval & IW_AUTH_WPA_VERSION_DISABLED) + val = WPA_AUTH_DISABLED; + else if (paramval & (IW_AUTH_WPA_VERSION_WPA)) + val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; + else if (paramval & IW_AUTH_WPA_VERSION_WPA2) + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; + WL_ERROR(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val)); + if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) + return error; break; case IW_AUTH_CIPHER_PAIRWISE: @@ -5491,7 +5580,27 @@ wl_iw_set_wpaauth( break; case IW_AUTH_KEY_MGMT: - if (paramval & IW_AUTH_KEY_MGMT_PSK) { + if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) + return error; + + if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { + if (paramval & IW_AUTH_KEY_MGMT_PSK) + val = WPA_AUTH_PSK; + else + val = WPA_AUTH_UNSPECIFIED; + if (paramval & 0x04) + val |= WPA2_AUTH_FT; + } + else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + if (paramval & IW_AUTH_KEY_MGMT_PSK) + val = WPA2_AUTH_PSK; + else + val = WPA2_AUTH_UNSPECIFIED; + if (paramval & 0x04) + val |= WPA2_AUTH_FT; + } + + else if (paramval & IW_AUTH_KEY_MGMT_PSK) { if (iw->wpaversion == IW_AUTH_WPA_VERSION_WPA) val = WPA_AUTH_PSK; else if (iw->wpaversion == IW_AUTH_WPA_VERSION_WPA2) @@ -7517,8 +7626,10 @@ wl_iw_set_priv( ret = wl_iw_get_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, DTIM_SKIP_SET_CMD, strlen(DTIM_SKIP_SET_CMD)) == 0) ret = wl_iw_set_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, SETSUSPEND_CMD, strlen(SETSUSPEND_CMD)) == 0) - ret = wl_iw_set_suspend(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, SETSUSPENDOPT_CMD, strlen(SETSUSPENDOPT_CMD)) == 0) + ret = wl_iw_set_suspend_opt(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, SETSUSPENDMODE_CMD, strlen(SETSUSPENDMODE_CMD)) == 0) + ret = wl_iw_set_suspend_mode(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, TXPOWER_SET_CMD, strlen(TXPOWER_SET_CMD)) == 0) ret = wl_iw_set_txpower(dev, info, (union iwreq_data *)dwrq, extra); #if defined(PNO_SUPPORT) @@ -7526,6 +7637,8 @@ wl_iw_set_priv( ret = wl_iw_set_pno_reset(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, PNOSETUP_SET_CMD, strlen(PNOSETUP_SET_CMD)) == 0) ret = wl_iw_set_pno_set(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, PNOSETADD_SET_CMD, strlen(PNOSETADD_SET_CMD)) == 0) + ret = wl_iw_set_pno_setadd(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, PNOENABLE_SET_CMD, strlen(PNOENABLE_SET_CMD)) == 0) ret = wl_iw_set_pno_enable(dev, info, (union iwreq_data *)dwrq, extra); #endif @@ -8251,6 +8364,21 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) break; } + + case WLC_E_ASSOC_REQ_IE: + cmd = IWEVASSOCREQIE; + wrqu.data.length = datalen; + if (datalen < sizeof(extra)) + memcpy(extra, data, datalen); + break; + + case WLC_E_ASSOC_RESP_IE: + cmd = IWEVASSOCRESPIE; + wrqu.data.length = datalen; + if (datalen < sizeof(extra)) + memcpy(extra, data, datalen); + break; + case WLC_E_PMKID_CACHE: { if (data) { diff --git a/drivers/net/wireless/bcmdhd/wl_iw.h b/drivers/net/wireless/bcmdhd/wl_iw.h index c0cc14b..9cdb53d 100644 --- a/drivers/net/wireless/bcmdhd/wl_iw.h +++ b/drivers/net/wireless/bcmdhd/wl_iw.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wl_iw.h,v 1.15.80.6 2010-12-23 01:13:23 Exp $ + * $Id: wl_iw.h,v 1.15.80.6 2010-12-23 01:13:23 $ */ @@ -47,13 +47,15 @@ #define BAND_SET_CMD "SETBAND" #define DTIM_SKIP_GET_CMD "DTIMSKIPGET" #define DTIM_SKIP_SET_CMD "DTIMSKIPSET" -#define SETSUSPEND_CMD "SETSUSPENDOPT" +#define SETSUSPENDOPT_CMD "SETSUSPENDOPT" +#define SETSUSPENDMODE_CMD "SETSUSPENDMODE" #define PNOSSIDCLR_SET_CMD "PNOSSIDCLR" #define PNOSETUP_SET_CMD "PNOSETUP " +#define PNOSETADD_SET_CMD "PNOSETADD" #define PNOENABLE_SET_CMD "PNOFORCE" #define PNODEBUG_SET_CMD "PNODEBUG" -#define TXPOWER_SET_CMD "TXPOWER" +#define TXPOWER_SET_CMD "TXPOWER" #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" @@ -203,11 +205,10 @@ void wl_iw_detach(void); extern int net_os_wake_lock(struct net_device *dev); extern int net_os_wake_unlock(struct net_device *dev); extern int net_os_wake_lock_timeout(struct net_device *dev); -extern int net_os_wake_lock_timeout_enable(struct net_device *dev, int val); +extern int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val); extern int net_os_set_suspend_disable(struct net_device *dev, int val); -extern int net_os_set_suspend(struct net_device *dev, int val); +extern int net_os_set_suspend(struct net_device *dev, int val, int force); extern int net_os_set_dtim_skip(struct net_device *dev, int val); -extern int net_os_send_hang_message(struct net_device *dev); extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) @@ -226,6 +227,18 @@ extern void get_customized_country_code(char *country_iso_code, wl_country_t *cs iwe_stream_add_point(stream, ends, iwe, extra) #endif +extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); +extern int dhd_pno_clean(dhd_pub_t *dhd); +extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, + ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_pno_get_status(dhd_pub_t *dhd); +extern int dhd_dev_pno_reset(struct net_device *dev); +extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, + int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); +extern int dhd_dev_get_pno_status(struct net_device *dev); +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); + void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec); #define PNO_TLV_PREFIX 'S' diff --git a/drivers/net/wireless/bcmdhd/dhd_linux_mon.c b/drivers/net/wireless/bcmdhd/wl_linux_mon.c index ce94ff9..f44b4b0 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux_mon.c +++ b/drivers/net/wireless/bcmdhd/wl_linux_mon.c @@ -2,13 +2,13 @@ * Broadcom Dongle Host Driver (DHD), Linux monitor network interface * * Copyright (C) 1999-2011, Broadcom Corporation - * + * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: - * + * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that @@ -16,12 +16,12 @@ * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. - * + * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_linux_mon.c,v 1.131.2.55 2011-02-09 05:31:56 Exp $ + * $Id: wl_linux_mon.c 303266 2011-12-16 00:15:23Z $ */ #include <linux/string.h> @@ -96,16 +96,30 @@ static const struct net_device_ops dhd_mon_if_ops = { static struct net_device* lookup_real_netdev(char *name) { int i; + int len = 0; int last_name_len = 0; struct net_device *ndev; struct net_device *ndev_found = NULL; - /* We want to find interface "p2p-eth0-0" for monitor interface "mon.p2p-eth0-0", so - * we skip "eth0" even if "mon.p2p-eth0-0" contains "eth0" + /* We need to find interface "p2p-p2p-0" corresponding to monitor interface "mon-p2p-0", + * Once mon iface name reaches IFNAMSIZ, it is reset to p2p0-0 and corresponding mon + * iface would be mon-p2p0-0. */ for (i = 0; i < DHD_MAX_IFS; i++) { ndev = dhd_idx2net(g_monitor.dhd_pub, i); - if (ndev && strstr(name, ndev->name)) { + + /* Skip "p2p" and look for "-p2p0-x" in monitor interface name. If it + * it matches, then this netdev is the corresponding real_netdev. + */ + if (ndev && strstr(ndev->name, "p2p-p2p0")) { + len = strlen("p2p"); + } else { + /* if p2p- is not present, then the IFNAMSIZ have reached and name + * would have got reset. In this casse,look for p2p0-x in mon-p2p0-x + */ + len = 0; + } + if (ndev && strstr(name, (ndev->name + len))) { if (strlen(ndev->name) > last_name_len) { ndev_found = ndev; last_name_len = strlen(ndev->name); diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c index bb3eaea..8270331 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -24,12 +24,13 @@ * $Id: wldev_common.c,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ */ -#include <linux/module.h> +#include <osl.h> +#include <linux/kernel.h> +#include <linux/kthread.h> #include <linux/netdevice.h> #include <wldev_common.h> #include <bcmutils.h> -#include <dhd_dbg.h> #define htod32(i) i #define htod16(i) i @@ -37,6 +38,13 @@ #define dtoh16(i) i #define htodchanspec(i) i #define dtohchanspec(i) i + +#define WLDEV_ERROR(args) \ + do { \ + printk(KERN_ERR "WLDEV-ERROR) %s : ", __func__); \ + printk args; \ + } while (0) + extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd); s32 wldev_ioctl( @@ -71,26 +79,34 @@ static s32 wldev_mkiovar( s32 wldev_iovar_getbuf( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen) + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) { s32 ret = 0; s32 iovar_len = 0; - + if (buf_sync) { + mutex_lock(buf_sync); + } iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); + if (buf_sync) + mutex_unlock(buf_sync); return ret; } s32 wldev_iovar_setbuf( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen) + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) { s32 ret = 0; s32 iovar_len; - + if (buf_sync) { + mutex_lock(buf_sync); + } iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE); + if (buf_sync) + mutex_unlock(buf_sync); return ret; } @@ -102,7 +118,7 @@ s32 wldev_iovar_setint( val = htod32(val); memset(iovar_buf, 0, sizeof(iovar_buf)); return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf, - sizeof(iovar_buf)); + sizeof(iovar_buf), NULL); } @@ -114,7 +130,7 @@ s32 wldev_iovar_getint( memset(iovar_buf, 0, sizeof(iovar_buf)); err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf, - sizeof(iovar_buf)); + sizeof(iovar_buf), NULL); if (err == 0) { memcpy(pval, iovar_buf, sizeof(*pval)); @@ -148,7 +164,7 @@ s32 wldev_mkiovar_bsscfg( if (buflen < 0 || iolen > (u32)buflen) { - DHD_ERROR(("%s: buffer is too short\n", __FUNCTION__)); + WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__)); return BCME_BUFTOOSHORT; } @@ -177,26 +193,37 @@ s32 wldev_mkiovar_bsscfg( s32 wldev_iovar_getbuf_bsscfg( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx) + void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) { s32 ret = 0; s32 iovar_len = 0; - + if (buf_sync) { + mutex_lock(buf_sync); + } iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); + if (buf_sync) { + mutex_unlock(buf_sync); + } return ret; } s32 wldev_iovar_setbuf_bsscfg( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx) + void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) { s32 ret = 0; s32 iovar_len; - + if (buf_sync) { + mutex_lock(buf_sync); + } iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); + ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE); + if (buf_sync) { + mutex_unlock(buf_sync); + } return ret; } @@ -208,7 +235,7 @@ s32 wldev_iovar_setint_bsscfg( val = htod32(val); memset(iovar_buf, 0, sizeof(iovar_buf)); return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf, - sizeof(iovar_buf), bssidx); + sizeof(iovar_buf), bssidx, NULL); } @@ -220,7 +247,7 @@ s32 wldev_iovar_getint_bsscfg( memset(iovar_buf, 0, sizeof(iovar_buf)); err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf, - sizeof(iovar_buf), bssidx); + sizeof(iovar_buf), bssidx, NULL); if (err == 0) { memcpy(pval, iovar_buf, sizeof(*pval)); @@ -309,16 +336,16 @@ int wldev_set_country( return error; error = wldev_iovar_getbuf(dev, "country", &cspec, sizeof(cspec), - smbuf, sizeof(smbuf)); + smbuf, sizeof(smbuf), NULL); if (error < 0) - DHD_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error)); + WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error)); if ((error < 0) || (strncmp(country_code, smbuf, WLC_CNTRY_BUF_SZ) != 0)) { bzero(&scbval, sizeof(scb_val_t)); error = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), 1); if (error < 0) { - DHD_ERROR(("%s: set country failed due to Disassoc error %d\n", + WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n", __FUNCTION__, error)); return error; } @@ -328,14 +355,64 @@ int wldev_set_country( memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); get_customized_country_code((char *)&cspec.country_abbrev, &cspec); error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), - smbuf, sizeof(smbuf)); + smbuf, sizeof(smbuf), NULL); if (error < 0) { - DHD_ERROR(("%s: set country for %s as %s rev %d failed\n", + WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n", __FUNCTION__, country_code, cspec.ccode, cspec.rev)); return error; } dhd_bus_country_set(dev, &cspec); - DHD_INFO(("%s: set country for %s as %s rev %d\n", + WLDEV_ERROR(("%s: set country for %s as %s rev %d\n", __FUNCTION__, country_code, cspec.ccode, cspec.rev)); return 0; } + +/* + * softap channel autoselect + */ +int wldev_get_auto_channel(struct net_device *dev, int *chan) +{ + int chosen = 0; + wl_uint32_list_t request; + int retry = 0; + int updown = 0; + int ret = 0; + wlc_ssid_t null_ssid; + + memset(&null_ssid, 0, sizeof(wlc_ssid_t)); + ret |= wldev_ioctl(dev, WLC_UP, &updown, sizeof(updown), true); + + ret |= wldev_ioctl(dev, WLC_SET_SSID, &null_ssid, sizeof(null_ssid), true); + + request.count = htod32(0); + ret = wldev_ioctl(dev, WLC_START_CHANNEL_SEL, &request, sizeof(request), true); + if (ret < 0) { + WLDEV_ERROR(("can't start auto channel scan:%d\n", ret)); + goto fail; + } + + while (retry++ < 15) { + + bcm_mdelay(350); + + ret = wldev_ioctl(dev, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen), false); + + if ((ret == 0) && (dtoh32(chosen) != 0)) { + *chan = (uint16)chosen & 0x00FF; /* covert chanspec --> chan number */ + printf("%s: Got channel = %d, attempt:%d\n", + __FUNCTION__, *chan, retry); + break; + } + } + + if ((ret = wldev_ioctl(dev, WLC_DOWN, &updown, sizeof(updown), true)) < 0) { + WLDEV_ERROR(("%s fail to WLC_DOWN ioctl err =%d\n", __FUNCTION__, ret)); + goto fail; + } + +fail : + if (ret < 0) { + WLDEV_ERROR(("%s: return value %d\n", __FUNCTION__, ret)); + } + return ret; +} diff --git a/drivers/net/wireless/bcmdhd/wldev_common.h b/drivers/net/wireless/bcmdhd/wldev_common.h index 4632680..f586609 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.h +++ b/drivers/net/wireless/bcmdhd/wldev_common.h @@ -21,33 +21,33 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wldev_common.h,v 1.1.4.1.2.14 2011-02-09 01:40:07 Exp $ + * $Id: wldev_common.h,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ */ #ifndef __WLDEV_COMMON_H__ #define __WLDEV_COMMON_H__ #include <wlioctl.h> -/** wl_dev_ioctl - get/set IOCTLs, will call net_device's do_ioctl (or +/* wl_dev_ioctl - get/set IOCTLs, will call net_device's do_ioctl (or * netdev_ops->ndo_do_ioctl in new kernels) * @dev: the net_device handle */ s32 wldev_ioctl( struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set); -/** Retrieve named IOVARs, this function calls wl_dev_ioctl with +/** Retrieve named IOVARs, this function calls wl_dev_ioctl with * WLC_GET_VAR IOCTL code */ s32 wldev_iovar_getbuf( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen); + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync); /** Set named IOVARs, this function calls wl_dev_ioctl with * WLC_SET_VAR IOCTL code */ s32 wldev_iovar_setbuf( struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen); + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync); s32 wldev_iovar_setint( struct net_device *dev, s8 *iovar, s32 val); @@ -67,15 +67,15 @@ s32 wldev_mkiovar_bsscfg( * WLC_GET_VAR IOCTL code */ s32 wldev_iovar_getbuf_bsscfg( - struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx); + struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, + void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync); /** Set named and bsscfg indexed IOVARs, this function calls wl_dev_ioctl with * WLC_SET_VAR IOCTL code */ s32 wldev_iovar_setbuf_bsscfg( - struct net_device *dev, s8 *iovar_name, - void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx); + struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, + void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync); s32 wldev_iovar_getint_bsscfg( struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx); @@ -92,9 +92,9 @@ extern int net_os_wake_lock_timeout(struct net_device *dev); extern int net_os_wake_lock_timeout_enable(struct net_device *dev, int val); extern int net_os_set_dtim_skip(struct net_device *dev, int val); extern int net_os_set_suspend_disable(struct net_device *dev, int val); -extern int net_os_set_suspend(struct net_device *dev, int val); +extern int net_os_set_suspend(struct net_device *dev, int val, int force); extern int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, - int max, int *bytes_left); + int max, int *bytes_left); /* Get the link speed from dongle, speed is in kpbs */ int wldev_get_link_speed(struct net_device *dev, int *plink_speed); @@ -107,4 +107,6 @@ int wldev_get_band(struct net_device *dev, uint *pband); int wldev_set_band(struct net_device *dev, uint band); +int wldev_get_auto_channel(struct net_device *dev, int *chan); + #endif /* __WLDEV_COMMON_H__ */ diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c index fa76ce7..3e09d57 100644 --- a/drivers/staging/android/logger.c +++ b/drivers/staging/android/logger.c @@ -57,6 +57,8 @@ struct logger_reader { struct logger_log *log; /* associated log */ struct list_head list; /* entry in logger_log's list */ size_t r_off; /* current read head offset */ + bool r_all; /* reader can read all entries */ + int r_ver; /* reader ABI version */ }; /* logger_offset - returns index 'n' into the log via (optimized) modulus */ @@ -86,25 +88,71 @@ static inline struct logger_log *file_get_log(struct file *file) } /* - * get_entry_len - Grabs the length of the payload of the next entry starting - * from 'off'. + * get_entry_header - returns a pointer to the logger_entry header within + * 'log' starting at offset 'off'. A temporary logger_entry 'scratch' must + * be provided. Typically the return value will be a pointer within + * 'logger->buf'. However, a pointer to 'scratch' may be returned if + * the log entry spans the end and beginning of the circular buffer. + */ +static struct logger_entry *get_entry_header(struct logger_log *log, + size_t off, struct logger_entry *scratch) +{ + size_t len = min(sizeof(struct logger_entry), log->size - off); + if (len != sizeof(struct logger_entry)) { + memcpy(((void *) scratch), log->buffer + off, len); + memcpy(((void *) scratch) + len, log->buffer, + sizeof(struct logger_entry) - len); + return scratch; + } + + return (struct logger_entry *) (log->buffer + off); +} + +/* + * get_entry_msg_len - Grabs the length of the message of the entry + * starting from from 'off'. * * Caller needs to hold log->mutex. */ -static __u32 get_entry_len(struct logger_log *log, size_t off) +static __u32 get_entry_msg_len(struct logger_log *log, size_t off) { - __u16 val; + struct logger_entry scratch; + struct logger_entry *entry; - switch (log->size - off) { - case 1: - memcpy(&val, log->buffer + off, 1); - memcpy(((char *) &val) + 1, log->buffer, 1); - break; - default: - memcpy(&val, log->buffer + off, 2); + entry = get_entry_header(log, off, &scratch); + return entry->len; +} + +static size_t get_user_hdr_len(int ver) +{ + if (ver < 2) + return sizeof(struct user_logger_entry_compat); + else + return sizeof(struct logger_entry); +} + +static ssize_t copy_header_to_user(int ver, struct logger_entry *entry, + char __user *buf) +{ + void *hdr; + size_t hdr_len; + struct user_logger_entry_compat v1; + + if (ver < 2) { + v1.len = entry->len; + v1.__pad = 0; + v1.pid = entry->pid; + v1.tid = entry->tid; + v1.sec = entry->sec; + v1.nsec = entry->nsec; + hdr = &v1; + hdr_len = sizeof(struct user_logger_entry_compat); + } else { + hdr = entry; + hdr_len = sizeof(struct logger_entry); } - return sizeof(struct logger_entry) + val; + return copy_to_user(buf, hdr, hdr_len); } /* @@ -118,15 +166,30 @@ static ssize_t do_read_log_to_user(struct logger_log *log, char __user *buf, size_t count) { + struct logger_entry scratch; + struct logger_entry *entry; size_t len; + size_t msg_start; /* - * We read from the log in two disjoint operations. First, we read from - * the current read head offset up to 'count' bytes or to the end of + * First, copy the header to userspace, using the version of + * the header requested + */ + entry = get_entry_header(log, reader->r_off, &scratch); + if (copy_header_to_user(reader->r_ver, entry, buf)) + return -EFAULT; + + count -= get_user_hdr_len(reader->r_ver); + buf += get_user_hdr_len(reader->r_ver); + msg_start = logger_offset(reader->r_off + sizeof(struct logger_entry)); + + /* + * We read from the msg in two disjoint operations. First, we read from + * the current msg head offset up to 'count' bytes or to the end of * the log, whichever comes first. */ - len = min(count, log->size - reader->r_off); - if (copy_to_user(buf, log->buffer + reader->r_off, len)) + len = min(count, log->size - msg_start); + if (copy_to_user(buf, log->buffer + msg_start, len)) return -EFAULT; /* @@ -137,9 +200,34 @@ static ssize_t do_read_log_to_user(struct logger_log *log, if (copy_to_user(buf + len, log->buffer, count - len)) return -EFAULT; - reader->r_off = logger_offset(reader->r_off + count); + reader->r_off = logger_offset(reader->r_off + + sizeof(struct logger_entry) + count); - return count; + return count + get_user_hdr_len(reader->r_ver); +} + +/* + * get_next_entry_by_uid - Starting at 'off', returns an offset into + * 'log->buffer' which contains the first entry readable by 'euid' + */ +static size_t get_next_entry_by_uid(struct logger_log *log, + size_t off, uid_t euid) +{ + while (off != log->w_off) { + struct logger_entry *entry; + struct logger_entry scratch; + size_t next_len; + + entry = get_entry_header(log, off, &scratch); + + if (entry->euid == euid) + return off; + + next_len = sizeof(struct logger_entry) + entry->len; + off = logger_offset(off + next_len); + } + + return off; } /* @@ -151,7 +239,7 @@ static ssize_t do_read_log_to_user(struct logger_log *log, * - If there are no log entries to read, blocks until log is written to * - Atomically reads exactly one log entry * - * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read + * Will set errno to EINVAL if read * buffer is insufficient to hold next entry. */ static ssize_t logger_read(struct file *file, char __user *buf, @@ -191,6 +279,10 @@ start: mutex_lock(&log->mutex); + if (!reader->r_all) + reader->r_off = get_next_entry_by_uid(log, + reader->r_off, current_euid()); + /* is there still something to read or did we race? */ if (unlikely(log->w_off == reader->r_off)) { mutex_unlock(&log->mutex); @@ -198,7 +290,8 @@ start: } /* get the size of the next entry */ - ret = get_entry_len(log, reader->r_off); + ret = get_user_hdr_len(reader->r_ver) + + get_entry_msg_len(log, reader->r_off); if (count < ret) { ret = -EINVAL; goto out; @@ -224,7 +317,8 @@ static size_t get_next_entry(struct logger_log *log, size_t off, size_t len) size_t count = 0; do { - size_t nr = get_entry_len(log, off); + size_t nr = sizeof(struct logger_entry) + + get_entry_msg_len(log, off); off = logger_offset(off + nr); count += nr; } while (count < len); @@ -336,7 +430,9 @@ ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov, header.tid = current->pid; header.sec = now.tv_sec; header.nsec = now.tv_nsec; + header.euid = current_euid(); header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD); + header.hdr_size = sizeof(struct logger_entry); /* null writes succeed, return zero */ if (unlikely(!header.len)) @@ -409,6 +505,10 @@ static int logger_open(struct inode *inode, struct file *file) return -ENOMEM; reader->log = log; + reader->r_ver = 1; + reader->r_all = in_egroup_p(inode->i_gid) || + capable(CAP_SYSLOG); + INIT_LIST_HEAD(&reader->list); mutex_lock(&log->mutex); @@ -463,6 +563,10 @@ static unsigned int logger_poll(struct file *file, poll_table *wait) poll_wait(file, &log->wq, wait); mutex_lock(&log->mutex); + if (!reader->r_all) + reader->r_off = get_next_entry_by_uid(log, + reader->r_off, current_euid()); + if (log->w_off != reader->r_off) ret |= POLLIN | POLLRDNORM; mutex_unlock(&log->mutex); @@ -470,11 +574,25 @@ static unsigned int logger_poll(struct file *file, poll_table *wait) return ret; } +static long logger_set_version(struct logger_reader *reader, void __user *arg) +{ + int version; + if (copy_from_user(&version, arg, sizeof(int))) + return -EFAULT; + + if ((version < 1) || (version > 2)) + return -EINVAL; + + reader->r_ver = version; + return 0; +} + static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct logger_log *log = file_get_log(file); struct logger_reader *reader; - long ret = -ENOTTY; + long ret = -EINVAL; + void __user *argp = (void __user *) arg; mutex_lock(&log->mutex); @@ -499,8 +617,14 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } reader = file->private_data; + + if (!reader->r_all) + reader->r_off = get_next_entry_by_uid(log, + reader->r_off, current_euid()); + if (log->w_off != reader->r_off) - ret = get_entry_len(log, reader->r_off); + ret = get_user_hdr_len(reader->r_ver) + + get_entry_msg_len(log, reader->r_off); else ret = 0; break; @@ -514,6 +638,22 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) log->head = log->w_off; ret = 0; break; + case LOGGER_GET_VERSION: + if (!(file->f_mode & FMODE_READ)) { + ret = -EBADF; + break; + } + reader = file->private_data; + ret = reader->r_ver; + break; + case LOGGER_SET_VERSION: + if (!(file->f_mode & FMODE_READ)) { + ret = -EBADF; + break; + } + reader = file->private_data; + ret = logger_set_version(reader, argp); + break; } mutex_unlock(&log->mutex); @@ -534,8 +674,8 @@ static const struct file_operations logger_fops = { /* * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which - * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than - * LONG_MAX minus LOGGER_ENTRY_MAX_LEN. + * must be a power of two, and greater than + * (LOGGER_ENTRY_MAX_PAYLOAD + sizeof(struct logger_entry)). */ #define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \ static unsigned char _buf_ ## VAR[SIZE]; \ diff --git a/drivers/staging/android/logger.h b/drivers/staging/android/logger.h index 2cb06e9..3f612a3 100644 --- a/drivers/staging/android/logger.h +++ b/drivers/staging/android/logger.h @@ -20,7 +20,12 @@ #include <linux/types.h> #include <linux/ioctl.h> -struct logger_entry { +/* + * The userspace structure for version 1 of the logger_entry ABI. + * This structure is returned to userspace unless the caller requests + * an upgrade to a newer ABI version. + */ +struct user_logger_entry_compat { __u16 len; /* length of the payload */ __u16 __pad; /* no matter what, we get 2 bytes of padding */ __s32 pid; /* generating process's pid */ @@ -30,14 +35,28 @@ struct logger_entry { char msg[0]; /* the entry's payload */ }; +/* + * The structure for version 2 of the logger_entry ABI. + * This structure is returned to userspace if ioctl(LOGGER_SET_VERSION) + * is called with version >= 2 + */ +struct logger_entry { + __u16 len; /* length of the payload */ + __u16 hdr_size; /* sizeof(struct logger_entry_v2) */ + __s32 pid; /* generating process's pid */ + __s32 tid; /* generating process's tid */ + __s32 sec; /* seconds since Epoch */ + __s32 nsec; /* nanoseconds */ + uid_t euid; /* effective UID of logger */ + char msg[0]; /* the entry's payload */ +}; + #define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */ #define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */ #define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */ #define LOGGER_LOG_MAIN "log_main" /* everything else */ -#define LOGGER_ENTRY_MAX_LEN (4*1024) -#define LOGGER_ENTRY_MAX_PAYLOAD \ - (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) +#define LOGGER_ENTRY_MAX_PAYLOAD 4076 #define __LOGGERIO 0xAE @@ -45,5 +64,7 @@ struct logger_entry { #define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ #define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ #define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ +#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ +#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ #endif /* _LINUX_LOGGER_H */ diff --git a/drivers/staging/android/ram_console.c b/drivers/staging/android/ram_console.c index cb42d89..b278d62 100644 --- a/drivers/staging/android/ram_console.c +++ b/drivers/staging/android/ram_console.c @@ -143,7 +143,7 @@ ram_console_write(struct console *console, const char *s, unsigned int count) static struct console ram_console = { .name = "ram", .write = ram_console_write, - .flags = CON_PRINTBUFFER | CON_ENABLED, + .flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME, .index = -1, }; diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c index e05fc25..cd2456a 100644 --- a/drivers/switch/switch_class.c +++ b/drivers/switch/switch_class.c @@ -22,6 +22,7 @@ #include <linux/fs.h> #include <linux/err.h> #include <linux/switch.h> +#include <linux/hrtimer.h> struct class *switch_class; static atomic_t device_count; @@ -61,8 +62,9 @@ void switch_set_state(struct switch_dev *sdev, int state) { char name_buf[120]; char state_buf[120]; + char timestamp_buf[120]; char *prop_buf; - char *envp[3]; + char *envp[4]; int env_offset = 0; int length; @@ -87,6 +89,9 @@ void switch_set_state(struct switch_dev *sdev, int state) "SWITCH_STATE=%s", prop_buf); envp[env_offset++] = state_buf; } + snprintf(timestamp_buf, sizeof(timestamp_buf), + "SWITCH_TIME=%llu", ktime_to_ns(ktime_get())); + envp[env_offset++] = timestamp_buf; envp[env_offset] = NULL; kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp); free_page((unsigned long)prop_buf); diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 00a446b..1236d6f 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -45,6 +45,7 @@ #include "epautoconf.c" #include "composite.c" +#include "f_audio_source.c" #include "f_mass_storage.c" #include "u_serial.c" #include "f_acm.c" @@ -82,6 +83,11 @@ struct android_usb_function { int (*init)(struct android_usb_function *, struct usb_composite_dev *); /* Optional: cleanup during gadget unbind */ void (*cleanup)(struct android_usb_function *); + /* Optional: called when the function is added the list of + * enabled functions */ + void (*enable)(struct android_usb_function *); + /* Optional: called when it is removed */ + void (*disable)(struct android_usb_function *); int (*bind_config)(struct android_usb_function *, struct usb_configuration *); @@ -100,6 +106,7 @@ struct android_dev { struct device *dev; bool enabled; + int disable_depth; struct mutex mutex; bool connected; bool sw_connected; @@ -153,8 +160,6 @@ static struct usb_configuration android_config_driver = { .label = "android", .unbind = android_unbind_config, .bConfigurationValue = 1, - .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, - .bMaxPower = 0xFA, /* 500ma */ }; static void android_work(struct work_struct *data) @@ -184,18 +189,55 @@ static void android_work(struct work_struct *data) } } +static void android_enable(struct android_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + + BUG_ON(!mutex_is_locked(&dev->mutex)); + BUG_ON(!dev->disable_depth); + + if (--dev->disable_depth == 0) { + usb_add_config(cdev, &android_config_driver, + android_bind_config); + usb_gadget_connect(cdev->gadget); + } +} + +static void android_disable(struct android_dev *dev) +{ + struct usb_composite_dev *cdev = dev->cdev; + + BUG_ON(!mutex_is_locked(&dev->mutex)); + + if (dev->disable_depth++ == 0) { + usb_gadget_disconnect(cdev->gadget); + /* Cancel pending control requests */ + usb_ep_dequeue(cdev->gadget->ep0, cdev->req); + usb_remove_config(cdev, &android_config_driver); + } +} /*-------------------------------------------------------------------------*/ /* Supported functions initialization */ +struct adb_data { + bool opened; + bool enabled; +}; + static int adb_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev) { + f->config = kzalloc(sizeof(struct adb_data), GFP_KERNEL); + if (!f->config) + return -ENOMEM; + return adb_setup(); } static void adb_function_cleanup(struct android_usb_function *f) { adb_cleanup(); + kfree(f->config); } static int adb_function_bind_config(struct android_usb_function *f, struct usb_configuration *c) @@ -203,13 +245,69 @@ static int adb_function_bind_config(struct android_usb_function *f, struct usb_c return adb_bind_config(c); } +static void adb_android_function_enable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = f->config; + + data->enabled = true; + + /* Disable the gadget until adbd is ready */ + if (!data->opened) + android_disable(dev); +} + +static void adb_android_function_disable(struct android_usb_function *f) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = f->config; + + data->enabled = false; + + /* Balance the disable that was called in closed_callback */ + if (!data->opened) + android_enable(dev); +} + static struct android_usb_function adb_function = { .name = "adb", + .enable = adb_android_function_enable, + .disable = adb_android_function_disable, .init = adb_function_init, .cleanup = adb_function_cleanup, .bind_config = adb_function_bind_config, }; +static void adb_ready_callback(void) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = adb_function.config; + + mutex_lock(&dev->mutex); + + data->opened = true; + + if (data->enabled) + android_enable(dev); + + mutex_unlock(&dev->mutex); +} + +static void adb_closed_callback(void) +{ + struct android_dev *dev = _android_dev; + struct adb_data *data = adb_function.config; + + mutex_lock(&dev->mutex); + + data->opened = false; + + if (data->enabled) + android_disable(dev); + + mutex_unlock(&dev->mutex); +} + #define MAX_ACM_INSTANCES 4 struct acm_function_config { @@ -643,6 +741,67 @@ static struct android_usb_function accessory_function = { .ctrlrequest = accessory_function_ctrlrequest, }; +static int audio_source_function_init(struct android_usb_function *f, + struct usb_composite_dev *cdev) +{ + struct audio_source_config *config; + + config = kzalloc(sizeof(struct audio_source_config), GFP_KERNEL); + if (!config) + return -ENOMEM; + config->card = -1; + config->device = -1; + f->config = config; + return 0; +} + +static void audio_source_function_cleanup(struct android_usb_function *f) +{ + kfree(f->config); +} + +static int audio_source_function_bind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct audio_source_config *config = f->config; + + return audio_source_bind_config(c, config); +} + +static void audio_source_function_unbind_config(struct android_usb_function *f, + struct usb_configuration *c) +{ + struct audio_source_config *config = f->config; + + config->card = -1; + config->device = -1; +} + +static ssize_t audio_source_pcm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct android_usb_function *f = dev_get_drvdata(dev); + struct audio_source_config *config = f->config; + + /* print PCM card and device numbers */ + return sprintf(buf, "%d %d\n", config->card, config->device); +} + +static DEVICE_ATTR(pcm, S_IRUGO | S_IWUSR, audio_source_pcm_show, NULL); + +static struct device_attribute *audio_source_function_attributes[] = { + &dev_attr_pcm, + NULL +}; + +static struct android_usb_function audio_source_function = { + .name = "audio_source", + .init = audio_source_function_init, + .cleanup = audio_source_function_cleanup, + .bind_config = audio_source_function_bind_config, + .unbind_config = audio_source_function_unbind_config, + .attributes = audio_source_function_attributes, +}; static struct android_usb_function *supported_functions[] = { &adb_function, @@ -652,6 +811,7 @@ static struct android_usb_function *supported_functions[] = { &rndis_function, &mass_storage_function, &accessory_function, + &audio_source_function, NULL }; @@ -834,13 +994,13 @@ static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, { struct android_dev *dev = dev_get_drvdata(pdev); struct usb_composite_dev *cdev = dev->cdev; + struct android_usb_function *f; int enabled = 0; mutex_lock(&dev->mutex); sscanf(buff, "%d", &enabled); if (enabled && !dev->enabled) { - cdev->next_string_id = 0; /* update values in composite driver's copy of device descriptor */ cdev->desc.idVendor = device_desc.idVendor; cdev->desc.idProduct = device_desc.idProduct; @@ -848,15 +1008,18 @@ static ssize_t enable_store(struct device *pdev, struct device_attribute *attr, cdev->desc.bDeviceClass = device_desc.bDeviceClass; cdev->desc.bDeviceSubClass = device_desc.bDeviceSubClass; cdev->desc.bDeviceProtocol = device_desc.bDeviceProtocol; - usb_add_config(cdev, &android_config_driver, - android_bind_config); - usb_gadget_connect(cdev->gadget); + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->enable) + f->enable(f); + } + android_enable(dev); dev->enabled = true; } else if (!enabled && dev->enabled) { - usb_gadget_disconnect(cdev->gadget); - /* Cancel pending control requests */ - usb_ep_dequeue(cdev->gadget->ep0, cdev->req); - usb_remove_config(cdev, &android_config_driver); + android_disable(dev); + list_for_each_entry(f, &dev->enabled_functions, enabled_list) { + if (f->disable) + f->disable(f); + } dev->enabled = false; } else { pr_err("android_usb: already %s\n", @@ -920,10 +1083,7 @@ field ## _store(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t size) \ { \ if (size >= sizeof(buffer)) return -EINVAL; \ - if (sscanf(buf, "%s", buffer) == 1) { \ - return size; \ - } \ - return -1; \ + return strlcpy(buffer, buf, sizeof(buffer)); \ } \ static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store); @@ -1034,7 +1194,6 @@ static int android_bind(struct usb_composite_dev *cdev) device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); } - usb_gadget_set_selfpowered(gadget); dev->cdev = cdev; return 0; @@ -1108,6 +1267,11 @@ static void android_disconnect(struct usb_gadget *gadget) unsigned long flags; composite_disconnect(gadget); + /* accessory HID support can be active while the + accessory function is not actually enabled, + so we need to inform it when we are disconnected. + */ + acc_disconnect(); spin_lock_irqsave(&cdev->lock, flags); dev->connected = 0; @@ -1152,6 +1316,7 @@ static int __init init(void) if (!dev) return -ENOMEM; + dev->disable_depth = 1; dev->functions = supported_functions; INIT_LIST_HEAD(&dev->enabled_functions); INIT_WORK(&dev->work, android_work); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index dc06da6..1d88a80 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -564,7 +564,7 @@ done: return status; } -static int remove_config(struct usb_composite_dev *cdev, +static int unbind_config(struct usb_composite_dev *cdev, struct usb_configuration *config) { while (!list_empty(&config->functions)) { @@ -579,7 +579,6 @@ static int remove_config(struct usb_composite_dev *cdev, /* may free memory for "f" */ } } - list_del(&config->list); if (config->unbind) { DBG(cdev, "unbind config '%s'/%p\n", config->label, config); config->unbind(config); @@ -598,9 +597,11 @@ int usb_remove_config(struct usb_composite_dev *cdev, if (cdev->config == config) reset_config(cdev); + list_del(&config->list); + spin_unlock_irqrestore(&cdev->lock, flags); - return remove_config(cdev, config); + return unbind_config(cdev, config); } /*-------------------------------------------------------------------------*/ @@ -1084,7 +1085,8 @@ composite_unbind(struct usb_gadget *gadget) struct usb_configuration *c; c = list_first_entry(&cdev->configs, struct usb_configuration, list); - remove_config(cdev, c); + list_del(&c->list); + unbind_config(cdev, c); } if (composite->unbind) composite->unbind(cdev); diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index b512e0a..431cc35 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -339,6 +339,10 @@ struct usb_ep *usb_ep_autoconfig ( ep = find_ep (gadget, "ep13-bulk"); if (ep && ep_matches (gadget, ep, desc)) return ep; + } else if (USB_ENDPOINT_XFER_ISOC == type) { + ep = find_ep(gadget, "ep15-iso"); + if (ep && ep_matches(gadget, ep, desc)) + return ep; } } diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 05e65e5..1df88a6 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -33,6 +33,8 @@ #include <linux/device.h> #include <linux/miscdevice.h> +#include <linux/hid.h> +#include <linux/hiddev.h> #include <linux/usb.h> #include <linux/usb/ch9.h> #include <linux/usb/f_accessory.h> @@ -40,7 +42,7 @@ #define BULK_BUFFER_SIZE 16384 #define ACC_STRING_SIZE 256 -#define PROTOCOL_VERSION 1 +#define PROTOCOL_VERSION 2 /* String IDs */ #define INTERFACE_STRING_INDEX 0 @@ -49,6 +51,20 @@ #define TX_REQ_MAX 4 #define RX_REQ_MAX 2 +struct acc_hid_dev { + struct list_head list; + struct hid_device *hid; + struct acc_dev *dev; + /* accessory defined ID */ + int id; + /* HID report descriptor */ + u8 *report_desc; + /* length of HID report descriptor */ + int report_desc_len; + /* number of bytes of report_desc we have received so far */ + int report_desc_offset; +}; + struct acc_dev { struct usb_function function; struct usb_composite_dev *cdev; @@ -78,6 +94,8 @@ struct acc_dev { /* set to 1 if we have a pending start request */ int start_requested; + int audio_mode; + /* synchronize access to our device file */ atomic_t open_excl; @@ -87,7 +105,21 @@ struct acc_dev { wait_queue_head_t write_wq; struct usb_request *rx_req[RX_REQ_MAX]; int rx_done; - struct delayed_work work; + + /* delayed work for handling ACCESSORY_START */ + struct delayed_work start_work; + + /* worker for registering and unregistering hid devices */ + struct work_struct hid_work; + + /* list of active HID devices */ + struct list_head hid_list; + + /* list of new HID devices to register */ + struct list_head new_hid_list; + + /* list of dead HID devices to unregister */ + struct list_head dead_hid_list; }; static struct usb_interface_descriptor acc_interface_desc = { @@ -296,6 +328,160 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) } } +static void acc_complete_set_hid_report_desc(struct usb_ep *ep, + struct usb_request *req) +{ + struct acc_hid_dev *hid = req->context; + struct acc_dev *dev = hid->dev; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_set_hid_report_desc, err %d\n", + req->status); + return; + } + + memcpy(hid->report_desc + hid->report_desc_offset, req->buf, length); + hid->report_desc_offset += length; + if (hid->report_desc_offset == hid->report_desc_len) { + /* After we have received the entire report descriptor + * we schedule work to initialize the HID device + */ + schedule_work(&dev->hid_work); + } +} + +static void acc_complete_send_hid_event(struct usb_ep *ep, + struct usb_request *req) +{ + struct acc_hid_dev *hid = req->context; + int length = req->actual; + + if (req->status != 0) { + pr_err("acc_complete_send_hid_event, err %d\n", req->status); + return; + } + + hid_report_raw_event(hid->hid, HID_INPUT_REPORT, req->buf, length, 1); +} + +static int acc_hid_parse(struct hid_device *hid) +{ + struct acc_hid_dev *hdev = hid->driver_data; + + hid_parse_report(hid, hdev->report_desc, hdev->report_desc_len); + return 0; +} + +static int acc_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void acc_hid_stop(struct hid_device *hid) +{ +} + +static int acc_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void acc_hid_close(struct hid_device *hid) +{ +} + +static struct hid_ll_driver acc_hid_ll_driver = { + .parse = acc_hid_parse, + .start = acc_hid_start, + .stop = acc_hid_stop, + .open = acc_hid_open, + .close = acc_hid_close, +}; + +static struct acc_hid_dev *acc_hid_new(struct acc_dev *dev, + int id, int desc_len) +{ + struct acc_hid_dev *hdev; + + hdev = kzalloc(sizeof(*hdev), GFP_ATOMIC); + if (!hdev) + return NULL; + hdev->report_desc = kzalloc(desc_len, GFP_ATOMIC); + if (!hdev->report_desc) { + kfree(hdev); + return NULL; + } + hdev->dev = dev; + hdev->id = id; + hdev->report_desc_len = desc_len; + + return hdev; +} + +static struct acc_hid_dev *acc_hid_get(struct list_head *list, int id) +{ + struct acc_hid_dev *hid; + + list_for_each_entry(hid, list, list) { + if (hid->id == id) + return hid; + } + return NULL; +} + +static int acc_register_hid(struct acc_dev *dev, int id, int desc_length) +{ + struct acc_hid_dev *hid; + unsigned long flags; + + /* report descriptor length must be > 0 */ + if (desc_length <= 0) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + /* replace HID if one already exists with this ID */ + hid = acc_hid_get(&dev->hid_list, id); + if (!hid) + hid = acc_hid_get(&dev->new_hid_list, id); + if (hid) + list_move(&hid->list, &dev->dead_hid_list); + + hid = acc_hid_new(dev, id, desc_length); + if (!hid) { + spin_unlock_irqrestore(&dev->lock, flags); + return -ENOMEM; + } + + list_add(&hid->list, &dev->new_hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + + /* schedule work to register the HID device */ + schedule_work(&dev->hid_work); + return 0; +} + +static int acc_unregister_hid(struct acc_dev *dev, int id) +{ + struct acc_hid_dev *hid; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->hid_list, id); + if (!hid) + hid = acc_hid_get(&dev->new_hid_list, id); + if (!hid) { + spin_unlock_irqrestore(&dev->lock, flags); + return -EINVAL; + } + + list_move(&hid->list, &dev->dead_hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + + schedule_work(&dev->hid_work); + return 0; +} + static int __init create_bulk_endpoints(struct acc_dev *dev, struct usb_endpoint_descriptor *in_desc, struct usb_endpoint_descriptor *out_desc) @@ -353,7 +539,7 @@ static int __init create_bulk_endpoints(struct acc_dev *dev, return 0; fail: - printk(KERN_ERR "acc_bind() could not allocate requests\n"); + pr_err("acc_bind() could not allocate requests\n"); while ((req = req_get(dev, &dev->tx_idle))) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -510,6 +696,8 @@ static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) break; case ACCESSORY_IS_START_REQUESTED: return dev->start_requested; + case ACCESSORY_GET_AUDIO_MODE: + return dev->audio_mode; } if (!src) return -EINVAL; @@ -540,7 +728,7 @@ static int acc_release(struct inode *ip, struct file *fp) return 0; } -/* file operations for /dev/acc_usb */ +/* file operations for /dev/usb_accessory */ static const struct file_operations acc_fops = { .owner = THIS_MODULE, .read = acc_read, @@ -550,23 +738,47 @@ static const struct file_operations acc_fops = { .release = acc_release, }; +static int acc_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) + return ret; + return hid_hw_start(hdev, HID_CONNECT_DEFAULT); +} + static struct miscdevice acc_device = { .minor = MISC_DYNAMIC_MINOR, .name = "usb_accessory", .fops = &acc_fops, }; +static const struct hid_device_id acc_hid_table[] = { + { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, + { } +}; + +static struct hid_driver acc_hid_driver = { + .name = "USB accessory", + .id_table = acc_hid_table, + .probe = acc_hid_probe, +}; static int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { struct acc_dev *dev = _acc_dev; int value = -EOPNOTSUPP; + struct acc_hid_dev *hid; + int offset; u8 b_requestType = ctrl->bRequestType; u8 b_request = ctrl->bRequest; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); + unsigned long flags; /* printk(KERN_INFO "acc_ctrlrequest " @@ -579,20 +791,56 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, if (b_request == ACCESSORY_START) { dev->start_requested = 1; schedule_delayed_work( - &dev->work, msecs_to_jiffies(10)); + &dev->start_work, msecs_to_jiffies(10)); value = 0; } else if (b_request == ACCESSORY_SEND_STRING) { dev->string_index = w_index; cdev->gadget->ep0->driver_data = dev; cdev->req->complete = acc_complete_set_string; value = w_length; + } else if (b_request == ACCESSORY_SET_AUDIO_MODE && + w_index == 0 && w_length == 0) { + dev->audio_mode = w_value; + value = 0; + } else if (b_request == ACCESSORY_REGISTER_HID) { + value = acc_register_hid(dev, w_value, w_index); + } else if (b_request == ACCESSORY_UNREGISTER_HID) { + value = acc_unregister_hid(dev, w_value); + } else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) { + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->new_hid_list, w_value); + spin_unlock_irqrestore(&dev->lock, flags); + if (!hid) { + value = -EINVAL; + goto err; + } + offset = w_index; + if (offset != hid->report_desc_offset + || offset + w_length > hid->report_desc_len) { + value = -EINVAL; + goto err; + } + cdev->req->context = hid; + cdev->req->complete = acc_complete_set_hid_report_desc; + value = w_length; + } else if (b_request == ACCESSORY_SEND_HID_EVENT) { + spin_lock_irqsave(&dev->lock, flags); + hid = acc_hid_get(&dev->hid_list, w_value); + spin_unlock_irqrestore(&dev->lock, flags); + if (!hid) { + value = -EINVAL; + goto err; + } + cdev->req->context = hid; + cdev->req->complete = acc_complete_send_hid_event; + value = w_length; } } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_GET_PROTOCOL) { *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; value = sizeof(u16); - /* clear any strings left over from a previous session */ + /* clear strings left over from a previous session */ memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); memset(dev->model, 0, sizeof(dev->model)); memset(dev->description, 0, sizeof(dev->description)); @@ -600,6 +848,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, memset(dev->uri, 0, sizeof(dev->uri)); memset(dev->serial, 0, sizeof(dev->serial)); dev->start_requested = 0; + dev->audio_mode = 0; } } @@ -612,6 +861,7 @@ static int acc_ctrlrequest(struct usb_composite_dev *cdev, __func__); } +err: if (value == -EOPNOTSUPP) VDBG(cdev, "unknown class-specific control req " @@ -631,6 +881,10 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) DBG(cdev, "acc_function_bind dev: %p\n", dev); + ret = hid_register_driver(&acc_hid_driver); + if (ret) + return ret; + dev->start_requested = 0; /* allocate interface ID(s) */ @@ -660,6 +914,36 @@ acc_function_bind(struct usb_configuration *c, struct usb_function *f) } static void +kill_all_hid_devices(struct acc_dev *dev) +{ + struct acc_hid_dev *hid; + struct list_head *entry, *temp; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_for_each_safe(entry, temp, &dev->hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + list_add(&hid->list, &dev->dead_hid_list); + } + list_for_each_safe(entry, temp, &dev->new_hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + list_add(&hid->list, &dev->dead_hid_list); + } + spin_unlock_irqrestore(&dev->lock, flags); + + schedule_work(&dev->hid_work); +} + +static void +acc_hid_unbind(struct acc_dev *dev) +{ + hid_unregister_driver(&acc_hid_driver); + kill_all_hid_devices(dev); +} + +static void acc_function_unbind(struct usb_configuration *c, struct usb_function *f) { struct acc_dev *dev = func_to_dev(f); @@ -670,14 +954,104 @@ acc_function_unbind(struct usb_configuration *c, struct usb_function *f) acc_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) acc_request_free(dev->rx_req[i], dev->ep_out); + + acc_hid_unbind(dev); } -static void acc_work(struct work_struct *data) +static void acc_start_work(struct work_struct *data) { char *envp[2] = { "ACCESSORY=START", NULL }; kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); } +static int acc_hid_init(struct acc_hid_dev *hdev) +{ + struct hid_device *hid; + int ret; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &acc_hid_ll_driver; + hid->dev.parent = acc_device.this_device; + + hid->bus = BUS_USB; + hid->vendor = HID_ANY_ID; + hid->product = HID_ANY_ID; + hid->driver_data = hdev; + ret = hid_add_device(hid); + if (ret) { + pr_err("can't add hid device: %d\n", ret); + hid_destroy_device(hid); + return ret; + } + + hdev->hid = hid; + return 0; +} + +static void acc_hid_delete(struct acc_hid_dev *hid) +{ + kfree(hid->report_desc); + kfree(hid); +} + +static void acc_hid_work(struct work_struct *data) +{ + struct acc_dev *dev = _acc_dev; + struct list_head *entry, *temp; + struct acc_hid_dev *hid; + struct list_head new_list, dead_list; + unsigned long flags; + + INIT_LIST_HEAD(&new_list); + + spin_lock_irqsave(&dev->lock, flags); + + /* copy hids that are ready for initialization to new_list */ + list_for_each_safe(entry, temp, &dev->new_hid_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + if (hid->report_desc_offset == hid->report_desc_len) + list_move(&hid->list, &new_list); + } + + if (list_empty(&dev->dead_hid_list)) { + INIT_LIST_HEAD(&dead_list); + } else { + /* move all of dev->dead_hid_list to dead_list */ + dead_list.prev = dev->dead_hid_list.prev; + dead_list.next = dev->dead_hid_list.next; + dead_list.next->prev = &dead_list; + dead_list.prev->next = &dead_list; + INIT_LIST_HEAD(&dev->dead_hid_list); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + /* register new HID devices */ + list_for_each_safe(entry, temp, &new_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + if (acc_hid_init(hid)) { + pr_err("can't add HID device %p\n", hid); + acc_hid_delete(hid); + } else { + spin_lock_irqsave(&dev->lock, flags); + list_move(&hid->list, &dev->hid_list); + spin_unlock_irqrestore(&dev->lock, flags); + } + } + + /* remove dead HID devices */ + list_for_each_safe(entry, temp, &dead_list) { + hid = list_entry(entry, struct acc_hid_dev, list); + list_del(&hid->list); + if (hid->hid) + hid_destroy_device(hid->hid); + acc_hid_delete(hid); + } +} + static int acc_function_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { @@ -767,7 +1141,11 @@ static int acc_setup(void) init_waitqueue_head(&dev->write_wq); atomic_set(&dev->open_excl, 0); INIT_LIST_HEAD(&dev->tx_idle); - INIT_DELAYED_WORK(&dev->work, acc_work); + INIT_LIST_HEAD(&dev->hid_list); + INIT_LIST_HEAD(&dev->new_hid_list); + INIT_LIST_HEAD(&dev->dead_hid_list); + INIT_DELAYED_WORK(&dev->start_work, acc_start_work); + INIT_WORK(&dev->hid_work, acc_hid_work); /* _acc_dev must be set before calling usb_gadget_register_driver */ _acc_dev = dev; @@ -780,10 +1158,16 @@ static int acc_setup(void) err: kfree(dev); - printk(KERN_ERR "USB accessory gadget driver failed to initialize\n"); + pr_err("USB accessory gadget driver failed to initialize\n"); return ret; } +static void acc_disconnect(void) +{ + /* unregister all HID devices if USB is disconnected */ + kill_all_hid_devices(_acc_dev); +} + static void acc_cleanup(void) { misc_deregister(&acc_device); diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c index fe4455e..1c0166c 100644 --- a/drivers/usb/gadget/f_adb.c +++ b/drivers/usb/gadget/f_adb.c @@ -111,6 +111,8 @@ static struct usb_descriptor_header *hs_adb_descs[] = { NULL, }; +static void adb_ready_callback(void); +static void adb_closed_callback(void); /* temporary variable used between adb_open() and adb_gadget_bind() */ static struct adb_dev *_adb_dev; @@ -406,7 +408,7 @@ static ssize_t adb_write(struct file *fp, const char __user *buf, static int adb_open(struct inode *ip, struct file *fp) { - printk(KERN_INFO "adb_open\n"); + pr_info("adb_open\n"); if (!_adb_dev) return -ENODEV; @@ -418,12 +420,17 @@ static int adb_open(struct inode *ip, struct file *fp) /* clear the error latch */ _adb_dev->error = 0; + adb_ready_callback(); + return 0; } static int adb_release(struct inode *ip, struct file *fp) { - printk(KERN_INFO "adb_release\n"); + pr_info("adb_release\n"); + + adb_closed_callback(); + adb_unlock(&_adb_dev->open_excl); return 0; } diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c new file mode 100644 index 0000000..23a7511 --- /dev/null +++ b/drivers/usb/gadget/f_audio_source.c @@ -0,0 +1,825 @@ +/* + * Gadget Function Driver for USB audio source device + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/device.h> +#include <linux/usb/audio.h> +#include <linux/wait.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> + +#define SAMPLE_RATE 44100 +/* Each frame is two 16 bit integers (one per channel) */ +#define BYTES_PER_FRAME 4 +#define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) + +#define IN_EP_MAX_PACKET_SIZE 256 + +/* Number of requests to allocate */ +#define IN_EP_REQ_COUNT 4 + +#define AUDIO_AC_INTERFACE 0 +#define AUDIO_AS_INTERFACE 1 +#define AUDIO_NUM_INTERFACES 2 + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, +}; + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); + +#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES) +/* 1 input terminal, 1 output terminal and 1 feature unit */ +#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ + + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \ + + UAC_DT_FEATURE_UNIT_SIZE(0)) +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_2 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_LENGTH, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_HEADER, + .bcdADC = __constant_cpu_to_le16(0x0100), + .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), + .bInCollection = AUDIO_NUM_INTERFACES, + .baInterfaceNr = { + [0] = AUDIO_AC_INTERFACE, + [1] = AUDIO_AS_INTERFACE, + } +}; + +#define INPUT_TERMINAL_ID 1 +static struct uac_input_terminal_descriptor input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = INPUT_TERMINAL_ID, + .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE, + .bAssocTerminal = 0, + .wChannelConfig = 0x3, +}; + +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); + +#define FEATURE_UNIT_ID 2 +static struct uac_feature_unit_descriptor_0 feature_unit_desc = { + .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FEATURE_UNIT, + .bUnitID = FEATURE_UNIT_ID, + .bSourceID = INPUT_TERMINAL_ID, + .bControlSize = 2, +}; + +#define OUTPUT_TERMINAL_ID 3 +static struct uac1_output_terminal_descriptor output_terminal_desc = { + .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = OUTPUT_TERMINAL_ID, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = FEATURE_UNIT_ID, + .bSourceID = FEATURE_UNIT_ID, +}; + +/* B.4.1 Standard AS Interface Descriptor */ +static struct usb_interface_descriptor as_interface_alt_0_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +static struct usb_interface_descriptor as_interface_alt_1_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 1, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, +}; + +/* B.4.2 Class-Specific AS Interface Descriptor */ +static struct uac1_as_header_descriptor as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = INPUT_TERMINAL_ID, + .bDelay = 1, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, +}; + +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); + +static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, + .bSubframeSize = 2, + .bBitResolution = 16, + .bSamFreqType = 1, +}; + +/* Standard ISO IN Endpoint Descriptor for highspeed */ +static struct usb_endpoint_descriptor hs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_SYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), + .bInterval = 4, /* poll 1 per millisecond */ +}; + +/* Standard ISO IN Endpoint Descriptor for highspeed */ +static struct usb_endpoint_descriptor fs_as_in_ep_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_SYNC_SYNC + | USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE), + .bInterval = 1, /* poll 1 per millisecond */ +}; + +/* Class-specific AS ISO OUT Endpoint Descriptor */ +static struct uac_iso_endpoint_descriptor as_iso_in_desc = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = 1, + .bLockDelayUnits = 1, + .wLockDelay = __constant_cpu_to_le16(1), +}; + +static struct usb_descriptor_header *hs_audio_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&hs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct usb_descriptor_header *fs_audio_desc[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + + (struct usb_descriptor_header *)&input_terminal_desc, + (struct usb_descriptor_header *)&output_terminal_desc, + (struct usb_descriptor_header *)&feature_unit_desc, + + (struct usb_descriptor_header *)&as_interface_alt_0_desc, + (struct usb_descriptor_header *)&as_interface_alt_1_desc, + (struct usb_descriptor_header *)&as_header_desc, + + (struct usb_descriptor_header *)&as_type_i_desc, + + (struct usb_descriptor_header *)&fs_as_in_ep_desc, + (struct usb_descriptor_header *)&as_iso_in_desc, + NULL, +}; + +static struct snd_pcm_hardware audio_hw_info = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = SAMPLE_RATE, + .rate_max = SAMPLE_RATE, + + .buffer_bytes_max = 1024 * 1024, + .period_bytes_min = 64, + .period_bytes_max = 512 * 1024, + .periods_min = 2, + .periods_max = 1024, +}; + +/*-------------------------------------------------------------------------*/ + +struct audio_source_config { + int card; + int device; +}; + +struct audio_dev { + struct usb_function func; + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + + struct list_head idle_reqs; + struct usb_ep *in_ep; + struct usb_endpoint_descriptor *in_desc; + + spinlock_t lock; + + /* beginning, end and current position in our buffer */ + void *buffer_start; + void *buffer_end; + void *buffer_pos; + + /* byte size of a "period" */ + unsigned int period; + /* bytes sent since last call to snd_pcm_period_elapsed */ + unsigned int period_offset; + /* time we started playing */ + ktime_t start_time; + /* number of frames sent since start_time */ + s64 frames_sent; +}; + +static inline struct audio_dev *func_to_audio(struct usb_function *f) +{ + return container_of(f, struct audio_dev, func); +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + req->length = buffer_size; + return req; +} + +static void audio_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static void audio_req_put(struct audio_dev *audio, struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + list_add_tail(&req->list, &audio->idle_reqs); + spin_unlock_irqrestore(&audio->lock, flags); +} + +static struct usb_request *audio_req_get(struct audio_dev *audio) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&audio->lock, flags); + if (list_empty(&audio->idle_reqs)) { + req = 0; + } else { + req = list_first_entry(&audio->idle_reqs, struct usb_request, + list); + list_del(&req->list); + } + spin_unlock_irqrestore(&audio->lock, flags); + return req; +} + +/* send the appropriate number of packets to match our bitrate */ +static void audio_send(struct audio_dev *audio) +{ + struct snd_pcm_runtime *runtime; + struct usb_request *req; + int length, length1, length2, ret; + s64 msecs; + s64 frames; + ktime_t now; + + /* audio->substream will be null if we have been closed */ + if (!audio->substream) + return; + /* audio->buffer_pos will be null if we have been stopped */ + if (!audio->buffer_pos) + return; + + runtime = audio->substream->runtime; + + /* compute number of frames to send */ + now = ktime_get(); + msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time); + do_div(msecs, 1000000); + frames = msecs * SAMPLE_RATE; + do_div(frames, 1000); + + /* Readjust our frames_sent if we fall too far behind. + * If we get too far behind it is better to drop some frames than + * to keep sending data too fast in an attempt to catch up. + */ + if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC) + audio->frames_sent = frames - FRAMES_PER_MSEC; + + frames -= audio->frames_sent; + + /* We need to send something to keep the pipeline going */ + if (frames <= 0) + frames = FRAMES_PER_MSEC; + + while (frames > 0) { + req = audio_req_get(audio); + if (!req) + break; + + length = frames_to_bytes(runtime, frames); + if (length > IN_EP_MAX_PACKET_SIZE) + length = IN_EP_MAX_PACKET_SIZE; + + if (audio->buffer_pos + length > audio->buffer_end) + length1 = audio->buffer_end - audio->buffer_pos; + else + length1 = length; + memcpy(req->buf, audio->buffer_pos, length1); + if (length1 < length) { + /* Wrap around and copy remaining length + * at beginning of buffer. + */ + length2 = length - length1; + memcpy(req->buf + length1, audio->buffer_start, + length2); + audio->buffer_pos = audio->buffer_start + length2; + } else { + audio->buffer_pos += length1; + if (audio->buffer_pos >= audio->buffer_end) + audio->buffer_pos = audio->buffer_start; + } + + req->length = length; + ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC); + if (ret < 0) { + pr_err("usb_ep_queue failed ret: %d\n", ret); + audio_req_put(audio, req); + break; + } + + frames -= bytes_to_frames(runtime, length); + audio->frames_sent += bytes_to_frames(runtime, length); + } +} + +static void audio_control_complete(struct usb_ep *ep, struct usb_request *req) +{ + /* nothing to do here */ +} + +static void audio_data_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct audio_dev *audio = req->context; + + pr_debug("audio_data_complete req->status %d req->actual %d\n", + req->status, req->actual); + + audio_req_put(audio, req); + + if (!audio->buffer_start) + return; + + audio->period_offset += req->actual; + if (audio->period_offset >= audio->period) { + snd_pcm_period_elapsed(audio->substream); + audio->period_offset = 0; + } + audio_send(audio); +} + +static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + int value = -EOPNOTSUPP; + u16 ep = le16_to_cpu(ctrl->wIndex); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + + pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + switch (ctrl->bRequest) { + case UAC_SET_CUR: + case UAC_SET_MIN: + case UAC_SET_MAX: + case UAC_SET_RES: + value = len; + break; + default: + break; + } + + return value; +} + +static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + int value = -EOPNOTSUPP; + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 *buf = cdev->req->buf; + + pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", + ctrl->bRequest, w_value, len, ep); + + if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) { + switch (ctrl->bRequest) { + case UAC_GET_CUR: + case UAC_GET_MIN: + case UAC_GET_MAX: + case UAC_GET_RES: + /* return our sample rate */ + buf[0] = (u8)SAMPLE_RATE; + buf[1] = (u8)(SAMPLE_RATE >> 8); + buf[2] = (u8)(SAMPLE_RATE >> 16); + value = 3; + break; + default: + break; + } + } + + return value; +} + +static int +audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + /* composite driver infrastructure handles everything; interface + * activation uses set_alt(). + */ + switch (ctrl->bRequestType) { + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_set_endpoint_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_get_endpoint_req(f, ctrl); + break; + } + + /* respond with data transfer or status phase? */ + if (value >= 0) { + pr_debug("audio req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + req->complete = audio_control_complete; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + pr_err("audio response on err %d\n", value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct audio_dev *audio = func_to_audio(f); + + pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); + usb_ep_enable(audio->in_ep, audio->in_desc); + return 0; +} + +static void audio_disable(struct usb_function *f) +{ + struct audio_dev *audio = func_to_audio(f); + + pr_debug("audio_disable\n"); + usb_ep_disable(audio->in_ep); +} + +/*-------------------------------------------------------------------------*/ + +static void audio_build_desc(struct audio_dev *audio) +{ + u8 *sam_freq; + int rate; + + /* Set channel numbers */ + input_terminal_desc.bNrChannels = 2; + as_type_i_desc.bNrChannels = 2; + + /* Set sample rates */ + rate = SAMPLE_RATE; + sam_freq = as_type_i_desc.tSamFreq[0]; + memcpy(sam_freq, &rate, 3); +} + +/* audio function driver setup/binding */ +static int +audio_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct audio_dev *audio = func_to_audio(f); + int status; + struct usb_ep *ep; + struct usb_request *req; + int i; + + audio_build_desc(audio); + + /* allocate instance-specific interface IDs, and patch descriptors */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + as_interface_alt_0_desc.bInterfaceNumber = status; + as_interface_alt_1_desc.bInterfaceNumber = status; + + status = -ENODEV; + + /* allocate our endpoint */ + ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc); + if (!ep) + goto fail; + audio->in_ep = ep; + ep->driver_data = audio; /* claim */ + + if (gadget_is_dualspeed(c->cdev->gadget)) + hs_as_in_ep_desc.bEndpointAddress = + fs_as_in_ep_desc.bEndpointAddress; + + f->descriptors = fs_audio_desc; + f->hs_descriptors = hs_audio_desc; + + for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) { + req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE); + if (req) { + req->context = audio; + req->complete = audio_data_complete; + audio_req_put(audio, req); + } else + status = -ENOMEM; + } + +fail: + return status; +} + +static void +audio_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct audio_dev *audio = func_to_audio(f); + struct usb_request *req; + + while ((req = audio_req_get(audio))) + audio_request_free(req, audio->in_ep); + + snd_card_free_when_closed(audio->card); + kfree(audio); +} + +static void audio_pcm_playback_start(struct audio_dev *audio) +{ + audio->start_time = ktime_get(); + audio->frames_sent = 0; + audio_send(audio); +} + +static void audio_pcm_playback_stop(struct audio_dev *audio) +{ + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + audio->buffer_start = 0; + audio->buffer_end = 0; + audio->buffer_pos = 0; + spin_unlock_irqrestore(&audio->lock, flags); +} + +static int audio_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = substream->private_data; + + runtime->private_data = audio; + runtime->hw = audio_hw_info; + snd_pcm_limit_hw_rates(runtime); + runtime->hw.channels_max = 2; + + audio->substream = substream; + return 0; +} + +static int audio_pcm_close(struct snd_pcm_substream *substream) +{ + struct audio_dev *audio = substream->private_data; + unsigned long flags; + + spin_lock_irqsave(&audio->lock, flags); + audio->substream = NULL; + spin_unlock_irqrestore(&audio->lock, flags); + + return 0; +} + +static int audio_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + + if (rate != SAMPLE_RATE) + return -EINVAL; + if (channels != 2) + return -EINVAL; + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(params)); +} + +static int audio_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int audio_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = runtime->private_data; + + audio->period = snd_pcm_lib_period_bytes(substream); + audio->period_offset = 0; + audio->buffer_start = runtime->dma_area; + audio->buffer_end = audio->buffer_start + + snd_pcm_lib_buffer_bytes(substream); + audio->buffer_pos = audio->buffer_start; + + return 0; +} + +static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audio_dev *audio = runtime->private_data; + ssize_t bytes = audio->buffer_pos - audio->buffer_start; + + /* return offset of next frame to fill in our buffer */ + return bytes_to_frames(runtime, bytes); +} + +static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct audio_dev *audio = substream->runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + audio_pcm_playback_start(audio); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + audio_pcm_playback_stop(audio); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static struct snd_pcm_ops audio_playback_ops = { + .open = audio_pcm_open, + .close = audio_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = audio_pcm_hw_params, + .hw_free = audio_pcm_hw_free, + .prepare = audio_pcm_prepare, + .trigger = audio_pcm_playback_trigger, + .pointer = audio_pcm_pointer, +}; + +int audio_source_bind_config(struct usb_configuration *c, + struct audio_source_config *config) +{ + struct audio_dev *audio; + struct snd_card *card; + struct snd_pcm *pcm; + int err; + + config->card = -1; + config->device = -1; + + audio = kzalloc(sizeof *audio, GFP_KERNEL); + if (!audio) + return -ENOMEM; + + audio->func.name = "audio_source"; + + spin_lock_init(&audio->lock); + + audio->func.bind = audio_bind; + audio->func.unbind = audio_unbind; + audio->func.set_alt = audio_set_alt; + audio->func.setup = audio_setup; + audio->func.disable = audio_disable; + audio->in_desc = &fs_as_in_ep_desc; + + INIT_LIST_HEAD(&audio->idle_reqs); + + err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &card); + if (err) + goto snd_card_fail; + + snd_card_set_dev(card, &c->cdev->gadget->dev); + + err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm); + if (err) + goto pcm_fail; + pcm->private_data = audio; + pcm->info_flags = 0; + audio->pcm = pcm; + + strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name)); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + NULL, 0, 64 * 1024); + + strlcpy(card->driver, "audio_source", sizeof(card->driver)); + strlcpy(card->shortname, card->driver, sizeof(card->shortname)); + strlcpy(card->longname, "USB accessory audio source", + sizeof(card->longname)); + + err = snd_card_register(card); + if (err) + goto register_fail; + + err = usb_add_function(c, &audio->func); + if (err) + goto add_fail; + + config->card = pcm->card->number; + config->device = pcm->device; + audio->card = card; + return 0; + +add_fail: +register_fail: +pcm_fail: + snd_card_free(audio->card); +snd_card_fail: + kfree(audio); + return err; +} diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index d03b11b..96adf45 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -755,8 +755,6 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f) rndis_deregister(rndis->config); rndis_exit(); - rndis_string_defs[0].id = 0; - if (gadget_is_dualspeed(c->cdev->gadget)) usb_free_descriptors(f->hs_descriptors); usb_free_descriptors(f->descriptors); @@ -796,14 +794,14 @@ rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], if (!can_support_rndis(c) || !ethaddr) return -EINVAL; + /* setup RNDIS itself */ + status = rndis_init(); + if (status < 0) + return status; + /* maybe allocate device-global string IDs */ if (rndis_string_defs[0].id == 0) { - /* ... and setup RNDIS itself */ - status = rndis_init(); - if (status < 0) - return status; - /* control interface label */ status = usb_string_id(c->cdev); if (status < 0) diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index d3cdffe..bbfbde7 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1147,11 +1147,15 @@ static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ +static bool rndis_initialized; int rndis_init(void) { u8 i; + if (rndis_initialized) + return 0; + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { #ifdef CONFIG_USB_GADGET_DEBUG_FILES char name [20]; @@ -1178,6 +1182,7 @@ int rndis_init(void) INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue)); } + rndis_initialized = true; return 0; } @@ -1186,7 +1191,13 @@ void rndis_exit(void) #ifdef CONFIG_USB_GADGET_DEBUG_FILES u8 i; char name[20]; +#endif + if (!rndis_initialized) + return; + rndis_initialized = false; + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { sprintf(name, NAME_TEMPLATE, i); remove_proc_entry(name, NULL); diff --git a/drivers/usb/gadget/s3c_udc.h b/drivers/usb/gadget/s3c_udc.h index 359b5f4..a776167 100644 --- a/drivers/usb/gadget/s3c_udc.h +++ b/drivers/usb/gadget/s3c_udc.h @@ -85,7 +85,7 @@ typedef enum ep_type { - ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt + ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt, ep_isochronous } ep_type_t; struct s3c_ep { diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c index ae09620..6170aba 100644 --- a/drivers/usb/gadget/s3c_udc_otg.c +++ b/drivers/usb/gadget/s3c_udc_otg.c @@ -44,9 +44,9 @@ #error " Unknown S3C OTG operation mode, Select a correct operation mode" #endif -#undef DEBUG_S3C_UDC_SETUP +#define DEBUG_S3C_UDC_SETUP #undef DEBUG_S3C_UDC_EP0 -#undef DEBUG_S3C_UDC_ISR +#define DEBUG_S3C_UDC_ISR #undef DEBUG_S3C_UDC_OUT_EP #undef DEBUG_S3C_UDC_IN_EP #undef DEBUG_S3C_UDC @@ -70,7 +70,7 @@ static char *state_names[] = { #endif #ifdef DEBUG_S3C_UDC_SETUP -#define DEBUG_SETUP(fmt, args...) printk(fmt, ##args) +#define DEBUG_SETUP(fmt, args...) pr_debug(fmt, ##args) #else #define DEBUG_SETUP(fmt, args...) do {} while (0) #endif @@ -88,7 +88,7 @@ static char *state_names[] = { #endif #ifdef DEBUG_S3C_UDC_ISR -#define DEBUG_ISR(fmt, args...) printk(fmt, ##args) +#define DEBUG_ISR(fmt, args...) pr_debug(fmt, ##args) #else #define DEBUG_ISR(fmt, args...) do {} while (0) #endif @@ -661,7 +661,8 @@ static int s3c_ep_enable(struct usb_ep *_ep, } /* xfer types must match, except that interrupt ~= bulk */ - if (ep->bmAttributes != desc->bmAttributes + if (ep->bmAttributes != + (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) && ep->bmAttributes != USB_ENDPOINT_XFER_BULK && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { @@ -1183,6 +1184,20 @@ static struct s3c_udc memory = { .ep_type = ep_bulk_in, .fifo = (unsigned int) S3C_UDC_OTG_EP14_FIFO, }, + .ep[15] = { + .ep = { + .name = "ep15-iso", + .ops = &s3c_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 0xf, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + + .ep_type = ep_isochronous, + .fifo = (unsigned int) S3C_UDC_OTG_EP15_FIFO, + }, }; #if defined CONFIG_USB_S3C_OTG_HOST || defined CONFIG_USB_DWC_OTG diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c index 6b20deb..70cf602 100644 --- a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -164,6 +164,8 @@ static int setdma_tx(struct s3c_ep *ep, struct s3c_request *req) writel(virt_to_phys(buf), S3C_UDC_OTG_DIEPDMA(ep_num)); writel((pktcnt<<19)|(length<<0), S3C_UDC_OTG_DIEPTSIZ(ep_num)); ctrl = readl(S3C_UDC_OTG_DIEPCTL(ep_num)); + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) + ctrl |= DEPCTL_SET_ODD_FRM; writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, S3C_UDC_OTG_DIEPCTL(ep_num)); DEBUG_IN_EP("%s:EP%d TX DMA start : DIEPDMA0 = 0x%x, DIEPTSIZ0 = 0x%x, DIEPCTL0 = 0x%x\n" diff --git a/drivers/video/samsung/s3cfb.c b/drivers/video/samsung/s3cfb.c index 21f758c..fbacba9 100755 --- a/drivers/video/samsung/s3cfb.c +++ b/drivers/video/samsung/s3cfb.c @@ -29,6 +29,7 @@ #include <linux/io.h> #include <linux/memory.h> #include <linux/cpufreq.h> +#include <linux/kthread.h> #include <plat/clock.h> #include <plat/cpu-freq.h> #include <plat/media.h> @@ -113,10 +114,18 @@ static irqreturn_t s3cfb_irq_frame(int irq, void *data) s3cfb_clear_interrupt(fbdev); - complete_all(&fbdev->fb_complete); + fbdev->vsync_timestamp = ktime_get(); + wmb(); + wake_up_interruptible(&fbdev->vsync_wq); return IRQ_HANDLED; } +static int s3cfb_vsync_timestamp_changed(struct s3cfb_global *fbdev, + ktime_t prev_timestamp) +{ + rmb(); + return !ktime_equal(prev_timestamp, fbdev->vsync_timestamp); +} static void s3cfb_set_window(struct s3cfb_global *ctrl, int id, int enable) { struct s3cfb_window *win = ctrl->fb[id]->par; @@ -134,7 +143,7 @@ static int s3cfb_init_global(struct s3cfb_global *ctrl) ctrl->output = OUTPUT_RGB; ctrl->rgb_mode = MODE_RGB_P; - init_completion(&ctrl->fb_complete); + init_waitqueue_head(&ctrl->vsync_wq); mutex_init(&ctrl->lock); s3cfb_set_output(ctrl); @@ -561,12 +570,15 @@ static int s3cfb_release(struct fb_info *fb, int user) static int s3cfb_wait_for_vsync(struct s3cfb_global *ctrl) { + ktime_t prev_timestamp; int ret; dev_dbg(ctrl->dev, "waiting for VSYNC interrupt\n"); - ret = wait_for_completion_interruptible_timeout( - &ctrl->fb_complete, msecs_to_jiffies(100)); + prev_timestamp = ctrl->vsync_timestamp; + ret = wait_event_interruptible_timeout(ctrl->vsync_wq, + s3cfb_vsync_timestamp_changed(ctrl, prev_timestamp), + msecs_to_jiffies(100)); if (ret == 0) return -ETIMEDOUT; if (ret < 0) @@ -907,6 +919,31 @@ static int s3cfb_sysfs_store_win_power(struct device *dev, return len; } +static int s3cfb_wait_for_vsync_thread(void *data) +{ + struct s3cfb_global *fbdev = data; + + while (!kthread_should_stop()) { + ktime_t prev_timestamp = fbdev->vsync_timestamp; + int ret = wait_event_interruptible_timeout(fbdev->vsync_wq, + s3cfb_vsync_timestamp_changed(fbdev, + prev_timestamp), + msecs_to_jiffies(100)); + if (ret > 0) { + char *envp[2]; + char buf[64]; + snprintf(buf, sizeof(buf), "VSYNC=%llu", + ktime_to_ns(fbdev->vsync_timestamp)); + envp[0] = buf; + envp[1] = NULL; + kobject_uevent_env(&fbdev->dev->kobj, KOBJ_CHANGE, + envp); + } + } + + return 0; +} + static DEVICE_ATTR(win_power, S_IRUGO | S_IWUSR, s3cfb_sysfs_show_win_power, s3cfb_sysfs_store_win_power); @@ -1045,6 +1082,13 @@ static int __devinit s3cfb_probe(struct platform_device *pdev) register_early_suspend(&fbdev->early_suspend); #endif + fbdev->vsync_thread = kthread_run(s3cfb_wait_for_vsync_thread, + fbdev, "s3cfb-vsync"); + if (fbdev->vsync_thread == ERR_PTR(-ENOMEM)) { + dev_err(fbdev->dev, "failed to run vsync thread\n"); + fbdev->vsync_thread = NULL; + } + ret = device_create_file(&(pdev->dev), &dev_attr_win_power); if (ret < 0) dev_err(fbdev->dev, "failed to add sysfs entries\n"); @@ -1137,6 +1181,9 @@ static int __devexit s3cfb_remove(struct platform_device *pdev) regulator_disable(fbdev->regulator); + if (fbdev->vsync_thread) + kthread_stop(fbdev->vsync_thread); + kfree(fbdev->fb); kfree(fbdev); diff --git a/drivers/video/samsung/s3cfb.h b/drivers/video/samsung/s3cfb.h index 190d90a..21e581d 100644 --- a/drivers/video/samsung/s3cfb.h +++ b/drivers/video/samsung/s3cfb.h @@ -231,7 +231,12 @@ struct s3cfb_global { struct regulator *vlcd; int irq; struct fb_info **fb; - struct completion fb_complete; + + wait_queue_head_t vsync_wq; + ktime_t vsync_timestamp; + + int vsync_state; + struct task_struct *vsync_thread; /* fimd */ int enabled; diff --git a/drivers/video/samsung/s3cfb_fimd6x.c b/drivers/video/samsung/s3cfb_fimd6x.c index b5b73a1..bfe85af 100644 --- a/drivers/video/samsung/s3cfb_fimd6x.c +++ b/drivers/video/samsung/s3cfb_fimd6x.c @@ -308,14 +308,15 @@ int s3cfb_set_vsync_interrupt(struct s3cfb_global *ctrl, int enable) u32 cfg = 0; cfg = readl(ctrl->regs + S3C_VIDINTCON0); - cfg &= ~S3C_VIDINTCON0_FRAMESEL0_MASK; if (enable) { dev_dbg(ctrl->dev, "vsync interrupt is on\n"); - cfg |= S3C_VIDINTCON0_FRAMESEL0_VSYNC; + cfg &= ~S3C_VIDINTCON0_FRAMESEL0_MASK; + cfg |= S3C_VIDINTCON0_INTFRMEN_ENABLE | + S3C_VIDINTCON0_FRAMESEL0_VSYNC; } else { dev_dbg(ctrl->dev, "vsync interrupt is off\n"); - cfg &= ~S3C_VIDINTCON0_FRAMESEL0_VSYNC; + cfg &= ~S3C_VIDINTCON0_INTFRMEN_ENABLE; } writel(cfg, ctrl->regs + S3C_VIDINTCON0); @@ -328,9 +329,9 @@ int s3cfb_get_vsync_interrupt(struct s3cfb_global *ctrl) u32 cfg = 0; cfg = readl(ctrl->regs + S3C_VIDINTCON0); - cfg &= S3C_VIDINTCON0_FRAMESEL0_VSYNC; + cfg &= S3C_VIDINTCON0_INTFRMEN_ENABLE; - if (cfg & S3C_VIDINTCON0_FRAMESEL0_VSYNC) { + if (cfg & S3C_VIDINTCON0_INTFRMEN_ENABLE) { dev_dbg(ctrl->dev, "vsync interrupt is on\n"); return 1; } else { diff --git a/fs/proc/base.c b/fs/proc/base.c index 0da2379..6a938aa 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -854,11 +854,16 @@ static ssize_t mem_read(struct file *file, char __user *buf, return mem_rw(file, buf, count, ppos, 0); } +#define mem_write NULL + +#ifndef mem_write +/* This is a security hazard */ static ssize_t mem_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { return mem_rw(file, (char __user*)buf, count, ppos, 1); } +#endif loff_t mem_lseek(struct file *file, loff_t offset, int orig) { diff --git a/include/linux/android_pmem.h b/include/linux/android_pmem.h deleted file mode 100644 index f633621..0000000 --- a/include/linux/android_pmem.h +++ /dev/null @@ -1,93 +0,0 @@ -/* include/linux/android_pmem.h - * - * Copyright (C) 2007 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - * - */ - -#ifndef _ANDROID_PMEM_H_ -#define _ANDROID_PMEM_H_ - -#define PMEM_IOCTL_MAGIC 'p' -#define PMEM_GET_PHYS _IOW(PMEM_IOCTL_MAGIC, 1, unsigned int) -#define PMEM_MAP _IOW(PMEM_IOCTL_MAGIC, 2, unsigned int) -#define PMEM_GET_SIZE _IOW(PMEM_IOCTL_MAGIC, 3, unsigned int) -#define PMEM_UNMAP _IOW(PMEM_IOCTL_MAGIC, 4, unsigned int) -/* This ioctl will allocate pmem space, backing the file, it will fail - * if the file already has an allocation, pass it the len as the argument - * to the ioctl */ -#define PMEM_ALLOCATE _IOW(PMEM_IOCTL_MAGIC, 5, unsigned int) -/* This will connect a one pmem file to another, pass the file that is already - * backed in memory as the argument to the ioctl - */ -#define PMEM_CONNECT _IOW(PMEM_IOCTL_MAGIC, 6, unsigned int) -/* Returns the total size of the pmem region it is sent to as a pmem_region - * struct (with offset set to 0). - */ -#define PMEM_GET_TOTAL_SIZE _IOW(PMEM_IOCTL_MAGIC, 7, unsigned int) -#define PMEM_CACHE_FLUSH _IOW(PMEM_IOCTL_MAGIC, 8, unsigned int) - -struct android_pmem_platform_data -{ - const char* name; - /* starting physical address of memory region */ - unsigned long start; - /* size of memory region */ - unsigned long size; - /* set to indicate the region should not be managed with an allocator */ - unsigned no_allocator; - /* set to indicate maps of this region should be cached, if a mix of - * cached and uncached is desired, set this and open the device with - * O_SYNC to get an uncached region */ - unsigned cached; - /* The MSM7k has bits to enable a write buffer in the bus controller*/ - unsigned buffered; -}; - -struct pmem_region { - unsigned long offset; - unsigned long len; -}; - -#ifdef CONFIG_ANDROID_PMEM -int is_pmem_file(struct file *file); -int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, - unsigned long *end, struct file **filp); -int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *end); -void put_pmem_file(struct file* file); -void flush_pmem_file(struct file *file, unsigned long start, unsigned long len); -int pmem_setup(struct android_pmem_platform_data *pdata, - long (*ioctl)(struct file *, unsigned int, unsigned long), - int (*release)(struct inode *, struct file *)); -int pmem_remap(struct pmem_region *region, struct file *file, - unsigned operation); - -#else -static inline int is_pmem_file(struct file *file) { return 0; } -static inline int get_pmem_file(int fd, unsigned long *start, - unsigned long *vstart, unsigned long *end, - struct file **filp) { return -ENOSYS; } -static inline int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *end) { return -ENOSYS; } -static inline void put_pmem_file(struct file* file) { return; } -static inline void flush_pmem_file(struct file *file, unsigned long start, - unsigned long len) { return; } -static inline int pmem_setup(struct android_pmem_platform_data *pdata, - long (*ioctl)(struct file *, unsigned int, unsigned long), - int (*release)(struct inode *, struct file *)) { return -ENOSYS; } - -static inline int pmem_remap(struct pmem_region *region, struct file *file, - unsigned operation) { return -ENOSYS; } -#endif - -#endif //_ANDROID_PPP_H_ - diff --git a/include/linux/input.h b/include/linux/input.h index 771d6d8..a207923 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -129,6 +129,9 @@ struct input_keymap_entry { #define EVIOCGRAB _IOW('E', 0x90, int) /* Grab/Release device */ +#define EVIOCGSUSPENDBLOCK _IOR('E', 0x91, int) /* get suspend block enable */ +#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) /* set suspend block enable */ + /* * Device properties and quirks */ diff --git a/include/linux/netfilter/xt_IDLETIMER.h b/include/linux/netfilter/xt_IDLETIMER.h index 208ae93..faaa28b 100644 --- a/include/linux/netfilter/xt_IDLETIMER.h +++ b/include/linux/netfilter/xt_IDLETIMER.h @@ -4,6 +4,7 @@ * Header file for Xtables timer target module. * * Copyright (C) 2004, 2010 Nokia Corporation + * * Written by Timo Teras <ext-timo.teras@nokia.com> * * Converted to x_tables and forward-ported to 2.6.34 @@ -32,12 +33,19 @@ #include <linux/types.h> #define MAX_IDLETIMER_LABEL_SIZE 28 +#define NLMSG_MAX_SIZE 64 + +#define NL_EVENT_TYPE_INACTIVE 0 +#define NL_EVENT_TYPE_ACTIVE 1 struct idletimer_tg_info { __u32 timeout; char label[MAX_IDLETIMER_LABEL_SIZE]; + /* Use netlink messages for notification in addition to sysfs */ + __u8 send_nl_msg; + /* for kernel module internal use only */ struct idletimer_tg *timer __attribute__((aligned(8))); }; diff --git a/include/linux/netlink.h b/include/linux/netlink.h index a9dd895..e8e9cc3 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -26,6 +26,7 @@ #define NETLINK_ECRYPTFS 19 #define NETLINK_RDMA 20 + #define MAX_LINKS 32 struct sockaddr_nl { diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index c7ccaae..27d2dd9 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -745,8 +745,14 @@ enum nl80211_commands { * * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with * a single scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can + * scan with a single scheduled scan request, a wiphy attribute. * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements * that can be added to a scan request + * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information + * elements that can be added to a scheduled scan request + * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be + * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute. * * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive @@ -987,6 +993,24 @@ enum nl80211_commands { * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan * cycles, in msecs. + + * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more + * sets of attributes to match during scheduled scans. Only BSSs + * that match any of the sets will be reported. These are + * pass-thru filter rules. + * For a match to succeed, the BSS must match all attributes of a + * set. Since not every hardware supports matching all types of + * attributes, there is no guarantee that the reported BSSs are + * fully complying with the match sets and userspace needs to be + * able to ignore them by itself. + * Thus, the implementation is somewhat hardware-dependent, but + * this is only an optimization and the userspace application + * needs to handle all the non-filtered results anyway. + * If the match attributes don't make sense when combined with + * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID + * is included in the probe request, but the match attributes + * will never let it go through), -EINVAL may be returned. + * If ommited, no filtering is done. * * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported * interface combinations. In each nested item, it contains attributes @@ -1194,6 +1218,26 @@ enum nl80211_attrs { NL80211_ATTR_INTERFACE_COMBINATIONS, NL80211_ATTR_SOFTWARE_IFTYPES, + NL80211_ATTR_REKEY_DATA, + + NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + + NL80211_ATTR_SCAN_SUPP_RATES, + + NL80211_ATTR_HIDDEN_SSID, + + NL80211_ATTR_IE_PROBE_RESP, + NL80211_ATTR_IE_ASSOC_RESP, + + NL80211_ATTR_STA_WME, + NL80211_ATTR_SUPPORT_AP_UAPSD, + + NL80211_ATTR_ROAM_SUPPORT, + + NL80211_ATTR_SCHED_SCAN_MATCH, + NL80211_ATTR_MAX_MATCH_SETS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1653,6 +1697,26 @@ enum nl80211_reg_rule_attr { }; /** + * enum nl80211_sched_scan_match_attr - scheduled scan match attributes + * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, + * only report BSS with matching SSID. + * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter + * attribute number currently defined + * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use + */ +enum nl80211_sched_scan_match_attr { + __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, + + NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + + /* keep last */ + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, + NL80211_SCHED_SCAN_MATCH_ATTR_MAX = + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 +}; + +/** * enum nl80211_reg_rule_flags - regulatory rule flags * * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed diff --git a/include/linux/sw_sync.h b/include/linux/sw_sync.h new file mode 100644 index 0000000..bd6f208 --- /dev/null +++ b/include/linux/sw_sync.h @@ -0,0 +1,58 @@ +/* + * include/linux/sw_sync.h + * + * Copyright (C) 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef _LINUX_SW_SYNC_H +#define _LINUX_SW_SYNC_H + +#include <linux/types.h> + +#ifdef __KERNEL__ + +#include <linux/sync.h> + +struct sw_sync_timeline { + struct sync_timeline obj; + + u32 value; +}; + +struct sw_sync_pt { + struct sync_pt pt; + + u32 value; +}; + +struct sw_sync_timeline *sw_sync_timeline_create(const char *name); +void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc); + +struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value); + +#endif /* __KERNEL __ */ + +struct sw_sync_create_fence_data { + __u32 value; + char name[32]; + __s32 fence; /* fd of new fence */ +}; + +#define SW_SYNC_IOC_MAGIC 'W' + +#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ + struct sw_sync_create_fence_data) +#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32) + + +#endif /* _LINUX_SW_SYNC_H */ diff --git a/include/linux/sync.h b/include/linux/sync.h new file mode 100644 index 0000000..4f19938 --- /dev/null +++ b/include/linux/sync.h @@ -0,0 +1,390 @@ +/* + * include/linux/sync.h + * + * Copyright (C) 2012 Google, Inc. + * + * 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. + * + */ + +#ifndef _LINUX_SYNC_H +#define _LINUX_SYNC_H + +#include <linux/types.h> +#ifdef __KERNEL__ + +#include <linux/ktime.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/wait.h> + +struct sync_timeline; +struct sync_pt; +struct sync_fence; + +/** + * struct sync_timeline_ops - sync object implementation ops + * @driver_name: name of the implentation + * @dup: duplicate a sync_pt + * @has_signaled: returns: + * 1 if pt has signaled + * 0 if pt has not signaled + * <0 on error + * @compare: returns: + * 1 if b will signal before a + * 0 if a and b will signal at the same time + * -1 if a will signabl before b + * @free_pt: called before sync_pt is freed + * @release_obj: called before sync_timeline is freed + * @print_obj: print aditional debug information about sync_timeline. + * should not print a newline + * @print_pt: print aditional debug information about sync_pt. + * should not print a newline + * @fill_driver_data: write implmentation specific driver data to data. + * should return an error if there is not enough room + * as specified by size. This information is returned + * to userspace by SYNC_IOC_FENCE_INFO. + */ +struct sync_timeline_ops { + const char *driver_name; + + /* required */ + struct sync_pt *(*dup)(struct sync_pt *pt); + + /* required */ + int (*has_signaled)(struct sync_pt *pt); + + /* required */ + int (*compare)(struct sync_pt *a, struct sync_pt *b); + + /* optional */ + void (*free_pt)(struct sync_pt *sync_pt); + + /* optional */ + void (*release_obj)(struct sync_timeline *sync_timeline); + + /* optional */ + void (*print_obj)(struct seq_file *s, + struct sync_timeline *sync_timeline); + + /* optional */ + void (*print_pt)(struct seq_file *s, struct sync_pt *sync_pt); + + /* optional */ + int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size); +}; + +/** + * struct sync_timeline - sync object + * @ops: ops that define the implementaiton of the sync_timeline + * @name: name of the sync_timeline. Useful for debugging + * @destoryed: set when sync_timeline is destroyed + * @child_list_head: list of children sync_pts for this sync_timeline + * @child_list_lock: lock protecting @child_list_head, destroyed, and + * sync_pt.status + * @active_list_head: list of active (unsignaled/errored) sync_pts + * @sync_timeline_list: membership in global sync_timeline_list + */ +struct sync_timeline { + const struct sync_timeline_ops *ops; + char name[32]; + + /* protected by child_list_lock */ + bool destroyed; + + struct list_head child_list_head; + spinlock_t child_list_lock; + + struct list_head active_list_head; + spinlock_t active_list_lock; + + struct list_head sync_timeline_list; +}; + +/** + * struct sync_pt - sync point + * @parent: sync_timeline to which this sync_pt belongs + * @child_list: membership in sync_timeline.child_list_head + * @active_list: membership in sync_timeline.active_list_head + * @fence: sync_fence to which the sync_pt belongs + * @pt_list: membership in sync_fence.pt_list_head + * @status: 1: signaled, 0:active, <0: error + * @timestamp: time which sync_pt status transitioned from active to + * singaled or error. + */ +struct sync_pt { + struct sync_timeline *parent; + struct list_head child_list; + + struct list_head active_list; + + struct sync_fence *fence; + struct list_head pt_list; + + /* protected by parent->active_list_lock */ + int status; + + ktime_t timestamp; +}; + +/** + * struct sync_fence - sync fence + * @file: file representing this fence + * @name: name of sync_fence. Useful for debugging + * @pt_list_head: list of sync_pts in ths fence. immutable once fence + * is created + * @waiter_list_head: list of asynchronous waiters on this fence + * @waiter_list_lock: lock protecting @waiter_list_head and @status + * @status: 1: signaled, 0:active, <0: error + * + * @wq: wait queue for fence signaling + * @sync_fence_list: membership in global fence list + */ +struct sync_fence { + struct file *file; + char name[32]; + + /* this list is immutable once the fence is created */ + struct list_head pt_list_head; + + struct list_head waiter_list_head; + spinlock_t waiter_list_lock; /* also protects status */ + int status; + + wait_queue_head_t wq; + + struct list_head sync_fence_list; +}; + +/** + * struct sync_fence_waiter - metadata for asynchronous waiter on a fence + * @waiter_list: membership in sync_fence.waiter_list_head + * @callback: function pointer to call when fence signals + * @callback_data: pointer to pass to @callback + */ +struct sync_fence_waiter { + struct list_head waiter_list; + + void (*callback)(struct sync_fence *fence, void *data); + void *callback_data; +}; + +/* + * API for sync_timeline implementers + */ + +/** + * sync_timeline_create() - creates a sync object + * @ops: specifies the implemention ops for the object + * @size: size to allocate for this obj + * @name: sync_timeline name + * + * Creates a new sync_timeline which will use the implemetation specified by + * @ops. @size bytes will be allocated allowing for implemntation specific + * data to be kept after the generic sync_timeline stuct. + */ +struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, + int size, const char *name); + +/** + * sync_timeline_destory() - destorys a sync object + * @obj: sync_timeline to destroy + * + * A sync implemntation should call this when the @obj is going away + * (i.e. module unload.) @obj won't actually be freed until all its childern + * sync_pts are freed. + */ +void sync_timeline_destroy(struct sync_timeline *obj); + +/** + * sync_timeline_signal() - signal a status change on a sync_timeline + * @obj: sync_timeline to signal + * + * A sync implemntation should call this any time one of it's sync_pts + * has signaled or has an error condition. + */ +void sync_timeline_signal(struct sync_timeline *obj); + +/** + * sync_pt_create() - creates a sync pt + * @parent: sync_pt's parent sync_timeline + * @size: size to allocate for this pt + * + * Creates a new sync_pt as a chiled of @parent. @size bytes will be + * allocated allowing for implemntation specific data to be kept after + * the generic sync_timeline struct. + */ +struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size); + +/** + * sync_pt_free() - frees a sync pt + * @pt: sync_pt to free + * + * This should only be called on sync_pts which have been created but + * not added to a fence. + */ +void sync_pt_free(struct sync_pt *pt); + +/** + * sync_fence_create() - creates a sync fence + * @name: name of fence to create + * @pt: sync_pt to add to the fence + * + * Creates a fence containg @pt. Once this is called, the fence takes + * ownership of @pt. + */ +struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt); + +/* + * API for sync_fence consumers + */ + +/** + * sync_fence_merge() - merge two fences + * @name: name of new fence + * @a: fence a + * @b: fence b + * + * Creates a new fence which contains copies of all the sync_pts in both + * @a and @b. @a and @b remain valid, independent fences. + */ +struct sync_fence *sync_fence_merge(const char *name, + struct sync_fence *a, struct sync_fence *b); + +/** + * sync_fence_fdget() - get a fence from an fd + * @fd: fd referencing a fence + * + * Ensures @fd references a valid fence, increments the refcount of the backing + * file, and returns the fence. + */ +struct sync_fence *sync_fence_fdget(int fd); + +/** + * sync_fence_put() - puts a refernnce of a sync fence + * @fence: fence to put + * + * Puts a reference on @fence. If this is the last reference, the fence and + * all it's sync_pts will be freed + */ +void sync_fence_put(struct sync_fence *fence); + +/** + * sync_fence_install() - installs a fence into a file descriptor + * @fence: fence to instal + * @fd: file descriptor in which to install the fence + * + * Installs @fence into @fd. @fd's should be acquired through get_unused_fd(). + */ +void sync_fence_install(struct sync_fence *fence, int fd); + +/** + * sync_fence_wait_async() - registers and async wait on the fence + * @fence: fence to wait on + * @callback: callback + * @callback_data data to pass to the callback + * + * Returns 1 if @fence has already signaled. + * + * Registers a callback to be called when @fence signals or has an error + */ +int sync_fence_wait_async(struct sync_fence *fence, + void (*callback)(struct sync_fence *, void *data), + void *callback_data); + +/** + * sync_fence_wait() - wait on fence + * @fence: fence to wait on + * @tiemout: timeout in ms + * + * Wait for @fence to be signaled or have an error. Waits indefintly + * if @timeout = 0 + */ +int sync_fence_wait(struct sync_fence *fence, long timeout); + +#endif /* __KERNEL__ */ + +/** + * struct sync_merge_data - data passed to merge ioctl + * @fd2: file descriptor of second fence + * @name: name of new fence + * @fence: returns the fd of the new fence to userspace + */ +struct sync_merge_data { + __s32 fd2; /* fd of second fence */ + char name[32]; /* name of new fence */ + __s32 fence; /* fd on newly created fence */ +}; + +/** + * struct sync_pt_info - detailed sync_pt information + * @len: length of sync_pt_info including any driver_data + * @obj_name: name of parent sync_timeline + * @driver_name: name of driver implmenting the parent + * @status: status of the sync_pt 0:active 1:signaled <0:error + * @timestamp_ns: timestamp of status change in nanoseconds + * @driver_data: any driver dependant data + */ +struct sync_pt_info { + __u32 len; + char obj_name[32]; + char driver_name[32]; + __s32 status; + __u64 timestamp_ns; + + __u8 driver_data[0]; +}; + +/** + * struct sync_fence_info_data - data returned from fence info ioctl + * @len: ioctl caller writes the size of the buffer its passing in. + * ioctl returns length of sync_fence_data reutnred to userspace + * including pt_info. + * @name: name of fence + * @status: status of fence. 1: signaled 0:active <0:error + * @pt_info: a sync_pt_info struct for every sync_pt in the fence + */ +struct sync_fence_info_data { + __u32 len; + char name[32]; + __s32 status; + + __u8 pt_info[0]; +}; + +#define SYNC_IOC_MAGIC '>' + +/** + * DOC: SYNC_IOC_WAIT - wait for a fence to signal + * + * pass timeout in milliseconds. + */ +#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __u32) + +/** + * DOC: SYNC_IOC_MERGE - merge two fences + * + * Takes a struct sync_merge_data. Creates a new fence containing copies of + * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the + * new fence's fd in sync_merge_data.fence + */ +#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data) + +/** + * DOC: SYNC_IOC_FENCE_INFO - get detailed information on a fence + * + * Takes a struct sync_fence_info_data with extra space allocated for pt_info. + * Caller should write the size of the buffer into len. On return, len is + * updated to reflect the total size of the sync_fence_info_data including + * pt_info. + * + * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence. + * To itterate over the sync_pt_infos, use the sync_pt_info.len field. + */ +#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\ + struct sync_fence_info_data) + +#endif /* _LINUX_SYNC_H */ diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index 5b2dcf9..61ebe0a 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -36,13 +36,15 @@ #define ACCESSORY_STRING_URI 4 #define ACCESSORY_STRING_SERIAL 5 -/* Control request for retrieving device's protocol version (currently 1) +/* Control request for retrieving device's protocol version * * requestType: USB_DIR_IN | USB_TYPE_VENDOR * request: ACCESSORY_GET_PROTOCOL * value: 0 * index: 0 * data version number (16 bits little endian) + * 1 for original accessory support + * 2 adds HID and device to host audio support */ #define ACCESSORY_GET_PROTOCOL 51 @@ -70,6 +72,65 @@ */ #define ACCESSORY_START 53 +/* Control request for registering a HID device. + * Upon registering, a unique ID is sent by the accessory in the + * value parameter. This ID will be used for future commands for + * the device + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID_DEVICE + * value: Accessory assigned ID for the HID device + * index: total length of the HID report descriptor + * data none + */ +#define ACCESSORY_REGISTER_HID 54 + +/* Control request for unregistering a HID device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_REGISTER_HID + * value: Accessory assigned ID for the HID device + * index: 0 + * data none + */ +#define ACCESSORY_UNREGISTER_HID 55 + +/* Control request for sending the HID report descriptor. + * If the HID descriptor is longer than the endpoint zero max packet size, + * the descriptor will be sent in multiple ACCESSORY_SET_HID_REPORT_DESC + * commands. The data for the descriptor must be sent sequentially + * if multiple packets are needed. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_HID_REPORT_DESC + * value: Accessory assigned ID for the HID device + * index: offset of data in descriptor + * (needed when HID descriptor is too big for one packet) + * data the HID report descriptor + */ +#define ACCESSORY_SET_HID_REPORT_DESC 56 + +/* Control request for sending HID events. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_HID_EVENT + * value: Accessory assigned ID for the HID device + * index: 0 + * data the HID report for the event + */ +#define ACCESSORY_SEND_HID_EVENT 57 + +/* Control request for setting the audio mode. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SET_AUDIO_MODE + * value: 0 - no audio + * 1 - device to host, 44100 16-bit stereo PCM + * index: 0 + * data none + */ +#define ACCESSORY_SET_AUDIO_MODE 58 + /* ioctls for retrieving strings set by the host */ #define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) #define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) @@ -79,5 +140,7 @@ #define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) /* returns 1 if there is a start request pending */ #define ACCESSORY_IS_START_REQUESTED _IO('M', 7) +/* returns audio mode (set via the ACCESSORY_SET_AUDIO_MODE control request) */ +#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) #endif /* __LINUX_USB_F_ACCESSORY_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 396e8fc..a2a97f3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -426,6 +426,7 @@ struct station_parameters { * @STATION_INFO_RX_BITRATE: @rxrate fields are filled * @STATION_INFO_BSS_PARAM: @bss_param filled * @STATION_INFO_CONNECTED_TIME: @connected_time filled + * @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -444,7 +445,8 @@ enum station_info_flags { STATION_INFO_SIGNAL_AVG = 1<<13, STATION_INFO_RX_BITRATE = 1<<14, STATION_INFO_BSS_PARAM = 1<<15, - STATION_INFO_CONNECTED_TIME = 1<<16 + STATION_INFO_CONNECTED_TIME = 1<<16, + STATION_INFO_ASSOC_REQ_IES = 1<<17 }; /** @@ -536,6 +538,11 @@ struct sta_bss_parameters { * This number should increase every time the list of stations * changes, i.e. when a station is added or removed, so that * userspace can tell whether it got a consistent snapshot. + * @assoc_req_ies: IEs from (Re)Association Request. + * This is used only when in AP mode with drivers that do not use + * user space MLME/SME implementation. The information is provided for + * the cfg80211_new_sta() calls to notify user space of the IEs. + * @assoc_req_ies_len: Length of assoc_req_ies buffer in octets. */ struct station_info { u32 filled; @@ -558,6 +565,14 @@ struct station_info { struct sta_bss_parameters bss_param; int generation; + + const u8 *assoc_req_ies; + size_t assoc_req_ies_len; + + /* + * Note: Add a new enum station_info_flags value for each new field and + * use it to check which fields are initialized. + */ }; /** @@ -798,6 +813,15 @@ struct cfg80211_scan_request { }; /** + * struct cfg80211_match_set - sets of attributes to match + * + * @ssid: SSID to be matched + */ +struct cfg80211_match_set { + struct cfg80211_ssid ssid; +}; + +/** * struct cfg80211_sched_scan_request - scheduled scan request description * * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans) @@ -806,6 +830,11 @@ struct cfg80211_scan_request { * @interval: interval between each scheduled scan cycle * @ie: optional information element(s) to add into Probe Request or %NULL * @ie_len: length of ie in octets + * @match_sets: sets of parameters to be matched for a scan result + * entry to be considered valid and to be passed to the host + * (others are filtered out). + * If ommited, all results are passed. + * @n_match_sets: number of match sets * @wiphy: the wiphy this was for * @dev: the interface * @channels: channels to scan @@ -817,6 +846,8 @@ struct cfg80211_sched_scan_request { u32 interval; const u8 *ie; size_t ie_len; + struct cfg80211_match_set *match_sets; + int n_match_sets; /* internal */ struct wiphy *wiphy; @@ -1716,9 +1747,16 @@ struct wiphy_wowlan_support { * this variable determines its size * @max_scan_ssids: maximum number of SSIDs the device can scan for in * any given scan + * @max_sched_scan_ssids: maximum number of SSIDs the device can scan + * for in any given scheduled scan + * @max_match_sets: maximum number of match sets the device can handle + * when performing a scheduled scan, 0 if filtering is not + * supported. * @max_scan_ie_len: maximum length of user-controlled IEs device can * add to probe request frames transmitted during a scan, must not * include fixed IEs like supported rates + * @max_sched_scan_ie_len: same as max_scan_ie_len, but for scheduled + * scans * @coverage_class: current coverage class * @fw_version: firmware version for ethtool reporting * @hw_version: hardware version for ethtool reporting @@ -1770,7 +1808,10 @@ struct wiphy { int bss_priv_size; u8 max_scan_ssids; + u8 max_sched_scan_ssids; + u8 max_match_sets; u16 max_scan_ie_len; + u16 max_sched_scan_ie_len; int n_cipher_suites; const u32 *cipher_suites; diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index 0791f27..ea83664 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -83,27 +83,27 @@ DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, ); TRACE_EVENT(cpufreq_interactive_boost, - TP_PROTO(char *s), + TP_PROTO(const char *s), TP_ARGS(s), TP_STRUCT__entry( - __field(char *, s) + __string(s, s) ), TP_fast_assign( - __entry->s = s; + __assign_str(s, s); ), - TP_printk("%s", __entry->s) + TP_printk("%s", __get_str(s)) ); TRACE_EVENT(cpufreq_interactive_unboost, - TP_PROTO(char *s), + TP_PROTO(const char *s), TP_ARGS(s), TP_STRUCT__entry( - __field(char *, s) + __string(s, s) ), TP_fast_assign( - __entry->s = s; + __assign_str(s, s); ), - TP_printk("%s", __entry->s) + TP_printk("%s", __get_str(s)) ); #endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 8e600f8..7ef5d08 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -233,7 +233,9 @@ static inline struct rt6_info *ip6_dst_alloc(struct dst_ops *ops, { struct rt6_info *rt = dst_alloc(ops, dev, 0, 0, flags); - memset(&rt->rt6i_table, 0, sizeof(*rt) - sizeof(struct dst_entry)); + if (rt != NULL) + memset(&rt->rt6i_table, 0, + sizeof(*rt) - sizeof(struct dst_entry)); return rt; } diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index ca7bf10..3ff633e 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -334,6 +334,7 @@ static int sta_info_finish_insert(struct sta_info *sta, bool async) ieee80211_sta_debugfs_add(sta); rate_control_add_sta_debugfs(sta); + memset(&sinfo, 0, sizeof(sinfo)); sinfo.filled = 0; sinfo.generation = local->sta_generation; cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); diff --git a/net/netfilter/xt_IDLETIMER.c b/net/netfilter/xt_IDLETIMER.c index 3bdd443..43a16d0 100644 --- a/net/netfilter/xt_IDLETIMER.c +++ b/net/netfilter/xt_IDLETIMER.c @@ -5,6 +5,7 @@ * After timer expires a kevent will be sent. * * Copyright (C) 2004, 2010 Nokia Corporation + * * Written by Timo Teras <ext-timo.teras@nokia.com> * * Converted to x_tables and reworked for upstream inclusion @@ -38,8 +39,10 @@ #include <linux/netfilter/xt_IDLETIMER.h> #include <linux/kdev_t.h> #include <linux/kobject.h> +#include <linux/skbuff.h> #include <linux/workqueue.h> #include <linux/sysfs.h> +#include <net/net_namespace.h> struct idletimer_tg_attr { struct attribute attr; @@ -56,6 +59,8 @@ struct idletimer_tg { struct idletimer_tg_attr attr; unsigned int refcnt; + bool send_nl_msg; + bool active; }; static LIST_HEAD(idletimer_tg_list); @@ -63,6 +68,32 @@ static DEFINE_MUTEX(list_mutex); static struct kobject *idletimer_tg_kobj; +static void notify_netlink_uevent(const char *iface, struct idletimer_tg *timer) +{ + char iface_msg[NLMSG_MAX_SIZE]; + char state_msg[NLMSG_MAX_SIZE]; + char *envp[] = { iface_msg, state_msg, NULL }; + int res; + + res = snprintf(iface_msg, NLMSG_MAX_SIZE, "INTERFACE=%s", + iface); + if (NLMSG_MAX_SIZE <= res) { + pr_err("message too long (%d)", res); + return; + } + res = snprintf(state_msg, NLMSG_MAX_SIZE, "STATE=%s", + timer->active ? "active" : "inactive"); + if (NLMSG_MAX_SIZE <= res) { + pr_err("message too long (%d)", res); + return; + } + pr_debug("putting nlmsg: <%s> <%s>\n", iface_msg, state_msg); + kobject_uevent_env(idletimer_tg_kobj, KOBJ_CHANGE, envp); + return; + + +} + static struct idletimer_tg *__idletimer_tg_find_by_label(const char *label) { @@ -83,6 +114,7 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, { struct idletimer_tg *timer; unsigned long expires = 0; + unsigned long now = jiffies; mutex_lock(&list_mutex); @@ -92,11 +124,15 @@ static ssize_t idletimer_tg_show(struct kobject *kobj, struct attribute *attr, mutex_unlock(&list_mutex); - if (time_after(expires, jiffies)) + if (time_after(expires, now)) return sprintf(buf, "%u\n", - jiffies_to_msecs(expires - jiffies) / 1000); + jiffies_to_msecs(expires - now) / 1000); - return sprintf(buf, "0\n"); + if (timer->send_nl_msg) + return sprintf(buf, "0 %d\n", + jiffies_to_msecs(now - expires) / 1000); + else + return sprintf(buf, "0\n"); } static void idletimer_tg_work(struct work_struct *work) @@ -105,6 +141,9 @@ static void idletimer_tg_work(struct work_struct *work) work); sysfs_notify(idletimer_tg_kobj, NULL, timer->attr.attr.name); + + if (timer->send_nl_msg) + notify_netlink_uevent(timer->attr.attr.name, timer); } static void idletimer_tg_expired(unsigned long data) @@ -113,6 +152,7 @@ static void idletimer_tg_expired(unsigned long data) pr_debug("timer %s expired\n", timer->attr.attr.name); + timer->active = false; schedule_work(&timer->work); } @@ -147,6 +187,8 @@ static int idletimer_tg_create(struct idletimer_tg_info *info) setup_timer(&info->timer->timer, idletimer_tg_expired, (unsigned long) info->timer); info->timer->refcnt = 1; + info->timer->send_nl_msg = (info->send_nl_msg == 0) ? false : true; + info->timer->active = true; mod_timer(&info->timer->timer, msecs_to_jiffies(info->timeout * 1000) + jiffies); @@ -170,14 +212,24 @@ static unsigned int idletimer_tg_target(struct sk_buff *skb, const struct xt_action_param *par) { const struct idletimer_tg_info *info = par->targinfo; + unsigned long now = jiffies; pr_debug("resetting timer %s, timeout period %u\n", info->label, info->timeout); BUG_ON(!info->timer); + info->timer->active = true; + + if (time_before(info->timer->timer.expires, now)) { + schedule_work(&info->timer->work); + pr_debug("Starting timer %s (Expired, Jiffies): %lu, %lu\n", + info->label, info->timer->timer.expires, now); + } + + /* TODO: Avoid modifying timers on each packet */ mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + jiffies); + msecs_to_jiffies(info->timeout * 1000) + now); return XT_CONTINUE; } @@ -186,8 +238,9 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) { struct idletimer_tg_info *info = par->targinfo; int ret; + unsigned long now = jiffies; - pr_debug("checkentry targinfo%s\n", info->label); + pr_debug("checkentry targinfo %s\n", info->label); if (info->timeout == 0) { pr_debug("timeout value is zero\n"); @@ -206,8 +259,17 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) info->timer = __idletimer_tg_find_by_label(info->label); if (info->timer) { info->timer->refcnt++; + info->timer->active = true; + + if (time_before(info->timer->timer.expires, now)) { + schedule_work(&info->timer->work); + pr_debug("Starting Checkentry timer" + "(Expired, Jiffies): %lu, %lu\n", + info->timer->timer.expires, now); + } + mod_timer(&info->timer->timer, - msecs_to_jiffies(info->timeout * 1000) + jiffies); + msecs_to_jiffies(info->timeout * 1000) + now); pr_debug("increased refcnt of timer %s to %u\n", info->label, info->timer->refcnt); @@ -221,6 +283,7 @@ static int idletimer_tg_checkentry(const struct xt_tgchk_param *par) } mutex_unlock(&list_mutex); + return 0; } @@ -242,7 +305,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) kfree(info->timer); } else { pr_debug("decreased refcnt of timer %s to %u\n", - info->label, info->timer->refcnt); + info->label, info->timer->refcnt); } mutex_unlock(&list_mutex); @@ -250,6 +313,7 @@ static void idletimer_tg_destroy(const struct xt_tgdtor_param *par) static struct xt_target idletimer_tg __read_mostly = { .name = "IDLETIMER", + .revision = 1, .family = NFPROTO_UNSPEC, .target = idletimer_tg_target, .targetsize = sizeof(struct idletimer_tg_info), @@ -315,3 +379,4 @@ MODULE_DESCRIPTION("Xtables: idle time monitor"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("ipt_IDLETIMER"); MODULE_ALIAS("ip6t_IDLETIMER"); +MODULE_ALIAS("arpt_IDLETIMER"); diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 08086d6..f6d4cfc 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -26,6 +26,10 @@ #include <net/tcp.h> #include <net/udp.h> +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +#include <linux/netfilter_ipv6/ip6_tables.h> +#endif + #include <linux/netfilter/xt_socket.h> #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" @@ -110,8 +114,15 @@ module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); /*---------------------------------------------------------------------------*/ static const char *iface_stat_procdirname = "iface_stat"; static struct proc_dir_entry *iface_stat_procdir; +/* + * The iface_stat_all* will go away once userspace gets use to the new fields + * that have a format line. + */ static const char *iface_stat_all_procfilename = "iface_stat_all"; static struct proc_dir_entry *iface_stat_all_procfile; +static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; +static struct proc_dir_entry *iface_stat_fmt_procfile; + /* * Ordering of locks: @@ -124,9 +135,9 @@ static struct proc_dir_entry *iface_stat_all_procfile; * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock * is acquired. * - * Call tree with all lock holders as of 2011-09-25: + * Call tree with all lock holders as of 2012-04-27: * - * iface_stat_all_proc_read() + * iface_stat_fmt_proc_read() * iface_stat_list_lock * (struct iface_stat) * @@ -777,13 +788,14 @@ done: return iface_entry; } -static int iface_stat_all_proc_read(char *page, char **num_items_returned, +static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, off_t items_to_skip, int char_count, int *eof, void *data) { char *outp = page; int item_index = 0; int len; + int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */ struct iface_stat *iface_entry; struct rtnl_link_stats64 dev_stats, *stats; struct rtnl_link_stats64 no_dev_stats = {0}; @@ -793,14 +805,32 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, return 0; } - CT_DEBUG("qtaguid:proc iface_stat_all " + CT_DEBUG("qtaguid:proc iface_stat_fmt " + "pid=%u tgid=%u uid=%u " "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", page, *num_items_returned, + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, items_to_skip, char_count, *eof); if (*eof) return 0; + if (fmt == 2 && item_index++ >= items_to_skip) { + len = snprintf(outp, char_count, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets\n" + ); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + /* * This lock will prevent iface_stat_update() from changing active, * and in turn prevent an interface from unregistering itself. @@ -816,18 +846,37 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, } else { stats = &no_dev_stats; } - len = snprintf(outp, char_count, - "%s %d " - "%llu %llu %llu %llu " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->active, - iface_entry->totals[IFS_RX].bytes, - iface_entry->totals[IFS_RX].packets, - iface_entry->totals[IFS_TX].bytes, - iface_entry->totals[IFS_TX].packets, - stats->rx_bytes, stats->rx_packets, - stats->tx_bytes, stats->tx_packets); + /* + * If the meaning of the data changes, then update the fmtX + * string. + */ + if (fmt == 1) { + len = snprintf( + outp, char_count, + "%s %d " + "%llu %llu %llu %llu " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals_via_dev[IFS_RX].bytes, + iface_entry->totals_via_dev[IFS_RX].packets, + iface_entry->totals_via_dev[IFS_TX].bytes, + iface_entry->totals_via_dev[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets + ); + } else { + len = snprintf( + outp, char_count, + "%s " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->totals_via_skb[IFS_RX].bytes, + iface_entry->totals_via_skb[IFS_RX].packets, + iface_entry->totals_via_skb[IFS_TX].bytes, + iface_entry->totals_via_skb[IFS_TX].packets + ); + } if (len >= char_count) { spin_unlock_bh(&iface_stat_list_lock); *outp = '\0'; @@ -861,13 +910,17 @@ static void iface_create_proc_worker(struct work_struct *work) new_iface->proc_ptr = proc_entry; create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_TX].bytes); + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].bytes); create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_RX].bytes); + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].bytes); create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_TX].packets); + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].packets); create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_RX].packets); + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].packets); create_proc_read_entry("active", proc_iface_perms, proc_entry, read_proc_bool, &new_iface->active); @@ -971,11 +1024,13 @@ static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, "iface reset its stats unexpectedly\n", __func__, net_dev->name); - iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes; - iface->totals[IFS_TX].packets += + iface->totals_via_dev[IFS_TX].bytes += + iface->last_known[IFS_TX].bytes; + iface->totals_via_dev[IFS_TX].packets += iface->last_known[IFS_TX].packets; - iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes; - iface->totals[IFS_RX].packets += + iface->totals_via_dev[IFS_RX].bytes += + iface->last_known[IFS_RX].bytes; + iface->totals_via_dev[IFS_RX].packets += iface->last_known[IFS_RX].packets; iface->last_known_valid = false; IF_DEBUG("qtaguid: %s(%s): iface=%p " @@ -1143,6 +1198,27 @@ static struct sock_tag *get_sock_stat(const struct sock *sk) return sock_tag_entry; } +static int ipx_proto(const struct sk_buff *skb, + struct xt_action_param *par) +{ + int thoff, tproto; + + switch (par->family) { + case NFPROTO_IPV6: + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); + if (tproto < 0) + MT_DEBUG("%s(): transport header not found in ipv6" + " skb=%p\n", __func__, skb); + break; + case NFPROTO_IPV4: + tproto = ip_hdr(skb)->protocol; + break; + default: + tproto = IPPROTO_RAW; + } + return tproto; +} + static void data_counters_update(struct data_counters *dc, int set, enum ifs_tx_rx direction, int proto, int bytes) @@ -1203,10 +1279,10 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); return; } - entry->totals[IFS_TX].bytes += stats->tx_bytes; - entry->totals[IFS_TX].packets += stats->tx_packets; - entry->totals[IFS_RX].bytes += stats->rx_bytes; - entry->totals[IFS_RX].packets += stats->rx_packets; + entry->totals_via_dev[IFS_TX].bytes += stats->tx_bytes; + entry->totals_via_dev[IFS_TX].packets += stats->tx_packets; + entry->totals_via_dev[IFS_RX].bytes += stats->rx_bytes; + entry->totals_via_dev[IFS_RX].packets += stats->rx_packets; /* We don't need the last_known[] anymore */ entry->last_known_valid = false; _iface_stat_set_active(entry, net_dev, false); @@ -1216,6 +1292,67 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); } +/* + * Update stats for the specified interface from the skb. + * Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called on each sk. + */ +static void iface_stat_update_from_skb(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct iface_stat *entry; + const struct net_device *el_dev; + enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; + int bytes = skb->len; + + if (!skb->dev) { + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); + BUG(); + } else if (unlikely(!el_dev->name)) { + pr_err("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); + BUG(); + } else { + int proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); + } + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(el_dev->name); + if (entry == NULL) { + IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n", + __func__, el_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + el_dev->name, entry); + + entry->totals_via_skb[direction].bytes += bytes; + entry->totals_via_skb[direction].packets++; + spin_unlock_bh(&iface_stat_list_lock); +} + static void tag_stat_update(struct tag_stat *tag_entry, enum ifs_tx_rx direction, int proto, int bytes) { @@ -1265,7 +1402,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, struct data_counters *uid_tag_counters; struct sock_tag *sock_tag_entry; struct iface_stat *iface_entry; - struct tag_stat *new_tag_stat; + struct tag_stat *new_tag_stat = NULL; MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", ifname, uid, sk, direction, proto, bytes); @@ -1330,8 +1467,19 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, } if (acct_tag) { + /* Create the child {acct_tag, uid_tag} and hook up parent. */ new_tag_stat = create_if_tag_stat(iface_entry, tag); new_tag_stat->parent_counters = uid_tag_counters; + } else { + /* + * For new_tag_stat to be still NULL here would require: + * {0, uid_tag} exists + * and {acct_tag, uid_tag} doesn't exist + * AND acct_tag == 0. + * Impossible. This reassures us that new_tag_stat + * below will always be assigned. + */ + BUG_ON(!new_tag_stat); } tag_stat_update(new_tag_stat, direction, proto, bytes); spin_unlock_bh(&iface_entry->tag_stat_list_lock); @@ -1452,18 +1600,31 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) parent_procdir); if (!iface_stat_all_procfile) { pr_err("qtaguid: iface_stat: init " - " failed to create stat_all proc entry\n"); + " failed to create stat_old proc entry\n"); err = -1; goto err_zap_entry; } - iface_stat_all_procfile->read_proc = iface_stat_all_proc_read; + iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_all_procfile->data = (void *)1; /* fmt1 */ + + iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_fmt_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_all proc entry\n"); + err = -1; + goto err_zap_all_stats_entry; + } + iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */ err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { pr_err("qtaguid: iface_stat: init " "failed to register dev event handler\n"); - goto err_zap_all_stats_entry; + goto err_zap_all_stats_entries; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { @@ -1484,6 +1645,8 @@ err_unreg_ip4_addr: unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); err_unreg_nd: unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_all_stats_entries: + remove_proc_entry(iface_stat_fmt_procfilename, parent_procdir); err_zap_all_stats_entry: remove_proc_entry(iface_stat_all_procfilename, parent_procdir); err_zap_entry: @@ -1561,15 +1724,15 @@ static void account_for_uid(const struct sk_buff *skb, } else if (unlikely(!el_dev->name)) { pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); } else { - MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n", - par->hooknum, - el_dev->name, - el_dev->type); + int proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); if_tag_stat_update(el_dev->name, uid, skb->sk ? skb->sk : alternate_sk, par->in ? IFS_RX : IFS_TX, - ip_hdr(skb)->protocol, skb->len); + proto, skb->len); } } @@ -1594,8 +1757,22 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) goto ret_res; } - sk = skb->sk; + switch (par->hooknum) { + case NF_INET_PRE_ROUTING: + case NF_INET_POST_ROUTING: + atomic64_inc(&qtu_events.match_calls_prepost); + iface_stat_update_from_skb(skb, par); + /* + * We are done in pre/post. The skb will get processed + * further alter. + */ + res = (info->match ^ info->invert); + goto ret_res; + break; + /* default: Fall through and do UID releated work */ + } + sk = skb->sk; if (sk == NULL) { /* * A missing sk->sk_socket happens when packets are in-flight @@ -1614,8 +1791,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } else { atomic64_inc(&qtu_events.match_found_sk); } - MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", - par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", + par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); if (sk != NULL) { MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", par->hooknum, sk, sk->sk_socket, @@ -1770,8 +1947,10 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, if (*eof) return 0; - CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", - page, items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u " + "page=%p off=%ld char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, items_to_skip, char_count, *eof); spin_lock_bh(&sock_tag_list_lock); for (node = rb_first(&sock_tag_tree); @@ -1815,6 +1994,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, "delete_cmds=%llu " "iface_events=%llu " "match_calls=%llu " + "match_calls_prepost=%llu " "match_found_sk=%llu " "match_found_sk_in_ct=%llu " "match_found_no_sk_in_ct=%llu " @@ -1826,6 +2006,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, atomic64_read(&qtu_events.delete_cmds), atomic64_read(&qtu_events.iface_events), atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_calls_prepost), atomic64_read(&qtu_events.match_found_sk), atomic64_read(&qtu_events.match_found_sk_in_ct), atomic64_read( @@ -2099,7 +2280,9 @@ static int ctrl_cmd_tag(const char *input) el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_tag(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); goto err; } CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2244,7 +2427,9 @@ static int ctrl_cmd_untag(const char *input) el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_untag(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); goto err; } CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2320,6 +2505,9 @@ static int qtaguid_ctrl_parse(const char *input, int count) char cmd; int res; + CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + cmd = input[0]; /* Collect params for commands */ switch (cmd) { @@ -2496,9 +2684,12 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, return len; } - CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", page, *num_items_returned, - items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, + items_to_skip, char_count, *eof); if (*eof) return 0; diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index 02479d6..d79f838 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -202,7 +202,8 @@ struct iface_stat { /* net_dev is only valid for active iface_stat */ struct net_device *net_dev; - struct byte_packet_counters totals[IFS_MAX_DIRECTIONS]; + struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; + struct byte_packet_counters totals_via_skb[IFS_MAX_DIRECTIONS]; /* * We keep the last_known, because some devices reset their counters * just before NETDEV_UP, while some will reset just before @@ -254,6 +255,8 @@ struct qtaguid_event_counts { atomic64_t iface_events; /* Number of NETDEV_* events handled */ atomic64_t match_calls; /* Number of times iptables called mt */ + /* Number of times iptables called mt from pre or post routing hooks */ + atomic64_t match_calls_prepost; /* * match_found_sk_*: numbers related to the netfilter matching * function finding a sock for the sk_buff. diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 3917678..8cbd8e4 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -183,7 +183,11 @@ char *pp_iface_stat(struct iface_stat *is) res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" "list=list_head{...}, " "ifname=%s, " - "total={rx={bytes=%llu, " + "total_dev={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "total_skb={rx={bytes=%llu, " "packets=%llu}, " "tx={bytes=%llu, " "packets=%llu}}, " @@ -198,10 +202,14 @@ char *pp_iface_stat(struct iface_stat *is) "tag_stat_tree=rb_root{...}}", is, is->ifname, - is->totals[IFS_RX].bytes, - is->totals[IFS_RX].packets, - is->totals[IFS_TX].bytes, - is->totals[IFS_TX].packets, + is->totals_via_dev[IFS_RX].bytes, + is->totals_via_dev[IFS_RX].packets, + is->totals_via_dev[IFS_TX].bytes, + is->totals_via_dev[IFS_TX].packets, + is->totals_via_skb[IFS_RX].bytes, + is->totals_via_skb[IFS_RX].packets, + is->totals_via_skb[IFS_TX].bytes, + is->totals_via_skb[IFS_TX].packets, is->last_known_valid, is->last_known[IFS_RX].bytes, is->last_known[IFS_RX].packets, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f310a0d..4e84e22 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -175,6 +175,15 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, + [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED }, + [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED }, + [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 }, + [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, + [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, }; /* policy for the key attributes */ @@ -205,6 +214,12 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, }; +static const struct nla_policy +nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { + [NL80211_ATTR_SCHED_SCAN_MATCH_SSID] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_SSID_LEN }, +}; + /* ifidx get helper */ static int nl80211_get_ifidx(struct netlink_callback *cb) { @@ -682,8 +697,14 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, dev->wiphy.coverage_class); NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, dev->wiphy.max_scan_ssids); + NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + dev->wiphy.max_sched_scan_ssids); NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, dev->wiphy.max_scan_ie_len); + NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + dev->wiphy.max_sched_scan_ie_len); + NLA_PUT_U8(msg, NL80211_ATTR_MAX_MATCH_SETS, + dev->wiphy.max_match_sets); if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN); @@ -2213,6 +2234,10 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, } nla_nest_end(msg, sinfoattr); + if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES) + NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len, + sinfo->assoc_req_ies); + return genlmsg_end(msg, hdr); nla_put_failure: @@ -2240,6 +2265,7 @@ static int nl80211_dump_station(struct sk_buff *skb, } while (1) { + memset(&sinfo, 0, sizeof(sinfo)); err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, mac_addr, &sinfo); if (err == -ENOENT) @@ -3453,10 +3479,11 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, struct net_device *dev = info->user_ptr[1]; struct nlattr *attr; struct wiphy *wiphy; - int err, tmp, n_ssids = 0, n_channels, i; + int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i; u32 interval; enum ieee80211_band band; size_t ie_len; + struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1]; if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || !rdev->ops->sched_scan_start) @@ -3492,7 +3519,16 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, tmp) n_ssids++; - if (n_ssids > wiphy->max_scan_ssids) + if (n_ssids > wiphy->max_sched_scan_ssids) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) + nla_for_each_nested(attr, + info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], + tmp) + n_match_sets++; + + if (n_match_sets > wiphy->max_match_sets) return -EINVAL; if (info->attrs[NL80211_ATTR_IE]) @@ -3500,7 +3536,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, else ie_len = 0; - if (ie_len > wiphy->max_scan_ie_len) + if (ie_len > wiphy->max_sched_scan_ie_len) return -EINVAL; mutex_lock(&rdev->sched_scan_mtx); @@ -3512,6 +3548,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, request = kzalloc(sizeof(*request) + sizeof(*request->ssids) * n_ssids + + sizeof(*request->match_sets) * n_match_sets + sizeof(*request->channels) * n_channels + ie_len, GFP_KERNEL); if (!request) { @@ -3529,6 +3566,18 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, request->ie = (void *)(request->channels + n_channels); } + if (n_match_sets) { + if (request->ie) + request->match_sets = (void *)(request->ie + ie_len); + else if (request->ssids) + request->match_sets = + (void *)(request->ssids + n_ssids); + else + request->match_sets = + (void *)(request->channels + n_channels); + } + request->n_match_sets = n_match_sets; + i = 0; if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { /* user specified, bail out if channel not found */ @@ -3593,6 +3642,31 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, } } + i = 0; + if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { + nla_for_each_nested(attr, + info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], + tmp) { + struct nlattr *ssid; + + nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, + nla_data(attr), nla_len(attr), + nl80211_match_policy); + ssid = tb[NL80211_ATTR_SCHED_SCAN_MATCH_SSID]; + if (ssid) { + if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out_free; + } + memcpy(request->match_sets[i].ssid.ssid, + nla_data(ssid), nla_len(ssid)); + request->match_sets[i].ssid.ssid_len = + nla_len(ssid); + } + i++; + } + } + if (info->attrs[NL80211_ATTR_IE]) { request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); memcpy((void *)request->ie, diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 0c0e40e..7372127 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1349,14 +1349,16 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) BUG(); } xdst = dst_alloc(dst_ops, NULL, 0, 0, 0); - memset(&xdst->u.rt6.rt6i_table, 0, sizeof(*xdst) - sizeof(struct dst_entry)); - xfrm_policy_put_afinfo(afinfo); - if (likely(xdst)) + if (likely(xdst)) { + memset(&xdst->u.rt6.rt6i_table, 0, + sizeof(*xdst) - sizeof(struct dst_entry)); xdst->flo.ops = &xfrm_bundle_fc_ops; - else + } else xdst = ERR_PTR(-ENOBUFS); + xfrm_policy_put_afinfo(afinfo); + return xdst; } |