w1_netlink.c revision abd52a13206e02537ca1dc08fc5438c7d27bdbf1
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) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE)))
31void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
32{
33	char buf[sizeof(struct cn_msg) + sizeof(struct w1_netlink_msg)];
34	struct cn_msg *m = (struct cn_msg *)buf;
35	struct w1_netlink_msg *w = (struct w1_netlink_msg *)(m+1);
36
37	memset(buf, 0, sizeof(buf));
38
39	m->id.idx = CN_W1_IDX;
40	m->id.val = CN_W1_VAL;
41
42	m->seq = dev->seq++;
43	m->len = sizeof(struct w1_netlink_msg);
44
45	memcpy(w, msg, sizeof(struct w1_netlink_msg));
46
47	cn_netlink_send(m, 0, GFP_KERNEL);
48}
49
50static int w1_process_command_master(struct w1_master *dev, struct cn_msg *msg,
51		struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
52{
53	dev_dbg(&dev->dev, "%s: %s: cmd=%02x, len=%u.\n",
54		__func__, dev->name, cmd->cmd, cmd->len);
55
56	if (cmd->cmd != W1_CMD_SEARCH && cmd->cmd != W1_CMD_ALARM_SEARCH)
57		return -EINVAL;
58
59	w1_search_process(dev, (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH);
60	return 0;
61}
62
63static int w1_send_read_reply(struct w1_slave *sl, struct cn_msg *msg,
64		struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
65{
66	void *data;
67	struct w1_netlink_msg *h;
68	struct w1_netlink_cmd *c;
69	struct cn_msg *cm;
70	int err;
71
72	data = kzalloc(sizeof(struct cn_msg) +
73			sizeof(struct w1_netlink_msg) +
74			sizeof(struct w1_netlink_cmd) +
75			cmd->len, GFP_KERNEL);
76	if (!data)
77		return -ENOMEM;
78
79	cm = (struct cn_msg *)(data);
80	h = (struct w1_netlink_msg *)(cm + 1);
81	c = (struct w1_netlink_cmd *)(h + 1);
82
83	memcpy(cm, msg, sizeof(struct cn_msg));
84	memcpy(h, hdr, sizeof(struct w1_netlink_msg));
85	memcpy(c, cmd, sizeof(struct w1_netlink_cmd));
86
87	cm->ack = msg->seq+1;
88	cm->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd) + cmd->len;
89
90	h->len = sizeof(struct w1_netlink_cmd) + cmd->len;
91
92	memcpy(c->data, cmd->data, c->len);
93
94	err = cn_netlink_send(cm, 0, GFP_KERNEL);
95
96	kfree(data);
97
98	return err;
99}
100
101static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg,
102		struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd)
103{
104	int err = 0;
105
106	dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n",
107		__func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id, sl->reg_num.crc,
108		cmd->cmd, cmd->len);
109
110	switch (cmd->cmd) {
111		case W1_CMD_READ:
112			w1_read_block(sl->master, cmd->data, cmd->len);
113			w1_send_read_reply(sl, msg, hdr, cmd);
114			break;
115		case W1_CMD_WRITE:
116			w1_write_block(sl->master, cmd->data, cmd->len);
117			break;
118		case W1_CMD_SEARCH:
119		case W1_CMD_ALARM_SEARCH:
120			w1_search_process(sl->master,
121					(cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH);
122			break;
123		default:
124			err = -1;
125			break;
126	}
127
128	return err;
129}
130
131static void w1_cn_callback(void *data)
132{
133	struct cn_msg *msg = data;
134	struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1);
135	struct w1_netlink_cmd *cmd;
136	struct w1_slave *sl;
137	struct w1_master *dev;
138	int err = 0;
139
140	while (msg->len && !err) {
141		struct w1_reg_num id;
142		u16 mlen = m->len;
143		u8 *cmd_data = m->data;
144
145		dev = NULL;
146		sl = NULL;
147
148		memcpy(&id, m->id.id, sizeof(id));
149#if 0
150		printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n",
151				__func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len);
152#endif
153		if (m->len + sizeof(struct w1_netlink_msg) > msg->len) {
154			err = -E2BIG;
155			break;
156		}
157
158		if (!mlen)
159			goto out_cont;
160
161		if (m->type == W1_MASTER_CMD) {
162			dev = w1_search_master_id(m->id.mst.id);
163		} else if (m->type == W1_SLAVE_CMD) {
164			sl = w1_search_slave(&id);
165			if (sl)
166				dev = sl->master;
167		}
168
169		if (!dev) {
170			err = -ENODEV;
171			goto out_cont;
172		}
173
174		mutex_lock(&dev->mutex);
175
176		if (sl && w1_reset_select_slave(sl)) {
177			err = -ENODEV;
178			goto out_up;
179		}
180
181		while (mlen) {
182			cmd = (struct w1_netlink_cmd *)cmd_data;
183
184			if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
185				err = -E2BIG;
186				break;
187			}
188
189			if (sl)
190				w1_process_command_slave(sl, msg, m, cmd);
191			else
192				w1_process_command_master(dev, msg, m, cmd);
193
194			cmd_data += cmd->len + sizeof(struct w1_netlink_cmd);
195			mlen -= cmd->len + sizeof(struct w1_netlink_cmd);
196		}
197out_up:
198		atomic_dec(&dev->refcnt);
199		if (sl)
200			atomic_dec(&sl->refcnt);
201		mutex_unlock(&dev->mutex);
202out_cont:
203		msg->len -= sizeof(struct w1_netlink_msg) + m->len;
204		m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len);
205
206		/*
207		 * Let's allow requests for nonexisting devices.
208		 */
209		if (err == -ENODEV)
210			err = 0;
211	}
212#if 0
213	if (err) {
214		printk("%s: malformed message. Dropping.\n", __func__);
215	}
216#endif
217}
218
219int w1_init_netlink(void)
220{
221	struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
222
223	return cn_add_callback(&w1_id, "w1", &w1_cn_callback);
224}
225
226void w1_fini_netlink(void)
227{
228	struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
229
230	cn_del_callback(&w1_id);
231}
232#else
233void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
234{
235}
236
237int w1_init_netlink(void)
238{
239	return 0;
240}
241
242void w1_fini_netlink(void)
243{
244}
245#endif
246