162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/skbuff.h>
862306a36Sopenharmony_ci#include <linux/netlink.h>
962306a36Sopenharmony_ci#include <linux/connector.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "w1_internal.h"
1262306a36Sopenharmony_ci#include "w1_netlink.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE)))
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* Bundle together everything required to process a request in one memory
1762306a36Sopenharmony_ci * allocation.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_cistruct w1_cb_block {
2062306a36Sopenharmony_ci	atomic_t refcnt;
2162306a36Sopenharmony_ci	u32 portid; /* Sending process port ID */
2262306a36Sopenharmony_ci	/* maximum value for first_cn->len */
2362306a36Sopenharmony_ci	u16 maxlen;
2462306a36Sopenharmony_ci	/* pointers to building up the reply message */
2562306a36Sopenharmony_ci	struct cn_msg *first_cn; /* fixed once the structure is populated */
2662306a36Sopenharmony_ci	struct cn_msg *cn; /* advances as cn_msg is appeneded */
2762306a36Sopenharmony_ci	struct w1_netlink_msg *msg; /* advances as w1_netlink_msg is appened */
2862306a36Sopenharmony_ci	struct w1_netlink_cmd *cmd; /* advances as cmds are appened */
2962306a36Sopenharmony_ci	struct w1_netlink_msg *cur_msg; /* currently message being processed */
3062306a36Sopenharmony_ci	/* copy of the original request follows */
3162306a36Sopenharmony_ci	struct cn_msg request_cn;
3262306a36Sopenharmony_ci	/* followed by variable length:
3362306a36Sopenharmony_ci	 * cn_msg, data (w1_netlink_msg and w1_netlink_cmd)
3462306a36Sopenharmony_ci	 * one or more struct w1_cb_node
3562306a36Sopenharmony_ci	 * reply first_cn, data (w1_netlink_msg and w1_netlink_cmd)
3662306a36Sopenharmony_ci	 */
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_cistruct w1_cb_node {
3962306a36Sopenharmony_ci	struct w1_async_cmd async;
4062306a36Sopenharmony_ci	/* pointers within w1_cb_block and cn data */
4162306a36Sopenharmony_ci	struct w1_cb_block *block;
4262306a36Sopenharmony_ci	struct w1_netlink_msg *msg;
4362306a36Sopenharmony_ci	struct w1_slave *sl;
4462306a36Sopenharmony_ci	struct w1_master *dev;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/**
4862306a36Sopenharmony_ci * w1_reply_len() - calculate current reply length, compare to maxlen
4962306a36Sopenharmony_ci * @block: block to calculate
5062306a36Sopenharmony_ci *
5162306a36Sopenharmony_ci * Calculates the current message length including possible multiple
5262306a36Sopenharmony_ci * cn_msg and data, excludes the first sizeof(struct cn_msg).  Direclty
5362306a36Sopenharmony_ci * compariable to maxlen and usable to send the message.
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_cistatic u16 w1_reply_len(struct w1_cb_block *block)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	if (!block->cn)
5862306a36Sopenharmony_ci		return 0;
5962306a36Sopenharmony_ci	return (u8 *)block->cn - (u8 *)block->first_cn + block->cn->len;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void w1_unref_block(struct w1_cb_block *block)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	if (atomic_sub_return(1, &block->refcnt) == 0) {
6562306a36Sopenharmony_ci		u16 len = w1_reply_len(block);
6662306a36Sopenharmony_ci		if (len) {
6762306a36Sopenharmony_ci			cn_netlink_send_mult(block->first_cn, len,
6862306a36Sopenharmony_ci					     block->portid, 0,
6962306a36Sopenharmony_ci					     GFP_KERNEL, NULL, NULL);
7062306a36Sopenharmony_ci		}
7162306a36Sopenharmony_ci		kfree(block);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/**
7662306a36Sopenharmony_ci * w1_reply_make_space() - send message if needed to make space
7762306a36Sopenharmony_ci * @block: block to make space on
7862306a36Sopenharmony_ci * @space: how many bytes requested
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci * Verify there is enough room left for the caller to add "space" bytes to the
8162306a36Sopenharmony_ci * message, if there isn't send the message and reset.
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_cistatic void w1_reply_make_space(struct w1_cb_block *block, u16 space)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	u16 len = w1_reply_len(block);
8662306a36Sopenharmony_ci	if (len + space >= block->maxlen) {
8762306a36Sopenharmony_ci		cn_netlink_send_mult(block->first_cn, len, block->portid,
8862306a36Sopenharmony_ci				     0, GFP_KERNEL, NULL, NULL);
8962306a36Sopenharmony_ci		block->first_cn->len = 0;
9062306a36Sopenharmony_ci		block->cn = NULL;
9162306a36Sopenharmony_ci		block->msg = NULL;
9262306a36Sopenharmony_ci		block->cmd = NULL;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/* Early send when replies aren't bundled. */
9762306a36Sopenharmony_cistatic void w1_netlink_check_send(struct w1_cb_block *block)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	if (!(block->request_cn.flags & W1_CN_BUNDLE) && block->cn)
10062306a36Sopenharmony_ci		w1_reply_make_space(block, block->maxlen);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/**
10462306a36Sopenharmony_ci * w1_netlink_setup_msg() - prepare to write block->msg
10562306a36Sopenharmony_ci * @block: block to operate on
10662306a36Sopenharmony_ci * @ack: determines if cn can be reused
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * block->cn will be setup with the correct ack, advancing if needed
10962306a36Sopenharmony_ci * block->cn->len does not include space for block->msg
11062306a36Sopenharmony_ci * block->msg advances but remains uninitialized
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_cistatic void w1_netlink_setup_msg(struct w1_cb_block *block, u32 ack)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	if (block->cn && block->cn->ack == ack) {
11562306a36Sopenharmony_ci		block->msg = (struct w1_netlink_msg *)(block->cn->data + block->cn->len);
11662306a36Sopenharmony_ci	} else {
11762306a36Sopenharmony_ci		/* advance or set to data */
11862306a36Sopenharmony_ci		if (block->cn)
11962306a36Sopenharmony_ci			block->cn = (struct cn_msg *)(block->cn->data +
12062306a36Sopenharmony_ci				block->cn->len);
12162306a36Sopenharmony_ci		else
12262306a36Sopenharmony_ci			block->cn = block->first_cn;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		memcpy(block->cn, &block->request_cn, sizeof(*block->cn));
12562306a36Sopenharmony_ci		block->cn->len = 0;
12662306a36Sopenharmony_ci		block->cn->ack = ack;
12762306a36Sopenharmony_ci		block->msg = (struct w1_netlink_msg *)block->cn->data;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/* Append cmd to msg, include cmd->data as well.  This is because
13262306a36Sopenharmony_ci * any following data goes with the command and in the case of a read is
13362306a36Sopenharmony_ci * the results.
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_cistatic void w1_netlink_queue_cmd(struct w1_cb_block *block,
13662306a36Sopenharmony_ci	struct w1_netlink_cmd *cmd)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	u32 space;
13962306a36Sopenharmony_ci	w1_reply_make_space(block, sizeof(struct cn_msg) +
14062306a36Sopenharmony_ci		sizeof(struct w1_netlink_msg) + sizeof(*cmd) + cmd->len);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* There's a status message sent after each command, so no point
14362306a36Sopenharmony_ci	 * in trying to bundle this cmd after an existing one, because
14462306a36Sopenharmony_ci	 * there won't be one.  Allocate and copy over a new cn_msg.
14562306a36Sopenharmony_ci	 */
14662306a36Sopenharmony_ci	w1_netlink_setup_msg(block, block->request_cn.seq + 1);
14762306a36Sopenharmony_ci	memcpy(block->msg, block->cur_msg, sizeof(*block->msg));
14862306a36Sopenharmony_ci	block->cn->len += sizeof(*block->msg);
14962306a36Sopenharmony_ci	block->msg->len = 0;
15062306a36Sopenharmony_ci	block->cmd = (struct w1_netlink_cmd *)(block->msg->data);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	space = sizeof(*cmd) + cmd->len;
15362306a36Sopenharmony_ci	if (block->cmd != cmd)
15462306a36Sopenharmony_ci		memcpy(block->cmd, cmd, space);
15562306a36Sopenharmony_ci	block->cn->len += space;
15662306a36Sopenharmony_ci	block->msg->len += space;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/* Append req_msg and req_cmd, no other commands and no data from req_cmd are
16062306a36Sopenharmony_ci * copied.
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_cistatic void w1_netlink_queue_status(struct w1_cb_block *block,
16362306a36Sopenharmony_ci	struct w1_netlink_msg *req_msg, struct w1_netlink_cmd *req_cmd,
16462306a36Sopenharmony_ci	int error)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	u16 space = sizeof(struct cn_msg) + sizeof(*req_msg) + sizeof(*req_cmd);
16762306a36Sopenharmony_ci	w1_reply_make_space(block, space);
16862306a36Sopenharmony_ci	w1_netlink_setup_msg(block, block->request_cn.ack);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	memcpy(block->msg, req_msg, sizeof(*req_msg));
17162306a36Sopenharmony_ci	block->cn->len += sizeof(*req_msg);
17262306a36Sopenharmony_ci	block->msg->len = 0;
17362306a36Sopenharmony_ci	block->msg->status = (u8)-error;
17462306a36Sopenharmony_ci	if (req_cmd) {
17562306a36Sopenharmony_ci		struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)block->msg->data;
17662306a36Sopenharmony_ci		memcpy(cmd, req_cmd, sizeof(*cmd));
17762306a36Sopenharmony_ci		block->cn->len += sizeof(*cmd);
17862306a36Sopenharmony_ci		block->msg->len += sizeof(*cmd);
17962306a36Sopenharmony_ci		cmd->len = 0;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci	w1_netlink_check_send(block);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/**
18562306a36Sopenharmony_ci * w1_netlink_send_error() - sends the error message now
18662306a36Sopenharmony_ci * @cn: original cn_msg
18762306a36Sopenharmony_ci * @msg: original w1_netlink_msg
18862306a36Sopenharmony_ci * @portid: where to send it
18962306a36Sopenharmony_ci * @error: error status
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * Use when a block isn't available to queue the message to and cn, msg
19262306a36Sopenharmony_ci * might not be contiguous.
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_cistatic void w1_netlink_send_error(struct cn_msg *cn, struct w1_netlink_msg *msg,
19562306a36Sopenharmony_ci	int portid, int error)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct {
19862306a36Sopenharmony_ci		struct cn_msg cn;
19962306a36Sopenharmony_ci		struct w1_netlink_msg msg;
20062306a36Sopenharmony_ci	} packet;
20162306a36Sopenharmony_ci	memcpy(&packet.cn, cn, sizeof(packet.cn));
20262306a36Sopenharmony_ci	memcpy(&packet.msg, msg, sizeof(packet.msg));
20362306a36Sopenharmony_ci	packet.cn.len = sizeof(packet.msg);
20462306a36Sopenharmony_ci	packet.msg.len = 0;
20562306a36Sopenharmony_ci	packet.msg.status = (u8)-error;
20662306a36Sopenharmony_ci	cn_netlink_send(&packet.cn, portid, 0, GFP_KERNEL);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/**
21062306a36Sopenharmony_ci * w1_netlink_send() - sends w1 netlink notifications
21162306a36Sopenharmony_ci * @dev: w1_master the even is associated with or for
21262306a36Sopenharmony_ci * @msg: w1_netlink_msg message to be sent
21362306a36Sopenharmony_ci *
21462306a36Sopenharmony_ci * This are notifications generated from the kernel.
21562306a36Sopenharmony_ci */
21662306a36Sopenharmony_civoid w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct {
21962306a36Sopenharmony_ci		struct cn_msg cn;
22062306a36Sopenharmony_ci		struct w1_netlink_msg msg;
22162306a36Sopenharmony_ci	} packet;
22262306a36Sopenharmony_ci	memset(&packet, 0, sizeof(packet));
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	packet.cn.id.idx = CN_W1_IDX;
22562306a36Sopenharmony_ci	packet.cn.id.val = CN_W1_VAL;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	packet.cn.seq = dev->seq++;
22862306a36Sopenharmony_ci	packet.cn.len = sizeof(*msg);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	memcpy(&packet.msg, msg, sizeof(*msg));
23162306a36Sopenharmony_ci	packet.msg.len = 0;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	cn_netlink_send(&packet.cn, 0, 0, GFP_KERNEL);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic void w1_send_slave(struct w1_master *dev, u64 rn)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct w1_cb_block *block = dev->priv;
23962306a36Sopenharmony_ci	struct w1_netlink_cmd *cache_cmd = block->cmd;
24062306a36Sopenharmony_ci	u64 *data;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	w1_reply_make_space(block, sizeof(*data));
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Add cmd back if the packet was sent */
24562306a36Sopenharmony_ci	if (!block->cmd) {
24662306a36Sopenharmony_ci		cache_cmd->len = 0;
24762306a36Sopenharmony_ci		w1_netlink_queue_cmd(block, cache_cmd);
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	data = (u64 *)(block->cmd->data + block->cmd->len);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	*data = rn;
25362306a36Sopenharmony_ci	block->cn->len += sizeof(*data);
25462306a36Sopenharmony_ci	block->msg->len += sizeof(*data);
25562306a36Sopenharmony_ci	block->cmd->len += sizeof(*data);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic void w1_found_send_slave(struct w1_master *dev, u64 rn)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	/* update kernel slave list */
26162306a36Sopenharmony_ci	w1_slave_found(dev, rn);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	w1_send_slave(dev, rn);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/* Get the current slave list, or search (with or without alarm) */
26762306a36Sopenharmony_cistatic int w1_get_slaves(struct w1_master *dev, struct w1_netlink_cmd *req_cmd)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct w1_slave *sl;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	req_cmd->len = 0;
27262306a36Sopenharmony_ci	w1_netlink_queue_cmd(dev->priv, req_cmd);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (req_cmd->cmd == W1_CMD_LIST_SLAVES) {
27562306a36Sopenharmony_ci		u64 rn;
27662306a36Sopenharmony_ci		mutex_lock(&dev->list_mutex);
27762306a36Sopenharmony_ci		list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
27862306a36Sopenharmony_ci			memcpy(&rn, &sl->reg_num, sizeof(rn));
27962306a36Sopenharmony_ci			w1_send_slave(dev, rn);
28062306a36Sopenharmony_ci		}
28162306a36Sopenharmony_ci		mutex_unlock(&dev->list_mutex);
28262306a36Sopenharmony_ci	} else {
28362306a36Sopenharmony_ci		w1_search_process_cb(dev, req_cmd->cmd == W1_CMD_ALARM_SEARCH ?
28462306a36Sopenharmony_ci			W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave);
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int w1_process_command_io(struct w1_master *dev,
29162306a36Sopenharmony_ci	struct w1_netlink_cmd *cmd)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	int err = 0;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	switch (cmd->cmd) {
29662306a36Sopenharmony_ci	case W1_CMD_TOUCH:
29762306a36Sopenharmony_ci		w1_touch_block(dev, cmd->data, cmd->len);
29862306a36Sopenharmony_ci		w1_netlink_queue_cmd(dev->priv, cmd);
29962306a36Sopenharmony_ci		break;
30062306a36Sopenharmony_ci	case W1_CMD_READ:
30162306a36Sopenharmony_ci		w1_read_block(dev, cmd->data, cmd->len);
30262306a36Sopenharmony_ci		w1_netlink_queue_cmd(dev->priv, cmd);
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci	case W1_CMD_WRITE:
30562306a36Sopenharmony_ci		w1_write_block(dev, cmd->data, cmd->len);
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci	default:
30862306a36Sopenharmony_ci		err = -EINVAL;
30962306a36Sopenharmony_ci		break;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return err;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int w1_process_command_addremove(struct w1_master *dev,
31662306a36Sopenharmony_ci	struct w1_netlink_cmd *cmd)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct w1_slave *sl;
31962306a36Sopenharmony_ci	int err = 0;
32062306a36Sopenharmony_ci	struct w1_reg_num *id;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (cmd->len != sizeof(*id))
32362306a36Sopenharmony_ci		return -EINVAL;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	id = (struct w1_reg_num *)cmd->data;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	sl = w1_slave_search_device(dev, id);
32862306a36Sopenharmony_ci	switch (cmd->cmd) {
32962306a36Sopenharmony_ci	case W1_CMD_SLAVE_ADD:
33062306a36Sopenharmony_ci		if (sl)
33162306a36Sopenharmony_ci			err = -EINVAL;
33262306a36Sopenharmony_ci		else
33362306a36Sopenharmony_ci			err = w1_attach_slave_device(dev, id);
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	case W1_CMD_SLAVE_REMOVE:
33662306a36Sopenharmony_ci		if (sl)
33762306a36Sopenharmony_ci			w1_slave_detach(sl);
33862306a36Sopenharmony_ci		else
33962306a36Sopenharmony_ci			err = -EINVAL;
34062306a36Sopenharmony_ci		break;
34162306a36Sopenharmony_ci	default:
34262306a36Sopenharmony_ci		err = -EINVAL;
34362306a36Sopenharmony_ci		break;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	return err;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int w1_process_command_master(struct w1_master *dev,
35062306a36Sopenharmony_ci	struct w1_netlink_cmd *req_cmd)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	int err = -EINVAL;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* drop bus_mutex for search (does it's own locking), and add/remove
35562306a36Sopenharmony_ci	 * which doesn't use the bus
35662306a36Sopenharmony_ci	 */
35762306a36Sopenharmony_ci	switch (req_cmd->cmd) {
35862306a36Sopenharmony_ci	case W1_CMD_SEARCH:
35962306a36Sopenharmony_ci	case W1_CMD_ALARM_SEARCH:
36062306a36Sopenharmony_ci	case W1_CMD_LIST_SLAVES:
36162306a36Sopenharmony_ci		mutex_unlock(&dev->bus_mutex);
36262306a36Sopenharmony_ci		err = w1_get_slaves(dev, req_cmd);
36362306a36Sopenharmony_ci		mutex_lock(&dev->bus_mutex);
36462306a36Sopenharmony_ci		break;
36562306a36Sopenharmony_ci	case W1_CMD_READ:
36662306a36Sopenharmony_ci	case W1_CMD_WRITE:
36762306a36Sopenharmony_ci	case W1_CMD_TOUCH:
36862306a36Sopenharmony_ci		err = w1_process_command_io(dev, req_cmd);
36962306a36Sopenharmony_ci		break;
37062306a36Sopenharmony_ci	case W1_CMD_RESET:
37162306a36Sopenharmony_ci		err = w1_reset_bus(dev);
37262306a36Sopenharmony_ci		break;
37362306a36Sopenharmony_ci	case W1_CMD_SLAVE_ADD:
37462306a36Sopenharmony_ci	case W1_CMD_SLAVE_REMOVE:
37562306a36Sopenharmony_ci		mutex_unlock(&dev->bus_mutex);
37662306a36Sopenharmony_ci		mutex_lock(&dev->mutex);
37762306a36Sopenharmony_ci		err = w1_process_command_addremove(dev, req_cmd);
37862306a36Sopenharmony_ci		mutex_unlock(&dev->mutex);
37962306a36Sopenharmony_ci		mutex_lock(&dev->bus_mutex);
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	default:
38262306a36Sopenharmony_ci		err = -EINVAL;
38362306a36Sopenharmony_ci		break;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return err;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int w1_process_command_slave(struct w1_slave *sl,
39062306a36Sopenharmony_ci		struct w1_netlink_cmd *cmd)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n",
39362306a36Sopenharmony_ci		__func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id,
39462306a36Sopenharmony_ci		sl->reg_num.crc, cmd->cmd, cmd->len);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return w1_process_command_io(sl->master, cmd);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int w1_process_command_root(struct cn_msg *req_cn, u32 portid)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct w1_master *dev;
40262306a36Sopenharmony_ci	struct cn_msg *cn;
40362306a36Sopenharmony_ci	struct w1_netlink_msg *msg;
40462306a36Sopenharmony_ci	u32 *id;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	cn = kmalloc(PAGE_SIZE, GFP_KERNEL);
40762306a36Sopenharmony_ci	if (!cn)
40862306a36Sopenharmony_ci		return -ENOMEM;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	cn->id.idx = CN_W1_IDX;
41162306a36Sopenharmony_ci	cn->id.val = CN_W1_VAL;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	cn->seq = req_cn->seq;
41462306a36Sopenharmony_ci	cn->ack = req_cn->seq + 1;
41562306a36Sopenharmony_ci	cn->len = sizeof(struct w1_netlink_msg);
41662306a36Sopenharmony_ci	msg = (struct w1_netlink_msg *)cn->data;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	msg->type = W1_LIST_MASTERS;
41962306a36Sopenharmony_ci	msg->status = 0;
42062306a36Sopenharmony_ci	msg->len = 0;
42162306a36Sopenharmony_ci	id = (u32 *)msg->data;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	mutex_lock(&w1_mlock);
42462306a36Sopenharmony_ci	list_for_each_entry(dev, &w1_masters, w1_master_entry) {
42562306a36Sopenharmony_ci		if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) {
42662306a36Sopenharmony_ci			cn_netlink_send(cn, portid, 0, GFP_KERNEL);
42762306a36Sopenharmony_ci			cn->len = sizeof(struct w1_netlink_msg);
42862306a36Sopenharmony_ci			msg->len = 0;
42962306a36Sopenharmony_ci			id = (u32 *)msg->data;
43062306a36Sopenharmony_ci		}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		*id = dev->id;
43362306a36Sopenharmony_ci		msg->len += sizeof(*id);
43462306a36Sopenharmony_ci		cn->len += sizeof(*id);
43562306a36Sopenharmony_ci		id++;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci	cn_netlink_send(cn, portid, 0, GFP_KERNEL);
43862306a36Sopenharmony_ci	mutex_unlock(&w1_mlock);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	kfree(cn);
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node,
44762306a36Sopenharmony_ci		async);
44862306a36Sopenharmony_ci	u16 mlen = node->msg->len;
44962306a36Sopenharmony_ci	u16 len;
45062306a36Sopenharmony_ci	int err = 0;
45162306a36Sopenharmony_ci	struct w1_slave *sl = node->sl;
45262306a36Sopenharmony_ci	struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)node->msg->data;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	mutex_lock(&dev->bus_mutex);
45562306a36Sopenharmony_ci	dev->priv = node->block;
45662306a36Sopenharmony_ci	if (sl && w1_reset_select_slave(sl))
45762306a36Sopenharmony_ci		err = -ENODEV;
45862306a36Sopenharmony_ci	node->block->cur_msg = node->msg;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	while (mlen && !err) {
46162306a36Sopenharmony_ci		if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
46262306a36Sopenharmony_ci			err = -E2BIG;
46362306a36Sopenharmony_ci			break;
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		if (sl)
46762306a36Sopenharmony_ci			err = w1_process_command_slave(sl, cmd);
46862306a36Sopenharmony_ci		else
46962306a36Sopenharmony_ci			err = w1_process_command_master(dev, cmd);
47062306a36Sopenharmony_ci		w1_netlink_check_send(node->block);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		w1_netlink_queue_status(node->block, node->msg, cmd, err);
47362306a36Sopenharmony_ci		err = 0;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		len = sizeof(*cmd) + cmd->len;
47662306a36Sopenharmony_ci		cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len);
47762306a36Sopenharmony_ci		mlen -= len;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (!cmd || err)
48162306a36Sopenharmony_ci		w1_netlink_queue_status(node->block, node->msg, cmd, err);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* ref taken in w1_search_slave or w1_search_master_id when building
48462306a36Sopenharmony_ci	 * the block
48562306a36Sopenharmony_ci	 */
48662306a36Sopenharmony_ci	if (sl)
48762306a36Sopenharmony_ci		w1_unref_slave(sl);
48862306a36Sopenharmony_ci	else
48962306a36Sopenharmony_ci		atomic_dec(&dev->refcnt);
49062306a36Sopenharmony_ci	dev->priv = NULL;
49162306a36Sopenharmony_ci	mutex_unlock(&dev->bus_mutex);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	mutex_lock(&dev->list_mutex);
49462306a36Sopenharmony_ci	list_del(&async_cmd->async_entry);
49562306a36Sopenharmony_ci	mutex_unlock(&dev->list_mutex);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	w1_unref_block(node->block);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic void w1_list_count_cmds(struct w1_netlink_msg *msg, int *cmd_count,
50162306a36Sopenharmony_ci	u16 *slave_len)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)msg->data;
50462306a36Sopenharmony_ci	u16 mlen = msg->len;
50562306a36Sopenharmony_ci	u16 len;
50662306a36Sopenharmony_ci	int slave_list = 0;
50762306a36Sopenharmony_ci	while (mlen) {
50862306a36Sopenharmony_ci		if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen)
50962306a36Sopenharmony_ci			break;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		switch (cmd->cmd) {
51262306a36Sopenharmony_ci		case W1_CMD_SEARCH:
51362306a36Sopenharmony_ci		case W1_CMD_ALARM_SEARCH:
51462306a36Sopenharmony_ci		case W1_CMD_LIST_SLAVES:
51562306a36Sopenharmony_ci			++slave_list;
51662306a36Sopenharmony_ci		}
51762306a36Sopenharmony_ci		++*cmd_count;
51862306a36Sopenharmony_ci		len = sizeof(*cmd) + cmd->len;
51962306a36Sopenharmony_ci		cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len);
52062306a36Sopenharmony_ci		mlen -= len;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (slave_list) {
52462306a36Sopenharmony_ci		struct w1_master *dev = w1_search_master_id(msg->id.mst.id);
52562306a36Sopenharmony_ci		if (dev) {
52662306a36Sopenharmony_ci			/* Bytes, and likely an overstimate, and if it isn't
52762306a36Sopenharmony_ci			 * the results can still be split between packets.
52862306a36Sopenharmony_ci			 */
52962306a36Sopenharmony_ci			*slave_len += sizeof(struct w1_reg_num) * slave_list *
53062306a36Sopenharmony_ci				(dev->slave_count + dev->max_slave_count);
53162306a36Sopenharmony_ci			/* search incremented it */
53262306a36Sopenharmony_ci			atomic_dec(&dev->refcnt);
53362306a36Sopenharmony_ci		}
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic void w1_cn_callback(struct cn_msg *cn, struct netlink_skb_parms *nsp)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct w1_netlink_msg *msg = (struct w1_netlink_msg *)(cn + 1);
54062306a36Sopenharmony_ci	struct w1_slave *sl;
54162306a36Sopenharmony_ci	struct w1_master *dev;
54262306a36Sopenharmony_ci	u16 msg_len;
54362306a36Sopenharmony_ci	u16 slave_len = 0;
54462306a36Sopenharmony_ci	int err = 0;
54562306a36Sopenharmony_ci	struct w1_cb_block *block = NULL;
54662306a36Sopenharmony_ci	struct w1_cb_node *node = NULL;
54762306a36Sopenharmony_ci	int node_count = 0;
54862306a36Sopenharmony_ci	int cmd_count = 0;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* If any unknown flag is set let the application know, that way
55162306a36Sopenharmony_ci	 * applications can detect the absence of features in kernels that
55262306a36Sopenharmony_ci	 * don't know about them.  http://lwn.net/Articles/587527/
55362306a36Sopenharmony_ci	 */
55462306a36Sopenharmony_ci	if (cn->flags & ~(W1_CN_BUNDLE)) {
55562306a36Sopenharmony_ci		w1_netlink_send_error(cn, msg, nsp->portid, -EINVAL);
55662306a36Sopenharmony_ci		return;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* Count the number of master or slave commands there are to allocate
56062306a36Sopenharmony_ci	 * space for one cb_node each.
56162306a36Sopenharmony_ci	 */
56262306a36Sopenharmony_ci	msg_len = cn->len;
56362306a36Sopenharmony_ci	while (msg_len && !err) {
56462306a36Sopenharmony_ci		if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {
56562306a36Sopenharmony_ci			err = -E2BIG;
56662306a36Sopenharmony_ci			break;
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		/* count messages for nodes and allocate any additional space
57062306a36Sopenharmony_ci		 * required for slave lists
57162306a36Sopenharmony_ci		 */
57262306a36Sopenharmony_ci		if (msg->type == W1_MASTER_CMD || msg->type == W1_SLAVE_CMD) {
57362306a36Sopenharmony_ci			++node_count;
57462306a36Sopenharmony_ci			w1_list_count_cmds(msg, &cmd_count, &slave_len);
57562306a36Sopenharmony_ci		}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci		msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
57862306a36Sopenharmony_ci		msg = (struct w1_netlink_msg *)(((u8 *)msg) +
57962306a36Sopenharmony_ci			sizeof(struct w1_netlink_msg) + msg->len);
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci	msg = (struct w1_netlink_msg *)(cn + 1);
58262306a36Sopenharmony_ci	if (node_count) {
58362306a36Sopenharmony_ci		int size;
58462306a36Sopenharmony_ci		int reply_size = sizeof(*cn) + cn->len + slave_len;
58562306a36Sopenharmony_ci		if (cn->flags & W1_CN_BUNDLE) {
58662306a36Sopenharmony_ci			/* bundling duplicats some of the messages */
58762306a36Sopenharmony_ci			reply_size += 2 * cmd_count * (sizeof(struct cn_msg) +
58862306a36Sopenharmony_ci				sizeof(struct w1_netlink_msg) +
58962306a36Sopenharmony_ci				sizeof(struct w1_netlink_cmd));
59062306a36Sopenharmony_ci		}
59162306a36Sopenharmony_ci		reply_size = min(CONNECTOR_MAX_MSG_SIZE, reply_size);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		/* allocate space for the block, a copy of the original message,
59462306a36Sopenharmony_ci		 * one node per cmd to point into the original message,
59562306a36Sopenharmony_ci		 * space for replies which is the original message size plus
59662306a36Sopenharmony_ci		 * space for any list slave data and status messages
59762306a36Sopenharmony_ci		 * cn->len doesn't include itself which is part of the block
59862306a36Sopenharmony_ci		 * */
59962306a36Sopenharmony_ci		size =  /* block + original message */
60062306a36Sopenharmony_ci			sizeof(struct w1_cb_block) + sizeof(*cn) + cn->len +
60162306a36Sopenharmony_ci			/* space for nodes */
60262306a36Sopenharmony_ci			node_count * sizeof(struct w1_cb_node) +
60362306a36Sopenharmony_ci			/* replies */
60462306a36Sopenharmony_ci			sizeof(struct cn_msg) + reply_size;
60562306a36Sopenharmony_ci		block = kzalloc(size, GFP_KERNEL);
60662306a36Sopenharmony_ci		if (!block) {
60762306a36Sopenharmony_ci			/* if the system is already out of memory,
60862306a36Sopenharmony_ci			 * (A) will this work, and (B) would it be better
60962306a36Sopenharmony_ci			 * to not try?
61062306a36Sopenharmony_ci			 */
61162306a36Sopenharmony_ci			w1_netlink_send_error(cn, msg, nsp->portid, -ENOMEM);
61262306a36Sopenharmony_ci			return;
61362306a36Sopenharmony_ci		}
61462306a36Sopenharmony_ci		atomic_set(&block->refcnt, 1);
61562306a36Sopenharmony_ci		block->portid = nsp->portid;
61662306a36Sopenharmony_ci		block->request_cn = *cn;
61762306a36Sopenharmony_ci		memcpy(block->request_cn.data, cn->data, cn->len);
61862306a36Sopenharmony_ci		node = (struct w1_cb_node *)(block->request_cn.data + cn->len);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		/* Sneeky, when not bundling, reply_size is the allocated space
62162306a36Sopenharmony_ci		 * required for the reply, cn_msg isn't part of maxlen so
62262306a36Sopenharmony_ci		 * it should be reply_size - sizeof(struct cn_msg), however
62362306a36Sopenharmony_ci		 * when checking if there is enough space, w1_reply_make_space
62462306a36Sopenharmony_ci		 * is called with the full message size including cn_msg,
62562306a36Sopenharmony_ci		 * because it isn't known at that time if an additional cn_msg
62662306a36Sopenharmony_ci		 * will need to be allocated.  So an extra cn_msg is added
62762306a36Sopenharmony_ci		 * above in "size".
62862306a36Sopenharmony_ci		 */
62962306a36Sopenharmony_ci		block->maxlen = reply_size;
63062306a36Sopenharmony_ci		block->first_cn = (struct cn_msg *)(node + node_count);
63162306a36Sopenharmony_ci		memset(block->first_cn, 0, sizeof(*block->first_cn));
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	msg_len = cn->len;
63562306a36Sopenharmony_ci	while (msg_len && !err) {
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci		dev = NULL;
63862306a36Sopenharmony_ci		sl = NULL;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {
64162306a36Sopenharmony_ci			err = -E2BIG;
64262306a36Sopenharmony_ci			break;
64362306a36Sopenharmony_ci		}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		/* execute on this thread, no need to process later */
64662306a36Sopenharmony_ci		if (msg->type == W1_LIST_MASTERS) {
64762306a36Sopenharmony_ci			err = w1_process_command_root(cn, nsp->portid);
64862306a36Sopenharmony_ci			goto out_cont;
64962306a36Sopenharmony_ci		}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		/* All following message types require additional data,
65262306a36Sopenharmony_ci		 * check here before references are taken.
65362306a36Sopenharmony_ci		 */
65462306a36Sopenharmony_ci		if (!msg->len) {
65562306a36Sopenharmony_ci			err = -EPROTO;
65662306a36Sopenharmony_ci			goto out_cont;
65762306a36Sopenharmony_ci		}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		/* both search calls take references */
66062306a36Sopenharmony_ci		if (msg->type == W1_MASTER_CMD) {
66162306a36Sopenharmony_ci			dev = w1_search_master_id(msg->id.mst.id);
66262306a36Sopenharmony_ci		} else if (msg->type == W1_SLAVE_CMD) {
66362306a36Sopenharmony_ci			sl = w1_search_slave((struct w1_reg_num *)msg->id.id);
66462306a36Sopenharmony_ci			if (sl)
66562306a36Sopenharmony_ci				dev = sl->master;
66662306a36Sopenharmony_ci		} else {
66762306a36Sopenharmony_ci			pr_notice("%s: cn: %x.%x, wrong type: %u, len: %u.\n",
66862306a36Sopenharmony_ci				__func__, cn->id.idx, cn->id.val,
66962306a36Sopenharmony_ci				msg->type, msg->len);
67062306a36Sopenharmony_ci			err = -EPROTO;
67162306a36Sopenharmony_ci			goto out_cont;
67262306a36Sopenharmony_ci		}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci		if (!dev) {
67562306a36Sopenharmony_ci			err = -ENODEV;
67662306a36Sopenharmony_ci			goto out_cont;
67762306a36Sopenharmony_ci		}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		err = 0;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		atomic_inc(&block->refcnt);
68262306a36Sopenharmony_ci		node->async.cb = w1_process_cb;
68362306a36Sopenharmony_ci		node->block = block;
68462306a36Sopenharmony_ci		node->msg = (struct w1_netlink_msg *)((u8 *)&block->request_cn +
68562306a36Sopenharmony_ci			(size_t)((u8 *)msg - (u8 *)cn));
68662306a36Sopenharmony_ci		node->sl = sl;
68762306a36Sopenharmony_ci		node->dev = dev;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		mutex_lock(&dev->list_mutex);
69062306a36Sopenharmony_ci		list_add_tail(&node->async.async_entry, &dev->async_list);
69162306a36Sopenharmony_ci		wake_up_process(dev->thread);
69262306a36Sopenharmony_ci		mutex_unlock(&dev->list_mutex);
69362306a36Sopenharmony_ci		++node;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ciout_cont:
69662306a36Sopenharmony_ci		/* Can't queue because that modifies block and another
69762306a36Sopenharmony_ci		 * thread could be processing the messages by now and
69862306a36Sopenharmony_ci		 * there isn't a lock, send directly.
69962306a36Sopenharmony_ci		 */
70062306a36Sopenharmony_ci		if (err)
70162306a36Sopenharmony_ci			w1_netlink_send_error(cn, msg, nsp->portid, err);
70262306a36Sopenharmony_ci		msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
70362306a36Sopenharmony_ci		msg = (struct w1_netlink_msg *)(((u8 *)msg) +
70462306a36Sopenharmony_ci			sizeof(struct w1_netlink_msg) + msg->len);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci		/*
70762306a36Sopenharmony_ci		 * Let's allow requests for nonexisting devices.
70862306a36Sopenharmony_ci		 */
70962306a36Sopenharmony_ci		if (err == -ENODEV)
71062306a36Sopenharmony_ci			err = 0;
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci	if (block)
71362306a36Sopenharmony_ci		w1_unref_block(block);
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ciint w1_init_netlink(void)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	return cn_add_callback(&w1_id, "w1", &w1_cn_callback);
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_civoid w1_fini_netlink(void)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	cn_del_callback(&w1_id);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci#else
73062306a36Sopenharmony_civoid w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *cn)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ciint w1_init_netlink(void)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	return 0;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_civoid w1_fini_netlink(void)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci#endif
743