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