123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- /*
- * Linux WiMAX
- * Generic messaging interface between userspace and driver/device
- *
- *
- * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
- * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- *
- * This implements a direct communication channel between user space and
- * the driver/device, by which free form messages can be sent back and
- * forth.
- *
- * This is intended for device-specific features, vendor quirks, etc.
- *
- * See include/net/wimax.h
- *
- * GENERIC NETLINK ENCODING AND CAPACITY
- *
- * A destination "pipe name" is added to each message; it is up to the
- * drivers to assign or use those names (if using them at all).
- *
- * Messages are encoded as a binary netlink attribute using nla_put()
- * using type NLA_UNSPEC (as some versions of libnl still in
- * deployment don't yet understand NLA_BINARY).
- *
- * The maximum capacity of this transport is PAGESIZE per message (so
- * the actual payload will be bit smaller depending on the
- * netlink/generic netlink attributes and headers).
- *
- * RECEPTION OF MESSAGES
- *
- * When a message is received from user space, it is passed verbatim
- * to the driver calling wimax_dev->op_msg_from_user(). The return
- * value from this function is passed back to user space as an ack
- * over the generic netlink protocol.
- *
- * The stack doesn't do any processing or interpretation of these
- * messages.
- *
- * SENDING MESSAGES
- *
- * Messages can be sent with wimax_msg().
- *
- * If the message delivery needs to happen on a different context to
- * that of its creation, wimax_msg_alloc() can be used to get a
- * pointer to the message that can be delivered later on with
- * wimax_msg_send().
- *
- * ROADMAP
- *
- * wimax_gnl_doit_msg_from_user() Process a message from user space
- * wimax_dev_get_by_genl_info()
- * wimax_dev->op_msg_from_user() Delivery of message to the driver
- *
- * wimax_msg() Send a message to user space
- * wimax_msg_alloc()
- * wimax_msg_send()
- */
- #include <linux/device.h>
- #include <linux/slab.h>
- #include <net/genetlink.h>
- #include <linux/netdevice.h>
- #include <linux/wimax.h>
- #include <linux/security.h>
- #include <linux/export.h>
- #include "wimax-internal.h"
- #define D_SUBMODULE op_msg
- #include "debug-levels.h"
- /**
- * wimax_msg_alloc - Create a new skb for sending a message to userspace
- *
- * @wimax_dev: WiMAX device descriptor
- * @pipe_name: "named pipe" the message will be sent to
- * @msg: pointer to the message data to send
- * @size: size of the message to send (in bytes), including the header.
- * @gfp_flags: flags for memory allocation.
- *
- * Returns: %0 if ok, negative errno code on error
- *
- * Description:
- *
- * Allocates an skb that will contain the message to send to user
- * space over the messaging pipe and initializes it, copying the
- * payload.
- *
- * Once this call is done, you can deliver it with
- * wimax_msg_send().
- *
- * IMPORTANT:
- *
- * Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as
- * wimax_msg_send() depends on skb->data being placed at the
- * beginning of the user message.
- *
- * Unlike other WiMAX stack calls, this call can be used way early,
- * even before wimax_dev_add() is called, as long as the
- * wimax_dev->net_dev pointer is set to point to a proper
- * net_dev. This is so that drivers can use it early in case they need
- * to send stuff around or communicate with user space.
- */
- struct sk_buff *wimax_msg_alloc(struct wimax_dev *wimax_dev,
- const char *pipe_name,
- const void *msg, size_t size,
- gfp_t gfp_flags)
- {
- int result;
- struct device *dev = wimax_dev_to_dev(wimax_dev);
- size_t msg_size;
- void *genl_msg;
- struct sk_buff *skb;
- msg_size = nla_total_size(size)
- + nla_total_size(sizeof(u32))
- + (pipe_name ? nla_total_size(strlen(pipe_name)) : 0);
- result = -ENOMEM;
- skb = genlmsg_new(msg_size, gfp_flags);
- if (skb == NULL)
- goto error_new;
- genl_msg = genlmsg_put(skb, 0, 0, &wimax_gnl_family,
- 0, WIMAX_GNL_OP_MSG_TO_USER);
- if (genl_msg == NULL) {
- dev_err(dev, "no memory to create generic netlink message\n");
- goto error_genlmsg_put;
- }
- result = nla_put_u32(skb, WIMAX_GNL_MSG_IFIDX,
- wimax_dev->net_dev->ifindex);
- if (result < 0) {
- dev_err(dev, "no memory to add ifindex attribute\n");
- goto error_nla_put;
- }
- if (pipe_name) {
- result = nla_put_string(skb, WIMAX_GNL_MSG_PIPE_NAME,
- pipe_name);
- if (result < 0) {
- dev_err(dev, "no memory to add pipe_name attribute\n");
- goto error_nla_put;
- }
- }
- result = nla_put(skb, WIMAX_GNL_MSG_DATA, size, msg);
- if (result < 0) {
- dev_err(dev, "no memory to add payload (msg %p size %zu) in "
- "attribute: %d\n", msg, size, result);
- goto error_nla_put;
- }
- genlmsg_end(skb, genl_msg);
- return skb;
- error_nla_put:
- error_genlmsg_put:
- error_new:
- nlmsg_free(skb);
- return ERR_PTR(result);
- }
- EXPORT_SYMBOL_GPL(wimax_msg_alloc);
- /**
- * wimax_msg_data_len - Return a pointer and size of a message's payload
- *
- * @msg: Pointer to a message created with wimax_msg_alloc()
- * @size: Pointer to where to store the message's size
- *
- * Returns the pointer to the message data.
- */
- const void *wimax_msg_data_len(struct sk_buff *msg, size_t *size)
- {
- struct nlmsghdr *nlh = (void *) msg->head;
- struct nlattr *nla;
- nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
- WIMAX_GNL_MSG_DATA);
- if (nla == NULL) {
- pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
- return NULL;
- }
- *size = nla_len(nla);
- return nla_data(nla);
- }
- EXPORT_SYMBOL_GPL(wimax_msg_data_len);
- /**
- * wimax_msg_data - Return a pointer to a message's payload
- *
- * @msg: Pointer to a message created with wimax_msg_alloc()
- */
- const void *wimax_msg_data(struct sk_buff *msg)
- {
- struct nlmsghdr *nlh = (void *) msg->head;
- struct nlattr *nla;
- nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
- WIMAX_GNL_MSG_DATA);
- if (nla == NULL) {
- pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
- return NULL;
- }
- return nla_data(nla);
- }
- EXPORT_SYMBOL_GPL(wimax_msg_data);
- /**
- * wimax_msg_len - Return a message's payload length
- *
- * @msg: Pointer to a message created with wimax_msg_alloc()
- */
- ssize_t wimax_msg_len(struct sk_buff *msg)
- {
- struct nlmsghdr *nlh = (void *) msg->head;
- struct nlattr *nla;
- nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
- WIMAX_GNL_MSG_DATA);
- if (nla == NULL) {
- pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
- return -EINVAL;
- }
- return nla_len(nla);
- }
- EXPORT_SYMBOL_GPL(wimax_msg_len);
- /**
- * wimax_msg_send - Send a pre-allocated message to user space
- *
- * @wimax_dev: WiMAX device descriptor
- *
- * @skb: &struct sk_buff returned by wimax_msg_alloc(). Note the
- * ownership of @skb is transferred to this function.
- *
- * Returns: 0 if ok, < 0 errno code on error
- *
- * Description:
- *
- * Sends a free-form message that was preallocated with
- * wimax_msg_alloc() and filled up.
- *
- * Assumes that once you pass an skb to this function for sending, it
- * owns it and will release it when done (on success).
- *
- * IMPORTANT:
- *
- * Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as
- * wimax_msg_send() depends on skb->data being placed at the
- * beginning of the user message.
- *
- * Unlike other WiMAX stack calls, this call can be used way early,
- * even before wimax_dev_add() is called, as long as the
- * wimax_dev->net_dev pointer is set to point to a proper
- * net_dev. This is so that drivers can use it early in case they need
- * to send stuff around or communicate with user space.
- */
- int wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb)
- {
- struct device *dev = wimax_dev_to_dev(wimax_dev);
- void *msg = skb->data;
- size_t size = skb->len;
- might_sleep();
- d_printf(1, dev, "CTX: wimax msg, %zu bytes\n", size);
- d_dump(2, dev, msg, size);
- genlmsg_multicast(&wimax_gnl_family, skb, 0, 0, GFP_KERNEL);
- d_printf(1, dev, "CTX: genl multicast done\n");
- return 0;
- }
- EXPORT_SYMBOL_GPL(wimax_msg_send);
- /**
- * wimax_msg - Send a message to user space
- *
- * @wimax_dev: WiMAX device descriptor (properly referenced)
- * @pipe_name: "named pipe" the message will be sent to
- * @buf: pointer to the message to send.
- * @size: size of the buffer pointed to by @buf (in bytes).
- * @gfp_flags: flags for memory allocation.
- *
- * Returns: %0 if ok, negative errno code on error.
- *
- * Description:
- *
- * Sends a free-form message to user space on the device @wimax_dev.
- *
- * NOTES:
- *
- * Once the @skb is given to this function, who will own it and will
- * release it when done (unless it returns error).
- */
- int wimax_msg(struct wimax_dev *wimax_dev, const char *pipe_name,
- const void *buf, size_t size, gfp_t gfp_flags)
- {
- int result = -ENOMEM;
- struct sk_buff *skb;
- skb = wimax_msg_alloc(wimax_dev, pipe_name, buf, size, gfp_flags);
- if (IS_ERR(skb))
- result = PTR_ERR(skb);
- else
- result = wimax_msg_send(wimax_dev, skb);
- return result;
- }
- EXPORT_SYMBOL_GPL(wimax_msg);
- /*
- * Relays a message from user space to the driver
- *
- * The skb is passed to the driver-specific function with the netlink
- * and generic netlink headers already stripped.
- *
- * This call will block while handling/relaying the message.
- */
- int wimax_gnl_doit_msg_from_user(struct sk_buff *skb, struct genl_info *info)
- {
- int result, ifindex;
- struct wimax_dev *wimax_dev;
- struct device *dev;
- struct nlmsghdr *nlh = info->nlhdr;
- char *pipe_name;
- void *msg_buf;
- size_t msg_len;
- might_sleep();
- d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
- result = -ENODEV;
- if (info->attrs[WIMAX_GNL_MSG_IFIDX] == NULL) {
- pr_err("WIMAX_GNL_MSG_FROM_USER: can't find IFIDX attribute\n");
- goto error_no_wimax_dev;
- }
- ifindex = nla_get_u32(info->attrs[WIMAX_GNL_MSG_IFIDX]);
- wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
- if (wimax_dev == NULL)
- goto error_no_wimax_dev;
- dev = wimax_dev_to_dev(wimax_dev);
- /* Unpack arguments */
- result = -EINVAL;
- if (info->attrs[WIMAX_GNL_MSG_DATA] == NULL) {
- dev_err(dev, "WIMAX_GNL_MSG_FROM_USER: can't find MSG_DATA "
- "attribute\n");
- goto error_no_data;
- }
- msg_buf = nla_data(info->attrs[WIMAX_GNL_MSG_DATA]);
- msg_len = nla_len(info->attrs[WIMAX_GNL_MSG_DATA]);
- if (info->attrs[WIMAX_GNL_MSG_PIPE_NAME] == NULL)
- pipe_name = NULL;
- else {
- struct nlattr *attr = info->attrs[WIMAX_GNL_MSG_PIPE_NAME];
- size_t attr_len = nla_len(attr);
- /* libnl-1.1 does not yet support NLA_NUL_STRING */
- result = -ENOMEM;
- pipe_name = kstrndup(nla_data(attr), attr_len + 1, GFP_KERNEL);
- if (pipe_name == NULL)
- goto error_alloc;
- pipe_name[attr_len] = 0;
- }
- mutex_lock(&wimax_dev->mutex);
- result = wimax_dev_is_ready(wimax_dev);
- if (result == -ENOMEDIUM)
- result = 0;
- if (result < 0)
- goto error_not_ready;
- result = -ENOSYS;
- if (wimax_dev->op_msg_from_user == NULL)
- goto error_noop;
- d_printf(1, dev,
- "CRX: nlmsghdr len %u type %u flags 0x%04x seq 0x%x pid %u\n",
- nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags,
- nlh->nlmsg_seq, nlh->nlmsg_pid);
- d_printf(1, dev, "CRX: wimax message %zu bytes\n", msg_len);
- d_dump(2, dev, msg_buf, msg_len);
- result = wimax_dev->op_msg_from_user(wimax_dev, pipe_name,
- msg_buf, msg_len, info);
- error_noop:
- error_not_ready:
- mutex_unlock(&wimax_dev->mutex);
- error_alloc:
- kfree(pipe_name);
- error_no_data:
- dev_put(wimax_dev->net_dev);
- error_no_wimax_dev:
- d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
- return result;
- }
|