Bluetooth: Add firmware update for Atheros 0cf3:311f
[pandora-kernel.git] / drivers / connector / cn_proc.c
index 77e1e6c..094a710 100644 (file)
 #include <linux/ptrace.h>
 #include <linux/atomic.h>
 
-#include <asm/unaligned.h>
-
 #include <linux/cn_proc.h>
 
-#define CN_PROC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event))
+/*
+ * Size of a cn_msg followed by a proc_event structure.  Since the
+ * sizeof struct cn_msg is a multiple of 4 bytes, but not 8 bytes, we
+ * add one 4-byte word to the size here, and then start the actual
+ * cn_msg structure 4 bytes into the stack buffer.  The result is that
+ * the immediately following proc_event structure is aligned to 8 bytes.
+ */
+#define CN_PROC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event) + 4)
+
+/* See comment above; we test our assumption about sizeof struct cn_msg here. */
+static inline struct cn_msg *buffer_to_cn_msg(__u8 *buffer)
+{
+       BUILD_BUG_ON(sizeof(struct cn_msg) != 20);
+       return (struct cn_msg *)(buffer + 4);
+}
 
 static atomic_t proc_event_num_listeners = ATOMIC_INIT(0);
 static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC };
@@ -55,18 +67,19 @@ void proc_fork_connector(struct task_struct *task)
 {
        struct cn_msg *msg;
        struct proc_event *ev;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
        struct timespec ts;
        struct task_struct *parent;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg*)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event*)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_FORK;
        rcu_read_lock();
        parent = rcu_dereference(task->real_parent);
@@ -79,6 +92,7 @@ void proc_fork_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        /*  If cn_netlink_send() failed, the data is not sent */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
@@ -88,16 +102,17 @@ void proc_exec_connector(struct task_struct *task)
        struct cn_msg *msg;
        struct proc_event *ev;
        struct timespec ts;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg*)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event*)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_EXEC;
        ev->event_data.exec.process_pid = task->pid;
        ev->event_data.exec.process_tgid = task->tgid;
@@ -105,6 +120,7 @@ void proc_exec_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -112,15 +128,16 @@ void proc_id_connector(struct task_struct *task, int which_id)
 {
        struct cn_msg *msg;
        struct proc_event *ev;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
        struct timespec ts;
        const struct cred *cred;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg*)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event*)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        ev->what = which_id;
        ev->event_data.id.process_pid = task->pid;
        ev->event_data.id.process_tgid = task->tgid;
@@ -139,11 +156,12 @@ void proc_id_connector(struct task_struct *task, int which_id)
        rcu_read_unlock();
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
 
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -152,16 +170,17 @@ void proc_sid_connector(struct task_struct *task)
        struct cn_msg *msg;
        struct proc_event *ev;
        struct timespec ts;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_SID;
        ev->event_data.sid.process_pid = task->pid;
        ev->event_data.sid.process_tgid = task->tgid;
@@ -169,6 +188,7 @@ void proc_sid_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -177,16 +197,17 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
        struct cn_msg *msg;
        struct proc_event *ev;
        struct timespec ts;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_PTRACE;
        ev->event_data.ptrace.process_pid  = task->pid;
        ev->event_data.ptrace.process_tgid = task->tgid;
@@ -202,6 +223,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -210,16 +232,17 @@ void proc_comm_connector(struct task_struct *task)
        struct cn_msg *msg;
        struct proc_event *ev;
        struct timespec ts;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg *)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_COMM;
        ev->event_data.comm.process_pid  = task->pid;
        ev->event_data.comm.process_tgid = task->tgid;
@@ -228,6 +251,7 @@ void proc_comm_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -235,17 +259,18 @@ void proc_exit_connector(struct task_struct *task)
 {
        struct cn_msg *msg;
        struct proc_event *ev;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
        struct timespec ts;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg*)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event*)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        get_seq(&msg->seq, &ev->cpu);
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->what = PROC_EVENT_EXIT;
        ev->event_data.exit.process_pid = task->pid;
        ev->event_data.exit.process_tgid = task->tgid;
@@ -255,6 +280,7 @@ void proc_exit_connector(struct task_struct *task)
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -270,23 +296,25 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
 {
        struct cn_msg *msg;
        struct proc_event *ev;
-       __u8 buffer[CN_PROC_MSG_SIZE];
+       __u8 buffer[CN_PROC_MSG_SIZE] __aligned(8);
        struct timespec ts;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
 
-       msg = (struct cn_msg*)buffer;
+       msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event*)msg->data;
+       memset(&ev->event_data, 0, sizeof(ev->event_data));
        msg->seq = rcvd_seq;
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
-       put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
+       ev->timestamp_ns = timespec_to_ns(&ts);
        ev->cpu = -1;
        ev->what = PROC_EVENT_NONE;
        ev->event_data.ack.err = err;
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = rcvd_ack + 1;
        msg->len = sizeof(*ev);
+       msg->flags = 0; /* not used */
        cn_netlink_send(msg, CN_IDX_PROC, GFP_KERNEL);
 }
 
@@ -303,6 +331,12 @@ static void cn_proc_mcast_ctl(struct cn_msg *msg,
        if (msg->len != sizeof(*mc_op))
                return;
 
+       /* Can only change if privileged. */
+       if (!capable(CAP_NET_ADMIN)) {
+               err = EPERM;
+               goto out;
+       }
+
        mc_op = (enum proc_cn_mcast_op*)msg->data;
        switch (*mc_op) {
        case PROC_CN_MCAST_LISTEN:
@@ -315,6 +349,8 @@ static void cn_proc_mcast_ctl(struct cn_msg *msg,
                err = EINVAL;
                break;
        }
+
+out:
        cn_proc_ack(err, msg->seq, msg->ack);
 }