Blame acpi_ids.c

Packit Service 26469c
/*
Packit Service 26469c
 *  acpi_ids.c - ACPI Netlink Group and Family IDs
Packit Service 26469c
 *
Packit Service 26469c
 *  Copyright (C) 2008 Ted Felix (www.tedfelix.com)
Packit Service 26469c
 *  Portions from acpi_genl Copyright (C) Zhang Rui <rui.zhang@intel.com>
Packit Service 26469c
 *
Packit Service 26469c
 *  This program is free software; you can redistribute it and/or modify
Packit Service 26469c
 *  it under the terms of the GNU General Public License as published by
Packit Service 26469c
 *  the Free Software Foundation; either version 2 of the License, or
Packit Service 26469c
 *  (at your option) any later version.
Packit Service 26469c
 *
Packit Service 26469c
 *  This program is distributed in the hope that it will be useful,
Packit Service 26469c
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 26469c
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 26469c
 *  GNU General Public License for more details.
Packit Service 26469c
 *
Packit Service 26469c
 *  You should have received a copy of the GNU General Public License
Packit Service 26469c
 *  along with this program; if not, write to the Free Software
Packit Service 26469c
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit Service 26469c
 */
Packit Service 26469c
Packit Service 26469c
#include <stdio.h>
Packit Service 26469c
/* needed by netlink.h, should be in there */
Packit Service 26469c
#include <arpa/inet.h>
Packit Service 26469c
#include <linux/types.h>
Packit Service 26469c
#include <string.h>
Packit Service 26469c
Packit Service 26469c
#include "genetlink.h"
Packit Service 26469c
#include "libnetlink.h"
Packit Service 26469c
Packit Service 26469c
#include "acpid.h"
Packit Service 26469c
#include "acpi_ids.h"
Packit Service 26469c
Packit Service 26469c
#define GENL_MAX_FAM_GRPS       256
Packit Service 26469c
#define ACPI_EVENT_FAMILY_NAME          "acpi_event"
Packit Service 26469c
#define ACPI_EVENT_MCAST_GROUP_NAME     "acpi_mc_group"
Packit Service 26469c
Packit Service 26469c
static int initialized = 0;
Packit Service 26469c
static __u16 acpi_event_family_id = 0;
Packit Service 26469c
static __u32 acpi_event_mcast_group_id = 0;
Packit Service 26469c
Packit Service 26469c
/*
Packit Service 26469c
 *  A CTRL_CMD_GETFAMILY message returns an attribute table that looks
Packit Service 26469c
 *    like this:
Packit Service 26469c
 *
Packit Service 26469c
 *  CTRL_ATTR_FAMILY_ID         Use this to make sure we get the proper msgs
Packit Service 26469c
 *  CTRL_ATTR_MCAST_GROUPS
Packit Service 26469c
 *    CTRL_ATTR_MCAST_GRP_NAME
Packit Service 26469c
 *    CTRL_ATTR_MCAST_GRP_ID    Need this for the group mask
Packit Service 26469c
 *    ...
Packit Service 26469c
 */
Packit Service 26469c
Packit Service 26469c
static int
Packit Service 26469c
get_ctrl_grp_id(struct rtattr *arg)
Packit Service 26469c
{
Packit Service 26469c
	struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
Packit Service 26469c
	char *name;
Packit Service 26469c
Packit Service 26469c
	if (arg == NULL)
Packit Service 26469c
		return -1;
Packit Service 26469c
Packit Service 26469c
	/* nested within the CTRL_ATTR_MCAST_GROUPS attribute are the  */
Packit Service 26469c
	/* group name and ID  */
Packit Service 26469c
	parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
Packit Service 26469c
Packit Service 26469c
	/* if either of the entries needed cannot be found, bail */
Packit Service 26469c
	if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || !tb[CTRL_ATTR_MCAST_GRP_ID])
Packit Service 26469c
		return -1;
Packit Service 26469c
Packit Service 26469c
	/* get the name of this multicast group we've found */
Packit Service 26469c
	name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
Packit Service 26469c
Packit Service 26469c
	/* if it does not match the ACPI event multicast group name, bail */
Packit Service 26469c
	if (strcmp(name, ACPI_EVENT_MCAST_GROUP_NAME))
Packit Service 26469c
		return -1;
Packit Service 26469c
Packit Service 26469c
	/* At this point, we've found what we were looking for.  We now  */
Packit Service 26469c
	/* have the multicast group ID for ACPI events over generic netlink. */
Packit Service 26469c
	acpi_event_mcast_group_id =
Packit Service 26469c
		*((__u32 *)RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]));
Packit Service 26469c
Packit Service 26469c
	return 0;
