-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathoppo_kevent_upload.c
138 lines (111 loc) · 3.17 KB
/
oppo_kevent_upload.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
* oppo_kevent_upload.c - for kevent action upload,root action upload to user layer
* author by wangzhenhua,Plf.Framework
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <net/net_namespace.h>
#include <linux/proc_fs.h>
#include <net/sock.h>
#include <linux/vmalloc.h>
#include <linux/version.h>
#include <linux/uaccess.h>
#include <linux/oppo_kevent.h>
#ifdef CONFIG_OPPO_KEVENT_UPLOAD
static struct sock *netlink_fd = NULL;
static volatile u32 kevent_pid;
/* send to user space */
int kevent_send_to_user(struct kernel_packet_info *userinfo)
{
int ret, size, size_use;
unsigned int o_tail;
struct sk_buff *skbuff;
struct nlmsghdr *nlh;
struct kernel_packet_info *packet;
/* protect payload too long problem*/
if (userinfo->payload_length >= 2048) {
printk(KERN_ERR "kevent_send_to_user: payload_length out of range\n");
return -1;
}
size = NLMSG_SPACE(sizeof(struct kernel_packet_info) + userinfo->payload_length);
/*allocate new buffer cache */
skbuff = alloc_skb(size, GFP_ATOMIC);
if (skbuff == NULL) {
printk(KERN_ERR "kevent_send_to_user: skbuff alloc_skb failed\n");
return -1;
}
/* fill in the data structure */
nlh = nlmsg_put(skbuff, 0, 0, 0, size - sizeof(*nlh), 0);
if (nlh == NULL) {
printk(KERN_ERR "nlmsg_put failaure\n");
nlmsg_free(skbuff);
return -1;
}
o_tail = skbuff->tail;
size_use = sizeof(struct kernel_packet_info) + userinfo->payload_length;
/* use struct kernel_packet_info for data of nlmsg */
packet = NLMSG_DATA(nlh);
memset(packet, 0, size_use);
/* copy the payload content */
memcpy(packet, userinfo, size_use);
//compute nlmsg length
nlh->nlmsg_len = skbuff->tail - o_tail;
/* set control field,sender's pid */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0))
NETLINK_CB(skbuff).pid = 0;
#else
NETLINK_CB(skbuff).portid = 0;
#endif
NETLINK_CB(skbuff).dst_group = 0;
/* send data */
ret = netlink_unicast(netlink_fd, skbuff, kevent_pid, MSG_DONTWAIT);
if(ret < 0){
printk(KERN_ERR "kevent_send_to_user:netlink_unicast: can not unicast skbuff\n");
return 1;
}
return 0;
}
EXPORT_SYMBOL(kevent_send_to_user);
/* kernel receive message from user space */
void kernel_kevent_receive(struct sk_buff *__skbbr)
{
struct sk_buff *skbu;
struct nlmsghdr *nlh = NULL;
skbu = skb_get(__skbbr);
if (skbu->len >= sizeof(struct nlmsghdr)) {
nlh = (struct nlmsghdr *)skbu->data;
if((nlh->nlmsg_len >= sizeof(struct nlmsghdr))
&& (__skbbr->len >= nlh->nlmsg_len)){
kevent_pid = nlh->nlmsg_pid;
}
}
kfree_skb(skbu);
}
int __init netlink_kevent_init(void)
{
struct netlink_kernel_cfg cfg = {
.groups = 0,
.input = kernel_kevent_receive,
};
netlink_fd = netlink_kernel_create(&init_net, NETLINK_OPPO_KEVENT, &cfg);
if (!netlink_fd) {
printk(KERN_ERR "Can not create a netlink socket\n");
return -1;
}
return 0;
}
void __exit netlink_kevent_exit(void)
{
sock_release(netlink_fd->sk_socket);
}
module_init(netlink_kevent_init);
module_exit(netlink_kevent_exit);
MODULE_LICENSE("GPL");
#endif /* CONFIG_OPPO_KEVENT_UPLOAD */