13e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez/*
23e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Linux WiMAX
33e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Generic messaging interface between userspace and driver/device
43e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
53e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
63e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
73e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
83e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
93e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * This program is free software; you can redistribute it and/or
103e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * modify it under the terms of the GNU General Public License version
113e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * 2 as published by the Free Software Foundation.
123e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
133e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * This program is distributed in the hope that it will be useful,
143e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * but WITHOUT ANY WARRANTY; without even the implied warranty of
153e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
163e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * GNU General Public License for more details.
173e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
183e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * You should have received a copy of the GNU General Public License
193e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * along with this program; if not, write to the Free Software
203e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
213e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * 02110-1301, USA.
223e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
233e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
243e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * This implements a direct communication channel between user space and
253e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * the driver/device, by which free form messages can be sent back and
263e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * forth.
273e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
283e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * This is intended for device-specific features, vendor quirks, etc.
293e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
303e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * See include/net/wimax.h
313e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
323e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * GENERIC NETLINK ENCODING AND CAPACITY
333e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
343e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * A destination "pipe name" is added to each message; it is up to the
353e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * drivers to assign or use those names (if using them at all).
363e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
373e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Messages are encoded as a binary netlink attribute using nla_put()
383e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * using type NLA_UNSPEC (as some versions of libnl still in
393e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * deployment don't yet understand NLA_BINARY).
403e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
413e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * The maximum capacity of this transport is PAGESIZE per message (so
423e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * the actual payload will be bit smaller depending on the
433e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * netlink/generic netlink attributes and headers).
443e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
453e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * RECEPTION OF MESSAGES
463e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
473e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * When a message is received from user space, it is passed verbatim
483e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * to the driver calling wimax_dev->op_msg_from_user(). The return
493e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * value from this function is passed back to user space as an ack
503e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * over the generic netlink protocol.
513e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
523e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * The stack doesn't do any processing or interpretation of these
533e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * messages.
543e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
553e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * SENDING MESSAGES
563e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
573e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Messages can be sent with wimax_msg().
583e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
593e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * If the message delivery needs to happen on a different context to
603e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * that of its creation, wimax_msg_alloc() can be used to get a
613e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * pointer to the message that can be delivered later on with
623e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg_send().
633e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
643e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * ROADMAP
653e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
663e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_gnl_doit_msg_from_user()    Process a message from user space
673e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *   wimax_dev_get_by_genl_info()
683e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *   wimax_dev->op_msg_from_user()   Delivery of message to the driver
693e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
703e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg()                       Send a message to user space
713e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *   wimax_msg_alloc()
723e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *   wimax_msg_send()
733e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez */
743e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez#include <linux/device.h>
755a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
763e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez#include <net/genetlink.h>
773e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez#include <linux/netdevice.h>
783e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez#include <linux/wimax.h>
793e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez#include <linux/security.h>
80bc3b2d7fb9b014d75ebb79ba371a763dbab5e8cfPaul Gortmaker#include <linux/export.h>
813e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez#include "wimax-internal.h"
823e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
833e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
843e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez#define D_SUBMODULE op_msg
853e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez#include "debug-levels.h"
863e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
873e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
883e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez/**
893e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg_alloc - Create a new skb for sending a message to userspace
903e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
913e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @wimax_dev: WiMAX device descriptor
923e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @pipe_name: "named pipe" the message will be sent to
933e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @msg: pointer to the message data to send
943e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @size: size of the message to send (in bytes), including the header.
953e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @gfp_flags: flags for memory allocation.
963e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
973e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Returns: %0 if ok, negative errno code on error
983e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
993e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Description:
1003e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
1013e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Allocates an skb that will contain the message to send to user
1023e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * space over the messaging pipe and initializes it, copying the
1033e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * payload.
1043e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
1053e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Once this call is done, you can deliver it with
1063e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg_send().
1073e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
1083e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * IMPORTANT:
1093e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
1103e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as
1113e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg_send() depends on skb->data being placed at the
1123e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * beginning of the user message.
11352a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez *
11452a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez * Unlike other WiMAX stack calls, this call can be used way early,
11552a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez * even before wimax_dev_add() is called, as long as the
11652a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez * wimax_dev->net_dev pointer is set to point to a proper
11752a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez * net_dev. This is so that drivers can use it early in case they need
11852a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez * to send stuff around or communicate with user space.
1193e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez */
1203e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezstruct sk_buff *wimax_msg_alloc(struct wimax_dev *wimax_dev,
1213e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez				const char *pipe_name,
1223e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez				const void *msg, size_t size,
1233e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez				gfp_t gfp_flags)
1243e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez{
1253e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	int result;
12652a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez	struct device *dev = wimax_dev_to_dev(wimax_dev);
1273e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	size_t msg_size;
1283e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	void *genl_msg;
1293e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct sk_buff *skb;
1303e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
1313e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	msg_size = nla_total_size(size)
1323e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		+ nla_total_size(sizeof(u32))
1333e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		+ (pipe_name ? nla_total_size(strlen(pipe_name)) : 0);
1343e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	result = -ENOMEM;
1353e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	skb = genlmsg_new(msg_size, gfp_flags);
1363e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (skb == NULL)
1373e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		goto error_new;
1383e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	genl_msg = genlmsg_put(skb, 0, 0, &wimax_gnl_family,
1393e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez			       0, WIMAX_GNL_OP_MSG_TO_USER);
1403e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (genl_msg == NULL) {
1413e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		dev_err(dev, "no memory to create generic netlink message\n");
1423e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		goto error_genlmsg_put;
1433e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	}
1443e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	result = nla_put_u32(skb, WIMAX_GNL_MSG_IFIDX,
1453e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez			     wimax_dev->net_dev->ifindex);
1463e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (result < 0) {
1473e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		dev_err(dev, "no memory to add ifindex attribute\n");
1483e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		goto error_nla_put;
1493e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	}
1503e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (pipe_name) {
1513e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		result = nla_put_string(skb, WIMAX_GNL_MSG_PIPE_NAME,
1523e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez					pipe_name);
1533e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		if (result < 0) {
1543e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez			dev_err(dev, "no memory to add pipe_name attribute\n");
1553e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez			goto error_nla_put;
1563e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		}
1573e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	}
1583e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	result = nla_put(skb, WIMAX_GNL_MSG_DATA, size, msg);
1593e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (result < 0) {
160d1a2627a29667fe7c4a9d06e1579a2d65bd39bbaInaky Perez-Gonzalez		dev_err(dev, "no memory to add payload (msg %p size %zu) in "
161d1a2627a29667fe7c4a9d06e1579a2d65bd39bbaInaky Perez-Gonzalez			"attribute: %d\n", msg, size, result);
1623e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		goto error_nla_put;
1633e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	}
1643e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	genlmsg_end(skb, genl_msg);
1653e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	return skb;
1663e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
1673e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezerror_nla_put:
1683e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezerror_genlmsg_put:
1693e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezerror_new:
1703e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	nlmsg_free(skb);
1713e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	return ERR_PTR(result);
1723e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez}
1733e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-GonzalezEXPORT_SYMBOL_GPL(wimax_msg_alloc);
1743e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
1753e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
1763e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez/**
1773e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg_data_len - Return a pointer and size of a message's payload
1783e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
1793e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @msg: Pointer to a message created with wimax_msg_alloc()
1803e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @size: Pointer to where to store the message's size
1813e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
1823e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Returns the pointer to the message data.
1833e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez */
1843e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezconst void *wimax_msg_data_len(struct sk_buff *msg, size_t *size)
1853e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez{
1863e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct nlmsghdr *nlh = (void *) msg->head;
1873e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct nlattr *nla;
1883e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
1893e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
1903e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez			      WIMAX_GNL_MSG_DATA);
1913e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (nla == NULL) {
19228b7deae75642c51f097391765fd39ff0dd6ce95Fabian Frederick		pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
1933e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		return NULL;
1943e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	}
1953e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	*size = nla_len(nla);
1963e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	return nla_data(nla);
1973e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez}
1983e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-GonzalezEXPORT_SYMBOL_GPL(wimax_msg_data_len);
1993e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2003e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2013e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez/**
2023e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg_data - Return a pointer to a message's payload
2033e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2043e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @msg: Pointer to a message created with wimax_msg_alloc()
2053e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez */
2063e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezconst void *wimax_msg_data(struct sk_buff *msg)
2073e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez{
2083e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct nlmsghdr *nlh = (void *) msg->head;
2093e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct nlattr *nla;
2103e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2113e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
2123e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez			      WIMAX_GNL_MSG_DATA);
2133e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (nla == NULL) {
21428b7deae75642c51f097391765fd39ff0dd6ce95Fabian Frederick		pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
2153e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		return NULL;
2163e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	}
2173e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	return nla_data(nla);
2183e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez}
2193e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-GonzalezEXPORT_SYMBOL_GPL(wimax_msg_data);
2203e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2213e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2223e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez/**
2233e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg_len - Return a message's payload length
2243e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2253e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @msg: Pointer to a message created with wimax_msg_alloc()
2263e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez */
2273e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezssize_t wimax_msg_len(struct sk_buff *msg)
2283e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez{
2293e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct nlmsghdr *nlh = (void *) msg->head;
2303e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct nlattr *nla;
2313e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2323e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
2333e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez			      WIMAX_GNL_MSG_DATA);
2343e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (nla == NULL) {
23528b7deae75642c51f097391765fd39ff0dd6ce95Fabian Frederick		pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
2363e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		return -EINVAL;
2373e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	}
2383e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	return nla_len(nla);
2393e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez}
2403e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-GonzalezEXPORT_SYMBOL_GPL(wimax_msg_len);
2413e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2423e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2433e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez/**
2443e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg_send - Send a pre-allocated message to user space
2453e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2463e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @wimax_dev: WiMAX device descriptor
2473e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2483e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @skb: &struct sk_buff returned by wimax_msg_alloc(). Note the
2493e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *     ownership of @skb is transferred to this function.
2503e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2513e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Returns: 0 if ok, < 0 errno code on error
2523e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2533e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Description:
2543e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2553e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Sends a free-form message that was preallocated with
2563e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg_alloc() and filled up.
2573e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2583e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Assumes that once you pass an skb to this function for sending, it
2593e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * owns it and will release it when done (on success).
2603e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2613e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * IMPORTANT:
2623e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2633e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as
2643e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg_send() depends on skb->data being placed at the
2653e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * beginning of the user message.
26652a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez *
26752a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez * Unlike other WiMAX stack calls, this call can be used way early,
26852a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez * even before wimax_dev_add() is called, as long as the
26952a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez * wimax_dev->net_dev pointer is set to point to a proper
27052a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez * net_dev. This is so that drivers can use it early in case they need
27152a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez * to send stuff around or communicate with user space.
2723e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez */
2733e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezint wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb)
2743e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez{
27552a8d9630890f2289354d03e8751bf0eba2b3376Inaky Perez-Gonzalez	struct device *dev = wimax_dev_to_dev(wimax_dev);
2763e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	void *msg = skb->data;
2773e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	size_t size = skb->len;
2783e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	might_sleep();
2793e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2803e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	d_printf(1, dev, "CTX: wimax msg, %zu bytes\n", size);
2813e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	d_dump(2, dev, msg, size);
2822a94fe48f32ccf7321450a2cc07f2b724a444e5bJohannes Berg	genlmsg_multicast(&wimax_gnl_family, skb, 0, 0, GFP_KERNEL);
283ff491a7334acfd74e515c896632e37e401f52676Pablo Neira Ayuso	d_printf(1, dev, "CTX: genl multicast done\n");
284ff491a7334acfd74e515c896632e37e401f52676Pablo Neira Ayuso	return 0;
2853e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez}
2863e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-GonzalezEXPORT_SYMBOL_GPL(wimax_msg_send);
2873e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2883e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
2893e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez/**
2903e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * wimax_msg - Send a message to user space
2913e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2923e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @wimax_dev: WiMAX device descriptor (properly referenced)
2933e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @pipe_name: "named pipe" the message will be sent to
2943e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @buf: pointer to the message to send.
2953e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @size: size of the buffer pointed to by @buf (in bytes).
2963e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * @gfp_flags: flags for memory allocation.
2973e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
2983e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Returns: %0 if ok, negative errno code on error.
2993e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
3003e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Description:
3013e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
3023e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Sends a free-form message to user space on the device @wimax_dev.
3033e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
3043e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * NOTES:
3053e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
3063e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Once the @skb is given to this function, who will own it and will
3073e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * release it when done (unless it returns error).
3083e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez */
3093e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezint wimax_msg(struct wimax_dev *wimax_dev, const char *pipe_name,
3103e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	      const void *buf, size_t size, gfp_t gfp_flags)
3113e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez{
3123e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	int result = -ENOMEM;
3133e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct sk_buff *skb;
3143e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
3153e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	skb = wimax_msg_alloc(wimax_dev, pipe_name, buf, size, gfp_flags);
316d1a2627a29667fe7c4a9d06e1579a2d65bd39bbaInaky Perez-Gonzalez	if (IS_ERR(skb))
317d1a2627a29667fe7c4a9d06e1579a2d65bd39bbaInaky Perez-Gonzalez		result = PTR_ERR(skb);
318d1a2627a29667fe7c4a9d06e1579a2d65bd39bbaInaky Perez-Gonzalez	else
319d1a2627a29667fe7c4a9d06e1579a2d65bd39bbaInaky Perez-Gonzalez		result = wimax_msg_send(wimax_dev, skb);
3203e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	return result;
3213e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez}
3223e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-GonzalezEXPORT_SYMBOL_GPL(wimax_msg);
3233e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
3243e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez/*
3253e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * Relays a message from user space to the driver
3263e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
3273e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * The skb is passed to the driver-specific function with the netlink
3283e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * and generic netlink headers already stripped.
3293e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez *
3303e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez * This call will block while handling/relaying the message.
3313e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez */
3323e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezint wimax_gnl_doit_msg_from_user(struct sk_buff *skb, struct genl_info *info)
3333e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez{
3343e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	int result, ifindex;
3353e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct wimax_dev *wimax_dev;
3363e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct device *dev;
3373e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	struct nlmsghdr *nlh = info->nlhdr;
3383e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	char *pipe_name;
3393e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	void *msg_buf;
3403e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	size_t msg_len;
3413e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
3423e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	might_sleep();
3433e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
3443e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	result = -ENODEV;
3453e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (info->attrs[WIMAX_GNL_MSG_IFIDX] == NULL) {
34628b7deae75642c51f097391765fd39ff0dd6ce95Fabian Frederick		pr_err("WIMAX_GNL_MSG_FROM_USER: can't find IFIDX attribute\n");
3473e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		goto error_no_wimax_dev;
3483e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	}
3493e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	ifindex = nla_get_u32(info->attrs[WIMAX_GNL_MSG_IFIDX]);
3503e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
3513e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (wimax_dev == NULL)
3523e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		goto error_no_wimax_dev;
3533e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	dev = wimax_dev_to_dev(wimax_dev);
3543e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
3553e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	/* Unpack arguments */
3563e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	result = -EINVAL;
3573e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (info->attrs[WIMAX_GNL_MSG_DATA] == NULL) {
3583e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		dev_err(dev, "WIMAX_GNL_MSG_FROM_USER: can't find MSG_DATA "
3593e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez			"attribute\n");
3603e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		goto error_no_data;
3613e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	}
3623e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	msg_buf = nla_data(info->attrs[WIMAX_GNL_MSG_DATA]);
3633e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	msg_len = nla_len(info->attrs[WIMAX_GNL_MSG_DATA]);
3643e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
3653e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (info->attrs[WIMAX_GNL_MSG_PIPE_NAME] == NULL)
3663e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		pipe_name = NULL;
3673e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	else {
3683e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		struct nlattr *attr = info->attrs[WIMAX_GNL_MSG_PIPE_NAME];
3693e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		size_t attr_len = nla_len(attr);
3703e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		/* libnl-1.1 does not yet support NLA_NUL_STRING */
3713e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		result = -ENOMEM;
3723e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		pipe_name = kstrndup(nla_data(attr), attr_len + 1, GFP_KERNEL);
3733e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		if (pipe_name == NULL)
3743e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez			goto error_alloc;
3753e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		pipe_name[attr_len] = 0;
3763e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	}
3773e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	mutex_lock(&wimax_dev->mutex);
3783e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	result = wimax_dev_is_ready(wimax_dev);
379de9315fa3a35ebe587cc1a1c88655d095846785eInaky Perez-Gonzalez	if (result == -ENOMEDIUM)
380de9315fa3a35ebe587cc1a1c88655d095846785eInaky Perez-Gonzalez		result = 0;
3813e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (result < 0)
3823e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		goto error_not_ready;
3833e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	result = -ENOSYS;
3843e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	if (wimax_dev->op_msg_from_user == NULL)
3853e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		goto error_noop;
3863e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
3873e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	d_printf(1, dev,
3883e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		 "CRX: nlmsghdr len %u type %u flags 0x%04x seq 0x%x pid %u\n",
3893e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		 nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags,
3903e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez		 nlh->nlmsg_seq, nlh->nlmsg_pid);
3913e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	d_printf(1, dev, "CRX: wimax message %zu bytes\n", msg_len);
3923e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	d_dump(2, dev, msg_buf, msg_len);
3933e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
3943e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	result = wimax_dev->op_msg_from_user(wimax_dev, pipe_name,
3953e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez					     msg_buf, msg_len, info);
3963e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezerror_noop:
3973e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezerror_not_ready:
3983e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	mutex_unlock(&wimax_dev->mutex);
3993e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezerror_alloc:
4003e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	kfree(pipe_name);
4013e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezerror_no_data:
4023e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	dev_put(wimax_dev->net_dev);
4033e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalezerror_no_wimax_dev:
4043e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
4053e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez	return result;
4063e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez}
4073e65646bb12be03b14dff53907391095a52d5f49Inaky Perez-Gonzalez
408