Packit Service 26469c
}
Packit Service 26469c
Packit Service 26469c
/* n = the response to a CTRL_CMD_GETFAMILY message */
Packit Service 26469c
static int
Packit Service 26469c
genl_get_mcast_group_id(struct nlmsghdr *n)
Packit Service 26469c
{
Packit Service 26469c
	/*
Packit Service 26469c
	 *  Attribute table.  Note the type name "rtattr" which means "route
Packit Service 26469c
	 *  attribute".  This is a vestige of one of netlink's main uses:
Packit Service 26469c
	 *  routing.
Packit Service 26469c
	 */
Packit Service 26469c
	struct rtattr *tb[CTRL_ATTR_MAX + 1];
Packit Service 26469c
	/* place for the generic netlink header in the incoming message */
Packit Service 26469c
	struct genlmsghdr ghdr;
Packit Service 26469c
	/* length of the attribute and payload */
Packit Service 26469c
	int len = n->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
Packit Service 26469c
	/* Pointer to the attribute portion of the message */
Packit Service 26469c
	struct rtattr *attrs;
Packit Service 26469c
Packit Service 26469c
	if (len < 0) {
Packit Service 26469c
		fprintf(stderr, "%s: netlink CTRL_CMD_GETFAMILY response, "
Packit Service 26469c
			"wrong controller message len: %d\n", progname, len);
Packit Service 26469c
		return -1;
Packit Service 26469c
	}
Packit Service 26469c
Packit Service 26469c
	if (n->nlmsg_type != GENL_ID_CTRL) {
Packit Service 26469c
		fprintf(stderr, "%s: not a netlink controller message, "
Packit Service 26469c
			"nlmsg_len=%d nlmsg_type=0x%x\n", 
Packit Service 26469c
			progname, n->nlmsg_len, n->nlmsg_type);
Packit Service 26469c
		return 0;
Packit Service 26469c
	}
Packit Service 26469c
Packit Service 26469c
	/* copy generic netlink header into structure */
Packit Service 26469c
	memcpy(&ghdr, NLMSG_DATA(n), GENL_HDRLEN);
Packit Service 26469c
Packit Service 26469c
	if (ghdr.cmd != CTRL_CMD_GETFAMILY &&
Packit Service 26469c
	    ghdr.cmd != CTRL_CMD_DELFAMILY &&
Packit Service 26469c
	    ghdr.cmd != CTRL_CMD_NEWFAMILY &&
Packit Service 26469c
	    ghdr.cmd != CTRL_CMD_NEWMCAST_GRP &&
Packit Service 26469c
	    ghdr.cmd != CTRL_CMD_DELMCAST_GRP) {
Packit Service 26469c
		fprintf(stderr, "%s: unknown netlink controller command %d\n",
Packit Service 26469c
			progname, ghdr.cmd);
Packit Service 26469c
		return 0;
Packit Service 26469c
	}
Packit Service 26469c
Packit Service 26469c
	/* set attrs to point to the attribute */
Packit Service 26469c
	attrs = (struct rtattr *)(NLMSG_DATA(n) + GENL_HDRLEN);
Packit Service 26469c
	/* Read the table from the message into "tb".  This actually just  */
Packit Service 26469c
	/* places pointers into the message into tb[].  */
Packit Service 26469c
	parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
Packit Service 26469c
Packit Service 26469c
	/* if a family ID attribute is present, get it */
Packit Service 26469c
	if (tb[CTRL_ATTR_FAMILY_ID])
Packit Service 26469c
	{
Packit Service 26469c
		acpi_event_family_id =
Packit Service 26469c
			*((__u32 *)RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]));
Packit Service 26469c
	}
Packit Service 26469c
Packit Service 26469c
	/* if a "multicast groups" attribute is present... */
Packit Service 26469c
	if (tb[CTRL_ATTR_MCAST_GROUPS]) {
Packit Service 26469c
		struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
Packit Service 26469c
		int i;
Packit Service 26469c
Packit Service 26469c
		/* get the group table within this attribute  */
Packit Service 26469c
		parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
Packit Service 26469c
			tb[CTRL_ATTR_MCAST_GROUPS]);
Packit Service 26469c
Packit Service 26469c
		/* for each group */
Packit Service 26469c
		for (i = 0; i < GENL_MAX_FAM_GRPS; i++)
Packit Service 26469c
			/* if this group is valid */
Packit Service 26469c
			if (tb2[i])
Packit Service 26469c
				/* Parse the ID.  If successful, we're done. */
Packit Service 26469c
				if (!get_ctrl_grp_id(tb2[i]))
Packit Service 26469c
					return 0;
Packit Service 26469c
	}
Packit Service 26469c
Packit Service 26469c
	return -1;
