1/*
2 * w1_netlink.c
3 *
4 * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/skbuff.h>
23#include <linux/netlink.h>
24#include <linux/connector.h>
25
26#include "w1.h"
27#include "w1_log.h"
28#include "w1_netlink.h"
29
30#if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || \
31	(defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE)))
32void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
33{
34	char buf[sizeof(struct cn_msg) + sizeof(struct w1_netlink_msg)];
35	struct cn_msg *m = (struct cn_msg *)buf;
36	struct w1_netlink_msg *w = (struct w1_netlink_msg *)(m+1);
37
38	memset(buf, 0, sizeof(buf));
39
40	m->id.idx = CN_W1_IDX;
41	m->id.val = CN_W1_VAL;
42
43	m->seq = dev->seq++;
44	m->len = sizeof(struct w1_netlink_msg);
45
46	memcpy(w, msg, sizeof(struct w1_netlink_msg));
47
48	cn_netlink_send(m, 0, GFP_KERNEL);
49}
50
51static int w1_process_command_master(struct w1_master *dev, struct cn_msg *msg,
52		struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
53{
54	dev_dbg(&dev->dev, "%s: %s: cmd=%02x, len=%u.\n",
55		__func__, dev->name, cmd->cmd, cmd->len);
56
57	if (cmd->cmd != W1_CMD_SEARCH && cmd->cmd != W1_CMD_ALARM_SEARCH)
58		return -EINVAL;
59
60	w1_search_process(dev, (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH);
61	return 0;
62}
63
64static int w1_send_read_reply(struct w1_slave *sl, struct cn_msg *msg,
65		struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
66{
67	void *data;
68	struct w1_netlink_msg *h;
69	struct w1_netlink_cmd *c;
70	struct cn_msg *cm;
71	int err;
72
73	data = kzalloc(sizeof(struct cn_msg) +
74			sizeof(struct w1_netlink_msg) +
75			sizeof(struct w1_netlink_cmd) +
76			cmd->len, GFP_KERNEL);
77	if (!data)
78		return -ENOMEM;
79
80	cm = (struct cn_msg *)(data);
81	h = (struct w1_netlink_msg *)(cm + 1);
82	c = (struct w1_netlink_cmd *)(h + 1);
83
84	memcpy(cm, msg, sizeof(struct cn_msg));
85	memcpy(h, hdr, sizeof(struct w1_netlink_msg));
86	memcpy(c, cmd, sizeof(struct w1_netlink_cmd));
87
88	cm->ack = msg->seq+1;
89	cm->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd) + cmd->len;
90
91	h->len = sizeof(struct w1_netlink_cmd) + cmd->len;
92
93	memcpy(c->data, cmd->data, c->len);
94
95	err = cn_netlink_send(cm, 0, GFP_KERNEL);
96
97	kfree(data);
98
99	return err;
100}
101
102static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg,
103		struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
104{
105	int err = 0;
106
107	dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n",
108		__func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id, sl->reg_num.crc,
109		cmd->cmd, cmd->len);
110
111	switch (cmd->cmd) {
112		case W1_CMD_READ:
113			w1_read_block(sl->master, cmd->data, cmd->len);
114			w1_send_read_reply(sl, msg, hdr, cmd);
115			break;
116		case W1_CMD_WRITE:
117			w1_write_block(sl->master, cmd->data, cmd->len);
118			break;
119		case W1_CMD_SEARCH:
120		case W1_CMD_ALARM_SEARCH:
121			w1_search_process(sl->master,
122					(cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH);
123			break;
124		default:
125			err = -1;
126			break;
127	}
128
129	return err;
130}
131
132static void w1_cn_callback(void *data)
133{
134	struct cn_msg *msg = data;
135	struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1);
136	struct w1_netlink_cmd *cmd;
137	struct w1_slave *sl;
138	struct w1_master *dev;
139	int err = 0;
140
141	while (msg->len && !err) {
142		struct w1_reg_num id;
143		u16 mlen = m->len;
144		u8 *cmd_data = m->data;
145
146		dev = NULL;
147		sl = NULL;
148
149		memcpy(&id, m->id.id, sizeof(id));
150		if (m->len + sizeof(struct w1_netlink_msg) > msg->len) {
151			err = -E2BIG;
152			break;
153		}
154
155		if (!mlen)
156			goto out_cont;
157
158		if (m->type == W1_MASTER_CMD) {
159			dev = w1_search_master_id(m->id.mst.id);
160		} else if (m->type == W1_SLAVE_CMD) {
161			sl = w1_search_slave(&id);
162			if (sl)
163				dev = sl->master;
164		}
165
166		if (!dev) {
167			err = -ENODEV;
168			goto out_cont;
169		}
170
171		mutex_lock(&dev->mutex);
172
173		if (sl && w1_reset_select_slave(sl)) {
174			err = -ENODEV;
175			goto out_up;
176		}
177
178		while (mlen) {
179			cmd = (struct w1_netlink_cmd *)cmd_data;
180
181			if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
182				err = -E2BIG;
183				break;
184			}
185
186			if (sl)
187				w1_process_command_slave(sl, msg, m, cmd);
188			else
189				w1_process_command_master(dev, msg, m, cmd);
190
191			cmd_data += cmd->len + sizeof(struct w1_netlink_cmd);
192			mlen -= cmd->len + sizeof(struct w1_netlink_cmd);
193		}
194out_up:
195		atomic_dec(&dev->refcnt);
196		if (sl)
197			atomic_dec(&sl->refcnt);
198		mutex_unlock(&dev->mutex);
199out_cont:
200		msg->len -= sizeof(struct w1_netlink_msg) + m->len;
201		m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len);
202
203		/*
204		 * Let's allow requests for nonexisting devices.
205		 */
206		if (err == -ENODEV)
207			err = 0;
208	}
209}
210
211int w1_init_netlink(void)
212{
213	struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
214
215	return cn_add_callback(&w1_id, "w1", &w1_cn_callback);
216}
217
218void w1_fini_netlink(void)
219{
220	struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
221
222	cn_del_callback(&w1_id);
223}
224#else
225void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
226{
227}
228
229int w1_init_netlink(void)
230{
231	return 0;
232}
233
234void w1_fini_netlink(void)
235{
236}
237#endif
238