18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/slab.h>
78c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
88c2ecf20Sopenharmony_ci#include <linux/netlink.h>
98c2ecf20Sopenharmony_ci#include <linux/connector.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "w1_internal.h"
128c2ecf20Sopenharmony_ci#include "w1_netlink.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE)))
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* Bundle together everything required to process a request in one memory
178c2ecf20Sopenharmony_ci * allocation.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_cistruct w1_cb_block {
208c2ecf20Sopenharmony_ci	atomic_t refcnt;
218c2ecf20Sopenharmony_ci	u32 portid; /* Sending process port ID */
228c2ecf20Sopenharmony_ci	/* maximum value for first_cn->len */
238c2ecf20Sopenharmony_ci	u16 maxlen;
248c2ecf20Sopenharmony_ci	/* pointers to building up the reply message */
258c2ecf20Sopenharmony_ci	struct cn_msg *first_cn; /* fixed once the structure is populated */
268c2ecf20Sopenharmony_ci	struct cn_msg *cn; /* advances as cn_msg is appeneded */
278c2ecf20Sopenharmony_ci	struct w1_netlink_msg *msg; /* advances as w1_netlink_msg is appened */
288c2ecf20Sopenharmony_ci	struct w1_netlink_cmd *cmd; /* advances as cmds are appened */
298c2ecf20Sopenharmony_ci	struct w1_netlink_msg *cur_msg; /* currently message being processed */
308c2ecf20Sopenharmony_ci	/* copy of the original request follows */
318c2ecf20Sopenharmony_ci	struct cn_msg request_cn;
328c2ecf20Sopenharmony_ci	/* followed by variable length:
338c2ecf20Sopenharmony_ci	 * cn_msg, data (w1_netlink_msg and w1_netlink_cmd)
348c2ecf20Sopenharmony_ci	 * one or more struct w1_cb_node
358c2ecf20Sopenharmony_ci	 * reply first_cn, data (w1_netlink_msg and w1_netlink_cmd)
368c2ecf20Sopenharmony_ci	 */
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_cistruct w1_cb_node {
398c2ecf20Sopenharmony_ci	struct w1_async_cmd async;
408c2ecf20Sopenharmony_ci	/* pointers within w1_cb_block and cn data */
418c2ecf20Sopenharmony_ci	struct w1_cb_block *block;
428c2ecf20Sopenharmony_ci	struct w1_netlink_msg *msg;
438c2ecf20Sopenharmony_ci	struct w1_slave *sl;
448c2ecf20Sopenharmony_ci	struct w1_master *dev;
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/**
488c2ecf20Sopenharmony_ci * w1_reply_len() - calculate current reply length, compare to maxlen
498c2ecf20Sopenharmony_ci * @block: block to calculate
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * Calculates the current message length including possible multiple
528c2ecf20Sopenharmony_ci * cn_msg and data, excludes the first sizeof(struct cn_msg).  Direclty
538c2ecf20Sopenharmony_ci * compariable to maxlen and usable to send the message.
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_cistatic u16 w1_reply_len(struct w1_cb_block *block)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	if (!block->cn)
588c2ecf20Sopenharmony_ci		return 0;
598c2ecf20Sopenharmony_ci	return (u8 *)block->cn - (u8 *)block->first_cn + block->cn->len;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void w1_unref_block(struct w1_cb_block *block)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	if (atomic_sub_return(1, &block->refcnt) == 0) {
658c2ecf20Sopenharmony_ci		u16 len = w1_reply_len(block);
668c2ecf20Sopenharmony_ci		if (len) {
678c2ecf20Sopenharmony_ci			cn_netlink_send_mult(block->first_cn, len,
688c2ecf20Sopenharmony_ci				block->portid, 0, GFP_KERNEL);
698c2ecf20Sopenharmony_ci		}
708c2ecf20Sopenharmony_ci		kfree(block);
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/**
758c2ecf20Sopenharmony_ci * w1_reply_make_space() - send message if needed to make space
768c2ecf20Sopenharmony_ci * @block: block to make space on
778c2ecf20Sopenharmony_ci * @space: how many bytes requested
788c2ecf20Sopenharmony_ci *
798c2ecf20Sopenharmony_ci * Verify there is enough room left for the caller to add "space" bytes to the
808c2ecf20Sopenharmony_ci * message, if there isn't send the message and reset.
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_cistatic void w1_reply_make_space(struct w1_cb_block *block, u16 space)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	u16 len = w1_reply_len(block);
858c2ecf20Sopenharmony_ci	if (len + space >= block->maxlen) {
868c2ecf20Sopenharmony_ci		cn_netlink_send_mult(block->first_cn, len, block->portid, 0, GFP_KERNEL);
878c2ecf20Sopenharmony_ci		block->first_cn->len = 0;
888c2ecf20Sopenharmony_ci		block->cn = NULL;
898c2ecf20Sopenharmony_ci		block->msg = NULL;
908c2ecf20Sopenharmony_ci		block->cmd = NULL;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/* Early send when replies aren't bundled. */
958c2ecf20Sopenharmony_cistatic void w1_netlink_check_send(struct w1_cb_block *block)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	if (!(block->request_cn.flags & W1_CN_BUNDLE) && block->cn)
988c2ecf20Sopenharmony_ci		w1_reply_make_space(block, block->maxlen);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/**
1028c2ecf20Sopenharmony_ci * w1_netlink_setup_msg() - prepare to write block->msg
1038c2ecf20Sopenharmony_ci * @block: block to operate on
1048c2ecf20Sopenharmony_ci * @ack: determines if cn can be reused
1058c2ecf20Sopenharmony_ci *
1068c2ecf20Sopenharmony_ci * block->cn will be setup with the correct ack, advancing if needed
1078c2ecf20Sopenharmony_ci * block->cn->len does not include space for block->msg
1088c2ecf20Sopenharmony_ci * block->msg advances but remains uninitialized
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_cistatic void w1_netlink_setup_msg(struct w1_cb_block *block, u32 ack)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	if (block->cn && block->cn->ack == ack) {
1138c2ecf20Sopenharmony_ci		block->msg = (struct w1_netlink_msg *)(block->cn->data + block->cn->len);
1148c2ecf20Sopenharmony_ci	} else {
1158c2ecf20Sopenharmony_ci		/* advance or set to data */
1168c2ecf20Sopenharmony_ci		if (block->cn)
1178c2ecf20Sopenharmony_ci			block->cn = (struct cn_msg *)(block->cn->data +
1188c2ecf20Sopenharmony_ci				block->cn->len);
1198c2ecf20Sopenharmony_ci		else
1208c2ecf20Sopenharmony_ci			block->cn = block->first_cn;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		memcpy(block->cn, &block->request_cn, sizeof(*block->cn));
1238c2ecf20Sopenharmony_ci		block->cn->len = 0;
1248c2ecf20Sopenharmony_ci		block->cn->ack = ack;
1258c2ecf20Sopenharmony_ci		block->msg = (struct w1_netlink_msg *)block->cn->data;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* Append cmd to msg, include cmd->data as well.  This is because
1308c2ecf20Sopenharmony_ci * any following data goes with the command and in the case of a read is
1318c2ecf20Sopenharmony_ci * the results.
1328c2ecf20Sopenharmony_ci */
1338c2ecf20Sopenharmony_cistatic void w1_netlink_queue_cmd(struct w1_cb_block *block,
1348c2ecf20Sopenharmony_ci	struct w1_netlink_cmd *cmd)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	u32 space;
1378c2ecf20Sopenharmony_ci	w1_reply_make_space(block, sizeof(struct cn_msg) +
1388c2ecf20Sopenharmony_ci		sizeof(struct w1_netlink_msg) + sizeof(*cmd) + cmd->len);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* There's a status message sent after each command, so no point
1418c2ecf20Sopenharmony_ci	 * in trying to bundle this cmd after an existing one, because
1428c2ecf20Sopenharmony_ci	 * there won't be one.  Allocate and copy over a new cn_msg.
1438c2ecf20Sopenharmony_ci	 */
1448c2ecf20Sopenharmony_ci	w1_netlink_setup_msg(block, block->request_cn.seq + 1);
1458c2ecf20Sopenharmony_ci	memcpy(block->msg, block->cur_msg, sizeof(*block->msg));
1468c2ecf20Sopenharmony_ci	block->cn->len += sizeof(*block->msg);
1478c2ecf20Sopenharmony_ci	block->msg->len = 0;
1488c2ecf20Sopenharmony_ci	block->cmd = (struct w1_netlink_cmd *)(block->msg->data);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	space = sizeof(*cmd) + cmd->len;
1518c2ecf20Sopenharmony_ci	if (block->cmd != cmd)
1528c2ecf20Sopenharmony_ci		memcpy(block->cmd, cmd, space);
1538c2ecf20Sopenharmony_ci	block->cn->len += space;
1548c2ecf20Sopenharmony_ci	block->msg->len += space;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/* Append req_msg and req_cmd, no other commands and no data from req_cmd are
1588c2ecf20Sopenharmony_ci * copied.
1598c2ecf20Sopenharmony_ci */
1608c2ecf20Sopenharmony_cistatic void w1_netlink_queue_status(struct w1_cb_block *block,
1618c2ecf20Sopenharmony_ci	struct w1_netlink_msg *req_msg, struct w1_netlink_cmd *req_cmd,
1628c2ecf20Sopenharmony_ci	int error)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	u16 space = sizeof(struct cn_msg) + sizeof(*req_msg) + sizeof(*req_cmd);
1658c2ecf20Sopenharmony_ci	w1_reply_make_space(block, space);
1668c2ecf20Sopenharmony_ci	w1_netlink_setup_msg(block, block->request_cn.ack);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	memcpy(block->msg, req_msg, sizeof(*req_msg));
1698c2ecf20Sopenharmony_ci	block->cn->len += sizeof(*req_msg);
1708c2ecf20Sopenharmony_ci	block->msg->len = 0;
1718c2ecf20Sopenharmony_ci	block->msg->status = (u8)-error;
1728c2ecf20Sopenharmony_ci	if (req_cmd) {
1738c2ecf20Sopenharmony_ci		struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)block->msg->data;
1748c2ecf20Sopenharmony_ci		memcpy(cmd, req_cmd, sizeof(*cmd));
1758c2ecf20Sopenharmony_ci		block->cn->len += sizeof(*cmd);
1768c2ecf20Sopenharmony_ci		block->msg->len += sizeof(*cmd);
1778c2ecf20Sopenharmony_ci		cmd->len = 0;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci	w1_netlink_check_send(block);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/**
1838c2ecf20Sopenharmony_ci * w1_netlink_send_error() - sends the error message now
1848c2ecf20Sopenharmony_ci * @cn: original cn_msg
1858c2ecf20Sopenharmony_ci * @msg: original w1_netlink_msg
1868c2ecf20Sopenharmony_ci * @portid: where to send it
1878c2ecf20Sopenharmony_ci * @error: error status
1888c2ecf20Sopenharmony_ci *
1898c2ecf20Sopenharmony_ci * Use when a block isn't available to queue the message to and cn, msg
1908c2ecf20Sopenharmony_ci * might not be contiguous.
1918c2ecf20Sopenharmony_ci */
1928c2ecf20Sopenharmony_cistatic void w1_netlink_send_error(struct cn_msg *cn, struct w1_netlink_msg *msg,
1938c2ecf20Sopenharmony_ci	int portid, int error)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	struct {
1968c2ecf20Sopenharmony_ci		struct cn_msg cn;
1978c2ecf20Sopenharmony_ci		struct w1_netlink_msg msg;
1988c2ecf20Sopenharmony_ci	} packet;
1998c2ecf20Sopenharmony_ci	memcpy(&packet.cn, cn, sizeof(packet.cn));
2008c2ecf20Sopenharmony_ci	memcpy(&packet.msg, msg, sizeof(packet.msg));
2018c2ecf20Sopenharmony_ci	packet.cn.len = sizeof(packet.msg);
2028c2ecf20Sopenharmony_ci	packet.msg.len = 0;
2038c2ecf20Sopenharmony_ci	packet.msg.status = (u8)-error;
2048c2ecf20Sopenharmony_ci	cn_netlink_send(&packet.cn, portid, 0, GFP_KERNEL);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/**
2088c2ecf20Sopenharmony_ci * w1_netlink_send() - sends w1 netlink notifications
2098c2ecf20Sopenharmony_ci * @dev: w1_master the even is associated with or for
2108c2ecf20Sopenharmony_ci * @msg: w1_netlink_msg message to be sent
2118c2ecf20Sopenharmony_ci *
2128c2ecf20Sopenharmony_ci * This are notifications generated from the kernel.
2138c2ecf20Sopenharmony_ci */
2148c2ecf20Sopenharmony_civoid w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct {
2178c2ecf20Sopenharmony_ci		struct cn_msg cn;
2188c2ecf20Sopenharmony_ci		struct w1_netlink_msg msg;
2198c2ecf20Sopenharmony_ci	} packet;
2208c2ecf20Sopenharmony_ci	memset(&packet, 0, sizeof(packet));
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	packet.cn.id.idx = CN_W1_IDX;
2238c2ecf20Sopenharmony_ci	packet.cn.id.val = CN_W1_VAL;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	packet.cn.seq = dev->seq++;
2268c2ecf20Sopenharmony_ci	packet.cn.len = sizeof(*msg);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	memcpy(&packet.msg, msg, sizeof(*msg));
2298c2ecf20Sopenharmony_ci	packet.msg.len = 0;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	cn_netlink_send(&packet.cn, 0, 0, GFP_KERNEL);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic void w1_send_slave(struct w1_master *dev, u64 rn)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct w1_cb_block *block = dev->priv;
2378c2ecf20Sopenharmony_ci	struct w1_netlink_cmd *cache_cmd = block->cmd;
2388c2ecf20Sopenharmony_ci	u64 *data;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	w1_reply_make_space(block, sizeof(*data));
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* Add cmd back if the packet was sent */
2438c2ecf20Sopenharmony_ci	if (!block->cmd) {
2448c2ecf20Sopenharmony_ci		cache_cmd->len = 0;
2458c2ecf20Sopenharmony_ci		w1_netlink_queue_cmd(block, cache_cmd);
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	data = (u64 *)(block->cmd->data + block->cmd->len);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	*data = rn;
2518c2ecf20Sopenharmony_ci	block->cn->len += sizeof(*data);
2528c2ecf20Sopenharmony_ci	block->msg->len += sizeof(*data);
2538c2ecf20Sopenharmony_ci	block->cmd->len += sizeof(*data);
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic void w1_found_send_slave(struct w1_master *dev, u64 rn)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	/* update kernel slave list */
2598c2ecf20Sopenharmony_ci	w1_slave_found(dev, rn);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	w1_send_slave(dev, rn);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/* Get the current slave list, or search (with or without alarm) */
2658c2ecf20Sopenharmony_cistatic int w1_get_slaves(struct w1_master *dev, struct w1_netlink_cmd *req_cmd)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct w1_slave *sl;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	req_cmd->len = 0;
2708c2ecf20Sopenharmony_ci	w1_netlink_queue_cmd(dev->priv, req_cmd);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (req_cmd->cmd == W1_CMD_LIST_SLAVES) {
2738c2ecf20Sopenharmony_ci		u64 rn;
2748c2ecf20Sopenharmony_ci		mutex_lock(&dev->list_mutex);
2758c2ecf20Sopenharmony_ci		list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
2768c2ecf20Sopenharmony_ci			memcpy(&rn, &sl->reg_num, sizeof(rn));
2778c2ecf20Sopenharmony_ci			w1_send_slave(dev, rn);
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci		mutex_unlock(&dev->list_mutex);
2808c2ecf20Sopenharmony_ci	} else {
2818c2ecf20Sopenharmony_ci		w1_search_process_cb(dev, req_cmd->cmd == W1_CMD_ALARM_SEARCH ?
2828c2ecf20Sopenharmony_ci			W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic int w1_process_command_io(struct w1_master *dev,
2898c2ecf20Sopenharmony_ci	struct w1_netlink_cmd *cmd)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	int err = 0;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	switch (cmd->cmd) {
2948c2ecf20Sopenharmony_ci	case W1_CMD_TOUCH:
2958c2ecf20Sopenharmony_ci		w1_touch_block(dev, cmd->data, cmd->len);
2968c2ecf20Sopenharmony_ci		w1_netlink_queue_cmd(dev->priv, cmd);
2978c2ecf20Sopenharmony_ci		break;
2988c2ecf20Sopenharmony_ci	case W1_CMD_READ:
2998c2ecf20Sopenharmony_ci		w1_read_block(dev, cmd->data, cmd->len);
3008c2ecf20Sopenharmony_ci		w1_netlink_queue_cmd(dev->priv, cmd);
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci	case W1_CMD_WRITE:
3038c2ecf20Sopenharmony_ci		w1_write_block(dev, cmd->data, cmd->len);
3048c2ecf20Sopenharmony_ci		break;
3058c2ecf20Sopenharmony_ci	default:
3068c2ecf20Sopenharmony_ci		err = -EINVAL;
3078c2ecf20Sopenharmony_ci		break;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return err;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic int w1_process_command_addremove(struct w1_master *dev,
3148c2ecf20Sopenharmony_ci	struct w1_netlink_cmd *cmd)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct w1_slave *sl;
3178c2ecf20Sopenharmony_ci	int err = 0;
3188c2ecf20Sopenharmony_ci	struct w1_reg_num *id;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (cmd->len != sizeof(*id))
3218c2ecf20Sopenharmony_ci		return -EINVAL;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	id = (struct w1_reg_num *)cmd->data;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	sl = w1_slave_search_device(dev, id);
3268c2ecf20Sopenharmony_ci	switch (cmd->cmd) {
3278c2ecf20Sopenharmony_ci	case W1_CMD_SLAVE_ADD:
3288c2ecf20Sopenharmony_ci		if (sl)
3298c2ecf20Sopenharmony_ci			err = -EINVAL;
3308c2ecf20Sopenharmony_ci		else
3318c2ecf20Sopenharmony_ci			err = w1_attach_slave_device(dev, id);
3328c2ecf20Sopenharmony_ci		break;
3338c2ecf20Sopenharmony_ci	case W1_CMD_SLAVE_REMOVE:
3348c2ecf20Sopenharmony_ci		if (sl)
3358c2ecf20Sopenharmony_ci			w1_slave_detach(sl);
3368c2ecf20Sopenharmony_ci		else
3378c2ecf20Sopenharmony_ci			err = -EINVAL;
3388c2ecf20Sopenharmony_ci		break;
3398c2ecf20Sopenharmony_ci	default:
3408c2ecf20Sopenharmony_ci		err = -EINVAL;
3418c2ecf20Sopenharmony_ci		break;
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	return err;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int w1_process_command_master(struct w1_master *dev,
3488c2ecf20Sopenharmony_ci	struct w1_netlink_cmd *req_cmd)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	int err = -EINVAL;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* drop bus_mutex for search (does it's own locking), and add/remove
3538c2ecf20Sopenharmony_ci	 * which doesn't use the bus
3548c2ecf20Sopenharmony_ci	 */
3558c2ecf20Sopenharmony_ci	switch (req_cmd->cmd) {
3568c2ecf20Sopenharmony_ci	case W1_CMD_SEARCH:
3578c2ecf20Sopenharmony_ci	case W1_CMD_ALARM_SEARCH:
3588c2ecf20Sopenharmony_ci	case W1_CMD_LIST_SLAVES:
3598c2ecf20Sopenharmony_ci		mutex_unlock(&dev->bus_mutex);
3608c2ecf20Sopenharmony_ci		err = w1_get_slaves(dev, req_cmd);
3618c2ecf20Sopenharmony_ci		mutex_lock(&dev->bus_mutex);
3628c2ecf20Sopenharmony_ci		break;
3638c2ecf20Sopenharmony_ci	case W1_CMD_READ:
3648c2ecf20Sopenharmony_ci	case W1_CMD_WRITE:
3658c2ecf20Sopenharmony_ci	case W1_CMD_TOUCH:
3668c2ecf20Sopenharmony_ci		err = w1_process_command_io(dev, req_cmd);
3678c2ecf20Sopenharmony_ci		break;
3688c2ecf20Sopenharmony_ci	case W1_CMD_RESET:
3698c2ecf20Sopenharmony_ci		err = w1_reset_bus(dev);
3708c2ecf20Sopenharmony_ci		break;
3718c2ecf20Sopenharmony_ci	case W1_CMD_SLAVE_ADD:
3728c2ecf20Sopenharmony_ci	case W1_CMD_SLAVE_REMOVE:
3738c2ecf20Sopenharmony_ci		mutex_unlock(&dev->bus_mutex);
3748c2ecf20Sopenharmony_ci		mutex_lock(&dev->mutex);
3758c2ecf20Sopenharmony_ci		err = w1_process_command_addremove(dev, req_cmd);
3768c2ecf20Sopenharmony_ci		mutex_unlock(&dev->mutex);
3778c2ecf20Sopenharmony_ci		mutex_lock(&dev->bus_mutex);
3788c2ecf20Sopenharmony_ci		break;
3798c2ecf20Sopenharmony_ci	default:
3808c2ecf20Sopenharmony_ci		err = -EINVAL;
3818c2ecf20Sopenharmony_ci		break;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return err;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int w1_process_command_slave(struct w1_slave *sl,
3888c2ecf20Sopenharmony_ci		struct w1_netlink_cmd *cmd)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n",
3918c2ecf20Sopenharmony_ci		__func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id,
3928c2ecf20Sopenharmony_ci		sl->reg_num.crc, cmd->cmd, cmd->len);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return w1_process_command_io(sl->master, cmd);
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic int w1_process_command_root(struct cn_msg *req_cn, u32 portid)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct w1_master *dev;
4008c2ecf20Sopenharmony_ci	struct cn_msg *cn;
4018c2ecf20Sopenharmony_ci	struct w1_netlink_msg *msg;
4028c2ecf20Sopenharmony_ci	u32 *id;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	cn = kmalloc(PAGE_SIZE, GFP_KERNEL);
4058c2ecf20Sopenharmony_ci	if (!cn)
4068c2ecf20Sopenharmony_ci		return -ENOMEM;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	cn->id.idx = CN_W1_IDX;
4098c2ecf20Sopenharmony_ci	cn->id.val = CN_W1_VAL;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	cn->seq = req_cn->seq;
4128c2ecf20Sopenharmony_ci	cn->ack = req_cn->seq + 1;
4138c2ecf20Sopenharmony_ci	cn->len = sizeof(struct w1_netlink_msg);
4148c2ecf20Sopenharmony_ci	msg = (struct w1_netlink_msg *)cn->data;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	msg->type = W1_LIST_MASTERS;
4178c2ecf20Sopenharmony_ci	msg->status = 0;
4188c2ecf20Sopenharmony_ci	msg->len = 0;
4198c2ecf20Sopenharmony_ci	id = (u32 *)msg->data;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	mutex_lock(&w1_mlock);
4228c2ecf20Sopenharmony_ci	list_for_each_entry(dev, &w1_masters, w1_master_entry) {
4238c2ecf20Sopenharmony_ci		if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) {
4248c2ecf20Sopenharmony_ci			cn_netlink_send(cn, portid, 0, GFP_KERNEL);
4258c2ecf20Sopenharmony_ci			cn->len = sizeof(struct w1_netlink_msg);
4268c2ecf20Sopenharmony_ci			msg->len = 0;
4278c2ecf20Sopenharmony_ci			id = (u32 *)msg->data;
4288c2ecf20Sopenharmony_ci		}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		*id = dev->id;
4318c2ecf20Sopenharmony_ci		msg->len += sizeof(*id);
4328c2ecf20Sopenharmony_ci		cn->len += sizeof(*id);
4338c2ecf20Sopenharmony_ci		id++;
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci	cn_netlink_send(cn, portid, 0, GFP_KERNEL);
4368c2ecf20Sopenharmony_ci	mutex_unlock(&w1_mlock);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	kfree(cn);
4398c2ecf20Sopenharmony_ci	return 0;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node,
4458c2ecf20Sopenharmony_ci		async);
4468c2ecf20Sopenharmony_ci	u16 mlen = node->msg->len;
4478c2ecf20Sopenharmony_ci	u16 len;
4488c2ecf20Sopenharmony_ci	int err = 0;
4498c2ecf20Sopenharmony_ci	struct w1_slave *sl = node->sl;
4508c2ecf20Sopenharmony_ci	struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)node->msg->data;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	mutex_lock(&dev->bus_mutex);
4538c2ecf20Sopenharmony_ci	dev->priv = node->block;
4548c2ecf20Sopenharmony_ci	if (sl && w1_reset_select_slave(sl))
4558c2ecf20Sopenharmony_ci		err = -ENODEV;
4568c2ecf20Sopenharmony_ci	node->block->cur_msg = node->msg;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	while (mlen && !err) {
4598c2ecf20Sopenharmony_ci		if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
4608c2ecf20Sopenharmony_ci			err = -E2BIG;
4618c2ecf20Sopenharmony_ci			break;
4628c2ecf20Sopenharmony_ci		}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		if (sl)
4658c2ecf20Sopenharmony_ci			err = w1_process_command_slave(sl, cmd);
4668c2ecf20Sopenharmony_ci		else
4678c2ecf20Sopenharmony_ci			err = w1_process_command_master(dev, cmd);
4688c2ecf20Sopenharmony_ci		w1_netlink_check_send(node->block);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		w1_netlink_queue_status(node->block, node->msg, cmd, err);
4718c2ecf20Sopenharmony_ci		err = 0;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		len = sizeof(*cmd) + cmd->len;
4748c2ecf20Sopenharmony_ci		cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len);
4758c2ecf20Sopenharmony_ci		mlen -= len;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (!cmd || err)
4798c2ecf20Sopenharmony_ci		w1_netlink_queue_status(node->block, node->msg, cmd, err);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* ref taken in w1_search_slave or w1_search_master_id when building
4828c2ecf20Sopenharmony_ci	 * the block
4838c2ecf20Sopenharmony_ci	 */
4848c2ecf20Sopenharmony_ci	if (sl)
4858c2ecf20Sopenharmony_ci		w1_unref_slave(sl);
4868c2ecf20Sopenharmony_ci	else
4878c2ecf20Sopenharmony_ci		atomic_dec(&dev->refcnt);
4888c2ecf20Sopenharmony_ci	dev->priv = NULL;
4898c2ecf20Sopenharmony_ci	mutex_unlock(&dev->bus_mutex);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	mutex_lock(&dev->list_mutex);
4928c2ecf20Sopenharmony_ci	list_del(&async_cmd->async_entry);
4938c2ecf20Sopenharmony_ci	mutex_unlock(&dev->list_mutex);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	w1_unref_block(node->block);
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic void w1_list_count_cmds(struct w1_netlink_msg *msg, int *cmd_count,
4998c2ecf20Sopenharmony_ci	u16 *slave_len)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)msg->data;
5028c2ecf20Sopenharmony_ci	u16 mlen = msg->len;
5038c2ecf20Sopenharmony_ci	u16 len;
5048c2ecf20Sopenharmony_ci	int slave_list = 0;
5058c2ecf20Sopenharmony_ci	while (mlen) {
5068c2ecf20Sopenharmony_ci		if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen)
5078c2ecf20Sopenharmony_ci			break;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		switch (cmd->cmd) {
5108c2ecf20Sopenharmony_ci		case W1_CMD_SEARCH:
5118c2ecf20Sopenharmony_ci		case W1_CMD_ALARM_SEARCH:
5128c2ecf20Sopenharmony_ci		case W1_CMD_LIST_SLAVES:
5138c2ecf20Sopenharmony_ci			++slave_list;
5148c2ecf20Sopenharmony_ci		}
5158c2ecf20Sopenharmony_ci		++*cmd_count;
5168c2ecf20Sopenharmony_ci		len = sizeof(*cmd) + cmd->len;
5178c2ecf20Sopenharmony_ci		cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len);
5188c2ecf20Sopenharmony_ci		mlen -= len;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (slave_list) {
5228c2ecf20Sopenharmony_ci		struct w1_master *dev = w1_search_master_id(msg->id.mst.id);
5238c2ecf20Sopenharmony_ci		if (dev) {
5248c2ecf20Sopenharmony_ci			/* Bytes, and likely an overstimate, and if it isn't
5258c2ecf20Sopenharmony_ci			 * the results can still be split between packets.
5268c2ecf20Sopenharmony_ci			 */
5278c2ecf20Sopenharmony_ci			*slave_len += sizeof(struct w1_reg_num) * slave_list *
5288c2ecf20Sopenharmony_ci				(dev->slave_count + dev->max_slave_count);
5298c2ecf20Sopenharmony_ci			/* search incremented it */
5308c2ecf20Sopenharmony_ci			atomic_dec(&dev->refcnt);
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic void w1_cn_callback(struct cn_msg *cn, struct netlink_skb_parms *nsp)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct w1_netlink_msg *msg = (struct w1_netlink_msg *)(cn + 1);
5388c2ecf20Sopenharmony_ci	struct w1_slave *sl;
5398c2ecf20Sopenharmony_ci	struct w1_master *dev;
5408c2ecf20Sopenharmony_ci	u16 msg_len;
5418c2ecf20Sopenharmony_ci	u16 slave_len = 0;
5428c2ecf20Sopenharmony_ci	int err = 0;
5438c2ecf20Sopenharmony_ci	struct w1_cb_block *block = NULL;
5448c2ecf20Sopenharmony_ci	struct w1_cb_node *node = NULL;
5458c2ecf20Sopenharmony_ci	int node_count = 0;
5468c2ecf20Sopenharmony_ci	int cmd_count = 0;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* If any unknown flag is set let the application know, that way
5498c2ecf20Sopenharmony_ci	 * applications can detect the absence of features in kernels that
5508c2ecf20Sopenharmony_ci	 * don't know about them.  http://lwn.net/Articles/587527/
5518c2ecf20Sopenharmony_ci	 */
5528c2ecf20Sopenharmony_ci	if (cn->flags & ~(W1_CN_BUNDLE)) {
5538c2ecf20Sopenharmony_ci		w1_netlink_send_error(cn, msg, nsp->portid, -EINVAL);
5548c2ecf20Sopenharmony_ci		return;
5558c2ecf20Sopenharmony_ci	}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* Count the number of master or slave commands there are to allocate
5588c2ecf20Sopenharmony_ci	 * space for one cb_node each.
5598c2ecf20Sopenharmony_ci	 */
5608c2ecf20Sopenharmony_ci	msg_len = cn->len;
5618c2ecf20Sopenharmony_ci	while (msg_len && !err) {
5628c2ecf20Sopenharmony_ci		if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {
5638c2ecf20Sopenharmony_ci			err = -E2BIG;
5648c2ecf20Sopenharmony_ci			break;
5658c2ecf20Sopenharmony_ci		}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci		/* count messages for nodes and allocate any additional space
5688c2ecf20Sopenharmony_ci		 * required for slave lists
5698c2ecf20Sopenharmony_ci		 */
5708c2ecf20Sopenharmony_ci		if (msg->type == W1_MASTER_CMD || msg->type == W1_SLAVE_CMD) {
5718c2ecf20Sopenharmony_ci			++node_count;
5728c2ecf20Sopenharmony_ci			w1_list_count_cmds(msg, &cmd_count, &slave_len);
5738c2ecf20Sopenharmony_ci		}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
5768c2ecf20Sopenharmony_ci		msg = (struct w1_netlink_msg *)(((u8 *)msg) +
5778c2ecf20Sopenharmony_ci			sizeof(struct w1_netlink_msg) + msg->len);
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci	msg = (struct w1_netlink_msg *)(cn + 1);
5808c2ecf20Sopenharmony_ci	if (node_count) {
5818c2ecf20Sopenharmony_ci		int size;
5828c2ecf20Sopenharmony_ci		int reply_size = sizeof(*cn) + cn->len + slave_len;
5838c2ecf20Sopenharmony_ci		if (cn->flags & W1_CN_BUNDLE) {
5848c2ecf20Sopenharmony_ci			/* bundling duplicats some of the messages */
5858c2ecf20Sopenharmony_ci			reply_size += 2 * cmd_count * (sizeof(struct cn_msg) +
5868c2ecf20Sopenharmony_ci				sizeof(struct w1_netlink_msg) +
5878c2ecf20Sopenharmony_ci				sizeof(struct w1_netlink_cmd));
5888c2ecf20Sopenharmony_ci		}
5898c2ecf20Sopenharmony_ci		reply_size = min(CONNECTOR_MAX_MSG_SIZE, reply_size);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		/* allocate space for the block, a copy of the original message,
5928c2ecf20Sopenharmony_ci		 * one node per cmd to point into the original message,
5938c2ecf20Sopenharmony_ci		 * space for replies which is the original message size plus
5948c2ecf20Sopenharmony_ci		 * space for any list slave data and status messages
5958c2ecf20Sopenharmony_ci		 * cn->len doesn't include itself which is part of the block
5968c2ecf20Sopenharmony_ci		 * */
5978c2ecf20Sopenharmony_ci		size =  /* block + original message */
5988c2ecf20Sopenharmony_ci			sizeof(struct w1_cb_block) + sizeof(*cn) + cn->len +
5998c2ecf20Sopenharmony_ci			/* space for nodes */
6008c2ecf20Sopenharmony_ci			node_count * sizeof(struct w1_cb_node) +
6018c2ecf20Sopenharmony_ci			/* replies */
6028c2ecf20Sopenharmony_ci			sizeof(struct cn_msg) + reply_size;
6038c2ecf20Sopenharmony_ci		block = kzalloc(size, GFP_KERNEL);
6048c2ecf20Sopenharmony_ci		if (!block) {
6058c2ecf20Sopenharmony_ci			/* if the system is already out of memory,
6068c2ecf20Sopenharmony_ci			 * (A) will this work, and (B) would it be better
6078c2ecf20Sopenharmony_ci			 * to not try?
6088c2ecf20Sopenharmony_ci			 */
6098c2ecf20Sopenharmony_ci			w1_netlink_send_error(cn, msg, nsp->portid, -ENOMEM);
6108c2ecf20Sopenharmony_ci			return;
6118c2ecf20Sopenharmony_ci		}
6128c2ecf20Sopenharmony_ci		atomic_set(&block->refcnt, 1);
6138c2ecf20Sopenharmony_ci		block->portid = nsp->portid;
6148c2ecf20Sopenharmony_ci		memcpy(&block->request_cn, cn, sizeof(*cn) + cn->len);
6158c2ecf20Sopenharmony_ci		node = (struct w1_cb_node *)(block->request_cn.data + cn->len);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		/* Sneeky, when not bundling, reply_size is the allocated space
6188c2ecf20Sopenharmony_ci		 * required for the reply, cn_msg isn't part of maxlen so
6198c2ecf20Sopenharmony_ci		 * it should be reply_size - sizeof(struct cn_msg), however
6208c2ecf20Sopenharmony_ci		 * when checking if there is enough space, w1_reply_make_space
6218c2ecf20Sopenharmony_ci		 * is called with the full message size including cn_msg,
6228c2ecf20Sopenharmony_ci		 * because it isn't known at that time if an additional cn_msg
6238c2ecf20Sopenharmony_ci		 * will need to be allocated.  So an extra cn_msg is added
6248c2ecf20Sopenharmony_ci		 * above in "size".
6258c2ecf20Sopenharmony_ci		 */
6268c2ecf20Sopenharmony_ci		block->maxlen = reply_size;
6278c2ecf20Sopenharmony_ci		block->first_cn = (struct cn_msg *)(node + node_count);
6288c2ecf20Sopenharmony_ci		memset(block->first_cn, 0, sizeof(*block->first_cn));
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	msg_len = cn->len;
6328c2ecf20Sopenharmony_ci	while (msg_len && !err) {
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci		dev = NULL;
6358c2ecf20Sopenharmony_ci		sl = NULL;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci		if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {
6388c2ecf20Sopenharmony_ci			err = -E2BIG;
6398c2ecf20Sopenharmony_ci			break;
6408c2ecf20Sopenharmony_ci		}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		/* execute on this thread, no need to process later */
6438c2ecf20Sopenharmony_ci		if (msg->type == W1_LIST_MASTERS) {
6448c2ecf20Sopenharmony_ci			err = w1_process_command_root(cn, nsp->portid);
6458c2ecf20Sopenharmony_ci			goto out_cont;
6468c2ecf20Sopenharmony_ci		}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci		/* All following message types require additional data,
6498c2ecf20Sopenharmony_ci		 * check here before references are taken.
6508c2ecf20Sopenharmony_ci		 */
6518c2ecf20Sopenharmony_ci		if (!msg->len) {
6528c2ecf20Sopenharmony_ci			err = -EPROTO;
6538c2ecf20Sopenharmony_ci			goto out_cont;
6548c2ecf20Sopenharmony_ci		}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		/* both search calls take references */
6578c2ecf20Sopenharmony_ci		if (msg->type == W1_MASTER_CMD) {
6588c2ecf20Sopenharmony_ci			dev = w1_search_master_id(msg->id.mst.id);
6598c2ecf20Sopenharmony_ci		} else if (msg->type == W1_SLAVE_CMD) {
6608c2ecf20Sopenharmony_ci			sl = w1_search_slave((struct w1_reg_num *)msg->id.id);
6618c2ecf20Sopenharmony_ci			if (sl)
6628c2ecf20Sopenharmony_ci				dev = sl->master;
6638c2ecf20Sopenharmony_ci		} else {
6648c2ecf20Sopenharmony_ci			pr_notice("%s: cn: %x.%x, wrong type: %u, len: %u.\n",
6658c2ecf20Sopenharmony_ci				__func__, cn->id.idx, cn->id.val,
6668c2ecf20Sopenharmony_ci				msg->type, msg->len);
6678c2ecf20Sopenharmony_ci			err = -EPROTO;
6688c2ecf20Sopenharmony_ci			goto out_cont;
6698c2ecf20Sopenharmony_ci		}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci		if (!dev) {
6728c2ecf20Sopenharmony_ci			err = -ENODEV;
6738c2ecf20Sopenharmony_ci			goto out_cont;
6748c2ecf20Sopenharmony_ci		}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci		err = 0;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci		atomic_inc(&block->refcnt);
6798c2ecf20Sopenharmony_ci		node->async.cb = w1_process_cb;
6808c2ecf20Sopenharmony_ci		node->block = block;
6818c2ecf20Sopenharmony_ci		node->msg = (struct w1_netlink_msg *)((u8 *)&block->request_cn +
6828c2ecf20Sopenharmony_ci			(size_t)((u8 *)msg - (u8 *)cn));
6838c2ecf20Sopenharmony_ci		node->sl = sl;
6848c2ecf20Sopenharmony_ci		node->dev = dev;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		mutex_lock(&dev->list_mutex);
6878c2ecf20Sopenharmony_ci		list_add_tail(&node->async.async_entry, &dev->async_list);
6888c2ecf20Sopenharmony_ci		wake_up_process(dev->thread);
6898c2ecf20Sopenharmony_ci		mutex_unlock(&dev->list_mutex);
6908c2ecf20Sopenharmony_ci		++node;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ciout_cont:
6938c2ecf20Sopenharmony_ci		/* Can't queue because that modifies block and another
6948c2ecf20Sopenharmony_ci		 * thread could be processing the messages by now and
6958c2ecf20Sopenharmony_ci		 * there isn't a lock, send directly.
6968c2ecf20Sopenharmony_ci		 */
6978c2ecf20Sopenharmony_ci		if (err)
6988c2ecf20Sopenharmony_ci			w1_netlink_send_error(cn, msg, nsp->portid, err);
6998c2ecf20Sopenharmony_ci		msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
7008c2ecf20Sopenharmony_ci		msg = (struct w1_netlink_msg *)(((u8 *)msg) +
7018c2ecf20Sopenharmony_ci			sizeof(struct w1_netlink_msg) + msg->len);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci		/*
7048c2ecf20Sopenharmony_ci		 * Let's allow requests for nonexisting devices.
7058c2ecf20Sopenharmony_ci		 */
7068c2ecf20Sopenharmony_ci		if (err == -ENODEV)
7078c2ecf20Sopenharmony_ci			err = 0;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci	if (block)
7108c2ecf20Sopenharmony_ci		w1_unref_block(block);
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ciint w1_init_netlink(void)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	return cn_add_callback(&w1_id, "w1", &w1_cn_callback);
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_civoid w1_fini_netlink(void)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	cn_del_callback(&w1_id);
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci#else
7278c2ecf20Sopenharmony_civoid w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *cn)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ciint w1_init_netlink(void)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	return 0;
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_civoid w1_fini_netlink(void)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci#endif
740