]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/misc/sti/sti-netlink.c
Merge branch 'omap-fixes'
[linux-2.6-omap-h63xx.git] / drivers / misc / sti / sti-netlink.c
1 /*
2  * OMAP STI/XTI communications interface via netlink socket.
3  *
4  * Copyright (C) 2004, 2005, 2006 Nokia Corporation
5  * Written by: Paul Mundt <paul.mundt@nokia.com>
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License.  See the file "COPYING" in the main directory of this archive
9  * for more details.
10  */
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/netlink.h>
14 #include <linux/socket.h>
15 #include <linux/skbuff.h>
16 #include <linux/mutex.h>
17 #include <net/sock.h>
18 #include <mach/sti.h>
19
20 static struct sock *sti_sock;
21 static DEFINE_MUTEX(sti_netlink_mutex);
22
23 enum {
24         STI_READ,
25         STI_WRITE,
26 };
27
28 #if defined(CONFIG_ARCH_OMAP1) || defined(CONFIG_ARCH_OMAP2)
29 static int sti_netlink_read(int pid, int seq, void *payload, int size)
30 {
31         struct sk_buff *skb;
32         struct nlmsghdr *nlh;
33         int ret, len = NLMSG_SPACE(size);
34         unsigned char *tail;
35
36         skb = alloc_skb(len, GFP_KERNEL);
37         if (!skb)
38                 return -ENOMEM;
39
40         tail = skb->tail;
41         nlh = NLMSG_PUT(skb, pid, seq, STI_READ,
42                         len - (sizeof(struct nlmsghdr)));
43         nlh->nlmsg_flags = 0;
44         memcpy(NLMSG_DATA(nlh), payload, size);
45         nlh->nlmsg_len = skb->tail - tail;
46
47         ret = netlink_unicast(sti_sock, skb, pid, MSG_DONTWAIT);
48         if (ret > 0)
49                 ret = 0;
50
51         return ret;
52
53 nlmsg_failure:
54         if (skb)
55                 kfree_skb(skb);
56
57         return -EINVAL;
58 }
59 #endif
60
61 /*
62  * We abuse nlmsg_type and nlmsg_flags for our purposes.
63  *
64  * The ID is encoded into the upper 8 bits of the nlmsg_type, while the
65  * channel number is encoded into the upper 8 bits of the nlmsg_flags.
66  */
67 static int sti_netlink_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
68 {
69         void *data;
70         u8 chan, id;
71         int size;
72         int ret = 0, len = 0;
73
74         data    = NLMSG_DATA(nlh);
75         chan    = (nlh->nlmsg_flags >> 8) & 0xff;
76         id      = (nlh->nlmsg_type  >> 8) & 0xff;
77         size    = (int)(nlh->nlmsg_len - ((char *)data - (char *)nlh));
78
79         switch (nlh->nlmsg_type & 0xff) {
80         case STI_WRITE:
81                 sti_channel_write_trace(size, id, data, chan);
82                 break;
83 #if defined(CONFIG_ARCH_OMAP1) || defined(CONFIG_ARCH_OMAP2)
84         case STI_READ:
85                 data = kmalloc(size, GFP_KERNEL);
86                 if (!data)
87                         return -ENOMEM;
88                 memset(data, 0, size);
89
90                 len = sti_read_packet(data, size);
91                 ret = sti_netlink_read(NETLINK_CB(skb).pid, nlh->nlmsg_seq,
92                                        data, len);
93                 kfree(data);
94                 break;
95 #endif
96         default:
97                 return -ENOTTY;
98         }
99
100         return ret;
101 }
102
103 static int sti_netlink_receive_skb(struct sk_buff *skb)
104 {
105         while (skb->len >= NLMSG_SPACE(0)) {
106                 struct nlmsghdr *nlh;
107                 u32 rlen;
108                 int ret;
109
110                 nlh = (struct nlmsghdr *)skb->data;
111                 if (nlh->nlmsg_len < sizeof(struct nlmsghdr) ||
112                     skb->len < nlh->nlmsg_len)
113                         break;
114
115                 rlen = NLMSG_ALIGN(nlh->nlmsg_len);
116                 if (rlen > skb->len)
117                         rlen = skb->len;
118
119                 ret = sti_netlink_receive_msg(skb, nlh);
120                 if (ret)
121                         netlink_ack(skb, nlh, -ret);
122                 else if (nlh->nlmsg_flags & NLM_F_ACK)
123                         netlink_ack(skb, nlh, 0);
124
125                 skb_pull(skb, rlen);
126         }
127
128         return 0;
129 }
130
131 static void sti_netlink_receive(struct sk_buff *skb)
132 {
133         if (!mutex_trylock(&sti_netlink_mutex))
134                 return;
135
136         sti_netlink_receive_skb(skb);
137         mutex_unlock(&sti_netlink_mutex);
138 }
139
140 static int __init sti_netlink_init(void)
141 {
142         sti_sock = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 0,
143                                          sti_netlink_receive, NULL,
144                                          THIS_MODULE);
145         if (!sti_sock) {
146                 printk(KERN_ERR "STI: Failed to create netlink socket\n");
147                 return -ENODEV;
148         }
149
150         return 0;
151 }
152
153 module_init(sti_netlink_init);
154
155 MODULE_AUTHOR("Paul Mundt");
156 MODULE_LICENSE("GPL");
157 MODULE_DESCRIPTION("STI netlink-driven communications interface");