18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Linux WiMAX
48c2ecf20Sopenharmony_ci * Generic messaging interface between userspace and driver/device
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
78c2ecf20Sopenharmony_ci * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This implements a direct communication channel between user space and
108c2ecf20Sopenharmony_ci * the driver/device, by which free form messages can be sent back and
118c2ecf20Sopenharmony_ci * forth.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This is intended for device-specific features, vendor quirks, etc.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * See include/net/wimax.h
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * GENERIC NETLINK ENCODING AND CAPACITY
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * A destination "pipe name" is added to each message; it is up to the
208c2ecf20Sopenharmony_ci * drivers to assign or use those names (if using them at all).
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * Messages are encoded as a binary netlink attribute using nla_put()
238c2ecf20Sopenharmony_ci * using type NLA_UNSPEC (as some versions of libnl still in
248c2ecf20Sopenharmony_ci * deployment don't yet understand NLA_BINARY).
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * The maximum capacity of this transport is PAGESIZE per message (so
278c2ecf20Sopenharmony_ci * the actual payload will be bit smaller depending on the
288c2ecf20Sopenharmony_ci * netlink/generic netlink attributes and headers).
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * RECEPTION OF MESSAGES
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * When a message is received from user space, it is passed verbatim
338c2ecf20Sopenharmony_ci * to the driver calling wimax_dev->op_msg_from_user(). The return
348c2ecf20Sopenharmony_ci * value from this function is passed back to user space as an ack
358c2ecf20Sopenharmony_ci * over the generic netlink protocol.
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * The stack doesn't do any processing or interpretation of these
388c2ecf20Sopenharmony_ci * messages.
398c2ecf20Sopenharmony_ci *
408c2ecf20Sopenharmony_ci * SENDING MESSAGES
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci * Messages can be sent with wimax_msg().
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci * If the message delivery needs to happen on a different context to
458c2ecf20Sopenharmony_ci * that of its creation, wimax_msg_alloc() can be used to get a
468c2ecf20Sopenharmony_ci * pointer to the message that can be delivered later on with
478c2ecf20Sopenharmony_ci * wimax_msg_send().
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * ROADMAP
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * wimax_gnl_doit_msg_from_user()    Process a message from user space
528c2ecf20Sopenharmony_ci *   wimax_dev_get_by_genl_info()
538c2ecf20Sopenharmony_ci *   wimax_dev->op_msg_from_user()   Delivery of message to the driver
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * wimax_msg()                       Send a message to user space
568c2ecf20Sopenharmony_ci *   wimax_msg_alloc()
578c2ecf20Sopenharmony_ci *   wimax_msg_send()
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_ci#include <linux/device.h>
608c2ecf20Sopenharmony_ci#include <linux/slab.h>
618c2ecf20Sopenharmony_ci#include <net/genetlink.h>
628c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
638c2ecf20Sopenharmony_ci#include <linux/wimax.h>
648c2ecf20Sopenharmony_ci#include <linux/security.h>
658c2ecf20Sopenharmony_ci#include <linux/export.h>
668c2ecf20Sopenharmony_ci#include "wimax-internal.h"
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define D_SUBMODULE op_msg
708c2ecf20Sopenharmony_ci#include "debug-levels.h"
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/**
748c2ecf20Sopenharmony_ci * wimax_msg_alloc - Create a new skb for sending a message to userspace
758c2ecf20Sopenharmony_ci *
768c2ecf20Sopenharmony_ci * @wimax_dev: WiMAX device descriptor
778c2ecf20Sopenharmony_ci * @pipe_name: "named pipe" the message will be sent to
788c2ecf20Sopenharmony_ci * @msg: pointer to the message data to send
798c2ecf20Sopenharmony_ci * @size: size of the message to send (in bytes), including the header.
808c2ecf20Sopenharmony_ci * @gfp_flags: flags for memory allocation.
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci * Returns: %0 if ok, negative errno code on error
838c2ecf20Sopenharmony_ci *
848c2ecf20Sopenharmony_ci * Description:
858c2ecf20Sopenharmony_ci *
868c2ecf20Sopenharmony_ci * Allocates an skb that will contain the message to send to user
878c2ecf20Sopenharmony_ci * space over the messaging pipe and initializes it, copying the
888c2ecf20Sopenharmony_ci * payload.
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci * Once this call is done, you can deliver it with
918c2ecf20Sopenharmony_ci * wimax_msg_send().
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci * IMPORTANT:
948c2ecf20Sopenharmony_ci *
958c2ecf20Sopenharmony_ci * Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as
968c2ecf20Sopenharmony_ci * wimax_msg_send() depends on skb->data being placed at the
978c2ecf20Sopenharmony_ci * beginning of the user message.
988c2ecf20Sopenharmony_ci *
998c2ecf20Sopenharmony_ci * Unlike other WiMAX stack calls, this call can be used way early,
1008c2ecf20Sopenharmony_ci * even before wimax_dev_add() is called, as long as the
1018c2ecf20Sopenharmony_ci * wimax_dev->net_dev pointer is set to point to a proper
1028c2ecf20Sopenharmony_ci * net_dev. This is so that drivers can use it early in case they need
1038c2ecf20Sopenharmony_ci * to send stuff around or communicate with user space.
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_cistruct sk_buff *wimax_msg_alloc(struct wimax_dev *wimax_dev,
1068c2ecf20Sopenharmony_ci				const char *pipe_name,
1078c2ecf20Sopenharmony_ci				const void *msg, size_t size,
1088c2ecf20Sopenharmony_ci				gfp_t gfp_flags)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	int result;
1118c2ecf20Sopenharmony_ci	struct device *dev = wimax_dev_to_dev(wimax_dev);
1128c2ecf20Sopenharmony_ci	size_t msg_size;
1138c2ecf20Sopenharmony_ci	void *genl_msg;
1148c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	msg_size = nla_total_size(size)
1178c2ecf20Sopenharmony_ci		+ nla_total_size(sizeof(u32))
1188c2ecf20Sopenharmony_ci		+ (pipe_name ? nla_total_size(strlen(pipe_name)) : 0);
1198c2ecf20Sopenharmony_ci	result = -ENOMEM;
1208c2ecf20Sopenharmony_ci	skb = genlmsg_new(msg_size, gfp_flags);
1218c2ecf20Sopenharmony_ci	if (skb == NULL)
1228c2ecf20Sopenharmony_ci		goto error_new;
1238c2ecf20Sopenharmony_ci	genl_msg = genlmsg_put(skb, 0, 0, &wimax_gnl_family,
1248c2ecf20Sopenharmony_ci			       0, WIMAX_GNL_OP_MSG_TO_USER);
1258c2ecf20Sopenharmony_ci	if (genl_msg == NULL) {
1268c2ecf20Sopenharmony_ci		dev_err(dev, "no memory to create generic netlink message\n");
1278c2ecf20Sopenharmony_ci		goto error_genlmsg_put;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	result = nla_put_u32(skb, WIMAX_GNL_MSG_IFIDX,
1308c2ecf20Sopenharmony_ci			     wimax_dev->net_dev->ifindex);
1318c2ecf20Sopenharmony_ci	if (result < 0) {
1328c2ecf20Sopenharmony_ci		dev_err(dev, "no memory to add ifindex attribute\n");
1338c2ecf20Sopenharmony_ci		goto error_nla_put;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci	if (pipe_name) {
1368c2ecf20Sopenharmony_ci		result = nla_put_string(skb, WIMAX_GNL_MSG_PIPE_NAME,
1378c2ecf20Sopenharmony_ci					pipe_name);
1388c2ecf20Sopenharmony_ci		if (result < 0) {
1398c2ecf20Sopenharmony_ci			dev_err(dev, "no memory to add pipe_name attribute\n");
1408c2ecf20Sopenharmony_ci			goto error_nla_put;
1418c2ecf20Sopenharmony_ci		}
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	result = nla_put(skb, WIMAX_GNL_MSG_DATA, size, msg);
1448c2ecf20Sopenharmony_ci	if (result < 0) {
1458c2ecf20Sopenharmony_ci		dev_err(dev, "no memory to add payload (msg %p size %zu) in "
1468c2ecf20Sopenharmony_ci			"attribute: %d\n", msg, size, result);
1478c2ecf20Sopenharmony_ci		goto error_nla_put;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci	genlmsg_end(skb, genl_msg);
1508c2ecf20Sopenharmony_ci	return skb;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cierror_nla_put:
1538c2ecf20Sopenharmony_cierror_genlmsg_put:
1548c2ecf20Sopenharmony_cierror_new:
1558c2ecf20Sopenharmony_ci	nlmsg_free(skb);
1568c2ecf20Sopenharmony_ci	return ERR_PTR(result);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_msg_alloc);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci/**
1628c2ecf20Sopenharmony_ci * wimax_msg_data_len - Return a pointer and size of a message's payload
1638c2ecf20Sopenharmony_ci *
1648c2ecf20Sopenharmony_ci * @msg: Pointer to a message created with wimax_msg_alloc()
1658c2ecf20Sopenharmony_ci * @size: Pointer to where to store the message's size
1668c2ecf20Sopenharmony_ci *
1678c2ecf20Sopenharmony_ci * Returns the pointer to the message data.
1688c2ecf20Sopenharmony_ci */
1698c2ecf20Sopenharmony_ciconst void *wimax_msg_data_len(struct sk_buff *msg, size_t *size)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh = (void *) msg->head;
1728c2ecf20Sopenharmony_ci	struct nlattr *nla;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
1758c2ecf20Sopenharmony_ci			      WIMAX_GNL_MSG_DATA);
1768c2ecf20Sopenharmony_ci	if (nla == NULL) {
1778c2ecf20Sopenharmony_ci		pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
1788c2ecf20Sopenharmony_ci		return NULL;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci	*size = nla_len(nla);
1818c2ecf20Sopenharmony_ci	return nla_data(nla);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_msg_data_len);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci/**
1878c2ecf20Sopenharmony_ci * wimax_msg_data - Return a pointer to a message's payload
1888c2ecf20Sopenharmony_ci *
1898c2ecf20Sopenharmony_ci * @msg: Pointer to a message created with wimax_msg_alloc()
1908c2ecf20Sopenharmony_ci */
1918c2ecf20Sopenharmony_ciconst void *wimax_msg_data(struct sk_buff *msg)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh = (void *) msg->head;
1948c2ecf20Sopenharmony_ci	struct nlattr *nla;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
1978c2ecf20Sopenharmony_ci			      WIMAX_GNL_MSG_DATA);
1988c2ecf20Sopenharmony_ci	if (nla == NULL) {
1998c2ecf20Sopenharmony_ci		pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
2008c2ecf20Sopenharmony_ci		return NULL;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	return nla_data(nla);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_msg_data);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/**
2088c2ecf20Sopenharmony_ci * wimax_msg_len - Return a message's payload length
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * @msg: Pointer to a message created with wimax_msg_alloc()
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_cissize_t wimax_msg_len(struct sk_buff *msg)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh = (void *) msg->head;
2158c2ecf20Sopenharmony_ci	struct nlattr *nla;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	nla = nlmsg_find_attr(nlh, sizeof(struct genlmsghdr),
2188c2ecf20Sopenharmony_ci			      WIMAX_GNL_MSG_DATA);
2198c2ecf20Sopenharmony_ci	if (nla == NULL) {
2208c2ecf20Sopenharmony_ci		pr_err("Cannot find attribute WIMAX_GNL_MSG_DATA\n");
2218c2ecf20Sopenharmony_ci		return -EINVAL;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci	return nla_len(nla);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_msg_len);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/**
2298c2ecf20Sopenharmony_ci * wimax_msg_send - Send a pre-allocated message to user space
2308c2ecf20Sopenharmony_ci *
2318c2ecf20Sopenharmony_ci * @wimax_dev: WiMAX device descriptor
2328c2ecf20Sopenharmony_ci *
2338c2ecf20Sopenharmony_ci * @skb: &struct sk_buff returned by wimax_msg_alloc(). Note the
2348c2ecf20Sopenharmony_ci *     ownership of @skb is transferred to this function.
2358c2ecf20Sopenharmony_ci *
2368c2ecf20Sopenharmony_ci * Returns: 0 if ok, < 0 errno code on error
2378c2ecf20Sopenharmony_ci *
2388c2ecf20Sopenharmony_ci * Description:
2398c2ecf20Sopenharmony_ci *
2408c2ecf20Sopenharmony_ci * Sends a free-form message that was preallocated with
2418c2ecf20Sopenharmony_ci * wimax_msg_alloc() and filled up.
2428c2ecf20Sopenharmony_ci *
2438c2ecf20Sopenharmony_ci * Assumes that once you pass an skb to this function for sending, it
2448c2ecf20Sopenharmony_ci * owns it and will release it when done (on success).
2458c2ecf20Sopenharmony_ci *
2468c2ecf20Sopenharmony_ci * IMPORTANT:
2478c2ecf20Sopenharmony_ci *
2488c2ecf20Sopenharmony_ci * Don't use skb_push()/skb_pull()/skb_reserve() on the skb, as
2498c2ecf20Sopenharmony_ci * wimax_msg_send() depends on skb->data being placed at the
2508c2ecf20Sopenharmony_ci * beginning of the user message.
2518c2ecf20Sopenharmony_ci *
2528c2ecf20Sopenharmony_ci * Unlike other WiMAX stack calls, this call can be used way early,
2538c2ecf20Sopenharmony_ci * even before wimax_dev_add() is called, as long as the
2548c2ecf20Sopenharmony_ci * wimax_dev->net_dev pointer is set to point to a proper
2558c2ecf20Sopenharmony_ci * net_dev. This is so that drivers can use it early in case they need
2568c2ecf20Sopenharmony_ci * to send stuff around or communicate with user space.
2578c2ecf20Sopenharmony_ci */
2588c2ecf20Sopenharmony_ciint wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct device *dev = wimax_dev_to_dev(wimax_dev);
2618c2ecf20Sopenharmony_ci	void *msg = skb->data;
2628c2ecf20Sopenharmony_ci	size_t size = skb->len;
2638c2ecf20Sopenharmony_ci	might_sleep();
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	d_printf(1, dev, "CTX: wimax msg, %zu bytes\n", size);
2668c2ecf20Sopenharmony_ci	d_dump(2, dev, msg, size);
2678c2ecf20Sopenharmony_ci	genlmsg_multicast(&wimax_gnl_family, skb, 0, 0, GFP_KERNEL);
2688c2ecf20Sopenharmony_ci	d_printf(1, dev, "CTX: genl multicast done\n");
2698c2ecf20Sopenharmony_ci	return 0;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_msg_send);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/**
2758c2ecf20Sopenharmony_ci * wimax_msg - Send a message to user space
2768c2ecf20Sopenharmony_ci *
2778c2ecf20Sopenharmony_ci * @wimax_dev: WiMAX device descriptor (properly referenced)
2788c2ecf20Sopenharmony_ci * @pipe_name: "named pipe" the message will be sent to
2798c2ecf20Sopenharmony_ci * @buf: pointer to the message to send.
2808c2ecf20Sopenharmony_ci * @size: size of the buffer pointed to by @buf (in bytes).
2818c2ecf20Sopenharmony_ci * @gfp_flags: flags for memory allocation.
2828c2ecf20Sopenharmony_ci *
2838c2ecf20Sopenharmony_ci * Returns: %0 if ok, negative errno code on error.
2848c2ecf20Sopenharmony_ci *
2858c2ecf20Sopenharmony_ci * Description:
2868c2ecf20Sopenharmony_ci *
2878c2ecf20Sopenharmony_ci * Sends a free-form message to user space on the device @wimax_dev.
2888c2ecf20Sopenharmony_ci *
2898c2ecf20Sopenharmony_ci * NOTES:
2908c2ecf20Sopenharmony_ci *
2918c2ecf20Sopenharmony_ci * Once the @skb is given to this function, who will own it and will
2928c2ecf20Sopenharmony_ci * release it when done (unless it returns error).
2938c2ecf20Sopenharmony_ci */
2948c2ecf20Sopenharmony_ciint wimax_msg(struct wimax_dev *wimax_dev, const char *pipe_name,
2958c2ecf20Sopenharmony_ci	      const void *buf, size_t size, gfp_t gfp_flags)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	int result = -ENOMEM;
2988c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	skb = wimax_msg_alloc(wimax_dev, pipe_name, buf, size, gfp_flags);
3018c2ecf20Sopenharmony_ci	if (IS_ERR(skb))
3028c2ecf20Sopenharmony_ci		result = PTR_ERR(skb);
3038c2ecf20Sopenharmony_ci	else
3048c2ecf20Sopenharmony_ci		result = wimax_msg_send(wimax_dev, skb);
3058c2ecf20Sopenharmony_ci	return result;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wimax_msg);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci/*
3108c2ecf20Sopenharmony_ci * Relays a message from user space to the driver
3118c2ecf20Sopenharmony_ci *
3128c2ecf20Sopenharmony_ci * The skb is passed to the driver-specific function with the netlink
3138c2ecf20Sopenharmony_ci * and generic netlink headers already stripped.
3148c2ecf20Sopenharmony_ci *
3158c2ecf20Sopenharmony_ci * This call will block while handling/relaying the message.
3168c2ecf20Sopenharmony_ci */
3178c2ecf20Sopenharmony_ciint wimax_gnl_doit_msg_from_user(struct sk_buff *skb, struct genl_info *info)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	int result, ifindex;
3208c2ecf20Sopenharmony_ci	struct wimax_dev *wimax_dev;
3218c2ecf20Sopenharmony_ci	struct device *dev;
3228c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh = info->nlhdr;
3238c2ecf20Sopenharmony_ci	char *pipe_name;
3248c2ecf20Sopenharmony_ci	void *msg_buf;
3258c2ecf20Sopenharmony_ci	size_t msg_len;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	might_sleep();
3288c2ecf20Sopenharmony_ci	d_fnstart(3, NULL, "(skb %p info %p)\n", skb, info);
3298c2ecf20Sopenharmony_ci	result = -ENODEV;
3308c2ecf20Sopenharmony_ci	if (info->attrs[WIMAX_GNL_MSG_IFIDX] == NULL) {
3318c2ecf20Sopenharmony_ci		pr_err("WIMAX_GNL_MSG_FROM_USER: can't find IFIDX attribute\n");
3328c2ecf20Sopenharmony_ci		goto error_no_wimax_dev;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci	ifindex = nla_get_u32(info->attrs[WIMAX_GNL_MSG_IFIDX]);
3358c2ecf20Sopenharmony_ci	wimax_dev = wimax_dev_get_by_genl_info(info, ifindex);
3368c2ecf20Sopenharmony_ci	if (wimax_dev == NULL)
3378c2ecf20Sopenharmony_ci		goto error_no_wimax_dev;
3388c2ecf20Sopenharmony_ci	dev = wimax_dev_to_dev(wimax_dev);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* Unpack arguments */
3418c2ecf20Sopenharmony_ci	result = -EINVAL;
3428c2ecf20Sopenharmony_ci	if (info->attrs[WIMAX_GNL_MSG_DATA] == NULL) {
3438c2ecf20Sopenharmony_ci		dev_err(dev, "WIMAX_GNL_MSG_FROM_USER: can't find MSG_DATA "
3448c2ecf20Sopenharmony_ci			"attribute\n");
3458c2ecf20Sopenharmony_ci		goto error_no_data;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	msg_buf = nla_data(info->attrs[WIMAX_GNL_MSG_DATA]);
3488c2ecf20Sopenharmony_ci	msg_len = nla_len(info->attrs[WIMAX_GNL_MSG_DATA]);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (info->attrs[WIMAX_GNL_MSG_PIPE_NAME] == NULL)
3518c2ecf20Sopenharmony_ci		pipe_name = NULL;
3528c2ecf20Sopenharmony_ci	else {
3538c2ecf20Sopenharmony_ci		struct nlattr *attr = info->attrs[WIMAX_GNL_MSG_PIPE_NAME];
3548c2ecf20Sopenharmony_ci		size_t attr_len = nla_len(attr);
3558c2ecf20Sopenharmony_ci		/* libnl-1.1 does not yet support NLA_NUL_STRING */
3568c2ecf20Sopenharmony_ci		result = -ENOMEM;
3578c2ecf20Sopenharmony_ci		pipe_name = kstrndup(nla_data(attr), attr_len + 1, GFP_KERNEL);
3588c2ecf20Sopenharmony_ci		if (pipe_name == NULL)
3598c2ecf20Sopenharmony_ci			goto error_alloc;
3608c2ecf20Sopenharmony_ci		pipe_name[attr_len] = 0;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci	mutex_lock(&wimax_dev->mutex);
3638c2ecf20Sopenharmony_ci	result = wimax_dev_is_ready(wimax_dev);
3648c2ecf20Sopenharmony_ci	if (result == -ENOMEDIUM)
3658c2ecf20Sopenharmony_ci		result = 0;
3668c2ecf20Sopenharmony_ci	if (result < 0)
3678c2ecf20Sopenharmony_ci		goto error_not_ready;
3688c2ecf20Sopenharmony_ci	result = -ENOSYS;
3698c2ecf20Sopenharmony_ci	if (wimax_dev->op_msg_from_user == NULL)
3708c2ecf20Sopenharmony_ci		goto error_noop;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	d_printf(1, dev,
3738c2ecf20Sopenharmony_ci		 "CRX: nlmsghdr len %u type %u flags 0x%04x seq 0x%x pid %u\n",
3748c2ecf20Sopenharmony_ci		 nlh->nlmsg_len, nlh->nlmsg_type, nlh->nlmsg_flags,
3758c2ecf20Sopenharmony_ci		 nlh->nlmsg_seq, nlh->nlmsg_pid);
3768c2ecf20Sopenharmony_ci	d_printf(1, dev, "CRX: wimax message %zu bytes\n", msg_len);
3778c2ecf20Sopenharmony_ci	d_dump(2, dev, msg_buf, msg_len);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	result = wimax_dev->op_msg_from_user(wimax_dev, pipe_name,
3808c2ecf20Sopenharmony_ci					     msg_buf, msg_len, info);
3818c2ecf20Sopenharmony_cierror_noop:
3828c2ecf20Sopenharmony_cierror_not_ready:
3838c2ecf20Sopenharmony_ci	mutex_unlock(&wimax_dev->mutex);
3848c2ecf20Sopenharmony_cierror_alloc:
3858c2ecf20Sopenharmony_ci	kfree(pipe_name);
3868c2ecf20Sopenharmony_cierror_no_data:
3878c2ecf20Sopenharmony_ci	dev_put(wimax_dev->net_dev);
3888c2ecf20Sopenharmony_cierror_no_wimax_dev:
3898c2ecf20Sopenharmony_ci	d_fnend(3, NULL, "(skb %p info %p) = %d\n", skb, info, result);
3908c2ecf20Sopenharmony_ci	return result;
3918c2ecf20Sopenharmony_ci}
392