Packit Service 26469c
}
Packit Service 26469c
Packit Service 26469c
static int
Packit Service 26469c
genl_get_ids(char *family_name)
Packit Service 26469c
{
Packit Service 26469c
	/* handle to the netlink connection */
Packit Service 26469c
	struct rtnl_handle rth;
Packit Service 26469c
	/* holds the request we are going to send and the reply */
Packit Service 26469c
	struct {
Packit Service 26469c
		struct nlmsghdr n;
Packit Service 26469c
		char buf[4096];    /* ??? Is this big enough for all cases? */
Packit Service 26469c
	} req;
Packit Service 26469c
	/* pointer to the nlmsghdr in req */
Packit Service 26469c
	struct nlmsghdr *nlh;
Packit Service 26469c
	/* place for the generic netlink header before copied into req */
Packit Service 26469c
	struct genlmsghdr ghdr;
Packit Service 26469c
	/* return value */
Packit Service 26469c
	int ret = -1;
Packit Service 26469c
Packit Service 26469c
	/* clear out the request */
Packit Service 26469c
	memset(&req, 0, sizeof(req));
Packit Service 26469c
Packit Service 26469c
	/* set up nlh to point to the netlink header in req */
Packit Service 26469c
	nlh = &req.n;
Packit Service 26469c
	/* set up the netlink header */
Packit Service 26469c
	nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
Packit Service 26469c
	nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
Packit Service 26469c
	nlh->nlmsg_type = GENL_ID_CTRL;
Packit Service 26469c
Packit Service 26469c
	/* clear out the generic netlink message header */
Packit Service 26469c
	memset(&ghdr, 0, sizeof(struct genlmsghdr));
Packit Service 26469c
	/* set the command we want to run: "GETFAMILY" */
Packit Service 26469c
	ghdr.cmd = CTRL_CMD_GETFAMILY;
Packit Service 26469c
	/* copy it into req */
Packit Service 26469c
	memcpy(NLMSG_DATA(&req.n), &ghdr, GENL_HDRLEN);
Packit Service 26469c
Packit Service 26469c
	/* the message payload is the family name */
Packit Service 26469c
	addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
Packit Service 26469c
			  family_name, strlen(family_name) + 1);
Packit Service 26469c
Packit Service 26469c
	/* open a generic netlink connection */
Packit Service 26469c
	if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
Packit Service 26469c
		fprintf(stderr, "%s: cannot open generic netlink socket\n", 
Packit Service 26469c
			progname);
Packit Service 26469c
		return -1;
Packit Service 26469c
	}
Packit Service 26469c
Packit Service 26469c
	/*
Packit Service 26469c
	 *  Send CTRL_CMD_GETFAMILY message (in nlh) to the generic
Packit Service 26469c
	 *  netlink controller.  Reply will be in nlh upon return.
Packit Service 26469c
	 */
Packit Service 26469c
	if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
Packit Service 26469c
		fprintf(stderr, "%s: error talking to the kernel via netlink\n",
Packit Service 26469c
			progname);
Packit Service 26469c
		goto ctrl_done;
Packit Service 26469c
	}
Packit Service 26469c
Packit Service 26469c
	/* process the response */
Packit Service 26469c
	if (genl_get_mcast_group_id(nlh) < 0) {
Packit Service 26469c
		fprintf(stderr, "%s: failed to get acpi_event netlink "
Packit Service 26469c
			"multicast group\n", progname);
Packit Service 26469c
		goto ctrl_done;
Packit Service 26469c
	}
Packit Service 26469c
Packit Service 26469c
	ret = 0;
Packit Service 26469c
Packit Service 26469c
ctrl_done:
Packit Service 26469c
	rtnl_close(&rth);
Packit Service 26469c
	return ret;
Packit Service 26469c
}
Packit Service 26469c
Packit Service 26469c
/* initialize the ACPI IDs */
Packit Service 26469c
static void
Packit Service 26469c
acpi_ids_init(void)
Packit Service 26469c
{
Packit Service 26469c
	genl_get_ids(ACPI_EVENT_FAMILY_NAME);
Packit Service 26469c
Packit Service 26469c
	initialized = 1;
Packit Service 26469c
}
Packit Service 26469c
Packit Service 26469c
/* returns the netlink family ID for ACPI event messages */
Packit Service 26469c
__u16
Packit Service 26469c
acpi_ids_getfamily(void)
Packit Service 26469c
{
Packit Service 26469c
	/* if the IDs haven't been initialized, initialize them */
Packit Service 26469c
	if (initialized == 0)
Packit Service 26469c
		acpi_ids_init();
Packit Service 26469c
Packit Service 26469c
	return acpi_event_family_id;
Packit Service 26469c
}
Packit Service 26469c
Packit Service 26469c
/* returns the netlink multicast group ID for ACPI event messages */
Packit Service 26469c
__u32
Packit Service 26469c
acpi_ids_getgroup(void)
Packit Service 26469c
{
Packit Service 26469c
	/* if the IDs haven't been initialized, initialize them */
Packit Service 26469c
	if (initialized == 0)
Packit Service 26469c
		acpi_ids_init();
Packit Service 26469c
Packit Service 26469c
	return acpi_event_mcast_group_id;
Packit Service 26469c
}