]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/misc/sti/sti-netlink.c
SDTI: Add netlink support for debug message output.
[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, ret = 0;
72
73         data    = NLMSG_DATA(nlh);
74         chan    = (nlh->nlmsg_flags >> 8) & 0xff;
75         id      = (nlh->nlmsg_type  >> 8) & 0xff;
76         size    = (int)(nlh->nlmsg_len - ((char *)data - (char *)nlh));
77
78         switch (nlh->nlmsg_type & 0xff) {
79         case STI_WRITE:
80                 sti_channel_write_trace(size, id, data, chan);
81                 break;
82 #if defined(CONFIG_ARCH_OMAP1) || defined(CONFIG_ARCH_OMAP2)
83         case STI_READ:
84                 int len = 0;
85
86                 data = kmalloc(size, GFP_KERNEL);
87                 if (!data)
88                         return -ENOMEM;
89                 memset(data, 0, size);
90
91                 len = sti_read_packet(data, size);
92                 ret = sti_netlink_read(NETLINK_CB(skb).pid, nlh->nlmsg_seq,
93                                        data, len);
94                 kfree(data);
95                 break;
96 #endif
97         default:
98                 return -ENOTTY;
99         }
100
101         return ret;
102 }
103
104 static int sti_netlink_receive_skb(struct sk_buff *skb)
105 {
106         while (skb->len >= NLMSG_SPACE(0)) {
107                 struct nlmsghdr *nlh;
108                 u32 rlen;
109                 int ret;
110
111                 nlh = (struct nlmsghdr *)skb->data;
112                 if (nlh->nlmsg_len < sizeof(struct nlmsghdr) ||
113                     skb->len < nlh->nlmsg_len)
114                         break;
115
116                 rlen = NLMSG_ALIGN(nlh->nlmsg_len);
117                 if (rlen > skb->len)
118                         rlen = skb->len;
119
120                 ret = sti_netlink_receive_msg(skb, nlh);
121                 if (ret)
122                         netlink_ack(skb, nlh, -ret);
123                 else if (nlh->nlmsg_flags & NLM_F_ACK)
124                         netlink_ack(skb, nlh, 0);
125
126                 skb_pull(skb, rlen);
127         }
128
129         return 0;
130 }
131
132 static void sti_netlink_receive(struct sk_buff *skb)
133 {
134         if (!mutex_trylock(&sti_netlink_mutex))
135                 return;
136
137         sti_netlink_receive_skb(skb);
138         mutex_unlock(&sti_netlink_mutex);
139 }
140
141 static int __init sti_netlink_init(void)
142 {
143         sti_sock = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 0,
144                                          sti_netlink_receive, NULL,
145                                          THIS_MODULE);
146         if (!sti_sock) {
147                 printk(KERN_ERR "STI: Failed to create netlink socket\n");
148                 return -ENODEV;
149         }
150
151         return 0;
152 }
153
154 module_init(sti_netlink_init);
155
156 MODULE_AUTHOR("Paul Mundt");
157 MODULE_LICENSE("GPL");
158 MODULE_DESCRIPTION("STI netlink-driven communications interface");