summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimone Willett <swillett@nvidia.com>2012-09-18 14:35:54 -0700
committerSimone Willett <swillett@nvidia.com>2012-09-18 14:35:54 -0700
commit439e9117e1626d54fa9a58826a066450ce0c5f45 (patch)
treec67c8532aed846d3855272289e6f9ede3e3c445d
parente4a5c87088b7beb773b7e5d3dc27e97d70068f1a (diff)
parentba19f9808d65c5c827878dbb2c71ff4cc9fc0f34 (diff)
Merge remote-tracking branch 'origin/dev/from-android-3.4' into promotion_build
-rw-r--r--Documentation/hid/uhid.txt169
-rw-r--r--MAINTAINERS7
-rw-r--r--arch/arm/kernel/process.c9
-rw-r--r--arch/arm/kernel/signal.c2
-rw-r--r--drivers/base/power/wakeup.c17
-rw-r--r--drivers/base/sync.c51
-rw-r--r--drivers/gpu/ion/ion.c93
-rw-r--r--drivers/gpu/ion/ion_priv.h17
-rw-r--r--drivers/gpu/ion/ion_system_heap.c6
-rw-r--r--drivers/hid/Kconfig21
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/uhid.c572
-rw-r--r--drivers/net/wireless/bcmdhd/Makefile2
-rw-r--r--drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c6
-rw-r--r--drivers/net/wireless/bcmdhd/dhd.h22
-rw-r--r--drivers/net/wireless/bcmdhd/dhd_cdc.c37
-rw-r--r--drivers/net/wireless/bcmdhd/dhd_cfg80211.c10
-rw-r--r--drivers/net/wireless/bcmdhd/dhd_common.c12
-rw-r--r--drivers/net/wireless/bcmdhd/dhd_linux.c86
-rw-r--r--drivers/net/wireless/bcmdhd/dhd_sdio.c13
-rw-r--r--drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h4
-rw-r--r--drivers/net/wireless/bcmdhd/include/bcmutils.h8
-rw-r--r--drivers/net/wireless/bcmdhd/include/dhdioctl.h3
-rw-r--r--drivers/net/wireless/bcmdhd/include/epivers.h16
-rw-r--r--drivers/net/wireless/bcmdhd/include/linuxver.h15
-rw-r--r--drivers/net/wireless/bcmdhd/include/wlioctl.h5
-rw-r--r--drivers/net/wireless/bcmdhd/linux_osl.c12
-rw-r--r--drivers/net/wireless/bcmdhd/wl_android.c12
-rw-r--r--drivers/net/wireless/bcmdhd/wl_cfg80211.c244
-rw-r--r--drivers/net/wireless/bcmdhd/wl_cfg80211.h11
-rw-r--r--drivers/net/wireless/bcmdhd/wl_cfgp2p.c29
-rw-r--r--drivers/net/wireless/bcmdhd/wl_cfgp2p.h4
-rw-r--r--drivers/power/android_battery.c89
-rw-r--r--drivers/power/smb347-charger.c344
-rw-r--r--drivers/usb/gadget/f_accessory.c2
-rw-r--r--drivers/usb/gadget/f_audio_source.c49
-rw-r--r--drivers/w1/masters/ds2482.c47
-rw-r--r--include/linux/Kbuild1
-rw-r--r--include/linux/freezer.h11
-rw-r--r--include/linux/kthread.h8
-rw-r--r--include/linux/platform_data/ds2482.h21
-rw-r--r--include/linux/power/smb347-charger.h2
-rw-r--r--include/linux/sync.h8
-rw-r--r--include/linux/uhid.h104
-rw-r--r--include/sound/soc-dapm.h5
-rw-r--r--include/sound/soc.h6
-rw-r--r--kernel/kthread.c88
-rw-r--r--kernel/signal.c2
-rw-r--r--net/netfilter/xt_qtaguid.c5
-rw-r--r--samples/uhid/Makefile10
-rw-r--r--samples/uhid/uhid-example.c381
-rw-r--r--sound/soc/soc-core.c3
-rw-r--r--sound/soc/soc-dapm.c71
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(&params->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;
}