diff options
author | Simone Willett <swillett@nvidia.com> | 2012-09-18 14:35:54 -0700 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-09-18 14:35:54 -0700 |
commit | 439e9117e1626d54fa9a58826a066450ce0c5f45 (patch) | |
tree | c67c8532aed846d3855272289e6f9ede3e3c445d | |
parent | e4a5c87088b7beb773b7e5d3dc27e97d70068f1a (diff) | |
parent | ba19f9808d65c5c827878dbb2c71ff4cc9fc0f34 (diff) |
Merge remote-tracking branch 'origin/dev/from-android-3.4' into promotion_build
53 files changed, 2244 insertions, 529 deletions
diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt new file mode 100644 index 000000000000..4627c4241ece --- /dev/null +++ b/Documentation/hid/uhid.txt @@ -0,0 +1,169 @@ + UHID - User-space I/O driver support for HID subsystem + ======================================================== + +The HID subsystem needs two kinds of drivers. In this document we call them: + + 1. The "HID I/O Driver" is the driver that performs raw data I/O to the + low-level device. Internally, they register an hid_ll_driver structure with + the HID core. They perform device setup, read raw data from the device and + push it into the HID subsystem and they provide a callback so the HID + subsystem can send data to the device. + + 2. The "HID Device Driver" is the driver that parses HID reports and reacts on + them. There are generic drivers like "generic-usb" and "generic-bluetooth" + which adhere to the HID specification and provide the standardizes features. + But there may be special drivers and quirks for each non-standard device out + there. Internally, they use the hid_driver structure. + +Historically, the USB stack was the first subsystem to provide an HID I/O +Driver. However, other standards like Bluetooth have adopted the HID specs and +may provide HID I/O Drivers, too. The UHID driver allows to implement HID I/O +Drivers in user-space and feed the data into the kernel HID-subsystem. + +This allows user-space to operate on the same level as USB-HID, Bluetooth-HID +and similar. It does not provide a way to write HID Device Drivers, though. Use +hidraw for this purpose. + +There is an example user-space application in ./samples/uhid/uhid-example.c + +The UHID API +------------ + +UHID is accessed through a character misc-device. The minor-number is allocated +dynamically so you need to rely on udev (or similar) to create the device node. +This is /dev/uhid by default. + +If a new device is detected by your HID I/O Driver and you want to register this +device with the HID subsystem, then you need to open /dev/uhid once for each +device you want to register. All further communication is done by read()'ing or +write()'ing "struct uhid_event" objects. Non-blocking operations are supported +by setting O_NONBLOCK. + +struct uhid_event { + __u32 type; + union { + struct uhid_create_req create; + struct uhid_data_req data; + ... + } u; +}; + +The "type" field contains the ID of the event. Depending on the ID different +payloads are sent. You must not split a single event across multiple read()'s or +multiple write()'s. A single event must always be sent as a whole. Furthermore, +only a single event can be sent per read() or write(). Pending data is ignored. +If you want to handle multiple events in a single syscall, then use vectored +I/O with readv()/writev(). + +The first thing you should do is sending an UHID_CREATE event. This will +register the device. UHID will respond with an UHID_START event. You can now +start sending data to and reading data from UHID. However, unless UHID sends the +UHID_OPEN event, the internally attached HID Device Driver has no user attached. +That is, you might put your device asleep unless you receive the UHID_OPEN +event. If you receive the UHID_OPEN event, you should start I/O. If the last +user closes the HID device, you will receive an UHID_CLOSE event. This may be +followed by an UHID_OPEN event again and so on. There is no need to perform +reference-counting in user-space. That is, you will never receive multiple +UHID_OPEN events without an UHID_CLOSE event. The HID subsystem performs +ref-counting for you. +You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed even +though the device may have no users. + +If you want to send data to the HID subsystem, you send an HID_INPUT event with +your raw data payload. If the kernel wants to send data to the device, you will +read an UHID_OUTPUT or UHID_OUTPUT_EV event. + +If your device disconnects, you should send an UHID_DESTROY event. This will +unregister the device. You can now send UHID_CREATE again to register a new +device. +If you close() the fd, the device is automatically unregistered and destroyed +internally. + +write() +------- +write() allows you to modify the state of the device and feed input data into +the kernel. The following types are supported: UHID_CREATE, UHID_DESTROY and +UHID_INPUT. The kernel will parse the event immediately and if the event ID is +not supported, it will return -EOPNOTSUPP. If the payload is invalid, then +-EINVAL is returned, otherwise, the amount of data that was read is returned and +the request was handled successfully. + + UHID_CREATE: + This creates the internal HID device. No I/O is possible until you send this + event to the kernel. The payload is of type struct uhid_create_req and + contains information about your device. You can start I/O now. + + UHID_DESTROY: + This destroys the internal HID device. No further I/O will be accepted. There + may still be pending messages that you can receive with read() but no further + UHID_INPUT events can be sent to the kernel. + You can create a new device by sending UHID_CREATE again. There is no need to + reopen the character device. + + UHID_INPUT: + You must send UHID_CREATE before sending input to the kernel! This event + contains a data-payload. This is the raw data that you read from your device. + The kernel will parse the HID reports and react on it. + + UHID_FEATURE_ANSWER: + If you receive a UHID_FEATURE request you must answer with this request. You + must copy the "id" field from the request into the answer. Set the "err" field + to 0 if no error occured or to EIO if an I/O error occurred. + If "err" is 0 then you should fill the buffer of the answer with the results + of the feature request and set "size" correspondingly. + +read() +------ +read() will return a queued ouput report. These output reports can be of type +UHID_START, UHID_STOP, UHID_OPEN, UHID_CLOSE, UHID_OUTPUT or UHID_OUTPUT_EV. No +reaction is required to any of them but you should handle them according to your +needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads. + + UHID_START: + This is sent when the HID device is started. Consider this as an answer to + UHID_CREATE. This is always the first event that is sent. + + UHID_STOP: + This is sent when the HID device is stopped. Consider this as an answer to + UHID_DESTROY. + If the kernel HID device driver closes the device manually (that is, you + didn't send UHID_DESTROY) then you should consider this device closed and send + an UHID_DESTROY event. You may want to reregister your device, though. This is + always the last message that is sent to you unless you reopen the device with + UHID_CREATE. + + UHID_OPEN: + This is sent when the HID device is opened. That is, the data that the HID + device provides is read by some other process. You may ignore this event but + it is useful for power-management. As long as you haven't received this event + there is actually no other process that reads your data so there is no need to + send UHID_INPUT events to the kernel. + + UHID_CLOSE: + This is sent when there are no more processes which read the HID data. It is + the counterpart of UHID_OPEN and you may as well ignore this event. + + UHID_OUTPUT: + This is sent if the HID device driver wants to send raw data to the I/O + device. You should read the payload and forward it to the device. The payload + is of type "struct uhid_data_req". + This may be received even though you haven't received UHID_OPEN, yet. + + UHID_OUTPUT_EV: + Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This + is called for force-feedback, LED or similar events which are received through + an input device by the HID subsystem. You should convert this into raw reports + and send them to your device similar to events of type UHID_OUTPUT. + + UHID_FEATURE: + This event is sent if the kernel driver wants to perform a feature request as + described in the HID specs. The report-type and report-number are available in + the payload. + The kernel serializes feature requests so there will never be two in parallel. + However, if you fail to respond with a UHID_FEATURE_ANSWER in a time-span of 5 + seconds, then the requests will be dropped and a new one might be sent. + Therefore, the payload also contains an "id" field that identifies every + request. + +Document by: + David Herrmann <dh.herrmann@googlemail.com> diff --git a/MAINTAINERS b/MAINTAINERS index a60009da7280..c56bd02248d3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6855,6 +6855,13 @@ S: Maintained F: Documentation/filesystems/ufs.txt F: fs/ufs/ +UHID USERSPACE HID IO DRIVER: +M: David Herrmann <dh.herrmann@googlemail.com> +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/hid/uhid.c +F: include/linux/uhid.h + ULTRA-WIDEBAND (UWB) SUBSYSTEM: L: linux-usb@vger.kernel.org S: Orphan diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 88e6dae7abd7..b9f7f3f1e9c6 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -297,6 +297,15 @@ __setup("reboot=", reboot_setup); void machine_shutdown(void) { #ifdef CONFIG_SMP + /* + * Disable preemption so we're guaranteed to + * run to power off or reboot and prevent + * the possibility of switching to another + * thread that might wind up blocking on + * one of the stopped CPUs. + */ + preempt_disable(); + smp_send_stop(); #endif } diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index d68d1b694680..eb3a2912cb9b 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -642,7 +642,7 @@ static void do_signal(struct pt_regs *regs, int syscall) } } - if (try_to_freeze()) + if (try_to_freeze_nowarn()) goto no_signal; /* diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index f39cb39f146c..c9a15bc681fe 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -127,6 +127,8 @@ EXPORT_SYMBOL_GPL(wakeup_source_destroy); */ void wakeup_source_add(struct wakeup_source *ws) { + unsigned long flags; + if (WARN_ON(!ws)) return; @@ -135,9 +137,9 @@ void wakeup_source_add(struct wakeup_source *ws) ws->active = false; ws->last_time = ktime_get(); - spin_lock_irq(&events_lock); + spin_lock_irqsave(&events_lock, flags); list_add_rcu(&ws->entry, &wakeup_sources); - spin_unlock_irq(&events_lock); + spin_unlock_irqrestore(&events_lock, flags); } EXPORT_SYMBOL_GPL(wakeup_source_add); @@ -147,12 +149,14 @@ EXPORT_SYMBOL_GPL(wakeup_source_add); */ void wakeup_source_remove(struct wakeup_source *ws) { + unsigned long flags; + if (WARN_ON(!ws)) return; - spin_lock_irq(&events_lock); + spin_lock_irqsave(&events_lock, flags); list_del_rcu(&ws->entry); - spin_unlock_irq(&events_lock); + spin_unlock_irqrestore(&events_lock, flags); synchronize_rcu(); } EXPORT_SYMBOL_GPL(wakeup_source_remove); @@ -760,15 +764,16 @@ bool pm_get_wakeup_count(unsigned int *count, bool block) bool pm_save_wakeup_count(unsigned int count) { unsigned int cnt, inpr; + unsigned long flags; events_check_enabled = false; - spin_lock_irq(&events_lock); + spin_lock_irqsave(&events_lock, flags); split_counters(&cnt, &inpr); if (cnt == count && inpr == 0) { saved_count = count; events_check_enabled = true; } - spin_unlock_irq(&events_lock); + spin_unlock_irqrestore(&events_lock, flags); return events_check_enabled; } diff --git a/drivers/base/sync.c b/drivers/base/sync.c index eee2e56cdaf5..1cc3e4e99461 100644 --- a/drivers/base/sync.c +++ b/drivers/base/sync.c @@ -31,6 +31,7 @@ static void sync_fence_signal_pt(struct sync_pt *pt); static int _sync_pt_has_signaled(struct sync_pt *pt); static void sync_fence_free(struct kref *kref); +static void sync_dump(void); static LIST_HEAD(sync_timeline_list_head); static DEFINE_SPINLOCK(sync_timeline_list_lock); @@ -87,9 +88,6 @@ static void sync_timeline_free(struct kref *kref) void sync_timeline_destroy(struct sync_timeline *obj) { - unsigned long flags; - bool needs_freeing; - obj->destroyed = true; /* @@ -117,7 +115,6 @@ static void sync_timeline_remove_pt(struct sync_pt *pt) { struct sync_timeline *obj = pt->parent; unsigned long flags; - bool needs_freeing = false; spin_lock_irqsave(&obj->active_list_lock, flags); if (!list_empty(&pt->active_list)) @@ -560,14 +557,14 @@ EXPORT_SYMBOL(sync_fence_cancel_async); int sync_fence_wait(struct sync_fence *fence, long timeout) { - int err; + int err = 0; - if (timeout) { + if (timeout > 0) { timeout = msecs_to_jiffies(timeout); err = wait_event_interruptible_timeout(fence->wq, fence->status != 0, timeout); - } else { + } else if (timeout < 0) { err = wait_event_interruptible(fence->wq, fence->status != 0); } @@ -577,8 +574,12 @@ int sync_fence_wait(struct sync_fence *fence, long timeout) if (fence->status < 0) return fence->status; - if (fence->status == 0) + if (fence->status == 0) { + pr_info("fence timeout on [%p] after %dms\n", fence, + jiffies_to_msecs(timeout)); + sync_dump(); return -ETIME; + } return 0; } @@ -635,7 +636,7 @@ static unsigned int sync_fence_poll(struct file *file, poll_table *wait) static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg) { - __u32 value; + __s32 value; if (copy_from_user(&value, (void __user *)arg, sizeof(value))) return -EFAULT; @@ -849,7 +850,8 @@ 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)); + seq_printf(s, "[%p] %s: %s\n", fence, fence->name, + sync_status_str(fence->status)); list_for_each(pos, &fence->pt_list_head) { struct sync_pt *pt = @@ -917,7 +919,34 @@ 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); +#define DUMP_CHUNK 256 +static char sync_dump_buf[64 * 1024]; +void sync_dump(void) +{ + struct seq_file s = { + .buf = sync_dump_buf, + .size = sizeof(sync_dump_buf) - 1, + }; + int i; + + sync_debugfs_show(&s, NULL); + + for (i = 0; i < s.count; i += DUMP_CHUNK) { + if ((s.count - i) > DUMP_CHUNK) { + char c = s.buf[i + DUMP_CHUNK]; + s.buf[i + DUMP_CHUNK] = 0; + pr_cont("%s", s.buf + i); + s.buf[i + DUMP_CHUNK] = c; + } else { + s.buf[s.count] = 0; + pr_cont("%s", s.buf + i); + } + } +} +#else +static void sync_dump(void) +{ +} #endif diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index 9128aed18f32..596152824f5b 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -166,24 +166,20 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, return ERR_PTR(PTR_ERR(table)); } buffer->sg_table = table; - if (buffer->flags & ION_FLAG_CACHED) + if (buffer->flags & ION_FLAG_CACHED) { for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { if (sg_dma_len(sg) == PAGE_SIZE) continue; pr_err("%s: cached mappings must have pagewise " "sg_lists\n", __func__); - heap->ops->unmap_dma(heap, buffer); - kfree(buffer); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto err; } - ret = ion_buffer_alloc_dirty(buffer); - if (ret) { - heap->ops->unmap_dma(heap, buffer); - heap->ops->free(buffer); - kfree(buffer); - return ERR_PTR(ret); + ret = ion_buffer_alloc_dirty(buffer); + if (ret) + goto err; } buffer->dev = dev; @@ -202,6 +198,12 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, sg_dma_address(sg) = sg_phys(sg); ion_buffer_add(dev, buffer); return buffer; + +err: + heap->ops->unmap_dma(heap, buffer); + heap->ops->free(buffer); + kfree(buffer); + return ERR_PTR(ret); } static void ion_buffer_destroy(struct kref *kref) @@ -211,12 +213,13 @@ static void ion_buffer_destroy(struct kref *kref) if (WARN_ON(buffer->kmap_cnt > 0)) buffer->heap->ops->unmap_kernel(buffer->heap, buffer); - buffer->heap->ops->unmap_dma(buffer->heap, buffer); buffer->heap->ops->free(buffer); mutex_lock(&dev->lock); rb_erase(&buffer->node, &dev->buffers); mutex_unlock(&dev->lock); + if (buffer->flags & ION_FLAG_CACHED) + kfree(buffer->dirty); kfree(buffer); } @@ -230,7 +233,38 @@ static int ion_buffer_put(struct ion_buffer *buffer) return kref_put(&buffer->ref, ion_buffer_destroy); } -struct ion_handle *ion_handle_create(struct ion_client *client, +static void ion_buffer_add_to_handle(struct ion_buffer *buffer) +{ + mutex_lock(&buffer->dev->lock); + buffer->handle_count++; + mutex_unlock(&buffer->dev->lock); +} + +static void ion_buffer_remove_from_handle(struct ion_buffer *buffer) +{ + /* + * when a buffer is removed from a handle, if it is not in + * any other handles, copy the taskcomm and the pid of the + * process it's being removed from into the buffer. At this + * point there will be no way to track what processes this buffer is + * being used by, it only exists as a dma_buf file descriptor. + * The taskcomm and pid can provide a debug hint as to where this fd + * is in the system + */ + mutex_lock(&buffer->dev->lock); + buffer->handle_count--; + BUG_ON(buffer->handle_count < 0); + if (!buffer->handle_count) { + struct task_struct *task; + + task = current->group_leader; + get_task_comm(buffer->task_comm, task); + buffer->pid = task_pid_nr(task); + } + mutex_unlock(&buffer->dev->lock); +} + +static struct ion_handle *ion_handle_create(struct ion_client *client, struct ion_buffer *buffer) { struct ion_handle *handle; @@ -242,6 +276,7 @@ struct ion_handle *ion_handle_create(struct ion_client *client, rb_init_node(&handle->node); handle->client = client; ion_buffer_get(buffer); + ion_buffer_add_to_handle(buffer); handle->buffer = buffer; return handle; @@ -255,8 +290,6 @@ static void ion_handle_destroy(struct kref *kref) struct ion_client *client = handle->client; struct ion_buffer *buffer = handle->buffer; - mutex_lock(&client->lock); - mutex_lock(&buffer->lock); while (handle->kmap_cnt) ion_handle_kmap_put(handle); @@ -264,9 +297,10 @@ static void ion_handle_destroy(struct kref *kref) if (!RB_EMPTY_NODE(&handle->node)) rb_erase(&handle->node, &client->handles); - mutex_unlock(&client->lock); + ion_buffer_remove_from_handle(buffer); ion_buffer_put(buffer); + kfree(handle); } @@ -410,13 +444,14 @@ void ion_free(struct ion_client *client, struct ion_handle *handle) mutex_lock(&client->lock); valid_handle = ion_handle_validate(client, handle); - mutex_unlock(&client->lock); if (!valid_handle) { WARN(1, "%s: invalid handle passed to free.\n", __func__); + mutex_unlock(&client->lock); return; } ion_handle_put(handle); + mutex_unlock(&client->lock); } EXPORT_SYMBOL(ion_free); @@ -874,7 +909,7 @@ static void ion_dma_buf_release(struct dma_buf *dmabuf) static void *ion_dma_buf_kmap(struct dma_buf *dmabuf, unsigned long offset) { struct ion_buffer *buffer = dmabuf->priv; - return buffer->vaddr + offset; + return buffer->vaddr + offset * PAGE_SIZE; } static void ion_dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long offset, @@ -1171,8 +1206,11 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) struct ion_heap *heap = s->private; struct ion_device *dev = heap->dev; struct rb_node *n; + size_t total_size = 0; + size_t total_orphaned_size = 0; seq_printf(s, "%16.s %16.s %16.s\n", "client", "pid", "size"); + seq_printf(s, "----------------------------------------------------\n"); for (n = rb_first(&dev->clients); n; n = rb_next(n)) { struct ion_client *client = rb_entry(n, struct ion_client, @@ -1191,6 +1229,27 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) client->pid, size); } } + seq_printf(s, "----------------------------------------------------\n"); + seq_printf(s, "orphaned allocations (info is from last known client):" + "\n"); + mutex_lock(&dev->lock); + for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { + struct ion_buffer *buffer = rb_entry(n, struct ion_buffer, + node); + if (buffer->heap->type == heap->type) + total_size += buffer->size; + if (!buffer->handle_count) { + seq_printf(s, "%16.s %16u %16u\n", buffer->task_comm, + buffer->pid, buffer->size); + total_orphaned_size += buffer->size; + } + } + mutex_unlock(&dev->lock); + seq_printf(s, "----------------------------------------------------\n"); + seq_printf(s, "%16.s %16u\n", "total orphaned", + total_orphaned_size); + seq_printf(s, "%16.s %16u\n", "total ", total_size); + return 0; } diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index 8df3a1f7d1d0..f9e9718ac184 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -21,6 +21,7 @@ #include <linux/mm_types.h> #include <linux/mutex.h> #include <linux/rbtree.h> +#include <linux/sched.h> #include <linux/ion.h> #include <linux/miscdevice.h> @@ -69,8 +70,16 @@ int ion_remap_dma(struct ion_client *client, * @vaddr: the kenrel mapping if kmap_cnt is not zero * @dmap_cnt: number of times the buffer is mapped for dma * @sg_table: the sg table for the buffer if dmap_cnt is not zero - * @pages: list for allocated pages for the buffer - */ + * @dirty: bitmask representing which pages of this buffer have + * been dirtied by the cpu and need cache maintenance + * before dma + * @vmas: list of vma's mapping this buffer + * @handle_count: count of handles referencing this buffer + * @task_comm: taskcomm of last client to reference this buffer in a + * handle, used for debugging + * @pid: pid of last client to reference this buffer in a + * handle, used for debugging +*/ struct ion_buffer { struct kref ref; struct rb_node node; @@ -90,6 +99,10 @@ struct ion_buffer { struct page **pages; unsigned long *dirty; struct list_head vmas; + /* used to track orphaned buffers */ + int handle_count; + char task_comm[TASK_COMM_LEN]; + pid_t pid; }; /** diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c index e7fb07d45a2a..ca6de04f6c29 100644 --- a/drivers/gpu/ion/ion_system_heap.c +++ b/drivers/gpu/ion/ion_system_heap.c @@ -46,7 +46,7 @@ static struct page_info *alloc_largest_available(unsigned long size) if (!page) continue; split_page(page, orders[i]); - info = kmap(page); + info = kmalloc(sizeof(struct page_info *), GFP_KERNEL); info->page = page; info->order = orders[i]; return info; @@ -93,7 +93,7 @@ static int ion_system_heap_allocate(struct ion_heap *heap, } list_del(&info->list); memset(info, 0, sizeof(struct page_info)); - kunmap(page); + kfree(info); } dma_sync_sg_for_device(NULL, table->sgl, table->nents, @@ -107,7 +107,7 @@ err: list_for_each_entry(info, &pages, list) { for (i = 0; i < (1 << info->order); i++) __free_page(info->page + i); - kunmap(info->page); + kfree(info); } return -ENOMEM; } diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ffddcba32af6..1283fa3b20a3 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -55,6 +55,27 @@ config HIDRAW If unsure, say Y. +config UHID + tristate "User-space I/O driver support for HID subsystem" + depends on HID + default n + ---help--- + Say Y here if you want to provide HID I/O Drivers from user-space. + This allows to write I/O drivers in user-space and feed the data from + the device into the kernel. The kernel parses the HID reports, loads the + corresponding HID Device Driver or provides input devices on top of your + user-space device. + + This driver cannot be used to parse HID-reports in user-space and write + special HID-drivers. You should use hidraw for that. + Instead, this driver allows to write the transport-layer driver in + user-space like USB-HID and Bluetooth-HID do in kernel-space. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called uhid. + source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 22f1d16cd79c..9dca84592cc4 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -8,6 +8,7 @@ ifdef CONFIG_DEBUG_FS endif obj-$(CONFIG_HID) += hid.o +obj-$(CONFIG_UHID) += uhid.o hid-$(CONFIG_HIDRAW) += hidraw.o diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c new file mode 100644 index 000000000000..714cd8cc9579 --- /dev/null +++ b/drivers/hid/uhid.c @@ -0,0 +1,572 @@ +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 David Herrmann + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/atomic.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/hid.h> +#include <linux/input.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/uhid.h> +#include <linux/wait.h> + +#define UHID_NAME "uhid" +#define UHID_BUFSIZE 32 + +struct uhid_device { + struct mutex devlock; + bool running; + + __u8 *rd_data; + uint rd_size; + + struct hid_device *hid; + struct uhid_event input_buf; + + wait_queue_head_t waitq; + spinlock_t qlock; + __u8 head; + __u8 tail; + struct uhid_event *outq[UHID_BUFSIZE]; + + struct mutex report_lock; + wait_queue_head_t report_wait; + atomic_t report_done; + atomic_t report_id; + struct uhid_event report_buf; +}; + +static struct miscdevice uhid_misc; + +static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) +{ + __u8 newhead; + + newhead = (uhid->head + 1) % UHID_BUFSIZE; + + if (newhead != uhid->tail) { + uhid->outq[uhid->head] = ev; + uhid->head = newhead; + wake_up_interruptible(&uhid->waitq); + } else { + hid_warn(uhid->hid, "Output queue is full\n"); + kfree(ev); + } +} + +static int uhid_queue_event(struct uhid_device *uhid, __u32 event) +{ + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = event; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return 0; +} + +static int uhid_hid_start(struct hid_device *hid) +{ + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_START); +} + +static void uhid_hid_stop(struct hid_device *hid) +{ + struct uhid_device *uhid = hid->driver_data; + + hid->claimed = 0; + uhid_queue_event(uhid, UHID_STOP); +} + +static int uhid_hid_open(struct hid_device *hid) +{ + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_OPEN); +} + +static void uhid_hid_close(struct hid_device *hid) +{ + struct uhid_device *uhid = hid->driver_data; + + uhid_queue_event(uhid, UHID_CLOSE); +} + +static int uhid_hid_input(struct input_dev *input, unsigned int type, + unsigned int code, int value) +{ + struct hid_device *hid = input_get_drvdata(input); + struct uhid_device *uhid = hid->driver_data; + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT_EV; + ev->u.output_ev.type = type; + ev->u.output_ev.code = code; + ev->u.output_ev.value = value; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return 0; +} + +static int uhid_hid_parse(struct hid_device *hid) +{ + struct uhid_device *uhid = hid->driver_data; + + return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); +} + +static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, + __u8 *buf, size_t count, unsigned char rtype) +{ + struct uhid_device *uhid = hid->driver_data; + __u8 report_type; + struct uhid_event *ev; + unsigned long flags; + int ret; + size_t uninitialized_var(len); + struct uhid_feature_answer_req *req; + + if (!uhid->running) + return -EIO; + + switch (rtype) { + case HID_FEATURE_REPORT: + report_type = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + report_type = UHID_OUTPUT_REPORT; + break; + case HID_INPUT_REPORT: + report_type = UHID_INPUT_REPORT; + break; + default: + return -EINVAL; + } + + ret = mutex_lock_interruptible(&uhid->report_lock); + if (ret) + return ret; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) { + ret = -ENOMEM; + goto unlock; + } + + spin_lock_irqsave(&uhid->qlock, flags); + ev->type = UHID_FEATURE; + ev->u.feature.id = atomic_inc_return(&uhid->report_id); + ev->u.feature.rnum = rnum; + ev->u.feature.rtype = report_type; + + atomic_set(&uhid->report_done, 0); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + ret = wait_event_interruptible_timeout(uhid->report_wait, + atomic_read(&uhid->report_done), 5 * HZ); + + /* + * Make sure "uhid->running" is cleared on shutdown before + * "uhid->report_done" is set. + */ + smp_rmb(); + if (!ret || !uhid->running) { + ret = -EIO; + } else if (ret < 0) { + ret = -ERESTARTSYS; + } else { + spin_lock_irqsave(&uhid->qlock, flags); + req = &uhid->report_buf.u.feature_answer; + + if (req->err) { + ret = -EIO; + } else { + ret = 0; + len = min(count, + min_t(size_t, req->size, UHID_DATA_MAX)); + memcpy(buf, req->data, len); + } + + spin_unlock_irqrestore(&uhid->qlock, flags); + } + + atomic_set(&uhid->report_done, 1); + +unlock: + mutex_unlock(&uhid->report_lock); + return ret ? ret : len; +} + +static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, + unsigned char report_type) +{ + struct uhid_device *uhid = hid->driver_data; + __u8 rtype; + unsigned long flags; + struct uhid_event *ev; + + switch (report_type) { + case HID_FEATURE_REPORT: + rtype = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + rtype = UHID_OUTPUT_REPORT; + break; + default: + return -EINVAL; + } + + if (count < 1 || count > UHID_DATA_MAX) + return -EINVAL; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT; + ev->u.output.size = count; + ev->u.output.rtype = rtype; + memcpy(ev->u.output.data, buf, count); + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return count; +} + +static struct hid_ll_driver uhid_hid_driver = { + .start = uhid_hid_start, + .stop = uhid_hid_stop, + .open = uhid_hid_open, + .close = uhid_hid_close, + .hidinput_input_event = uhid_hid_input, + .parse = uhid_hid_parse, +}; + +static int uhid_dev_create(struct uhid_device *uhid, + const struct uhid_event *ev) +{ + struct hid_device *hid; + int ret; + + if (uhid->running) + return -EALREADY; + + uhid->rd_size = ev->u.create.rd_size; + if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) + return -EINVAL; + + uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); + if (!uhid->rd_data) + return -ENOMEM; + + if (copy_from_user(uhid->rd_data, ev->u.create.rd_data, + uhid->rd_size)) { + ret = -EFAULT; + goto err_free; + } + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_free; + } + + strncpy(hid->name, ev->u.create.name, 127); + hid->name[127] = 0; + strncpy(hid->phys, ev->u.create.phys, 63); + hid->phys[63] = 0; + strncpy(hid->uniq, ev->u.create.uniq, 63); + hid->uniq[63] = 0; + + hid->ll_driver = &uhid_hid_driver; + hid->hid_get_raw_report = uhid_hid_get_raw; + hid->hid_output_raw_report = uhid_hid_output_raw; + hid->bus = ev->u.create.bus; + hid->vendor = ev->u.create.vendor; + hid->product = ev->u.create.product; + hid->version = ev->u.create.version; + hid->country = ev->u.create.country; + hid->driver_data = uhid; + hid->dev.parent = uhid_misc.this_device; + + uhid->hid = hid; + uhid->running = true; + + ret = hid_add_device(hid); + if (ret) { + hid_err(hid, "Cannot register HID device\n"); + goto err_hid; + } + + return 0; + +err_hid: + hid_destroy_device(hid); + uhid->hid = NULL; + uhid->running = false; +err_free: + kfree(uhid->rd_data); + return ret; +} + +static int uhid_dev_destroy(struct uhid_device *uhid) +{ + if (!uhid->running) + return -EINVAL; + + /* clear "running" before setting "report_done" */ + uhid->running = false; + smp_wmb(); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); + + hid_destroy_device(uhid->hid); + kfree(uhid->rd_data); + + return 0; +} + +static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) +{ + if (!uhid->running) + return -EINVAL; + + hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, + min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); + + return 0; +} + +static int uhid_dev_feature_answer(struct uhid_device *uhid, + struct uhid_event *ev) +{ + unsigned long flags; + + if (!uhid->running) + return -EINVAL; + + spin_lock_irqsave(&uhid->qlock, flags); + + /* id for old report; drop it silently */ + if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) + goto unlock; + if (atomic_read(&uhid->report_done)) + goto unlock; + + memcpy(&uhid->report_buf, ev, sizeof(*ev)); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); + +unlock: + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; +} + +static int uhid_char_open(struct inode *inode, struct file *file) +{ + struct uhid_device *uhid; + + uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); + if (!uhid) + return -ENOMEM; + + mutex_init(&uhid->devlock); + mutex_init(&uhid->report_lock); + spin_lock_init(&uhid->qlock); + init_waitqueue_head(&uhid->waitq); + init_waitqueue_head(&uhid->report_wait); + uhid->running = false; + atomic_set(&uhid->report_done, 1); + + file->private_data = uhid; + nonseekable_open(inode, file); + + return 0; +} + +static int uhid_char_release(struct inode *inode, struct file *file) +{ + struct uhid_device *uhid = file->private_data; + unsigned int i; + + uhid_dev_destroy(uhid); + + for (i = 0; i < UHID_BUFSIZE; ++i) + kfree(uhid->outq[i]); + + kfree(uhid); + + return 0; +} + +static ssize_t uhid_char_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct uhid_device *uhid = file->private_data; + int ret; + unsigned long flags; + size_t len; + + /* they need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + +try_again: + if (file->f_flags & O_NONBLOCK) { + if (uhid->head == uhid->tail) + return -EAGAIN; + } else { + ret = wait_event_interruptible(uhid->waitq, + uhid->head != uhid->tail); + if (ret) + return ret; + } + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + if (uhid->head == uhid->tail) { + mutex_unlock(&uhid->devlock); + goto try_again; + } else { + len = min(count, sizeof(**uhid->outq)); + if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) { + ret = -EFAULT; + } else { + kfree(uhid->outq[uhid->tail]); + uhid->outq[uhid->tail] = NULL; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE; + spin_unlock_irqrestore(&uhid->qlock, flags); + } + } + + mutex_unlock(&uhid->devlock); + return ret ? ret : len; +} + +static ssize_t uhid_char_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct uhid_device *uhid = file->private_data; + int ret; + size_t len; + + /* we need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + memset(&uhid->input_buf, 0, sizeof(uhid->input_buf)); + len = min(count, sizeof(uhid->input_buf)); + if (copy_from_user(&uhid->input_buf, buffer, len)) { + ret = -EFAULT; + goto unlock; + } + + switch (uhid->input_buf.type) { + case UHID_CREATE: + ret = uhid_dev_create(uhid, &uhid->input_buf); + break; + case UHID_DESTROY: + ret = uhid_dev_destroy(uhid); + break; + case UHID_INPUT: + ret = uhid_dev_input(uhid, &uhid->input_buf); + break; + case UHID_FEATURE_ANSWER: + ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); + break; + default: + ret = -EOPNOTSUPP; + } + +unlock: + mutex_unlock(&uhid->devlock); + + /* return "count" not "len" to not confuse the caller */ + return ret ? ret : count; +} + +static unsigned int uhid_char_poll(struct file *file, poll_table *wait) +{ + struct uhid_device *uhid = file->private_data; + + poll_wait(file, &uhid->waitq, wait); + + if (uhid->head != uhid->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +static const struct file_operations uhid_fops = { + .owner = THIS_MODULE, + .open = uhid_char_open, + .release = uhid_char_release, + .read = uhid_char_read, + .write = uhid_char_write, + .poll = uhid_char_poll, + .llseek = no_llseek, +}; + +static struct miscdevice uhid_misc = { + .fops = &uhid_fops, + .minor = MISC_DYNAMIC_MINOR, + .name = UHID_NAME, +}; + +static int __init uhid_init(void) +{ + return misc_register(&uhid_misc); +} + +static void __exit uhid_exit(void) +{ + misc_deregister(&uhid_misc); +} + +module_init(uhid_init); +module_exit(uhid_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>"); +MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile index 76ee1b6d7757..6efad6066c19 100644 --- a/drivers/net/wireless/bcmdhd/Makefile +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -53,6 +53,8 @@ endif ifneq ($(CONFIG_BCMDHD_CFG80211),) bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o dhd_cfg80211.o DHDCFLAGS += -DWL_CFG80211 -DWL_CFG80211_STA_EVENT -DWL_ENABLE_P2P_IF +DHDCFLAGS += -DCUSTOM_ROAM_TRIGGER_SETTING=-70 +DHDCFLAGS += -DCUSTOM_ROAM_DELTA_SETTING=15 endif ifneq ($(CONFIG_DHD_USE_SCHED_SCAN),) DHDCFLAGS += -DWL_SCHED_SCAN diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c index b017cf74e51c..35c7c1fb64c7 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 353908 2012-08-29 08:09:02Z $ + * $Id: bcmsdh_sdmmc_linux.c 355594 2012-09-07 10:22:02Z $ */ #include <typedefs.h> @@ -192,7 +192,7 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev) if (func->num != 2) return 0; - sd_trace(("%s Enter\n", __FUNCTION__)); + sd_trace_hw4(("%s Enter\n", __FUNCTION__)); if (dhd_os_check_wakelock(bcmsdh_get_drvdata())) return -EBUSY; @@ -243,7 +243,7 @@ static int bcmsdh_sdmmc_resume(struct device *pdev) #if defined(OOB_INTR_ONLY) struct sdio_func *func = dev_to_sdio_func(pdev); #endif /* defined(OOB_INTR_ONLY) */ - sd_trace(("%s Enter\n", __FUNCTION__)); + sd_trace_hw4(("%s Enter\n", __FUNCTION__)); dhd_mmc_suspend = FALSE; #if defined(OOB_INTR_ONLY) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 81c66875c912..dabae4cd1313 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 354236 2012-08-30 13:00:58Z $ + * $Id: dhd.h 356056 2012-09-11 01:08:09Z $ */ /**************** @@ -89,6 +89,10 @@ enum dhd_bus_state { #define MAX_CNTL_TIMEOUT 2 #endif +#define DHD_SCAN_ASSOC_ACTIVE_TIME 40 /* ms: Embedded default Active setting from DHD */ +#define DHD_SCAN_UNASSOC_ACTIVE_TIME 40 /* ms: Embedded def. Unassoc Active setting from DHD */ +#define DHD_SCAN_PASSIVE_TIME 130 /* ms: Embedded default Passive setting from DHD */ + #ifndef POWERUP_MAX_RETRY #define POWERUP_MAX_RETRY 3 /* how many times we retry to power up the chip */ #endif @@ -638,6 +642,21 @@ extern uint dhd_force_tx_queueing; #define CUSTOM_GLOM_SETTING DEFAULT_GLOM_VALUE #endif +/* hooks for custom Roaming Trigger setting via Makefile */ +#define DEFAULT_ROAM_TRIGGER_VALUE -75 /* dBm default roam trigger all band */ +#define DEFAULT_ROAM_TRIGGER_SETTING -1 +#ifndef CUSTOM_ROAM_TRIGGER_SETTING +#define CUSTOM_ROAM_TRIGGER_SETTING DEFAULT_ROAM_TRIGGER_VALUE +#endif + +/* hooks for custom Roaming Romaing setting via Makefile */ +#define DEFAULT_ROAM_DELTA_VALUE 10 /* dBm default roam delta all band */ +#define DEFAULT_ROAM_DELTA_SETTING -1 +#ifndef CUSTOM_ROAM_DELTA_SETTING +#define CUSTOM_ROAM_DELTA_SETTING DEFAULT_ROAM_DELTA_VALUE +#endif + + /* hooks for custom dhd_dpc_prio setting option via Makefile */ #define DEFAULT_DHP_DPC_PRIO 1 #ifndef CUSTOM_DPC_PRIO_SETTING @@ -670,7 +689,6 @@ extern char fw_path2[MOD_PARAM_PATHLEN]; extern uint dhd_download_fw_on_driverload; - /* For supporting multiple interfaces */ #define DHD_MAX_IFS 16 #define DHD_DEL_IF -0xe diff --git a/drivers/net/wireless/bcmdhd/dhd_cdc.c b/drivers/net/wireless/bcmdhd/dhd_cdc.c index 33ebeb559059..820bbade720c 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 353370 2012-08-27 09:10:22Z $ + * $Id: dhd_cdc.c 355825 2012-09-10 03:22:40Z $ * * 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 @@ -854,6 +854,12 @@ _dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf) /* pull BDC header */ PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN); + + if (PKTLEN(ctx->osh, pktbuf) < (h->dataOffset << 2)) { + WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__, + PKTLEN(ctx->osh, pktbuf), (h->dataOffset << 2))); + return BCME_ERROR; + } /* pull wl-header */ PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2)); return BCME_OK; @@ -916,7 +922,15 @@ _dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx, } else { /* remove header first */ - _dhd_wlfc_pullheader(ctx, p); + rc = _dhd_wlfc_pullheader(ctx, p); + if (rc != BCME_OK) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + /* free the hanger slot */ + dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1); + PKTFREE(ctx->osh, p, TRUE); + rc = BCME_ERROR; + return rc; + } if (pkt_type == eWLFC_PKTTYPE_DELAYED) { /* delay-q packets are going to delay-q */ @@ -1166,16 +1180,17 @@ _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx, int gen; /* remove old header */ - _dhd_wlfc_pullheader(ctx, p); - - hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); - dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen); + rc = _dhd_wlfc_pullheader(ctx, p); + if (rc == BCME_OK) { + hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); + dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen); - WLFC_PKTFLAG_SET_GENERATION(htod, gen); - free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); - /* push new header */ - _dhd_wlfc_pushheader(ctx, p, send_tim_update, - entry->traffic_lastreported_bmp, entry->mac_handle, htod); + WLFC_PKTFLAG_SET_GENERATION(htod, gen); + free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); + /* push new header */ + _dhd_wlfc_pushheader(ctx, p, send_tim_update, + entry->traffic_lastreported_bmp, entry->mac_handle, htod); + } } *slot = hslot; return rc; diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c index 0f3761308d4f..b2e441790dee 100644 --- a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c @@ -81,14 +81,10 @@ s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); dhd->op_mode |= val; WL_ERR(("Set : op_mode=%d\n", dhd->op_mode)); - #ifdef ARP_OFFLOAD_SUPPORT - if ((dhd->op_mode & CONCURRENT_MULTI_CHAN) != - CONCURRENT_MULTI_CHAN) { - /* IF P2P is enabled, disable arpoe */ - dhd_arp_offload_set(dhd, 0); - dhd_arp_offload_enable(dhd, false); - } + /* IF P2P is enabled, disable arpoe */ + dhd_arp_offload_set(dhd, 0); + dhd_arp_offload_enable(dhd, false); #endif /* ARP_OFFLOAD_SUPPORT */ return 0; diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c index 78623f29a24d..69ea6f090789 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 354527 2012-08-31 12:37:03Z $ + * $Id: dhd_common.c 355340 2012-09-06 09:34:37Z $ */ #include <typedefs.h> #include <osl.h> @@ -317,10 +317,12 @@ dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const ch case IOV_SVAL(IOV_MSGLEVEL): #ifdef WL_CFG80211 /* Enable DHD and WL logs in oneshot */ - if (int_val & DHD_WL_VAL) - wl_cfg80211_enable_trace(int_val & (~DHD_WL_VAL)); - else -#endif + if (int_val & DHD_WL_VAL2) + wl_cfg80211_enable_trace(TRUE, int_val & (~DHD_WL_VAL2)); + else if (int_val & DHD_WL_VAL) + wl_cfg80211_enable_trace(FALSE, WL_DBG_DBG); + if (!(int_val & DHD_WL_VAL2)) +#endif /* WL_CFG80211 */ dhd_msg_level = int_val; break; case IOV_GVAL(IOV_BCMERRORSTR): diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 53515e86ed3f..66cfcb18f5b6 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 354288 2012-08-30 18:14:56Z $ + * $Id: dhd_linux.c 356141 2012-09-11 09:43:16Z $ */ #include <typedefs.h> @@ -620,7 +620,7 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd) dhd->early_suspended = 1; #endif /* Kernel suspended */ - DHD_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__)); + DHD_ERROR(("%s: force extra Suspend setting\n", __FUNCTION__)); #ifndef SUPPORT_PM2_ONLY dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, @@ -662,7 +662,7 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd) dhd->early_suspended = 0; #endif /* Kernel resumed */ - DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__)); + DHD_ERROR(("%s: Remove extra suspend setting\n", __FUNCTION__)); #ifndef SUPPORT_PM2_ONLY power_mode = PM_FAST; @@ -1499,11 +1499,11 @@ dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - dhdp->txoff = state; ASSERT(dhd); if (ifidx == ALL_INTERFACES) { /* Flow control on all active interfaces */ + dhdp->txoff = state; for (i = 0; i < DHD_MAX_IFS; i++) { if (dhd->iflist[i]) { net = dhd->iflist[i]->net; @@ -1594,11 +1594,11 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) /* Dropping packets before registering net device to avoid kernel panic */ -#ifdef BCMDHDUSB +#ifndef PROP_TXSTATUS_VSDB if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) { #else if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || !dhd->pub.up) { -#endif /* BCMDHDUSB */ +#endif /* PROP_TXSTATUS_VSDB */ DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n", __FUNCTION__)); PKTFREE(dhdp->osh, pktbuf, TRUE); @@ -1675,12 +1675,8 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) __FUNCTION__, dump_data[0x15])); } } else if (dump_data[0] & 1) { - DHD_ERROR(("%s: MULTICAST: " - "%02X:%02X:%02X:%02X:%02X:%02X\n", - __FUNCTION__, dump_data[0], - dump_data[1], dump_data[2], - dump_data[3], dump_data[4], - dump_data[5])); + DHD_ERROR(("%s: MULTICAST: " MACDBG "\n", + __FUNCTION__, MAC2STRDBG(dump_data))); } if (protocol == ETHER_TYPE_802_1X) { @@ -2923,6 +2919,11 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen, void *dev) #ifdef PROP_TXSTATUS spin_lock_init(&dhd->wlfc_spinlock); +#ifdef PROP_TXSTATUS_VSDB + dhd->pub.wlfc_enabled = FALSE; +#else + dhd->pub.wlfc_enabled = TRUE; +#endif /* PROP_TXSTATUS_VSDB */ #endif /* PROP_TXSTATUS */ /* Initialize other structure content */ @@ -3261,17 +3262,17 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(ARP_OFFLOAD_SUPPORT) int arpoe = 1; #endif - int scan_assoc_time = DHD_SCAN_ACTIVE_TIME; - int scan_unassoc_time = 80; + int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME; + int scan_unassoc_time = DHD_SCAN_UNASSOC_ACTIVE_TIME; 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 */ #ifdef ROAM_ENABLE uint roamvar = 0; - int roam_trigger[2] = {-75, WLC_BAND_ALL}; + int roam_trigger[2] = {CUSTOM_ROAM_TRIGGER_SETTING, WLC_BAND_ALL}; int roam_scan_period[2] = {10, WLC_BAND_ALL}; - int roam_delta[2] = {10, WLC_BAND_ALL}; + int roam_delta[2] = {CUSTOM_ROAM_DELTA_SETTING, WLC_BAND_ALL}; #ifdef FULL_ROAMING_SCAN_PERIOD_60_SEC int roam_fullscan_period = 60; #else /* FULL_ROAMING_SCAN_PERIOD_60_SEC */ @@ -3301,8 +3302,12 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) uint32 hostreorder = 1; #endif #ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB dhd->wlfc_enabled = FALSE; /* enable WLFC only if the firmware is VSDB */ +#else + dhd->wlfc_enabled = TRUE; +#endif /* PROP_TXSTATUS_VSDB */ #endif /* PROP_TXSTATUS */ DHD_TRACE(("Enter %s\n", __FUNCTION__)); dhd->op_mode = 0; @@ -3415,14 +3420,13 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) } #else (void)concurrent_capab; -#endif +#endif } DHD_ERROR(("Firmware up: op_mode=%d, " - "Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + "Broadcom Dongle Host Driver mac="MACDBG"\n", dhd->op_mode, - dhd->mac.octet[0], dhd->mac.octet[1], dhd->mac.octet[2], - dhd->mac.octet[3], dhd->mac.octet[4], dhd->mac.octet[5])); + MAC2STRDBG(dhd->mac.octet))); /* Set Country code */ if (dhd->dhd_cspec.ccode[0] != 0) { bcm_mkiovar("country", (char *)&dhd->dhd_cspec, @@ -3442,12 +3446,18 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); #endif /* ROAM_ENABLE || DISABLE_BUILTIN_ROAM */ #ifdef ROAM_ENABLE - dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), TRUE, 0); - dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_SCAN_PERIOD, roam_scan_period, - sizeof(roam_scan_period), TRUE, 0); - dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, roam_delta, sizeof(roam_delta), TRUE, 0); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_TRIGGER, roam_trigger, + sizeof(roam_trigger), TRUE, 0)) < 0) + DHD_ERROR(("%s: roam trigger set failed %d\n", __FUNCTION__, ret)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_SCAN_PERIOD, roam_scan_period, + sizeof(roam_scan_period), TRUE, 0)) < 0) + DHD_ERROR(("%s: roam scan period set failed %d\n", __FUNCTION__, ret)); + if ((dhd_wl_ioctl_cmd(dhd, WLC_SET_ROAM_DELTA, roam_delta, + sizeof(roam_delta), TRUE, 0)) < 0) + DHD_ERROR(("%s: roam delta set failed %d\n", __FUNCTION__, ret)); bcm_mkiovar("fullroamperiod", (char *)&roam_fullscan_period, 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("%s: roam fullscan period set failed %d\n", __FUNCTION__, ret)); #endif /* ROAM_ENABLE */ /* Set PowerSave mode */ @@ -3457,9 +3467,11 @@ 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); - DHD_INFO(("%s set glom=0x%X\n", __FUNCTION__, glom)); - bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + if (glom != DEFAULT_GLOM_VALUE) { + DHD_INFO(("%s set glom=0x%X\n", __FUNCTION__, glom)); + 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)); @@ -3597,7 +3609,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #ifdef AMPDU_HOSTREORDER bcm_mkiovar("ampdu_hostreorder", (char *)&hostreorder, 4, buf, sizeof(buf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); -#endif +#endif /* AMPDU_HOSTREORDER */ #if !defined(WL_CFG80211) /* Force STA UP */ @@ -3905,12 +3917,11 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) goto fail; } printf("Broadcom Dongle Host Driver: register interface [%s]" - " MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + " MAC: "MACDBG"\n", net->name, - net->dev_addr[0], net->dev_addr[1], net->dev_addr[2], - net->dev_addr[3], net->dev_addr[4], net->dev_addr[5]); + MAC2STRDBG(net->dev_addr)); -#if defined(SOFTAP) && defined(CONFIG_BCMDHD_WEXT) && !defined(WL_CFG80211) +#if defined(SOFTAP) && defined(CONFIG_WIRELESS_EXT) && !defined(WL_CFG80211) wl_iw_iscan_set_scan_broadcast_prep(net, 1); #endif @@ -4741,9 +4752,12 @@ int net_os_set_suspend(struct net_device *dev, int val, int force) int ret = 0; dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - if (dhd) + if (dhd) { ret = dhd_suspend_resume_helper(dhd, val, force); - +#ifdef WL_CFG80211 + wl_cfg80211_update_power_mode(dev); +#endif + } return ret; } @@ -4790,7 +4804,7 @@ int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) return ret; #else return 0; -#endif +#endif } int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val) diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index c18a06484c42..b5fa6335ab0a 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 354488 2012-08-31 07:18:10Z $ + * $Id: dhd_sdio.c 355144 2012-09-05 14:04:28Z $ */ #include <typedefs.h> @@ -4666,18 +4666,12 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) if (PKTLEN(osh, pfirst) == 0) { PKTFREE(bus->dhd->osh, pfirst, FALSE); - if (plast) { - PKTSETNEXT(osh, plast, pnext); - } continue; } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pfirst, reorder_info_buf, &reorder_info_len) != 0) { DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__)); bus->dhd->rx_errors++; PKTFREE(osh, pfirst, FALSE); - if (plast) { - PKTSETNEXT(osh, plast, pnext); - } continue; } if (reorder_info_len) { @@ -4690,9 +4684,6 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) reorder_info_len, &ppfirst, &free_buf_count); if (free_buf_count == 0) { - if (plast) { - PKTSETNEXT(osh, plast, pnext); - } continue; } else { @@ -4712,14 +4703,12 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) PKTSETNEXT(osh, list_tail[ifidx], ppfirst); list_tail[ifidx] = pfirst; } - plast = pfirst; } num += (uint8)free_buf_count; } else { /* this packet will go up, link back into chain and count it */ - plast = pfirst; if (list_tail[ifidx] == NULL) { list_head[ifidx] = list_tail[ifidx] = pfirst; diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h index 80f39003ea4a..0e11b11eb3f4 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 313732 2012-02-08 19:49:00Z $ + * $Id: bcmsdh_sdmmc.h 355594 2012-09-07 10:22:02Z $ */ #ifndef __BCMSDH_SDMMC_H__ @@ -34,6 +34,8 @@ #define sd_data(x) #define sd_ctrl(x) +#define sd_trace_hw4 sd_trace + #define sd_sync_dma(sd, read, nbytes) #define sd_init_dma(sd) #define sd_ack_intr(sd) diff --git a/drivers/net/wireless/bcmdhd/include/bcmutils.h b/drivers/net/wireless/bcmdhd/include/bcmutils.h index ba807013e1a1..6db5e9326cf6 100644 --- a/drivers/net/wireless/bcmdhd/include/bcmutils.h +++ b/drivers/net/wireless/bcmdhd/include/bcmutils.h @@ -649,7 +649,13 @@ extern void *_bcmutils_dummy_fn; (ea).octet[3], \ (ea).octet[4], \ (ea).octet[5] - +#if !defined(SIMPLE_MAC_PRINT) +#define MACDBG "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC2STRDBG(ea) (ea)[0], (ea)[1], (ea)[2], (ea)[3], (ea)[4], (ea)[5] +#else +#define MACDBG "%02x:%02x:%02x" +#define MAC2STRDBG(ea) (ea)[0], (ea)[4], (ea)[5] +#endif /* SIMPLE_MAC_PRINT */ typedef struct bcm_bit_desc { uint32 bit; diff --git a/drivers/net/wireless/bcmdhd/include/dhdioctl.h b/drivers/net/wireless/bcmdhd/include/dhdioctl.h index f2fc2445e692..11fff555a8fc 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 353180 2012-08-24 22:37:10Z $ + * $Id: dhdioctl.h 354894 2012-09-04 12:34:07Z $ */ #ifndef _dhdioctl_h_ @@ -93,6 +93,7 @@ enum { #define DHD_ARPOE_VAL 0x4000 #define DHD_REORDER_VAL 0x8000 #define DHD_WL_VAL 0x10000 +#define DHD_WL_VAL2 0x20000 #ifdef SDTEST /* For pktgen iovar */ diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h index b4becbbe6fd7..1f362ce01ae1 100644 --- a/drivers/net/wireless/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -30,26 +30,26 @@ #define EPI_MINOR_VERSION 28 -#define EPI_RC_NUMBER 11 +#define EPI_RC_NUMBER 12 -#define EPI_INCREMENTAL_NUMBER 2 +#define EPI_INCREMENTAL_NUMBER 1 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 1, 28, 11, 2 +#define EPI_VERSION 1, 28, 12, 1 -#define EPI_VERSION_NUM 0x011c0b02 +#define EPI_VERSION_NUM 0x011c0c01 -#define EPI_VERSION_DEV 1.28.11 +#define EPI_VERSION_DEV 1.28.12 /* Driver Version String, ASCII, 32 chars max */ #ifdef BCMINTERNAL -#define EPI_VERSION_STR "1.28.11.2 (r BCMINT)" +#define EPI_VERSION_STR "1.28.12.1 (r BCMINT)" #else #ifdef WLTEST -#define EPI_VERSION_STR "1.28.11.2 (r WLTEST)" +#define EPI_VERSION_STR "1.28.12.1 (r WLTEST)" #else -#define EPI_VERSION_STR "1.28.11.2 (r)" +#define EPI_VERSION_STR "1.28.12.1 (r)" #endif #endif /* BCMINTERNAL */ diff --git a/drivers/net/wireless/bcmdhd/include/linuxver.h b/drivers/net/wireless/bcmdhd/include/linuxver.h index 55144d9208be..f242aad4ec3b 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 342829 2012-07-04 06:46:58Z $ + * $Id: linuxver.h 353905 2012-08-29 07:33:08Z $ */ #ifndef _linuxver_h_ @@ -520,6 +520,19 @@ typedef struct { DBG_THR(("%s thr:%lx started\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ } +#ifdef USE_KTHREAD_API +#define PROC_START2(thread_func, owner, tsk_ctl, flags, name) \ +{ \ + sema_init(&((tsk_ctl)->sema), 0); \ + init_completion(&((tsk_ctl)->completed)); \ + (tsk_ctl)->parent = owner; \ + (tsk_ctl)->terminated = FALSE; \ + (tsk_ctl)->p_task = kthread_run(thread_func, tsk_ctl, (char*)name); \ + (tsk_ctl)->thr_pid = (tsk_ctl)->p_task->pid; \ + DBG_THR(("%s thr:%lx created\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ +} +#endif + #define PROC_STOP(tsk_ctl) \ { \ (tsk_ctl)->terminated = TRUE; \ diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h index decadf4d1d9c..4149db458aab 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 354037 2012-08-29 21:19:25Z $ + * $Id: wlioctl.h 354686 2012-09-01 12:17:25Z $ */ #ifndef _wlioctl_h_ @@ -1886,9 +1886,6 @@ typedef struct wl_po { /* when sgi_tx==WLC_SGI_ALL, bypass rate selection, enable sgi for all mcs */ #define WLC_SGI_ALL 0x02 -#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 LISTEN_INTERVAL 10 /* interference mitigation options */ #define INTERFERE_OVRRIDE_OFF -1 /* interference override off */ diff --git a/drivers/net/wireless/bcmdhd/linux_osl.c b/drivers/net/wireless/bcmdhd/linux_osl.c index ddb8372941ae..d03a095f5fea 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 350283 2012-08-12 07:47:25Z $ + * $Id: linux_osl.c 355147 2012-09-05 15:03:49Z $ */ #define LINUX_PORT @@ -191,7 +191,7 @@ osl_attach(void *pdev, uint bustype, bool pkttag) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) gfp_t flags; - flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; osh = kmalloc(sizeof(osl_t), flags); #else osh = kmalloc(sizeof(osl_t), GFP_ATOMIC); @@ -288,7 +288,7 @@ osl_detach(osl_t *osh) static struct sk_buff *osl_alloc_skb(unsigned int len) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) - gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + gfp_t flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; return __dev_alloc_skb(len, flags); #else @@ -373,7 +373,7 @@ osl_ctfpool_init(osl_t *osh, uint numobj, uint size) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) gfp_t flags; - flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; osh->ctfpool = kmalloc(sizeof(ctfpool_t), flags); #else osh->ctfpool = kmalloc(sizeof(ctfpool_t), GFP_ATOMIC); @@ -896,7 +896,7 @@ original: #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; if ((addr = kmalloc(size, flags)) == NULL) { #else if ((addr = kmalloc(size, GFP_ATOMIC)) == NULL) { @@ -1058,7 +1058,7 @@ osl_pktdup(osl_t *osh, void *skb) PKTCTFMAP(osh, skb); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) - flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + flags = (in_atomic() || in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; if ((p = skb_clone((struct sk_buff *)skb, flags)) == NULL) #else if ((p = skb_clone((struct sk_buff*)skb, GFP_ATOMIC)) == NULL) diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index 8a88762be9e3..162d77ddc1e3 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.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_android.c 354527 2012-08-31 12:37:03Z $ + * $Id: wl_android.c 355613 2012-09-07 13:03:47Z $ */ #include <linux/module.h> @@ -131,6 +131,10 @@ int wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len) #endif /* WL_CFG80211 */ extern int dhd_os_check_if_up(void *dhdp); extern void *bcmsdh_get_drvdata(void); +#if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) +extern int dhd_wlfc_init(dhd_pub_t *dhd); +extern void dhd_wlfc_deinit(dhd_pub_t *dhd); +#endif extern bool ap_fw_loaded; #if defined(CUSTOMER_HW2) @@ -406,6 +410,9 @@ int wl_android_wifi_on(struct net_device *dev) if (dhd_dev_init_ioctl(dev) < 0) ret = -EFAULT; } +#if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) + dhd_wlfc_init(bcmsdh_get_drvdata()); +#endif g_wifi_on = TRUE; } @@ -427,6 +434,9 @@ int wl_android_wifi_off(struct net_device *dev) dhd_net_if_lock(dev); if (g_wifi_on) { +#if defined(PROP_TXSTATUS) && !defined(PROP_TXSTATUS_VSDB) + dhd_wlfc_deinit(bcmsdh_get_drvdata()); +#endif ret = dhd_dev_reset(dev, TRUE); sdioh_stop(NULL); dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 57aea81ac2a6..8c4ceff81c74 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.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_cfg80211.c 354527 2012-08-31 12:37:03Z $ + * $Id: wl_cfg80211.c 356173 2012-09-11 13:55:32Z $ */ #include <typedefs.h> @@ -63,14 +63,10 @@ #define IW_WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED)) - static struct device *cfg80211_parent_dev = NULL; struct wl_priv *wlcfg_drv_priv = NULL; - u32 wl_dbg_level = WL_DBG_ERR; -#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 #ifdef VSDB @@ -212,6 +208,10 @@ static s32 wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, static s32 wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx); static s32 wl_cfg80211_resume(struct wiphy *wiphy); +static s32 wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, u64 cookie); +static s32 wl_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *ndev, u8* mac_addr); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) static s32 wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow); #else @@ -427,11 +427,11 @@ do { \ extern int dhd_wait_pend8021x(struct net_device *dev); -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB extern int disable_proptx; extern int dhd_wlfc_init(dhd_pub_t *dhd); extern void dhd_wlfc_deinit(dhd_pub_t *dhd); -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ #if (WL_DBG_LEVEL > 0) #define WL_DBG_ESTR_MAX 50 @@ -937,17 +937,17 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, struct ether_addr primary_mac; int (*net_attach)(void *dhdp, int ifidx); bool rollback_lock = false; -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB s32 up = 1; dhd_pub_t *dhd; -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ if (!wl) return ERR_PTR(-EINVAL); -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB dhd = (dhd_pub_t *)(wl->pub); -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ /* Use primary I/F for sending cmds down to firmware */ @@ -1018,10 +1018,10 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, return ERR_PTR(-ENOMEM); } -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB if (!dhd) return ERR_PTR(-ENODEV); -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ if (!wl->p2p || !wl->p2p->vir_ifname) return ERR_PTR(-ENODEV); @@ -1038,7 +1038,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, strncpy(wl->p2p->vir_ifname, name, IFNAMSIZ - 1); wl_notify_escan_complete(wl, _ndev, true, true); -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB if (!wl->wlfc_on && !disable_proptx) { dhd->wlfc_enabled = true; dhd_wlfc_init(dhd); @@ -1047,7 +1047,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, WL_ERR(("WLC_UP return err:%d\n", err)); wl->wlfc_on = true; } -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ /* 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 @@ -1127,13 +1127,13 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, WL_ERR((" virtual interface(%s) is not created \n", wl->p2p->vir_ifname)); memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); wl->p2p->vif_created = false; -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB if (dhd->wlfc_enabled && wl->wlfc_on) { dhd->wlfc_enabled = false; dhd_wlfc_deinit(dhd); wl->wlfc_on = false; } -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ } } fail: @@ -1361,9 +1361,9 @@ wl_cfg80211_ifdel_ops(struct net_device *ndev) struct wl_priv *wl = wlcfg_drv_priv; bool rollback_lock = false; s32 index = 0; -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ if (!ndev || (strlen(ndev->name) == 0)) { WL_ERR(("net is NULL\n")); return 0; @@ -1395,13 +1395,13 @@ wl_cfg80211_ifdel_ops(struct net_device *ndev) wl_cfgp2p_clear_management_ie(wl, index); WL_DBG(("index : %d\n", index)); -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB if (dhd->wlfc_enabled && wl->wlfc_on) { dhd->wlfc_enabled = false; dhd_wlfc_deinit(dhd); wl->wlfc_on = false; } -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ wl_clr_drv_status(wl, CONNECTED, ndev); } /* Wake up any waiting thread */ @@ -1512,8 +1512,8 @@ static void wl_scan_prep(struct wl_scan_params *params, struct cfg80211_scan_req params->bss_type = DOT11_BSSTYPE_ANY; params->scan_type = 0; params->nprobes = -1; - params->active_time = DHD_SCAN_ACTIVE_TIME; - params->passive_time = DHD_SCAN_PASSIVE_TIME; + params->active_time = -1; + params->passive_time = -1; params->home_time = -1; params->channel_num = 0; memset(¶ms->ssid, 0, sizeof(wlc_ssid_t)); @@ -2086,8 +2086,8 @@ scan_out: bzero(&bssid, sizeof(bssid)); if ((ret = wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false)) == 0) - WL_ERR(("FW is connected with " MACSTR "/n", - MAC2STR(bssid.octet))); + WL_ERR(("FW is connected with " MACDBG "/n", + MAC2STRDBG(bssid.octet))); else WL_ERR(("GET BSSID failed with %d\n", ret)); @@ -2625,8 +2625,8 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, memcpy(&scbval.ea, &bssid, ETHER_ADDR_LEN); scbval.val = htod32(scbval.val); - WL_DBG(("drv status CONNECTED is not set, but connected in FW!" MACSTR "/n", - MAC2STR(bssid.octet))); + WL_DBG(("drv status CONNECTED is not set, but connected in FW!" MACDBG "/n", + MAC2STRDBG(bssid.octet))); err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true); if (unlikely(err)) { @@ -2736,8 +2736,8 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, /* 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 = DHD_SCAN_ACTIVE_TIME*8; - ext_join_params->scan.passive_time = DHD_SCAN_PASSIVE_TIME*3; + ext_join_params->scan.active_time = WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS; + ext_join_params->scan.passive_time = WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS; /* Set up join scan parameters */ ext_join_params->scan.scan_type = -1; ext_join_params->scan.nprobes @@ -3295,8 +3295,8 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, return err; } if (memcmp(mac, curmacp, ETHER_ADDR_LEN)) { - WL_ERR(("Wrong Mac address: "MACSTR" != "MACSTR"\n", - MAC2STR(mac), MAC2STR(curmacp))); + WL_ERR(("Wrong Mac address: "MACDBG" != "MACDBG"\n", + MAC2STRDBG(mac), MAC2STRDBG(curmacp))); } /* Report the current tx rate */ @@ -3347,6 +3347,23 @@ get_station_err: return err; } +int wl_cfg80211_update_power_mode(struct net_device *dev) +{ + int pm = -1; + int err; + + err = wldev_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), false); + if (err || (pm == -1)) { + WL_ERR(("error (%d)\n", err)); + } else { + pm = (pm == PM_OFF) ? false : true; + WL_DBG(("%s: %d\n", __func__, pm)); + if (dev->ieee80211_ptr) + dev->ieee80211_ptr->ps = pm; + } + return err; +} + static s32 wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, s32 timeout) @@ -3355,6 +3372,7 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, s32 err = 0; struct wl_priv *wl = wiphy_priv(wiphy); struct net_info *_net_info = wl_get_netinfo_by_netdev(wl, dev); + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); CHECK_SYS_UP(wl); @@ -3362,7 +3380,7 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, return err; } - pm = enabled ? PM_FAST : PM_OFF; + pm = enabled ? ((dhd->in_suspend) ? PM_MAX : PM_FAST) : PM_OFF; /* Do not enable the power save after assoc if it is p2p interface */ if (_net_info->pm_block || wl->vsdb_mode) { WL_DBG(("Do not enable the power save\n")); @@ -4228,8 +4246,10 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, 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, + err = wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, sizeof(scb_val_t), true); + if (err < 0) + WL_ERR(("WLC_SCB_DEAUTHENTICATE_FOR_REASON error %d\n", err)); WL_DBG(("Disconnect STA : %s scb_val.val %d\n", bcm_ether_ntoa((const struct ether_addr *)mgmt->da, eabuf), scb_val.val)); @@ -5076,6 +5096,51 @@ static s32 wl_cfg80211_hostapd_sec( return 0; } +static s32 +wl_cfg80211_del_station( + struct wiphy *wiphy, + struct net_device *ndev, + u8* mac_addr) +{ + struct net_device *dev; + struct wl_priv *wl = wiphy_priv(wiphy); + scb_val_t scb_val; + s8 eabuf[ETHER_ADDR_STR_LEN]; + + WL_DBG(("Entry\n")); + if (mac_addr == NULL) { + WL_DBG(("mac_addr is NULL ignore it\n")); + return 0; + } + + if (ndev == wl->p2p_net) { + dev = wl_to_prmry_ndev(wl); + } else { + dev = ndev; + } + + if (p2p_is_on(wl)) { + /* Suspend P2P discovery search-listen to prevent it from changing the + * channel. + */ + if ((wl_cfgp2p_discover_enable_search(wl, false)) < 0) { + WL_ERR(("Can not disable discovery mode\n")); + return -EFAULT; + } + } + + memcpy(scb_val.ea.octet, mac_addr, ETHER_ADDR_LEN); + scb_val.val = DOT11_RC_DEAUTH_LEAVING; + if (wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, + sizeof(scb_val_t), true)) + WL_ERR(("WLC_SCB_DEAUTHENTICATE_FOR_REASON failed\n")); + WL_DBG(("Disconnect STA : %s scb_val.val %d\n", + bcm_ether_ntoa((const struct ether_addr *)mac_addr, eabuf), + scb_val.val)); + wl_delay(400); + return 0; +} + #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) static s32 wl_cfg80211_start_ap( @@ -5207,50 +5272,6 @@ exit: } static s32 -wl_cfg80211_del_station( - struct wiphy *wiphy, - struct net_device *ndev, - u8* mac_addr) -{ - struct net_device *dev; - struct wl_priv *wl = wiphy_priv(wiphy); - scb_val_t scb_val; - s8 eabuf[ETHER_ADDR_STR_LEN]; - - WL_DBG(("Entry\n")); - if (mac_addr == NULL) { - WL_DBG(("mac_addr is NULL ignore it\n")); - return 0; - } - - if (ndev == wl->p2p_net) { - dev = wl_to_prmry_ndev(wl); - } else { - dev = ndev; - } - - if (p2p_is_on(wl)) { - /* Suspend P2P discovery search-listen to prevent it from changing the - * channel. - */ - if ((wl_cfgp2p_discover_enable_search(wl, false)) < 0) { - WL_ERR(("Can not disable discovery mode\n")); - return -EFAULT; - } - } - - memcpy(scb_val.ea.octet, mac_addr, ETHER_ADDR_LEN); - scb_val.val = DOT11_RC_DEAUTH_LEAVING; - 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 *)mac_addr, eabuf), - scb_val.val)); - wl_delay(400); - return 0; -} - -static s32 wl_cfg80211_change_beacon( struct wiphy *wiphy, struct net_device *dev, @@ -5555,12 +5576,13 @@ static struct cfg80211_ops wl_cfg80211_ops = { .change_beacon = wl_cfg80211_change_beacon, .start_ap = wl_cfg80211_start_ap, .stop_ap = wl_cfg80211_stop_ap, - .del_station = wl_cfg80211_del_station, #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ #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) */ + .del_station = wl_cfg80211_del_station, + .mgmt_tx_cancel_wait = wl_cfg80211_mgmt_tx_cancel_wait, }; s32 wl_mode_to_nl80211_iftype(s32 mode) @@ -6081,8 +6103,8 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, "(reason=%d)\n", ndev->name, ntoh32(e->reason)); if (memcmp(curbssid, &e->addr, ETHER_ADDR_LEN) != 0) { WL_ERR(("BSSID of event is not the connected BSSID" - "(ignore it) cur: " MACSTR " event: " MACSTR"\n", - MAC2STR(curbssid), MAC2STR((u8*)(&e->addr)))); + "(ignore it) cur: " MACDBG " event: " MACDBG"\n", + MAC2STRDBG(curbssid), MAC2STRDBG((u8*)(&e->addr)))); return 0; } wl_clr_drv_status(wl, CONNECTED, ndev); @@ -6094,8 +6116,12 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); scbval.val = htod32(scbval.val); - wldev_ioctl(ndev, WLC_DISASSOC, &scbval, + err = wldev_ioctl(ndev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true); + if (err < 0) { + WL_ERR(("WLC_DISASSOC error %d\n", err)); + err = 0; + } cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); wl_link_down(wl); wl_init_prof(wl, ndev); @@ -6463,7 +6489,6 @@ wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, */ wl_notify_sched_scan_results(wl, ndev, e, data); #endif /* WL_SCHED_SCAN */ - return 0; } #endif /* PNO_SUPPORT */ @@ -6646,7 +6671,9 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, wldev_iovar_getbuf_bsscfg(dev, "cur_etheraddr", NULL, 0, wl->ioctl_buf, WLC_IOCTL_SMLEN, bsscfgidx, &wl->ioctl_buf_sync); - wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + err = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + if (err < 0) + WL_ERR(("WLC_GET_BSSID error %d\n", err)); 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, @@ -7486,8 +7513,8 @@ static s32 wl_escan_handler(struct wl_priv *wl, wl->afx_hdl->tx_dst_addr.octet, ETHER_ADDR_LEN)) { s32 channel = CHSPEC_CHANNEL( wl_chspec_driver_to_host(bi->chanspec)); - WL_DBG(("ACTION FRAME SCAN : Peer " MACSTR " found, channel : %d\n", - MAC2STR(wl->afx_hdl->tx_dst_addr.octet), channel)); + WL_DBG(("ACTION FRAME SCAN : Peer " MACDBG " found, channel : %d\n", + MAC2STRDBG(wl->afx_hdl->tx_dst_addr.octet), channel)); wl_clr_p2p_status(wl, SCANNING); wl->afx_hdl->peer_chan = channel; complete(&wl->act_frm_scan); @@ -7530,9 +7557,9 @@ static s32 wl_escan_handler(struct wl_priv *wl, (bi->flags & WL_BSS_FLAGS_FROM_BEACON)) goto exit; - WL_DBG(("%s("MACSTR"), i=%d prev: RSSI %d" + WL_DBG(("%s("MACDBG"), i=%d prev: RSSI %d" " flags 0x%x, new: RSSI %d flags 0x%x\n", - bss->SSID, MAC2STR(bi->BSSID.octet), i, + bss->SSID, MAC2STRDBG(bi->BSSID.octet), i, bss->RSSI, bss->flags, bi->RSSI, bi->flags)); if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) == @@ -7540,9 +7567,9 @@ static s32 wl_escan_handler(struct wl_priv *wl, /* preserve max RSSI if the measurements are * both on-channel or both off-channel */ - WL_SCAN(("%s("MACSTR"), same onchan" + WL_SCAN(("%s("MACDBG"), same onchan" ", RSSI: prev %d new %d\n", - bss->SSID, MAC2STR(bi->BSSID.octet), + bss->SSID, MAC2STRDBG(bi->BSSID.octet), bss->RSSI, bi->RSSI)); bi->RSSI = MAX(bss->RSSI, bi->RSSI); } else if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) && @@ -7550,9 +7577,9 @@ static s32 wl_escan_handler(struct wl_priv *wl, /* preserve the on-channel rssi measurement * if the new measurement is off channel */ - WL_SCAN(("%s("MACSTR"), prev onchan" + WL_SCAN(("%s("MACDBG"), prev onchan" ", RSSI: prev %d new %d\n", - bss->SSID, MAC2STR(bi->BSSID.octet), + bss->SSID, MAC2STRDBG(bi->BSSID.octet), bss->RSSI, bi->RSSI)); bi->RSSI = bss->RSSI; bi->flags |= WL_BSS_FLAGS_RSSI_ONCHANNEL; @@ -7563,8 +7590,8 @@ static s32 wl_escan_handler(struct wl_priv *wl, WL_SCAN(("bss info replacement" " is occured(bcast:%d->probresp%d)\n", bss->ie_length, bi->ie_length)); - WL_DBG(("%s("MACSTR"), replacement!(%d -> %d)\n", - bss->SSID, MAC2STR(bi->BSSID.octet), + WL_DBG(("%s("MACDBG"), replacement!(%d -> %d)\n", + bss->SSID, MAC2STRDBG(bi->BSSID.octet), prev_len, bi_length)); if (list->buflen - prev_len + bi_length @@ -7949,8 +7976,9 @@ s32 wl_cfg80211_attach_post(struct net_device *ndev) /* 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; - WL_ERR(("%s: p2p_dev_addr="MACSTR "\n", - wl->p2p_net->name, MAC2STR(wl->p2p_net->dev_addr))); + WL_ERR(("%s: p2p_dev_addr="MACDBG "\n", + wl->p2p_net->name, + MAC2STRDBG(wl->p2p_net->dev_addr))); } else { WL_ERR(("p2p_net not yet populated." " Couldn't update the MAC Address for p2p0 \n")); @@ -8401,8 +8429,7 @@ static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) ht40_allowed = (bw_cap == WLC_N_BW_20ALL)? false : true; } else { WL_ERR(("Invalid channel Sepc. 0x%x.\n", c)); - kfree(pbuf); - return BCME_ERROR; + continue; } for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { if (band_chan_arr[j].hw_value == channel) { @@ -8556,6 +8583,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) 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; + /* An HT shall support all EQM rates for one spatial stream */ wiphy->bands[index]->ht_cap.mcs.rx_mask[0] = 0xff; } @@ -8604,7 +8632,7 @@ static s32 __wl_cfg80211_down(struct wl_priv *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 +#endif /* WL_ENABLE_P2P_IF */ WL_DBG(("In\n")); /* Check if cfg80211 interface is already down */ if (!wl_get_drv_status(wl, READY, ndev)) @@ -9119,7 +9147,23 @@ int wl_cfg80211_do_driver_init(struct net_device *net) return 0; } -void wl_cfg80211_enable_trace(int level) +void wl_cfg80211_enable_trace(bool set, u32 level) { - wl_dbg_level = (u32)level; + if (set) + wl_dbg_level = level & WL_DBG_LEVEL; + else + wl_dbg_level |= (WL_DBG_LEVEL & level); +} + +static s32 +wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, u64 cookie) +{ + /* CFG80211 checks for tx_cancel_wait callback when ATTR_DURATION + * is passed with CMD_FRAME. This callback is supposed to cancel + * the OFFCHANNEL Wait. Since we are already taking care of that + * with the tx_mgmt logic, do nothing here. + */ + + return 0; } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index b727d6d5985a..fecfb35318a4 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.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_cfg80211.h 354527 2012-08-31 12:37:03Z $ + * $Id: wl_cfg80211.h 355340 2012-09-06 09:34:37Z $ */ #ifndef _wl_cfg80211_h_ @@ -147,8 +147,10 @@ do { \ #define WL_MIN_DWELL_TIME 100 #define WL_LONG_DWELL_TIME 1000 #define IFACE_MAX_CNT 2 -#define WL_SCAN_CONNECT_DWELL_TIME_MS 200 -#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20 +#define WL_SCAN_CONNECT_DWELL_TIME_MS 200 +#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20 +#define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 +#define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 #define WL_AF_TX_MAX_RETRY 5 #define WL_SCAN_TIMER_INTERVAL_MS 8000 /* Scan timeout */ @@ -802,10 +804,11 @@ 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); 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); +void wl_cfg80211_enable_trace(bool set, u32 level); extern s32 wl_update_wiphybands(struct wl_priv *wl); extern s32 wl_cfg80211_if_is_group_owner(void); extern chanspec_t wl_ch_host_to_driver(u16 channel); extern s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add); extern void wl_stop_wait_next_action_frame(struct wl_priv *wl, struct net_device *ndev); +extern int wl_cfg80211_update_power_mode(struct net_device *dev); #endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index 643d436f5f42..1274e5f939b9 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.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_cfgp2p.c 353899 2012-08-29 06:41:22Z $ + * $Id: wl_cfgp2p.c 354837 2012-09-04 06:58:44Z $ * */ #include <typedefs.h> @@ -352,9 +352,8 @@ wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, ifreq.chspec = chspec; memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); - 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], + CFGP2P_DBG(("---wl p2p_ifadd "MACDBG" %s %u\n", + MAC2STRDBG(ifreq.addr.octet), (if_type == WL_P2P_IF_GO) ? "go" : "client", (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); @@ -382,9 +381,8 @@ wl_cfgp2p_ifdisable(struct wl_priv *wl, struct ether_addr *mac) s32 ret; struct net_device *netdev = wl_to_prmry_ndev(wl); - CFGP2P_INFO(("------primary idx %d : wl p2p_ifdis %02x:%02x:%02x:%02x:%02x:%02x\n", - netdev->ifindex, mac->octet[0], mac->octet[1], mac->octet[2], - mac->octet[3], mac->octet[4], mac->octet[5])); + CFGP2P_INFO(("------primary idx %d : wl p2p_ifdis "MACDBG"\n", + netdev->ifindex, MAC2STRDBG(mac->octet))); ret = wldev_iovar_setbuf(netdev, "p2p_ifdis", mac, sizeof(*mac), wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (unlikely(ret < 0)) { @@ -404,9 +402,8 @@ wl_cfgp2p_ifdel(struct wl_priv *wl, struct ether_addr *mac) s32 ret; struct net_device *netdev = wl_to_prmry_ndev(wl); - CFGP2P_INFO(("------primary idx %d : wl p2p_ifdel %02x:%02x:%02x:%02x:%02x:%02x\n", - netdev->ifindex, mac->octet[0], mac->octet[1], mac->octet[2], - mac->octet[3], mac->octet[4], mac->octet[5])); + CFGP2P_INFO(("------primary idx %d : wl p2p_ifdel "MACDBG"\n", + netdev->ifindex, MAC2STRDBG(mac->octet))); ret = wldev_iovar_setbuf(netdev, "p2p_ifdel", mac, sizeof(*mac), wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); if (unlikely(ret < 0)) { @@ -434,10 +431,8 @@ wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, ifreq.chspec = chspec; memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); - CFGP2P_INFO(("---wl p2p_ifchange %02x:%02x:%02x:%02x:%02x:%02x %s %u" - " chanspec 0x%04x\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], + CFGP2P_INFO(("---wl p2p_ifchange "MACDBG" %s %u" + " chanspec 0x%04x\n", MAC2STRDBG(ifreq.addr.octet), (if_type == WL_P2P_IF_GO) ? "go" : "client", (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT, ifreq.chspec)); @@ -469,9 +464,7 @@ wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index) u8 getbuf[64]; struct net_device *dev = wl_to_prmry_ndev(wl); - CFGP2P_INFO(("---wl p2p_if %02x:%02x:%02x:%02x:%02x:%02x\n", - mac->octet[0], mac->octet[1], mac->octet[2], - mac->octet[3], mac->octet[4], mac->octet[5])); + CFGP2P_INFO(("---wl p2p_if "MACDBG"\n", MAC2STRDBG(mac->octet))); ret = wldev_iovar_getbuf_bsscfg(dev, "p2p_if", mac, sizeof(*mac), getbuf, sizeof(getbuf), wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY), NULL); @@ -1980,6 +1973,8 @@ wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int WLC_SET_PM, &legacy_ps, sizeof(legacy_ps), true); if (unlikely(ret)) { CFGP2P_ERR(("error (%d)\n", ret)); + } else { + wl_cfg80211_update_power_mode(ndev); } } else diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h index a3d30a637020..51cdf82e7c98 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.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_cfgp2p.h 353885 2012-08-29 05:21:34Z $ + * $Id: wl_cfgp2p.h 354837 2012-09-04 06:58:44Z $ */ #ifndef _wl_cfgp2p_h_ #define _wl_cfgp2p_h_ @@ -133,8 +133,6 @@ enum wl_cfgp2p_status { /* dword align allocation */ #define WLC_IOCTL_MAXLEN 8192 -#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] -#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" #define CFGP2P_ERR(args) \ do { \ diff --git a/drivers/power/android_battery.c b/drivers/power/android_battery.c index 3f8d0bf0086f..3977c8c1f2b6 100644 --- a/drivers/power/android_battery.c +++ b/drivers/power/android_battery.c @@ -46,6 +46,8 @@ struct android_bat_data { struct device *dev; struct power_supply psy_bat; + struct power_supply psy_usb; + struct power_supply psy_ac; struct wake_lock monitor_wake_lock; struct wake_lock charger_wake_lock; @@ -69,6 +71,10 @@ struct android_bat_data { struct dentry *debugfs_entry; }; +static char *supply_list[] = { + "android-battery", +}; + static enum power_supply_property android_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, @@ -81,6 +87,10 @@ static enum power_supply_property android_battery_props[] = { POWER_SUPPLY_PROP_CURRENT_NOW, }; +static enum power_supply_property android_power_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + static void android_bat_update_data(struct android_bat_data *battery); static char *charge_source_str(int charge_source) @@ -146,9 +156,39 @@ static int android_bat_get_property(struct power_supply *ps, return 0; } +static int android_usb_get_property(struct power_supply *ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct android_bat_data *battery = container_of(ps, + struct android_bat_data, psy_usb); + + if (psp != POWER_SUPPLY_PROP_ONLINE) + return -EINVAL; + + val->intval = (battery->charge_source == CHARGE_SOURCE_USB); + + return 0; +} + +static int android_ac_get_property(struct power_supply *ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct android_bat_data *battery = container_of(ps, + struct android_bat_data, psy_ac); + + if (psp != POWER_SUPPLY_PROP_ONLINE) + return -EINVAL; + + val->intval = (battery->charge_source == CHARGE_SOURCE_AC); + + return 0; +} + static void android_bat_get_temp(struct android_bat_data *battery) { - int batt_temp = 250000; /* 25.0C */ + int batt_temp = 42; /* 4.2C */ int health = battery->batt_health; if (battery->pdata->get_temperature) @@ -179,7 +219,7 @@ static void android_bat_get_temp(struct android_bat_data *battery) } } - battery->batt_temp = batt_temp/1000; + battery->batt_temp = batt_temp; } static void android_bat_update_data(struct android_bat_data *battery) @@ -192,12 +232,12 @@ static void android_bat_update_data(struct android_bat_data *battery) if (battery->pdata->get_voltage_now) { ret = battery->pdata->get_voltage_now(); - battery->batt_vcell = ret >= 0 ? ret : -1; + battery->batt_vcell = ret >= 0 ? ret : 4242000; } if (battery->pdata->get_capacity) { ret = battery->pdata->get_capacity(); - battery->batt_soc = ret >= 0 ? ret : -1; + battery->batt_soc = ret >= 0 ? ret : 42; } if (battery->pdata->get_current_now) { @@ -271,6 +311,9 @@ static void android_bat_charger_work(struct work_struct *work) break; } + power_supply_changed(&battery->psy_ac); + power_supply_changed(&battery->psy_usb); + wake_lock_timeout(&battery->charger_wake_lock, HZ * 2); } @@ -409,6 +452,22 @@ static __devinit int android_bat_probe(struct platform_device *pdev) battery->psy_bat.num_properties = ARRAY_SIZE(android_battery_props), battery->psy_bat.get_property = android_bat_get_property, + battery->psy_usb.name = "android-usb", + battery->psy_usb.type = POWER_SUPPLY_TYPE_USB, + battery->psy_usb.supplied_to = supply_list, + battery->psy_usb.num_supplicants = ARRAY_SIZE(supply_list), + battery->psy_usb.properties = android_power_props, + battery->psy_usb.num_properties = ARRAY_SIZE(android_power_props), + battery->psy_usb.get_property = android_usb_get_property, + + battery->psy_ac.name = "android-ac", + battery->psy_ac.type = POWER_SUPPLY_TYPE_MAINS, + battery->psy_ac.supplied_to = supply_list, + battery->psy_ac.num_supplicants = ARRAY_SIZE(supply_list), + battery->psy_ac.properties = android_power_props, + battery->psy_ac.num_properties = ARRAY_SIZE(android_power_props), + battery->psy_ac.get_property = android_ac_get_property; + battery->batt_vcell = -1; battery->batt_soc = -1; @@ -421,7 +480,21 @@ static __devinit int android_bat_probe(struct platform_device *pdev) if (ret) { dev_err(battery->dev, "%s: failed to register psy_bat\n", __func__); - goto err_psy_reg; + goto err_psy_bat_reg; + } + + ret = power_supply_register(&pdev->dev, &battery->psy_usb); + if (ret) { + dev_err(battery->dev, "%s: failed to register psy_usb\n", + __func__); + goto err_psy_usb_reg; + } + + ret = power_supply_register(&pdev->dev, &battery->psy_ac); + if (ret) { + dev_err(battery->dev, "%s: failed to register psy_ac\n", + __func__); + goto err_psy_ac_reg; } battery->monitor_wqueue = @@ -462,8 +535,12 @@ static __devinit int android_bat_probe(struct platform_device *pdev) return 0; err_wq: + power_supply_unregister(&battery->psy_ac); +err_psy_ac_reg: + power_supply_unregister(&battery->psy_usb); +err_psy_usb_reg: power_supply_unregister(&battery->psy_bat); -err_psy_reg: +err_psy_bat_reg: wake_lock_destroy(&battery->monitor_wake_lock); wake_lock_destroy(&battery->charger_wake_lock); err_pdata: diff --git a/drivers/power/smb347-charger.c b/drivers/power/smb347-charger.c index cb7b183d15fc..5d1b5d797a91 100644 --- a/drivers/power/smb347-charger.c +++ b/drivers/power/smb347-charger.c @@ -43,7 +43,10 @@ #define CFG_INPUT_SOURCE_PRIORITY BIT(2) #define CFG_FLOAT_VOLTAGE 0x03 #define CFG_FLOAT_VOLTAGE_THRESHOLD_MASK 0xc0 +#define CFG_FLOAT_VOLTAGE_MASK 0x3F #define CFG_FLOAT_VOLTAGE_THRESHOLD_SHIFT 6 +#define CFG_CHARGE_CONTROL 0x04 +#define CFG_AUTOMATIC_RECHARGE_DISABLE BIT(7) #define CFG_STAT 0x05 #define CFG_STAT_DISABLED BIT(5) #define CFG_STAT_ACTIVE_HIGH BIT(7) @@ -116,6 +119,7 @@ #define STAT_B 0x3c #define STAT_C 0x3d #define STAT_C_CHG_ENABLED BIT(0) +#define STAT_C_CHG_STATUS BIT(5) #define STAT_C_CHG_MASK 0x06 #define STAT_C_CHG_SHIFT 1 #define STAT_C_CHARGER_ERROR BIT(6) @@ -146,6 +150,7 @@ struct smb347_charger { unsigned int mains_current_limit; bool usb_hc_mode; bool usb_otg_enabled; + bool is_fully_charged; int en_gpio; struct dentry *dentry; const struct smb347_charger_platform_data *pdata; @@ -327,6 +332,9 @@ static int smb347_charging_set(struct smb347_charger *smb, bool enable) { int ret = 0; + if (enable && !smb->charging_enabled) + smb->is_fully_charged = false; + if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) { smb->charging_enabled = enable; @@ -490,6 +498,7 @@ static int smb347_set_voltage_limits(struct smb347_charger *smb) val = clamp_val(val, 3500000, 4500000) - 3500000; val /= 20000; + ret &= ~CFG_FLOAT_VOLTAGE_MASK; ret |= val; } @@ -679,7 +688,7 @@ static int smb347_set_writable(struct smb347_charger *smb, bool writable) return smb347_write(smb, CMD_A, ret); } -static int smb347_hw_init(struct smb347_charger *smb) +static int smb347_irq_set(struct smb347_charger *smb, bool enable) { int ret; @@ -688,98 +697,61 @@ static int smb347_hw_init(struct smb347_charger *smb) return ret; /* - * Program the platform specific configuration values to the device - * first. + * Enable/disable interrupts for: + * - under voltage + * - termination current reached + * - charger error */ - ret = smb347_set_charge_current(smb); - if (ret < 0) - goto fail; - - ret = smb347_set_current_limits(smb); - if (ret < 0) - goto fail; - - ret = smb347_set_voltage_limits(smb); - if (ret < 0) - goto fail; - - ret = smb347_set_temp_limits(smb); - if (ret < 0) - goto fail; - - /* If USB charging is disabled we put the USB in suspend mode */ - if (!smb->pdata->use_usb) { - ret = smb347_read(smb, CMD_A); + if (enable) { + ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV); if (ret < 0) goto fail; - ret |= CMD_A_SUSPEND_ENABLED; + ret = smb347_write(smb, CFG_STATUS_IRQ, + CFG_STATUS_IRQ_TERMINATION_OR_TAPER); + if (ret < 0) + goto fail; - ret = smb347_write(smb, CMD_A, ret); + ret = smb347_read(smb, CFG_PIN); if (ret < 0) goto fail; - } - ret = smb347_read(smb, CFG_OTHER); - if (ret < 0) - goto fail; + ret |= CFG_PIN_EN_CHARGER_ERROR; - /* - * If configured by platform data, we enable hardware Auto-OTG - * support for driving VBUS. Otherwise we disable it. - */ - ret &= ~CFG_OTHER_RID_MASK; - if (smb->pdata->use_usb_otg) - ret |= CFG_OTHER_RID_ENABLED_AUTO_OTG; + ret = smb347_write(smb, CFG_PIN, ret); + } else { + ret = smb347_write(smb, CFG_FAULT_IRQ, 0); + if (ret < 0) + goto fail; - ret = smb347_write(smb, CFG_OTHER, ret); - if (ret < 0) - goto fail; + ret = smb347_write(smb, CFG_STATUS_IRQ, 0); + if (ret < 0) + goto fail; - ret = smb347_read(smb, CFG_PIN); - if (ret < 0) - goto fail; + ret = smb347_read(smb, CFG_PIN); + if (ret < 0) + goto fail; - /* - * Make the charging functionality controllable by a write to the - * command register unless pin control is specified in the platform - * data. - */ - ret &= ~(CFG_PIN_EN_CTRL_MASK | CFG_PIN_USB_MODE_CTRL); + ret &= ~CFG_PIN_EN_CHARGER_ERROR; - switch (smb->pdata->enable_control) { - case SMB347_CHG_ENABLE_SW: - /* Do nothing, 0 means i2c control */ - break; - case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW: - ret |= CFG_PIN_EN_CTRL_ACTIVE_LOW; - break; - case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH: - ret |= CFG_PIN_EN_CTRL_ACTIVE_HIGH; - break; + ret = smb347_write(smb, CFG_PIN, ret); } - if (smb->pdata->usb_mode_pin_ctrl) - ret |= CFG_PIN_USB_MODE_CTRL; - - /* Disable Automatic Power Source Detection (APSD) interrupt. */ - ret &= ~CFG_PIN_EN_APSD_IRQ; - - ret = smb347_write(smb, CFG_PIN, ret); - if (ret < 0) - goto fail; - - ret = smb347_update_status(smb); - if (ret < 0) - goto fail; - - ret = smb347_update_online(smb); - fail: smb347_set_writable(smb, false); return ret; } +static inline int smb347_irq_enable(struct smb347_charger *smb) +{ + return smb347_irq_set(smb, true); +} + +static inline int smb347_irq_disable(struct smb347_charger *smb) +{ + return smb347_irq_set(smb, false); +} + static irqreturn_t smb347_interrupt(int irq, void *data) { struct smb347_charger *smb = data; @@ -816,14 +788,12 @@ static irqreturn_t smb347_interrupt(int irq, void *data) power_supply_changed(&smb->battery); ret = IRQ_HANDLED; - } - - /* - * If we reached the termination current the battery is charged. - * Disable charging to ACK the interrupt and update status. - */ - if (irqstat[2] & (IRQSTAT_C_TERMINATION_IRQ | - IRQSTAT_C_TERMINATION_STAT)) { + } else if (((stat_c & STAT_C_CHG_STATUS) || + (irqstat[2] & (IRQSTAT_C_TERMINATION_IRQ | + IRQSTAT_C_TERMINATION_STAT))) && + !smb->is_fully_charged) { + dev_info(&smb->client->dev, "charge terminated"); + smb->is_fully_charged = true; smb347_charging_disable(smb); power_supply_changed(&smb->battery); ret = IRQ_HANDLED; @@ -849,7 +819,38 @@ static irqreturn_t smb347_interrupt(int irq, void *data) return ret; } -static int smb347_irq_set(struct smb347_charger *smb, bool enable) +static int smb347_irq_init(struct smb347_charger *smb) +{ + const struct smb347_charger_platform_data *pdata = smb->pdata; + int ret, irq = gpio_to_irq(pdata->irq_gpio); + + ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name); + if (ret < 0) + goto fail; + + ret = request_threaded_irq(irq, NULL, smb347_interrupt, + pdata->disable_stat_interrupts ? + IRQF_TRIGGER_RISING | IRQF_ONESHOT : + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + smb->client->name, smb); + if (ret < 0) + goto fail_gpio; + + ret = enable_irq_wake(irq); + if (ret) + pr_err("%s: failed to enable wake on irq %d\n", __func__, irq); + + smb->client->irq = irq; + return 0; + +fail_gpio: + gpio_free(pdata->irq_gpio); +fail: + smb->client->irq = 0; + return ret; +} + +static int smb347_hw_init(struct smb347_charger *smb) { int ret; @@ -858,116 +859,134 @@ static int smb347_irq_set(struct smb347_charger *smb, bool enable) return ret; /* - * Enable/disable interrupts for: - * - under voltage - * - termination current reached - * - charger error + * Program the platform specific configuration values to the device + * first. */ - if (enable) { - ret = smb347_write(smb, CFG_FAULT_IRQ, CFG_FAULT_IRQ_DCIN_UV); - if (ret < 0) - goto fail; + ret = smb347_set_charge_current(smb); + if (ret < 0) + goto fail; - ret = smb347_write(smb, CFG_STATUS_IRQ, - CFG_STATUS_IRQ_TERMINATION_OR_TAPER); - if (ret < 0) - goto fail; + ret = smb347_set_current_limits(smb); + if (ret < 0) + goto fail; - ret = smb347_read(smb, CFG_PIN); - if (ret < 0) - goto fail; + ret = smb347_set_voltage_limits(smb); + if (ret < 0) + goto fail; - ret |= CFG_PIN_EN_CHARGER_ERROR; +// HACK for Manta pre-alpha 0.2, TH_BATTERY not connected properly +#if 0 // HACK + ret = smb347_set_temp_limits(smb); + if (ret < 0) + goto fail; +#endif // HACK - ret = smb347_write(smb, CFG_PIN, ret); - } else { - ret = smb347_write(smb, CFG_FAULT_IRQ, 0); + /* If USB charging is disabled we put the USB in suspend mode */ + if (!smb->pdata->use_usb) { + ret = smb347_read(smb, CMD_A); if (ret < 0) goto fail; - ret = smb347_write(smb, CFG_STATUS_IRQ, 0); - if (ret < 0) - goto fail; + ret |= CMD_A_SUSPEND_ENABLED; - ret = smb347_read(smb, CFG_PIN); + ret = smb347_write(smb, CMD_A, ret); if (ret < 0) goto fail; - - ret &= ~CFG_PIN_EN_CHARGER_ERROR; - - ret = smb347_write(smb, CFG_PIN, ret); } -fail: - smb347_set_writable(smb, false); - return ret; -} - -static inline int smb347_irq_enable(struct smb347_charger *smb) -{ - return smb347_irq_set(smb, true); -} - -static inline int smb347_irq_disable(struct smb347_charger *smb) -{ - return smb347_irq_set(smb, false); -} + ret = smb347_read(smb, CFG_OTHER); + if (ret < 0) + goto fail; -static int smb347_irq_init(struct smb347_charger *smb) -{ - const struct smb347_charger_platform_data *pdata = smb->pdata; - int ret, irq = gpio_to_irq(pdata->irq_gpio); + /* + * If configured by platform data, we enable hardware Auto-OTG + * support for driving VBUS. Otherwise we disable it. + */ + ret &= ~CFG_OTHER_RID_MASK; + if (smb->pdata->use_usb_otg) + ret |= CFG_OTHER_RID_ENABLED_AUTO_OTG; - ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, smb->client->name); + ret = smb347_write(smb, CFG_OTHER, ret); if (ret < 0) goto fail; - ret = request_threaded_irq(irq, NULL, smb347_interrupt, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - smb->client->name, smb); - if (ret < 0) - goto fail_gpio; + /* If configured by platform data, disable AUTOMATIC RECHARGE */ + if (smb->pdata->disable_automatic_recharge) { + ret = smb347_read(smb, CFG_CHARGE_CONTROL); + if (ret < 0) + goto fail; - ret = enable_irq_wake(irq); - if (ret) - pr_err("%s: failed to enable wake on irq %d\n", __func__, irq); + ret |= CFG_AUTOMATIC_RECHARGE_DISABLE; - ret = smb347_set_writable(smb, true); + ret = smb347_write(smb, CFG_CHARGE_CONTROL, ret); + if (ret < 0) + goto fail; + } + + ret = smb347_read(smb, CFG_PIN); if (ret < 0) - goto fail_irq; + goto fail; /* - * Configure the STAT output to be suitable for interrupts: disable - * all other output (except interrupts) and make it active low. + * Make the charging functionality controllable by a write to the + * command register unless pin control is specified in the platform + * data. */ - ret = smb347_read(smb, CFG_STAT); - if (ret < 0) - goto fail_readonly; + ret &= ~(CFG_PIN_EN_CTRL_MASK | CFG_PIN_USB_MODE_CTRL); - ret &= ~CFG_STAT_ACTIVE_HIGH; - ret |= CFG_STAT_DISABLED; + switch (smb->pdata->enable_control) { + case SMB347_CHG_ENABLE_SW: + /* Do nothing, 0 means i2c control */ + break; + case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW: + ret |= CFG_PIN_EN_CTRL_ACTIVE_LOW; + break; + case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH: + ret |= CFG_PIN_EN_CTRL_ACTIVE_HIGH; + break; + } + + if (smb->pdata->usb_mode_pin_ctrl) + ret |= CFG_PIN_USB_MODE_CTRL; + + /* Disable Automatic Power Source Detection (APSD) interrupt. */ + ret &= ~CFG_PIN_EN_APSD_IRQ; - ret = smb347_write(smb, CFG_STAT, ret); + ret = smb347_write(smb, CFG_PIN, ret); if (ret < 0) - goto fail_readonly; + goto fail; - ret = smb347_irq_enable(smb); + ret = smb347_update_status(smb); if (ret < 0) - goto fail_readonly; + goto fail; - smb347_set_writable(smb, false); - smb->client->irq = irq; - return 0; + ret = smb347_update_online(smb); + + if ((smb->pdata->irq_gpio >= 0) && + !smb->pdata->disable_stat_interrupts) { + /* + * Configure the STAT output to be suitable for interrupts: + * disable all other output (except interrupts) and make it + * active low. + */ + ret = smb347_read(smb, CFG_STAT); + if (ret < 0) + goto fail; + + ret &= ~CFG_STAT_ACTIVE_HIGH; + ret |= CFG_STAT_DISABLED; + + ret = smb347_write(smb, CFG_STAT, ret); + if (ret < 0) + goto fail; + + ret = smb347_irq_enable(smb); + if (ret < 0) + goto fail; + } -fail_readonly: - smb347_set_writable(smb, false); -fail_irq: - disable_irq_wake(irq); - free_irq(irq, smb); -fail_gpio: - gpio_free(pdata->irq_gpio); fail: - smb->client->irq = 0; + smb347_set_writable(smb, false); return ret; } @@ -1176,13 +1195,16 @@ static int smb347_battery_get_property(struct power_supply *psy, switch (prop) { case POWER_SUPPLY_PROP_STATUS: if (!smb347_is_online(smb)) { + smb->is_fully_charged = false; val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; } if (smb347_charging_status(smb)) val->intval = POWER_SUPPLY_STATUS_CHARGING; else - val->intval = POWER_SUPPLY_STATUS_FULL; + val->intval = smb->is_fully_charged ? + POWER_SUPPLY_STATUS_FULL : + POWER_SUPPLY_STATUS_NOT_CHARGING; break; case POWER_SUPPLY_PROP_CHARGE_TYPE: diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index a244265c1143..921db5a193d6 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -482,7 +482,7 @@ static int acc_unregister_hid(struct acc_dev *dev, int id) return 0; } -static int create_bulk_endpoints(struct acc_dev *dev, +static int __init create_bulk_endpoints(struct acc_dev *dev, struct usb_endpoint_descriptor *in_desc, struct usb_endpoint_descriptor *out_desc) { diff --git a/drivers/usb/gadget/f_audio_source.c b/drivers/usb/gadget/f_audio_source.c index 3ba7d7569b12..f6f7725de22d 100644 --- a/drivers/usb/gadget/f_audio_source.c +++ b/drivers/usb/gadget/f_audio_source.c @@ -26,7 +26,7 @@ #define BYTES_PER_FRAME 4 #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000) -#define IN_EP_MAX_PACKET_SIZE 256 +#define IN_EP_MAX_PACKET_SIZE 384 /* Number of requests to allocate */ #define IN_EP_REQ_COUNT 4 @@ -414,7 +414,7 @@ static void audio_data_complete(struct usb_ep *ep, struct usb_request *req) audio_req_put(audio, req); - if (!audio->buffer_start) + if (!audio->buffer_start || req->status) return; audio->period_offset += req->actual; @@ -527,9 +527,14 @@ static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct audio_dev *audio = func_to_audio(f); struct usb_composite_dev *cdev = f->config->cdev; + int ret; pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); - config_ep_by_speed(cdev->gadget, f, audio->in_ep); + + ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep); + if (ret) + return ret; + usb_ep_enable(audio->in_ep); return 0; } @@ -624,7 +629,10 @@ audio_unbind(struct usb_configuration *c, struct usb_function *f) audio_request_free(req, audio->in_ep); snd_card_free_when_closed(audio->card); - kfree(audio); + audio->card = NULL; + audio->pcm = NULL; + audio->substream = NULL; + audio->in_ep = NULL; } static void audio_pcm_playback_start(struct audio_dev *audio) @@ -740,6 +748,19 @@ static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream, return ret; } +static struct audio_dev _audio_dev = { + .func = { + .name = "audio_source", + .bind = audio_bind, + .unbind = audio_unbind, + .set_alt = audio_set_alt, + .setup = audio_setup, + .disable = audio_disable, + }, + .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock), + .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs), +}; + static struct snd_pcm_ops audio_playback_ops = { .open = audio_pcm_open, .close = audio_pcm_close, @@ -762,27 +783,13 @@ int audio_source_bind_config(struct usb_configuration *c, 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); + audio = &_audio_dev; err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); if (err) - goto snd_card_fail; + return err; snd_card_set_dev(card, &c->cdev->gadget->dev); @@ -821,7 +828,5 @@ add_fail: register_fail: pcm_fail: snd_card_free(audio->card); -snd_card_fail: - kfree(audio); return err; } diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index e5f74416d4b7..d409352fe51f 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -18,6 +18,8 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/platform_data/ds2482.h> #include <asm/delay.h> #include "../w1.h" @@ -84,7 +86,8 @@ static const u8 ds2482_chan_rd[8] = static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id); static int ds2482_remove(struct i2c_client *client); - +static int ds2482_suspend(struct device *dev); +static int ds2482_resume(struct device *dev); /** * Driver data (common to all clients) @@ -94,10 +97,16 @@ static const struct i2c_device_id ds2482_id[] = { { } }; +static const struct dev_pm_ops ds2482_pm_ops = { + .suspend = ds2482_suspend, + .resume = ds2482_resume, +}; + static struct i2c_driver ds2482_driver = { .driver = { .owner = THIS_MODULE, .name = "ds2482", + .pm = &ds2482_pm_ops, }, .probe = ds2482_probe, .remove = ds2482_remove, @@ -119,6 +128,7 @@ struct ds2482_w1_chan { struct ds2482_data { struct i2c_client *client; struct mutex access_lock; + int slpz_gpio; /* 1-wire interface(s) */ int w1_count; /* 1 or 8 */ @@ -407,11 +417,31 @@ static u8 ds2482_w1_reset_bus(void *data) return retval; } +static int ds2482_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds2482_data *data = i2c_get_clientdata(client); + + if (data->slpz_gpio >= 0) + gpio_set_value(data->slpz_gpio, 0); + return 0; +} + +static int ds2482_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds2482_data *data = i2c_get_clientdata(client); + + if (data->slpz_gpio >= 0) + gpio_set_value(data->slpz_gpio, 1); + return 0; +} static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ds2482_data *data; + struct ds2482_platform_data *pdata; int err = -ENODEV; int temp1; int idx; @@ -476,6 +506,16 @@ static int ds2482_probe(struct i2c_client *client, } } + pdata = client->dev.platform_data; + data->slpz_gpio = pdata ? pdata->slpz_gpio : -1; + + if (data->slpz_gpio >= 0) { + err = gpio_request_one(data->slpz_gpio, GPIOF_OUT_INIT_HIGH, + "ds2482.slpz"); + if (err < 0) + goto exit_w1_remove; + } + return 0; exit_w1_remove: @@ -500,6 +540,11 @@ static int ds2482_remove(struct i2c_client *client) w1_remove_master_device(&data->w1_ch[idx].w1_bm); } + if (data->slpz_gpio >= 0) { + gpio_set_value(data->slpz_gpio, 0); + gpio_free(data->slpz_gpio); + } + /* Free the memory */ kfree(data); return 0; diff --git a/include/linux/Kbuild b/include/linux/Kbuild index f2f73f9b986f..a615f6fbd737 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -375,6 +375,7 @@ header-y += tty.h header-y += types.h header-y += udf_fs_i.h header-y += udp.h +header-y += uhid.h header-y += uinput.h header-y += uio.h header-y += ultrasound.h diff --git a/include/linux/freezer.h b/include/linux/freezer.h index d09af4b67cf1..a628084eea3c 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -41,6 +41,17 @@ extern int freeze_kernel_threads(void); extern void thaw_processes(void); extern void thaw_kernel_threads(void); +/* + * HACK: prevent sleeping while atomic warnings due to ARM signal handling + * disabling irqs + */ +static inline bool try_to_freeze_nowarn(void) +{ + if (likely(!freezing(current))) + return false; + return __refrigerator(false); +} + static inline bool try_to_freeze(void) { might_sleep(); diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 0714b24c0e45..22ccf9dee177 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -49,8 +49,6 @@ extern int tsk_fork_get_node(struct task_struct *tsk); * can be queued and flushed using queue/flush_kthread_work() * respectively. Queued kthread_works are processed by a kthread * running kthread_worker_fn(). - * - * A kthread_work can't be freed while it is executing. */ struct kthread_work; typedef void (*kthread_work_func_t)(struct kthread_work *work); @@ -59,15 +57,14 @@ struct kthread_worker { spinlock_t lock; struct list_head work_list; struct task_struct *task; + struct kthread_work *current_work; }; struct kthread_work { struct list_head node; kthread_work_func_t func; wait_queue_head_t done; - atomic_t flushing; - int queue_seq; - int done_seq; + struct kthread_worker *worker; }; #define KTHREAD_WORKER_INIT(worker) { \ @@ -79,7 +76,6 @@ struct kthread_work { .node = LIST_HEAD_INIT((work).node), \ .func = (fn), \ .done = __WAIT_QUEUE_HEAD_INITIALIZER((work).done), \ - .flushing = ATOMIC_INIT(0), \ } #define DEFINE_KTHREAD_WORKER(worker) \ diff --git a/include/linux/platform_data/ds2482.h b/include/linux/platform_data/ds2482.h new file mode 100644 index 000000000000..5a6879e2a09a --- /dev/null +++ b/include/linux/platform_data/ds2482.h @@ -0,0 +1,21 @@ +/* + * 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 __PLATFORM_DATA_DS2482__ +#define __PLATFORM_DATA_DS2482__ + +struct ds2482_platform_data { + int slpz_gpio; +}; + +#endif /* __PLATFORM_DATA_DS2482__ */ diff --git a/include/linux/power/smb347-charger.h b/include/linux/power/smb347-charger.h index f944dfcb23f2..e9aab94437fd 100644 --- a/include/linux/power/smb347-charger.h +++ b/include/linux/power/smb347-charger.h @@ -110,7 +110,9 @@ struct smb347_charger_platform_data { bool use_mains; bool use_usb; bool use_usb_otg; + bool disable_automatic_recharge; int irq_gpio; + bool disable_stat_interrupts; enum smb347_chg_enable enable_control; bool usb_mode_pin_ctrl; char **supplied_to; diff --git a/include/linux/sync.h b/include/linux/sync.h index 15863a6ebe51..75ed5f1b75da 100644 --- a/include/linux/sync.h +++ b/include/linux/sync.h @@ -329,8 +329,8 @@ int sync_fence_cancel_async(struct sync_fence *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 + * Wait for @fence to be signaled or have an error. Waits indefinitely + * if @timeout < 0 */ int sync_fence_wait(struct sync_fence *fence, long timeout); @@ -389,9 +389,9 @@ struct sync_fence_info_data { /** * DOC: SYNC_IOC_WAIT - wait for a fence to signal * - * pass timeout in milliseconds. + * pass timeout in milliseconds. Waits indefinitely timeout < 0. */ -#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __u32) +#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32) /** * DOC: SYNC_IOC_MERGE - merge two fences diff --git a/include/linux/uhid.h b/include/linux/uhid.h new file mode 100644 index 000000000000..9c6974f16966 --- /dev/null +++ b/include/linux/uhid.h @@ -0,0 +1,104 @@ +#ifndef __UHID_H_ +#define __UHID_H_ + +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 David Herrmann + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* + * Public header for user-space communication. We try to keep every structure + * aligned but to be safe we also use __attribute__((__packed__)). Therefore, + * the communication should be ABI compatible even between architectures. + */ + +#include <linux/input.h> +#include <linux/types.h> + +enum uhid_event_type { + UHID_CREATE, + UHID_DESTROY, + UHID_START, + UHID_STOP, + UHID_OPEN, + UHID_CLOSE, + UHID_OUTPUT, + UHID_OUTPUT_EV, + UHID_INPUT, + UHID_FEATURE, + UHID_FEATURE_ANSWER, +}; + +struct uhid_create_req { + __u8 name[128]; + __u8 phys[64]; + __u8 uniq[64]; + __u8 __user *rd_data; + __u16 rd_size; + + __u16 bus; + __u32 vendor; + __u32 product; + __u32 version; + __u32 country; +} __attribute__((__packed__)); + +#define UHID_DATA_MAX 4096 + +enum uhid_report_type { + UHID_FEATURE_REPORT, + UHID_OUTPUT_REPORT, + UHID_INPUT_REPORT, +}; + +struct uhid_input_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; +} __attribute__((__packed__)); + +struct uhid_output_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; + __u8 rtype; +} __attribute__((__packed__)); + +struct uhid_output_ev_req { + __u16 type; + __u16 code; + __s32 value; +} __attribute__((__packed__)); + +struct uhid_feature_req { + __u32 id; + __u8 rnum; + __u8 rtype; +} __attribute__((__packed__)); + +struct uhid_feature_answer_req { + __u32 id; + __u16 err; + __u16 size; + __u8 data[UHID_DATA_MAX]; +}; + +struct uhid_event { + __u32 type; + + union { + struct uhid_create_req create; + struct uhid_input_req input; + struct uhid_output_req output; + struct uhid_output_ev_req output_ev; + struct uhid_feature_req feature; + struct uhid_feature_answer_req feature_answer; + } u; +} __attribute__((__packed__)); + +#endif /* __UHID_H_ */ diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8da3c2409060..055242e69dc3 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -432,6 +432,11 @@ enum snd_soc_dapm_type { snd_soc_dapm_dai, /* link to DAI structure */ }; +enum snd_soc_dapm_subclass { + SND_SOC_DAPM_CLASS_INIT = 0, + SND_SOC_DAPM_CLASS_PCM = 1, +}; + /* * DAPM audio route definition. * diff --git a/include/sound/soc.h b/include/sound/soc.h index 2ebf7877c148..66fd9bc9789d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -288,6 +288,11 @@ enum snd_soc_pcm_subclass { SND_SOC_PCM_CLASS_BE = 1, }; +enum snd_soc_card_subclass { + SND_SOC_CARD_CLASS_INIT = 0, + SND_SOC_CARD_CLASS_PCM = 1, +}; + int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source, unsigned int freq, int dir); int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, @@ -800,6 +805,7 @@ struct snd_soc_card { struct list_head list; struct mutex mutex; + struct mutex dapm_mutex; bool instantiated; diff --git a/kernel/kthread.c b/kernel/kthread.c index b68236b45ba9..757d8ddc8469 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -371,16 +371,12 @@ repeat: struct kthread_work, node); list_del_init(&work->node); } + worker->current_work = work; spin_unlock_irq(&worker->lock); if (work) { __set_current_state(TASK_RUNNING); work->func(work); - smp_wmb(); /* wmb worker-b0 paired with flush-b1 */ - work->done_seq = work->queue_seq; - smp_mb(); /* mb worker-b1 paired with flush-b0 */ - if (atomic_read(&work->flushing)) - wake_up_all(&work->done); } else if (!freezing(current)) schedule(); @@ -389,6 +385,19 @@ repeat: } EXPORT_SYMBOL_GPL(kthread_worker_fn); +/* insert @work before @pos in @worker */ +static void insert_kthread_work(struct kthread_worker *worker, + struct kthread_work *work, + struct list_head *pos) +{ + lockdep_assert_held(&worker->lock); + + list_add_tail(&work->node, pos); + work->worker = worker; + if (likely(worker->task)) + wake_up_process(worker->task); +} + /** * queue_kthread_work - queue a kthread_work * @worker: target kthread_worker @@ -406,10 +415,7 @@ bool queue_kthread_work(struct kthread_worker *worker, spin_lock_irqsave(&worker->lock, flags); if (list_empty(&work->node)) { - list_add_tail(&work->node, &worker->work_list); - work->queue_seq++; - if (likely(worker->task)) - wake_up_process(worker->task); + insert_kthread_work(worker, work, &worker->work_list); ret = true; } spin_unlock_irqrestore(&worker->lock, flags); @@ -417,6 +423,18 @@ bool queue_kthread_work(struct kthread_worker *worker, } EXPORT_SYMBOL_GPL(queue_kthread_work); +struct kthread_flush_work { + struct kthread_work work; + struct completion done; +}; + +static void kthread_flush_work_fn(struct kthread_work *work) +{ + struct kthread_flush_work *fwork = + container_of(work, struct kthread_flush_work, work); + complete(&fwork->done); +} + /** * flush_kthread_work - flush a kthread_work * @work: work to flush @@ -425,39 +443,37 @@ EXPORT_SYMBOL_GPL(queue_kthread_work); */ void flush_kthread_work(struct kthread_work *work) { - int seq = work->queue_seq; - - atomic_inc(&work->flushing); + struct kthread_flush_work fwork = { + KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn), + COMPLETION_INITIALIZER_ONSTACK(fwork.done), + }; + struct kthread_worker *worker; + bool noop = false; - /* - * mb flush-b0 paired with worker-b1, to make sure either - * worker sees the above increment or we see done_seq update. - */ - smp_mb__after_atomic_inc(); +retry: + worker = work->worker; + if (!worker) + return; - /* A - B <= 0 tests whether B is in front of A regardless of overflow */ - wait_event(work->done, seq - work->done_seq <= 0); - atomic_dec(&work->flushing); + spin_lock_irq(&worker->lock); + if (work->worker != worker) { + spin_unlock_irq(&worker->lock); + goto retry; + } - /* - * rmb flush-b1 paired with worker-b0, to make sure our caller - * sees every change made by work->func(). - */ - smp_mb__after_atomic_dec(); -} -EXPORT_SYMBOL_GPL(flush_kthread_work); + if (!list_empty(&work->node)) + insert_kthread_work(worker, &fwork.work, work->node.next); + else if (worker->current_work == work) + insert_kthread_work(worker, &fwork.work, worker->work_list.next); + else + noop = true; -struct kthread_flush_work { - struct kthread_work work; - struct completion done; -}; + spin_unlock_irq(&worker->lock); -static void kthread_flush_work_fn(struct kthread_work *work) -{ - struct kthread_flush_work *fwork = - container_of(work, struct kthread_flush_work, work); - complete(&fwork->done); + if (!noop) + wait_for_completion(&fwork.done); } +EXPORT_SYMBOL_GPL(flush_kthread_work); /** * flush_kthread_worker - flush all current works on a kthread_worker diff --git a/kernel/signal.c b/kernel/signal.c index 17afcaf582d0..1dd48f7e7d6f 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2209,7 +2209,7 @@ relock: * Now that we woke up, it's crucial if we're supposed to be * frozen that we freeze now before running anything substantial. */ - try_to_freeze(); + try_to_freeze_nowarn(); spin_lock_irq(&sighand->siglock); /* diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index f6d4cfc05f3c..ea716b31e2af 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2588,8 +2588,9 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) } else { tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); - - if (!can_read_other_uid_stats(stat_uid)) { + /* Detailed tags are not available to everybody */ + if (get_atag_from_tag(tag) + && !can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " "from pid=%u tgid=%u uid=%u\n", diff --git a/samples/uhid/Makefile b/samples/uhid/Makefile new file mode 100644 index 000000000000..c95a696560a7 --- /dev/null +++ b/samples/uhid/Makefile @@ -0,0 +1,10 @@ +# kbuild trick to avoid linker error. Can be omitted if a module is built. +obj- := dummy.o + +# List of programs to build +hostprogs-y := uhid-example + +# Tell kbuild to always build the programs +always := $(hostprogs-y) + +HOSTCFLAGS_uhid-example.o += -I$(objtree)/usr/include diff --git a/samples/uhid/uhid-example.c b/samples/uhid/uhid-example.c new file mode 100644 index 000000000000..03ce3c059a5e --- /dev/null +++ b/samples/uhid/uhid-example.c @@ -0,0 +1,381 @@ +/* + * UHID Example + * + * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com> + * + * The code may be used by anyone for any purpose, + * and can serve as a starting point for developing + * applications using uhid. + */ + +/* UHID Example + * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this + * program as root and then use the following keys to control the mouse: + * q: Quit the application + * 1: Toggle left button (down, up, ...) + * 2: Toggle right button + * 3: Toggle middle button + * a: Move mouse left + * d: Move mouse right + * w: Move mouse up + * s: Move mouse down + * r: Move wheel up + * f: Move wheel down + * + * If uhid is not available as /dev/uhid, then you can pass a different path as + * first argument. + * If <linux/uhid.h> is not installed in /usr, then compile this with: + * gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c + * And ignore the warning about kernel headers. However, it is recommended to + * use the installed uhid.h if available. + */ + +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <linux/uhid.h> + +/* HID Report Desciptor + * We emulate a basic 3 button mouse with wheel. This is the report-descriptor + * as the kernel will parse it: + * + * INPUT[INPUT] + * Field(0) + * Physical(GenericDesktop.Pointer) + * Application(GenericDesktop.Mouse) + * Usage(3) + * Button.0001 + * Button.0002 + * Button.0003 + * Logical Minimum(0) + * Logical Maximum(1) + * Report Size(1) + * Report Count(3) + * Report Offset(0) + * Flags( Variable Absolute ) + * Field(1) + * Physical(GenericDesktop.Pointer) + * Application(GenericDesktop.Mouse) + * Usage(3) + * GenericDesktop.X + * GenericDesktop.Y + * GenericDesktop.Wheel + * Logical Minimum(-128) + * Logical Maximum(127) + * Report Size(8) + * Report Count(3) + * Report Offset(8) + * Flags( Variable Relative ) + * + * This is the mapping that we expect: + * Button.0001 ---> Key.LeftBtn + * Button.0002 ---> Key.RightBtn + * Button.0003 ---> Key.MiddleBtn + * GenericDesktop.X ---> Relative.X + * GenericDesktop.Y ---> Relative.Y + * GenericDesktop.Wheel ---> Relative.Wheel + * + * This information can be verified by reading /sys/kernel/debug/hid/<dev>/rdesc + * This file should print the same information as showed above. + */ + +static unsigned char rdesc[] = { + 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, + 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, + 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, + 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, + 0x15, 0x80, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, + 0x81, 0x06, 0xc0, 0xc0, +}; + +static int uhid_write(int fd, const struct uhid_event *ev) +{ + ssize_t ret; + + ret = write(fd, ev, sizeof(*ev)); + if (ret < 0) { + fprintf(stderr, "Cannot write to uhid: %m\n"); + return -errno; + } else if (ret != sizeof(*ev)) { + fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n", + ret, sizeof(ev)); + return -EFAULT; + } else { + return 0; + } +} + +static int create(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + strcpy((char*)ev.u.create.name, "test-uhid-device"); + ev.u.create.rd_data = rdesc; + ev.u.create.rd_size = sizeof(rdesc); + ev.u.create.bus = BUS_USB; + ev.u.create.vendor = 0x15d9; + ev.u.create.product = 0x0a37; + ev.u.create.version = 0; + ev.u.create.country = 0; + + return uhid_write(fd, &ev); +} + +static void destroy(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + + uhid_write(fd, &ev); +} + +static int event(int fd) +{ + struct uhid_event ev; + ssize_t ret; + + memset(&ev, 0, sizeof(ev)); + ret = read(fd, &ev, sizeof(ev)); + if (ret == 0) { + fprintf(stderr, "Read HUP on uhid-cdev\n"); + return -EFAULT; + } else if (ret < 0) { + fprintf(stderr, "Cannot read uhid-cdev: %m\n"); + return -errno; + } else if (ret != sizeof(ev)) { + fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n", + ret, sizeof(ev)); + return -EFAULT; + } + + switch (ev.type) { + case UHID_START: + fprintf(stderr, "UHID_START from uhid-dev\n"); + break; + case UHID_STOP: + fprintf(stderr, "UHID_STOP from uhid-dev\n"); + break; + case UHID_OPEN: + fprintf(stderr, "UHID_OPEN from uhid-dev\n"); + break; + case UHID_CLOSE: + fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); + break; + case UHID_OUTPUT: + fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); + break; + case UHID_OUTPUT_EV: + fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); + break; + default: + fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); + } + + return 0; +} + +static bool btn1_down; +static bool btn2_down; +static bool btn3_down; +static signed char abs_hor; +static signed char abs_ver; +static signed char wheel; + +static int send_event(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT; + ev.u.input.size = 4; + + if (btn1_down) + ev.u.input.data[0] |= 0x1; + if (btn2_down) + ev.u.input.data[0] |= 0x2; + if (btn3_down) + ev.u.input.data[0] |= 0x4; + + ev.u.input.data[1] = abs_hor; + ev.u.input.data[2] = abs_ver; + ev.u.input.data[3] = wheel; + + return uhid_write(fd, &ev); +} + +static int keyboard(int fd) +{ + char buf[128]; + ssize_t ret, i; + + ret = read(STDIN_FILENO, buf, sizeof(buf)); + if (ret == 0) { + fprintf(stderr, "Read HUP on stdin\n"); + return -EFAULT; + } else if (ret < 0) { + fprintf(stderr, "Cannot read stdin: %m\n"); + return -errno; + } + + for (i = 0; i < ret; ++i) { + switch (buf[i]) { + case '1': + btn1_down = !btn1_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case '2': + btn2_down = !btn2_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case '3': + btn3_down = !btn3_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case 'a': + abs_hor = -20; + ret = send_event(fd); + abs_hor = 0; + if (ret) + return ret; + break; + case 'd': + abs_hor = 20; + ret = send_event(fd); + abs_hor = 0; + if (ret) + return ret; + break; + case 'w': + abs_ver = -20; + ret = send_event(fd); + abs_ver = 0; + if (ret) + return ret; + break; + case 's': + abs_ver = 20; + ret = send_event(fd); + abs_ver = 0; + if (ret) + return ret; + break; + case 'r': + wheel = 1; + ret = send_event(fd); + wheel = 0; + if (ret) + return ret; + break; + case 'f': + wheel = -1; + ret = send_event(fd); + wheel = 0; + if (ret) + return ret; + break; + case 'q': + return -ECANCELED; + default: + fprintf(stderr, "Invalid input: %c\n", buf[i]); + } + } + + return 0; +} + +int main(int argc, char **argv) +{ + int fd; + const char *path = "/dev/uhid"; + struct pollfd pfds[2]; + int ret; + struct termios state; + + ret = tcgetattr(STDIN_FILENO, &state); + if (ret) { + fprintf(stderr, "Cannot get tty state\n"); + } else { + state.c_lflag &= ~ICANON; + state.c_cc[VMIN] = 1; + ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); + if (ret) + fprintf(stderr, "Cannot set tty state\n"); + } + + if (argc >= 2) { + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); + return EXIT_SUCCESS; + } else { + path = argv[1]; + } + } + + fprintf(stderr, "Open uhid-cdev %s\n", path); + fd = open(path, O_RDWR | O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); + return EXIT_FAILURE; + } + + fprintf(stderr, "Create uhid device\n"); + ret = create(fd); + if (ret) { + close(fd); + return EXIT_FAILURE; + } + + pfds[0].fd = STDIN_FILENO; + pfds[0].events = POLLIN; + pfds[1].fd = fd; + pfds[1].events = POLLIN; + + fprintf(stderr, "Press 'q' to quit...\n"); + while (1) { + ret = poll(pfds, 2, -1); + if (ret < 0) { + fprintf(stderr, "Cannot poll for fds: %m\n"); + break; + } + if (pfds[0].revents & POLLHUP) { + fprintf(stderr, "Received HUP on stdin\n"); + break; + } + if (pfds[1].revents & POLLHUP) { + fprintf(stderr, "Received HUP on uhid-cdev\n"); + break; + } + + if (pfds[0].revents & POLLIN) { + ret = keyboard(fd); + if (ret) + break; + } + if (pfds[1].revents & POLLIN) { + ret = event(fd); + if (ret) + break; + } + } + + fprintf(stderr, "Destroy uhid device\n"); + destroy(fd); + return EXIT_SUCCESS; +} diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c88d9741b9e7..18711a509882 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1412,7 +1412,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) struct snd_soc_dai_link *dai_link; int ret, i, order; - mutex_lock(&card->mutex); + mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); if (card->instantiated) { mutex_unlock(&card->mutex); @@ -3123,6 +3123,7 @@ int snd_soc_register_card(struct snd_soc_card *card) INIT_LIST_HEAD(&card->dapm_dirty); card->instantiated = 0; mutex_init(&card->mutex); + mutex_init(&card->dapm_mutex); mutex_lock(&client_mutex); list_add(&card->list, &card_list); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 367c50607920..f9aa9b50f82b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1947,6 +1947,8 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, */ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) { + int ret; + /* * Suppress early reports (eg, jacks syncing their state) to avoid * silly DAPM runs during card startup. @@ -1954,7 +1956,10 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) if (!dapm->card || !dapm->card->instantiated) return 0; - return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&dapm->card->dapm_mutex); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); @@ -2118,19 +2123,21 @@ err: int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num) { - int i, ret; + int i, ret = 0; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { ret = snd_soc_dapm_add_route(dapm, route); if (ret < 0) { dev_err(dapm->dev, "Failed to add route %s->%s\n", route->source, route->sink); - return ret; + break; } route++; } + mutex_unlock(&dapm->card->dapm_mutex); - return 0; + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); @@ -2201,12 +2208,14 @@ int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, int i, err; int ret = 0; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { err = snd_soc_dapm_weak_route(dapm, route); if (err) ret = err; route++; } + mutex_unlock(&dapm->card->dapm_mutex); return ret; } @@ -2225,6 +2234,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) struct snd_soc_dapm_widget *w; unsigned int val; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + list_for_each_entry(w, &dapm->card->widgets, list) { if (w->new) @@ -2234,8 +2245,10 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) w->kcontrols = kzalloc(w->num_kcontrols * sizeof(struct snd_kcontrol *), GFP_KERNEL); - if (!w->kcontrols) + if (!w->kcontrols) { + mutex_unlock(&dapm->card->dapm_mutex); return -ENOMEM; + } } switch(w->id) { @@ -2275,6 +2288,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) } dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&dapm->card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); @@ -2334,6 +2348,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2362,7 +2377,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, connect = invert ? 1 : 0; #endif - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = snd_soc_test_bits(widget->codec, reg, mask, val); if (change) { @@ -2384,7 +2399,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); @@ -2433,6 +2448,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask, bitmask; @@ -2453,7 +2469,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mask |= (bitmask - 1) << e->shift_r; } - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { @@ -2475,7 +2491,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); @@ -2512,6 +2528,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; @@ -2521,7 +2538,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL; - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = widget->value != ucontrol->value.enumerated.item[0]; if (change) { @@ -2534,7 +2551,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); @@ -2599,6 +2616,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; + struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask; @@ -2617,7 +2635,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - mutex_lock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { @@ -2639,7 +2657,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, } } - mutex_unlock(&codec->mutex); + mutex_unlock(&card->dapm_mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); @@ -2676,12 +2694,12 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); const char *pin = (const char *)kcontrol->private_value; - mutex_lock(&card->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); ucontrol->value.integer.value[0] = snd_soc_dapm_get_pin_status(&card->dapm, pin); - mutex_unlock(&card->mutex); + mutex_unlock(&card->dapm_mutex); return 0; } @@ -2699,17 +2717,16 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); const char *pin = (const char *)kcontrol->private_value; - mutex_lock(&card->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); if (ucontrol->value.integer.value[0]) snd_soc_dapm_enable_pin(&card->dapm, pin); else snd_soc_dapm_disable_pin(&card->dapm, pin); - snd_soc_dapm_sync(&card->dapm); - - mutex_unlock(&card->mutex); + mutex_unlock(&card->dapm_mutex); + snd_soc_dapm_sync(&card->dapm); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); @@ -2826,18 +2843,22 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w; int i; + int ret = 0; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); for (i = 0; i < num; i++) { w = snd_soc_dapm_new_control(dapm, widget); if (!w) { dev_err(dapm->dev, "ASoC: Failed to create DAPM control %s\n", widget->name); - return -ENOMEM; + ret = -ENOMEM; + break; } widget++; } - return 0; + mutex_unlock(&dapm->card->dapm_mutex); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); @@ -2991,11 +3012,11 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, struct snd_soc_dai *dai, int event) { - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = rtd->card; - mutex_lock(&codec->mutex); - soc_dapm_stream_event(&codec->dapm, stream, dai, event); - mutex_unlock(&codec->mutex); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_PCM); + soc_dapm_stream_event(&card->dapm, stream, dai, event); + mutex_unlock(&card->dapm_mutex); return 0; } |