Bump to version 2.0.22
[platform/upstream/acpid.git] / 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 #include "acpid.h"
32 #include "acpi_ids.h"
33
34 #define GENL_MAX_FAM_GRPS       256
35 #define ACPI_EVENT_FAMILY_NAME          "acpi_event"
36 #define ACPI_EVENT_MCAST_GROUP_NAME     "acpi_mc_group"
37
38 static int initialized = 0;
39 static __u16 acpi_event_family_id = 0;
40 static __u32 acpi_event_mcast_group_id = 0;
41
42 /*
43  *  A CTRL_CMD_GETFAMILY message returns an attribute table that looks
44  *    like this:
45  *
46  *  CTRL_ATTR_FAMILY_ID         Use this to make sure we get the proper msgs
47  *  CTRL_ATTR_MCAST_GROUPS
48  *    CTRL_ATTR_MCAST_GRP_NAME
49  *    CTRL_ATTR_MCAST_GRP_ID    Need this for the group mask
50  *    ...
51  */
52
53 static int
54 get_ctrl_grp_id(struct rtattr *arg)
55 {
56         struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
57         char *name;
58
59         if (arg == NULL)
60                 return -1;
61
62         /* nested within the CTRL_ATTR_MCAST_GROUPS attribute are the  */
63         /* group name and ID  */
64         parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
65
66         /* if either of the entries needed cannot be found, bail */
67         if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || !tb[CTRL_ATTR_MCAST_GRP_ID])
68                 return -1;
69
70         /* get the name of this multicast group we've found */
71         name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
72
73         /* if it does not match the ACPI event multicast group name, bail */
74         if (strcmp(name, ACPI_EVENT_MCAST_GROUP_NAME))
75                 return -1;
76
77         /* At this point, we've found what we were looking for.  We now  */
78         /* have the multicast group ID for ACPI events over generic netlink. */
79         acpi_event_mcast_group_id =
80                 *((__u32 *)RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]));
81
82         return 0;
83 }
84
85 /* n = the response to a CTRL_CMD_GETFAMILY message */
86 static int
87 genl_get_mcast_group_id(struct nlmsghdr *n)
88 {
89         /*
90          *  Attribute table.  Note the type name "rtattr" which means "route
91          *  attribute".  This is a vestige of one of netlink's main uses:
92          *  routing.
93          */
94         struct rtattr *tb[CTRL_ATTR_MAX + 1];
95         /* place for the generic netlink header in the incoming message */
96         struct genlmsghdr ghdr;
97         /* length of the attribute and payload */
98         int len = n->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
99         /* Pointer to the attribute portion of the message */
100         struct rtattr *attrs;
101
102         if (len < 0) {
103                 fprintf(stderr, "%s: netlink CTRL_CMD_GETFAMILY response, "
104                         "wrong controller message len: %d\n", progname, len);
105                 return -1;
106         }
107
108         if (n->nlmsg_type != GENL_ID_CTRL) {
109                 fprintf(stderr, "%s: not a netlink controller message, "
110                         "nlmsg_len=%d nlmsg_type=0x%x\n", 
111                         progname, n->nlmsg_len, n->nlmsg_type);
112                 return 0;
113         }
114
115         /* copy generic netlink header into structure */
116         memcpy(&ghdr, NLMSG_DATA(n), GENL_HDRLEN);
117
118         if (ghdr.cmd != CTRL_CMD_GETFAMILY &&
119             ghdr.cmd != CTRL_CMD_DELFAMILY &&
120             ghdr.cmd != CTRL_CMD_NEWFAMILY &&
121             ghdr.cmd != CTRL_CMD_NEWMCAST_GRP &&
122             ghdr.cmd != CTRL_CMD_DELMCAST_GRP) {
123                 fprintf(stderr, "%s: unknown netlink controller command %d\n",
124                         progname, ghdr.cmd);
125                 return 0;
126         }
127
128         /* set attrs to point to the attribute */
129         attrs = (struct rtattr *)(NLMSG_DATA(n) + GENL_HDRLEN);
130         /* Read the table from the message into "tb".  This actually just  */
131         /* places pointers into the message into tb[].  */
132         parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
133
134         /* if a family ID attribute is present, get it */
135         if (tb[CTRL_ATTR_FAMILY_ID])
136         {
137                 acpi_event_family_id =
138                         *((__u32 *)RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]));
139         }
140
141         /* if a "multicast groups" attribute is present... */
142         if (tb[CTRL_ATTR_MCAST_GROUPS]) {
143                 struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
144                 int i;
145
146                 /* get the group table within this attribute  */
147                 parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
148                         tb[CTRL_ATTR_MCAST_GROUPS]);
149
150                 /* for each group */
151                 for (i = 0; i < GENL_MAX_FAM_GRPS; i++)
152                         /* if this group is valid */
153                         if (tb2[i])
154                                 /* Parse the ID.  If successful, we're done. */
155                                 if (!get_ctrl_grp_id(tb2[i]))
156                                         return 0;
157         }
158
159         return -1;
160 }
161
162 static int
163 genl_get_ids(char *family_name)
164 {
165         /* handle to the netlink connection */
166         struct rtnl_handle rth;
167         /* holds the request we are going to send and the reply */
168         struct {
169                 struct nlmsghdr n;
170                 char buf[4096];    /* ??? Is this big enough for all cases? */
171         } req;
172         /* pointer to the nlmsghdr in req */
173         struct nlmsghdr *nlh;
174         /* place for the generic netlink header before copied into req */
175         struct genlmsghdr ghdr;
176         /* return value */
177         int ret = -1;
178
179         /* clear out the request */
180         memset(&req, 0, sizeof(req));
181
182         /* set up nlh to point to the netlink header in req */
183         nlh = &req.n;
184         /* set up the netlink header */
185         nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
186         nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
187         nlh->nlmsg_type = GENL_ID_CTRL;
188
189         /* clear out the generic netlink message header */
190         memset(&ghdr, 0, sizeof(struct genlmsghdr));
191         /* set the command we want to run: "GETFAMILY" */
192         ghdr.cmd = CTRL_CMD_GETFAMILY;
193         /* copy it into req */
194         memcpy(NLMSG_DATA(&req.n), &ghdr, GENL_HDRLEN);
195
196         /* the message payload is the family name */
197         addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
198                           family_name, strlen(family_name) + 1);
199
200         /* open a generic netlink connection */
201         if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
202                 fprintf(stderr, "%s: cannot open generic netlink socket\n", 
203                         progname);
204                 return -1;
205         }
206
207         /*
208          *  Send CTRL_CMD_GETFAMILY message (in nlh) to the generic
209          *  netlink controller.  Reply will be in nlh upon return.
210          */
211         if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
212                 fprintf(stderr, "%s: error talking to the kernel via netlink\n",
213                         progname);
214                 goto ctrl_done;
215         }
216
217         /* process the response */
218         if (genl_get_mcast_group_id(nlh) < 0) {
219                 fprintf(stderr, "%s: failed to get acpi_event netlink "
220                         "multicast group\n", progname);
221                 goto ctrl_done;
222         }
223
224         ret = 0;
225
226 ctrl_done:
227         rtnl_close(&rth);
228         return ret;
229 }
230
231 /* initialize the ACPI IDs */
232 static void
233 acpi_ids_init(void)
234 {
235         genl_get_ids(ACPI_EVENT_FAMILY_NAME);
236
237         initialized = 1;
238 }
239
240 /* returns the netlink family ID for ACPI event messages */
241 __u16
242 acpi_ids_getfamily(void)
243 {
244         /* if the IDs haven't been initialized, initialize them */
245         if (initialized == 0)
246                 acpi_ids_init();
247
248         return acpi_event_family_id;
249 }
250
251 /* returns the netlink multicast group ID for ACPI event messages */
252 __u32
253 acpi_ids_getgroup(void)
254 {
255         /* if the IDs haven't been initialized, initialize them */
256         if (initialized == 0)
257                 acpi_ids_init();
258
259         return acpi_event_mcast_group_id;
260 }