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