Tizen 2.0 Release
[external/acpid.git] / kacpimon / acpi_ids.c
1 /*
2  *  acpi_ids.c - ACPI Netlink Group and Family IDs
3  *
4  *  Copyright (C) 2008 Ted Felix (www.tedfelix.com)
5  *  Portions from acpi_genl Copyright (C) Zhang Rui <rui.zhang@intel.com>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <stdio.h>
23 /* needed by netlink.h, should be in there */
24 #include <arpa/inet.h>
25 #include <linux/types.h>
26 #include <string.h>
27
28 #include "genetlink.h"
29 #include "libnetlink.h"
30
31 #define GENL_MAX_FAM_GRPS       256
32 #define ACPI_EVENT_FAMILY_NAME          "acpi_event"
33 #define ACPI_EVENT_MCAST_GROUP_NAME     "acpi_mc_group"
34
35 static int initialized = 0;
36 static __u16 acpi_event_family_id = 0;
37 static __u32 acpi_event_mcast_group_id = 0;
38
39 /*
40  *  A CTRL_CMD_GETFAMILY message returns an attribute table that looks
41  *    like this:
42  *
43  *  CTRL_ATTR_FAMILY_ID         Use this to make sure we get the proper msgs
44  *  CTRL_ATTR_MCAST_GROUPS
45  *    CTRL_ATTR_MCAST_GRP_NAME
46  *    CTRL_ATTR_MCAST_GRP_ID    Need this for the group mask
47  *    ...
48  */
49
50 static int
51 get_ctrl_grp_id(struct rtattr *arg)
52 {
53         struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
54         char *name;
55
56         if (arg == NULL)
57                 return -1;
58
59         /* nested within the CTRL_ATTR_MCAST_GROUPS attribute are the  */
60         /* group name and ID  */
61         parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
62
63         /* if either of the entries needed cannot be found, bail */
64         if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || !tb[CTRL_ATTR_MCAST_GRP_ID])
65                 return -1;
66
67         /* get the name of this multicast group we've found */
68         name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
69
70         /* if it does not match the ACPI event multicast group name, bail */
71         if (strcmp(name, ACPI_EVENT_MCAST_GROUP_NAME))
72                 return -1;
73
74         /* At this point, we've found what we were looking for.  We now  */
75         /* have the multicast group ID for ACPI events over generic netlink. */
76         acpi_event_mcast_group_id =
77                 *((__u32 *)RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]));
78
79         return 0;
80 }
81
82 /* n = the response to a CTRL_CMD_GETFAMILY message */
83 static int
84 genl_get_mcast_group_id(struct nlmsghdr *n)
85 {
86         /*
87          *  Attribute table.  Note the type name "rtattr" which means "route
88          *  attribute".  This is a vestige of one of netlink's main uses:
89          *  routing.
90          */
91         struct rtattr *tb[CTRL_ATTR_MAX + 1];
92         /* place for the generic netlink header in the incoming message */
93         struct genlmsghdr ghdr;
94         /* length of the attribute and payload */
95         int len = n->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
96         /* Pointer to the attribute portion of the message */
97         struct rtattr *attrs;
98
99         if (len < 0) {
100                 printf("Netlink: CTRL_CMD_GETFAMILY response, "
101                         "wrong controller message len: %d\n", len);
102                 return -1;
103         }
104
105         if (n->nlmsg_type != GENL_ID_CTRL) {
106                 printf("Netlink: Not a controller message, nlmsg_len=%d "
107                         "nlmsg_type=0x%x\n", n->nlmsg_len, n->nlmsg_type);
108                 return 0;
109         }
110
111         /* copy generic netlink header into structure */
112         memcpy(&ghdr, NLMSG_DATA(n), GENL_HDRLEN);
113
114         if (ghdr.cmd != CTRL_CMD_GETFAMILY &&
115             ghdr.cmd != CTRL_CMD_DELFAMILY &&
116             ghdr.cmd != CTRL_CMD_NEWFAMILY &&
117             ghdr.cmd != CTRL_CMD_NEWMCAST_GRP &&
118             ghdr.cmd != CTRL_CMD_DELMCAST_GRP) {
119                 printf("Netlink: Unknown controller command %d\n", ghdr.cmd);
120                 return 0;
121         }
122
123         /* set attrs to point to the attribute */
124         attrs = (struct rtattr *)(NLMSG_DATA(n) + GENL_HDRLEN);
125         /* Read the table from the message into "tb".  This actually just  */
126         /* places pointers into the message into tb[].  */
127         parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
128
129         /* if a family ID attribute is present, get it */
130         if (tb[CTRL_ATTR_FAMILY_ID])
131         {
132                 acpi_event_family_id =
133                         *((__u32 *)RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]));
134         }
135
136         /* if a "multicast groups" attribute is present... */
137         if (tb[CTRL_ATTR_MCAST_GROUPS]) {
138                 struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
139                 int i;
140
141                 /* get the group table within this attribute  */
142                 parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
143                         tb[CTRL_ATTR_MCAST_GROUPS]);
144
145                 /* for each group */
146                 for (i = 0; i < GENL_MAX_FAM_GRPS; i++)
147                         /* if this group is valid */
148                         if (tb2[i])
149                                 /* Parse the ID.  If successful, we're done. */
150                                 if (!get_ctrl_grp_id(tb2[i]))
151                                         return 0;
152         }
153
154         return -1;
155 }
156
157 static int
158 genl_get_ids(char *family_name)
159 {
160         /* handle to the netlink connection */
161         struct rtnl_handle rth;
162         /* holds the request we are going to send and the reply */
163         struct {
164                 struct nlmsghdr n;
165                 char buf[4096];    /* ??? Is this big enough for all cases? */
166         } req;
167         /* pointer to the nlmsghdr in req */
168         struct nlmsghdr *nlh;
169         /* place for the generic netlink header before copied into req */
170         struct genlmsghdr ghdr;
171         /* return value */
172         int ret = -1;
173
174         /* clear out the request */
175         memset(&req, 0, sizeof(req));
176
177         /* set up nlh to point to the netlink header in req */
178         nlh = &req.n;
179         /* set up the netlink header */
180         nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
181         nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
182         nlh->nlmsg_type = GENL_ID_CTRL;
183
184         /* clear out the generic netlink message header */
185         memset(&ghdr, 0, sizeof(struct genlmsghdr));
186         /* set the command we want to run: "GETFAMILY" */
187         ghdr.cmd = CTRL_CMD_GETFAMILY;
188         /* copy it into req */
189         memcpy(NLMSG_DATA(&req.n), &ghdr, GENL_HDRLEN);
190
191         /* the message payload is the family name */
192         addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
193                           family_name, strlen(family_name) + 1);
194
195         /* open a generic netlink connection */
196         if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
197                 printf("Netlink: Cannot open generic netlink socket\n");
198                 return -1;
199         }
200
201         /*
202          *  Send CTRL_CMD_GETFAMILY message (in nlh) to the generic
203          *  netlink controller.  Reply will be in nlh upon return.
204          */
205         if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
206                 printf("Netlink: Error talking to the kernel\n");
207                 goto ctrl_done;
208         }
209
210         /* process the response */
211         if (genl_get_mcast_group_id(nlh) < 0) {
212                 printf("Netlink: Failed to get acpi_event multicast group\n");
213                 goto ctrl_done;
214         }
215
216         ret = 0;
217
218 ctrl_done:
219         rtnl_close(&rth);
220         return ret;
221 }
222
223 /* initialize the ACPI IDs */
224 static void
225 acpi_ids_init()
226 {
227         genl_get_ids(ACPI_EVENT_FAMILY_NAME);
228
229         initialized = 1;
230 }
231
232 /* returns the netlink family ID for ACPI event messages */
233 __u16
234 acpi_ids_getfamily()
235 {
236         /* if the IDs haven't been initialized, initialize them */
237         if (initialized == 0)
238                 acpi_ids_init();
239
240         return acpi_event_family_id;
241 }
242
243 /* returns the netlink multicast group ID for ACPI event messages */
244 __u32
245 acpi_ids_getgroup()
246 {
247         /* if the IDs haven't been initialized, initialize them */
248         if (initialized == 0)
249                 acpi_ids_init();
250
251         return acpi_event_mcast_group_id;
252 }