Blame view

drivers/w1/w1_netlink.c 18.8 KB
c942fddf8   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
  /*
a80187663   Evgeniy Polyakov   MAINTAINERS: Evge...
3
   * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
   */
5a0e3ad6a   Tejun Heo   include cleanup: ...
5
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
  #include <linux/skbuff.h>
  #include <linux/netlink.h>
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
8
  #include <linux/connector.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9

de0d6dbdb   Andrew F. Davis   w1: Add subsystem...
10
  #include "w1_internal.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  #include "w1_netlink.h"
46e07f6e5   Evgeniy Polyakov   [PATCH] w1: Make ...
12
  #if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE)))
8a0427d19   David Fries   w1: optional bund...
13

8a0427d19   David Fries   w1: optional bund...
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  /* Bundle together everything required to process a request in one memory
   * allocation.
   */
  struct w1_cb_block {
  	atomic_t refcnt;
  	u32 portid; /* Sending process port ID */
  	/* maximum value for first_cn->len */
  	u16 maxlen;
  	/* pointers to building up the reply message */
  	struct cn_msg *first_cn; /* fixed once the structure is populated */
  	struct cn_msg *cn; /* advances as cn_msg is appeneded */
  	struct w1_netlink_msg *msg; /* advances as w1_netlink_msg is appened */
  	struct w1_netlink_cmd *cmd; /* advances as cmds are appened */
  	struct w1_netlink_msg *cur_msg; /* currently message being processed */
  	/* copy of the original request follows */
  	struct cn_msg request_cn;
  	/* followed by variable length:
  	 * cn_msg, data (w1_netlink_msg and w1_netlink_cmd)
  	 * one or more struct w1_cb_node
  	 * reply first_cn, data (w1_netlink_msg and w1_netlink_cmd)
  	 */
  };
  struct w1_cb_node {
  	struct w1_async_cmd async;
  	/* pointers within w1_cb_block and cn data */
  	struct w1_cb_block *block;
  	struct w1_netlink_msg *msg;
  	struct w1_slave *sl;
  	struct w1_master *dev;
  };
  
  /**
   * w1_reply_len() - calculate current reply length, compare to maxlen
   * @block: block to calculate
   *
   * Calculates the current message length including possible multiple
   * cn_msg and data, excludes the first sizeof(struct cn_msg).  Direclty
   * compariable to maxlen and usable to send the message.
   */
  static u16 w1_reply_len(struct w1_cb_block *block)
  {
  	if (!block->cn)
  		return 0;
  	return (u8 *)block->cn - (u8 *)block->first_cn + block->cn->len;
  }
  
  static void w1_unref_block(struct w1_cb_block *block)
  {
  	if (atomic_sub_return(1, &block->refcnt) == 0) {
  		u16 len = w1_reply_len(block);
  		if (len) {
  			cn_netlink_send_mult(block->first_cn, len,
  				block->portid, 0, GFP_KERNEL);
  		}
  		kfree(block);
  	}
  }
  
  /**
   * w1_reply_make_space() - send message if needed to make space
   * @block: block to make space on
   * @space: how many bytes requested
   *
   * Verify there is enough room left for the caller to add "space" bytes to the
   * message, if there isn't send the message and reset.
   */
  static void w1_reply_make_space(struct w1_cb_block *block, u16 space)
  {
  	u16 len = w1_reply_len(block);
  	if (len + space >= block->maxlen) {
  		cn_netlink_send_mult(block->first_cn, len, block->portid, 0, GFP_KERNEL);
  		block->first_cn->len = 0;
  		block->cn = NULL;
  		block->msg = NULL;
  		block->cmd = NULL;
  	}
  }
  
  /* Early send when replies aren't bundled. */
  static void w1_netlink_check_send(struct w1_cb_block *block)
  {
  	if (!(block->request_cn.flags & W1_CN_BUNDLE) && block->cn)
  		w1_reply_make_space(block, block->maxlen);
  }
  
  /**
   * w1_netlink_setup_msg() - prepare to write block->msg
   * @block: block to operate on
   * @ack: determines if cn can be reused
   *
   * block->cn will be setup with the correct ack, advancing if needed
   * block->cn->len does not include space for block->msg
   * block->msg advances but remains uninitialized
   */
  static void w1_netlink_setup_msg(struct w1_cb_block *block, u32 ack)
  {
  	if (block->cn && block->cn->ack == ack) {
  		block->msg = (struct w1_netlink_msg *)(block->cn->data + block->cn->len);
  	} else {
  		/* advance or set to data */
  		if (block->cn)
  			block->cn = (struct cn_msg *)(block->cn->data +
  				block->cn->len);
  		else
  			block->cn = block->first_cn;
  
  		memcpy(block->cn, &block->request_cn, sizeof(*block->cn));
  		block->cn->len = 0;
  		block->cn->ack = ack;
  		block->msg = (struct w1_netlink_msg *)block->cn->data;
  	}
  }
  
  /* Append cmd to msg, include cmd->data as well.  This is because
   * any following data goes with the command and in the case of a read is
   * the results.
   */
  static void w1_netlink_queue_cmd(struct w1_cb_block *block,
  	struct w1_netlink_cmd *cmd)
  {
  	u32 space;
  	w1_reply_make_space(block, sizeof(struct cn_msg) +
  		sizeof(struct w1_netlink_msg) + sizeof(*cmd) + cmd->len);
  
  	/* There's a status message sent after each command, so no point
  	 * in trying to bundle this cmd after an existing one, because
  	 * there won't be one.  Allocate and copy over a new cn_msg.
  	 */
  	w1_netlink_setup_msg(block, block->request_cn.seq + 1);
  	memcpy(block->msg, block->cur_msg, sizeof(*block->msg));
  	block->cn->len += sizeof(*block->msg);
  	block->msg->len = 0;
  	block->cmd = (struct w1_netlink_cmd *)(block->msg->data);
  
  	space = sizeof(*cmd) + cmd->len;
  	if (block->cmd != cmd)
  		memcpy(block->cmd, cmd, space);
  	block->cn->len += space;
  	block->msg->len += space;
  }
  
  /* Append req_msg and req_cmd, no other commands and no data from req_cmd are
   * copied.
   */
  static void w1_netlink_queue_status(struct w1_cb_block *block,
  	struct w1_netlink_msg *req_msg, struct w1_netlink_cmd *req_cmd,
  	int error)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
  {
8a0427d19   David Fries   w1: optional bund...
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  	u16 space = sizeof(struct cn_msg) + sizeof(*req_msg) + sizeof(*req_cmd);
  	w1_reply_make_space(block, space);
  	w1_netlink_setup_msg(block, block->request_cn.ack);
  
  	memcpy(block->msg, req_msg, sizeof(*req_msg));
  	block->cn->len += sizeof(*req_msg);
  	block->msg->len = 0;
  	block->msg->status = (u8)-error;
  	if (req_cmd) {
  		struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)block->msg->data;
  		memcpy(cmd, req_cmd, sizeof(*cmd));
  		block->cn->len += sizeof(*cmd);
  		block->msg->len += sizeof(*cmd);
  		cmd->len = 0;
  	}
  	w1_netlink_check_send(block);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179

8a0427d19   David Fries   w1: optional bund...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
  /**
   * w1_netlink_send_error() - sends the error message now
   * @cn: original cn_msg
   * @msg: original w1_netlink_msg
   * @portid: where to send it
   * @error: error status
   *
   * Use when a block isn't available to queue the message to and cn, msg
   * might not be contiguous.
   */
  static void w1_netlink_send_error(struct cn_msg *cn, struct w1_netlink_msg *msg,
  	int portid, int error)
  {
  	struct {
  		struct cn_msg cn;
  		struct w1_netlink_msg msg;
  	} packet;
  	memcpy(&packet.cn, cn, sizeof(packet.cn));
  	memcpy(&packet.msg, msg, sizeof(packet.msg));
  	packet.cn.len = sizeof(packet.msg);
  	packet.msg.len = 0;
  	packet.msg.status = (u8)-error;
  	cn_netlink_send(&packet.cn, portid, 0, GFP_KERNEL);
  }
  
  /**
   * w1_netlink_send() - sends w1 netlink notifications
   * @dev: w1_master the even is associated with or for
   * @msg: w1_netlink_msg message to be sent
   *
   * This are notifications generated from the kernel.
   */
  void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
  {
  	struct {
  		struct cn_msg cn;
  		struct w1_netlink_msg msg;
  	} packet;
  	memset(&packet, 0, sizeof(packet));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219

8a0427d19   David Fries   w1: optional bund...
220
221
  	packet.cn.id.idx = CN_W1_IDX;
  	packet.cn.id.val = CN_W1_VAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222

8a0427d19   David Fries   w1: optional bund...
223
224
  	packet.cn.seq = dev->seq++;
  	packet.cn.len = sizeof(*msg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225

8a0427d19   David Fries   w1: optional bund...
226
227
  	memcpy(&packet.msg, msg, sizeof(*msg));
  	packet.msg.len = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228

8a0427d19   David Fries   w1: optional bund...
229
  	cn_netlink_send(&packet.cn, 0, 0, GFP_KERNEL);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
230
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231

3b8384070   Evgeniy Polyakov   w1: list slaves c...
232
233
  static void w1_send_slave(struct w1_master *dev, u64 rn)
  {
8a0427d19   David Fries   w1: optional bund...
234
235
  	struct w1_cb_block *block = dev->priv;
  	struct w1_netlink_cmd *cache_cmd = block->cmd;
6b355b33a   David Fries   w1: fix w1_send_s...
236
  	u64 *data;
3b8384070   Evgeniy Polyakov   w1: list slaves c...
237

8a0427d19   David Fries   w1: optional bund...
238
  	w1_reply_make_space(block, sizeof(*data));
3b8384070   Evgeniy Polyakov   w1: list slaves c...
239

8a0427d19   David Fries   w1: optional bund...
240
241
242
243
  	/* Add cmd back if the packet was sent */
  	if (!block->cmd) {
  		cache_cmd->len = 0;
  		w1_netlink_queue_cmd(block, cache_cmd);
3b8384070   Evgeniy Polyakov   w1: list slaves c...
244
  	}
8a0427d19   David Fries   w1: optional bund...
245
  	data = (u64 *)(block->cmd->data + block->cmd->len);
3b8384070   Evgeniy Polyakov   w1: list slaves c...
246

6b355b33a   David Fries   w1: fix w1_send_s...
247
  	*data = rn;
8a0427d19   David Fries   w1: optional bund...
248
249
250
  	block->cn->len += sizeof(*data);
  	block->msg->len += sizeof(*data);
  	block->cmd->len += sizeof(*data);
3b8384070   Evgeniy Polyakov   w1: list slaves c...
251
  }
70b34d2ed   David Fries   w1: new netlink c...
252
  static void w1_found_send_slave(struct w1_master *dev, u64 rn)
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
253
  {
70b34d2ed   David Fries   w1: new netlink c...
254
255
  	/* update kernel slave list */
  	w1_slave_found(dev, rn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256

70b34d2ed   David Fries   w1: new netlink c...
257
258
259
260
  	w1_send_slave(dev, rn);
  }
  
  /* Get the current slave list, or search (with or without alarm) */
8a0427d19   David Fries   w1: optional bund...
261
  static int w1_get_slaves(struct w1_master *dev, struct w1_netlink_cmd *req_cmd)
70b34d2ed   David Fries   w1: new netlink c...
262
  {
70b34d2ed   David Fries   w1: new netlink c...
263
  	struct w1_slave *sl;
8a0427d19   David Fries   w1: optional bund...
264
265
  	req_cmd->len = 0;
  	w1_netlink_queue_cmd(dev->priv, req_cmd);
70b34d2ed   David Fries   w1: new netlink c...
266
267
  
  	if (req_cmd->cmd == W1_CMD_LIST_SLAVES) {
8a0427d19   David Fries   w1: optional bund...
268
  		u64 rn;
9fcbbac5d   David Fries   w1: process w1 ne...
269
  		mutex_lock(&dev->list_mutex);
70b34d2ed   David Fries   w1: new netlink c...
270
271
272
273
  		list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
  			memcpy(&rn, &sl->reg_num, sizeof(rn));
  			w1_send_slave(dev, rn);
  		}
9fcbbac5d   David Fries   w1: process w1 ne...
274
  		mutex_unlock(&dev->list_mutex);
70b34d2ed   David Fries   w1: new netlink c...
275
  	} else {
8a0427d19   David Fries   w1: optional bund...
276
  		w1_search_process_cb(dev, req_cmd->cmd == W1_CMD_ALARM_SEARCH ?
70b34d2ed   David Fries   w1: new netlink c...
277
278
  			W1_ALARM_SEARCH : W1_SEARCH, w1_found_send_slave);
  	}
3b8384070   Evgeniy Polyakov   w1: list slaves c...
279

12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
280
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  }
2d8331792   Evgeniy Polyakov   [PATCH] W1: w1_ne...
282

8a0427d19   David Fries   w1: optional bund...
283
284
  static int w1_process_command_io(struct w1_master *dev,
  	struct w1_netlink_cmd *cmd)
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
285
286
  {
  	int err = 0;
c7e26631d   Evgeniy Polyakov   w1: allow master ...
287
288
289
  	switch (cmd->cmd) {
  	case W1_CMD_TOUCH:
  		w1_touch_block(dev, cmd->data, cmd->len);
8a0427d19   David Fries   w1: optional bund...
290
  		w1_netlink_queue_cmd(dev->priv, cmd);
c7e26631d   Evgeniy Polyakov   w1: allow master ...
291
292
293
  		break;
  	case W1_CMD_READ:
  		w1_read_block(dev, cmd->data, cmd->len);
8a0427d19   David Fries   w1: optional bund...
294
  		w1_netlink_queue_cmd(dev->priv, cmd);
c7e26631d   Evgeniy Polyakov   w1: allow master ...
295
296
297
298
299
  		break;
  	case W1_CMD_WRITE:
  		w1_write_block(dev, cmd->data, cmd->len);
  		break;
  	default:
4037014e3   Evgeniy Polyakov   w1: send status m...
300
  		err = -EINVAL;
c7e26631d   Evgeniy Polyakov   w1: allow master ...
301
302
303
304
305
  		break;
  	}
  
  	return err;
  }
70b34d2ed   David Fries   w1: new netlink c...
306
  static int w1_process_command_addremove(struct w1_master *dev,
70b34d2ed   David Fries   w1: new netlink c...
307
  	struct w1_netlink_cmd *cmd)
c7e26631d   Evgeniy Polyakov   w1: allow master ...
308
  {
70b34d2ed   David Fries   w1: new netlink c...
309
310
311
  	struct w1_slave *sl;
  	int err = 0;
  	struct w1_reg_num *id;
c7e26631d   Evgeniy Polyakov   w1: allow master ...
312

8a0427d19   David Fries   w1: optional bund...
313
  	if (cmd->len != sizeof(*id))
70b34d2ed   David Fries   w1: new netlink c...
314
  		return -EINVAL;
c7e26631d   Evgeniy Polyakov   w1: allow master ...
315

70b34d2ed   David Fries   w1: new netlink c...
316
  	id = (struct w1_reg_num *)cmd->data;
c7e26631d   Evgeniy Polyakov   w1: allow master ...
317

70b34d2ed   David Fries   w1: new netlink c...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
  	sl = w1_slave_search_device(dev, id);
  	switch (cmd->cmd) {
  	case W1_CMD_SLAVE_ADD:
  		if (sl)
  			err = -EINVAL;
  		else
  			err = w1_attach_slave_device(dev, id);
  		break;
  	case W1_CMD_SLAVE_REMOVE:
  		if (sl)
  			w1_slave_detach(sl);
  		else
  			err = -EINVAL;
  		break;
  	default:
  		err = -EINVAL;
  		break;
  	}
c7e26631d   Evgeniy Polyakov   w1: allow master ...
336

70b34d2ed   David Fries   w1: new netlink c...
337
338
  	return err;
  }
c7e26631d   Evgeniy Polyakov   w1: allow master ...
339

70b34d2ed   David Fries   w1: new netlink c...
340
  static int w1_process_command_master(struct w1_master *dev,
70b34d2ed   David Fries   w1: new netlink c...
341
342
343
  	struct w1_netlink_cmd *req_cmd)
  {
  	int err = -EINVAL;
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
344

d3a8a9dbb   David Fries   w1: hold bus_mute...
345
346
347
  	/* drop bus_mutex for search (does it's own locking), and add/remove
  	 * which doesn't use the bus
  	 */
70b34d2ed   David Fries   w1: new netlink c...
348
  	switch (req_cmd->cmd) {
c7e26631d   Evgeniy Polyakov   w1: allow master ...
349
350
  	case W1_CMD_SEARCH:
  	case W1_CMD_ALARM_SEARCH:
70b34d2ed   David Fries   w1: new netlink c...
351
  	case W1_CMD_LIST_SLAVES:
d3a8a9dbb   David Fries   w1: hold bus_mute...
352
  		mutex_unlock(&dev->bus_mutex);
8a0427d19   David Fries   w1: optional bund...
353
  		err = w1_get_slaves(dev, req_cmd);
d3a8a9dbb   David Fries   w1: hold bus_mute...
354
  		mutex_lock(&dev->bus_mutex);
c7e26631d   Evgeniy Polyakov   w1: allow master ...
355
356
357
358
  		break;
  	case W1_CMD_READ:
  	case W1_CMD_WRITE:
  	case W1_CMD_TOUCH:
8a0427d19   David Fries   w1: optional bund...
359
  		err = w1_process_command_io(dev, req_cmd);
c7e26631d   Evgeniy Polyakov   w1: allow master ...
360
  		break;
f89735c4e   Evgeniy Polyakov   w1: added w1 rese...
361
362
363
  	case W1_CMD_RESET:
  		err = w1_reset_bus(dev);
  		break;
70b34d2ed   David Fries   w1: new netlink c...
364
365
  	case W1_CMD_SLAVE_ADD:
  	case W1_CMD_SLAVE_REMOVE:
d3a8a9dbb   David Fries   w1: hold bus_mute...
366
367
  		mutex_unlock(&dev->bus_mutex);
  		mutex_lock(&dev->mutex);
8a0427d19   David Fries   w1: optional bund...
368
  		err = w1_process_command_addremove(dev, req_cmd);
d3a8a9dbb   David Fries   w1: hold bus_mute...
369
370
  		mutex_unlock(&dev->mutex);
  		mutex_lock(&dev->bus_mutex);
70b34d2ed   David Fries   w1: new netlink c...
371
  		break;
c7e26631d   Evgeniy Polyakov   w1: allow master ...
372
  	default:
4037014e3   Evgeniy Polyakov   w1: send status m...
373
  		err = -EINVAL;
c7e26631d   Evgeniy Polyakov   w1: allow master ...
374
  		break;
2d8331792   Evgeniy Polyakov   [PATCH] W1: w1_ne...
375
  	}
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
376
377
  	return err;
  }
8a0427d19   David Fries   w1: optional bund...
378
379
  static int w1_process_command_slave(struct w1_slave *sl,
  		struct w1_netlink_cmd *cmd)
c7e26631d   Evgeniy Polyakov   w1: allow master ...
380
381
382
383
384
  {
  	dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.
  ",
  		__func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id,
  		sl->reg_num.crc, cmd->cmd, cmd->len);
8a0427d19   David Fries   w1: optional bund...
385
  	return w1_process_command_io(sl->master, cmd);
c7e26631d   Evgeniy Polyakov   w1: allow master ...
386
  }
8a0427d19   David Fries   w1: optional bund...
387
  static int w1_process_command_root(struct cn_msg *req_cn, u32 portid)
610705e78   Evgeniy Polyakov   w1: add list mast...
388
  {
8a0427d19   David Fries   w1: optional bund...
389
  	struct w1_master *dev;
610705e78   Evgeniy Polyakov   w1: add list mast...
390
  	struct cn_msg *cn;
8a0427d19   David Fries   w1: optional bund...
391
  	struct w1_netlink_msg *msg;
610705e78   Evgeniy Polyakov   w1: add list mast...
392
  	u32 *id;
610705e78   Evgeniy Polyakov   w1: add list mast...
393
394
395
396
397
398
  	cn = kmalloc(PAGE_SIZE, GFP_KERNEL);
  	if (!cn)
  		return -ENOMEM;
  
  	cn->id.idx = CN_W1_IDX;
  	cn->id.val = CN_W1_VAL;
8a0427d19   David Fries   w1: optional bund...
399
400
  	cn->seq = req_cn->seq;
  	cn->ack = req_cn->seq + 1;
610705e78   Evgeniy Polyakov   w1: add list mast...
401
  	cn->len = sizeof(struct w1_netlink_msg);
8a0427d19   David Fries   w1: optional bund...
402
  	msg = (struct w1_netlink_msg *)cn->data;
610705e78   Evgeniy Polyakov   w1: add list mast...
403

8a0427d19   David Fries   w1: optional bund...
404
405
406
407
  	msg->type = W1_LIST_MASTERS;
  	msg->status = 0;
  	msg->len = 0;
  	id = (u32 *)msg->data;
610705e78   Evgeniy Polyakov   w1: add list mast...
408
409
  
  	mutex_lock(&w1_mlock);
8a0427d19   David Fries   w1: optional bund...
410
  	list_for_each_entry(dev, &w1_masters, w1_master_entry) {
610705e78   Evgeniy Polyakov   w1: add list mast...
411
  		if (cn->len + sizeof(*id) > PAGE_SIZE - sizeof(struct cn_msg)) {
5dbf5671c   David Fries   w1: reply only to...
412
  			cn_netlink_send(cn, portid, 0, GFP_KERNEL);
610705e78   Evgeniy Polyakov   w1: add list mast...
413
  			cn->len = sizeof(struct w1_netlink_msg);
8a0427d19   David Fries   w1: optional bund...
414
415
  			msg->len = 0;
  			id = (u32 *)msg->data;
610705e78   Evgeniy Polyakov   w1: add list mast...
416
  		}
8a0427d19   David Fries   w1: optional bund...
417
418
  		*id = dev->id;
  		msg->len += sizeof(*id);
610705e78   Evgeniy Polyakov   w1: add list mast...
419
420
421
  		cn->len += sizeof(*id);
  		id++;
  	}
5dbf5671c   David Fries   w1: reply only to...
422
  	cn_netlink_send(cn, portid, 0, GFP_KERNEL);
610705e78   Evgeniy Polyakov   w1: add list mast...
423
424
425
426
427
  	mutex_unlock(&w1_mlock);
  
  	kfree(cn);
  	return 0;
  }
9fcbbac5d   David Fries   w1: process w1 ne...
428
429
430
431
  static void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd)
  {
  	struct w1_cb_node *node = container_of(async_cmd, struct w1_cb_node,
  		async);
8a0427d19   David Fries   w1: optional bund...
432
433
  	u16 mlen = node->msg->len;
  	u16 len;
9fcbbac5d   David Fries   w1: process w1 ne...
434
435
  	int err = 0;
  	struct w1_slave *sl = node->sl;
8a0427d19   David Fries   w1: optional bund...
436
  	struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)node->msg->data;
9fcbbac5d   David Fries   w1: process w1 ne...
437

d3a8a9dbb   David Fries   w1: hold bus_mute...
438
  	mutex_lock(&dev->bus_mutex);
8a0427d19   David Fries   w1: optional bund...
439
  	dev->priv = node->block;
9fcbbac5d   David Fries   w1: process w1 ne...
440
441
  	if (sl && w1_reset_select_slave(sl))
  		err = -ENODEV;
8a0427d19   David Fries   w1: optional bund...
442
  	node->block->cur_msg = node->msg;
9fcbbac5d   David Fries   w1: process w1 ne...
443
444
  
  	while (mlen && !err) {
9fcbbac5d   David Fries   w1: process w1 ne...
445
446
447
448
449
450
  		if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) {
  			err = -E2BIG;
  			break;
  		}
  
  		if (sl)
8a0427d19   David Fries   w1: optional bund...
451
  			err = w1_process_command_slave(sl, cmd);
9fcbbac5d   David Fries   w1: process w1 ne...
452
  		else
8a0427d19   David Fries   w1: optional bund...
453
454
  			err = w1_process_command_master(dev, cmd);
  		w1_netlink_check_send(node->block);
9fcbbac5d   David Fries   w1: process w1 ne...
455

8a0427d19   David Fries   w1: optional bund...
456
  		w1_netlink_queue_status(node->block, node->msg, cmd, err);
9fcbbac5d   David Fries   w1: process w1 ne...
457
  		err = 0;
8a0427d19   David Fries   w1: optional bund...
458
459
460
  		len = sizeof(*cmd) + cmd->len;
  		cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len);
  		mlen -= len;
9fcbbac5d   David Fries   w1: process w1 ne...
461
462
463
  	}
  
  	if (!cmd || err)
8a0427d19   David Fries   w1: optional bund...
464
  		w1_netlink_queue_status(node->block, node->msg, cmd, err);
9fcbbac5d   David Fries   w1: process w1 ne...
465

593ceb0c7   David Fries   w1: fix netlink r...
466
467
468
  	/* ref taken in w1_search_slave or w1_search_master_id when building
  	 * the block
  	 */
9fcbbac5d   David Fries   w1: process w1 ne...
469
470
471
472
  	if (sl)
  		w1_unref_slave(sl);
  	else
  		atomic_dec(&dev->refcnt);
8a0427d19   David Fries   w1: optional bund...
473
  	dev->priv = NULL;
d3a8a9dbb   David Fries   w1: hold bus_mute...
474
  	mutex_unlock(&dev->bus_mutex);
9fcbbac5d   David Fries   w1: process w1 ne...
475
476
477
478
  
  	mutex_lock(&dev->list_mutex);
  	list_del(&async_cmd->async_entry);
  	mutex_unlock(&dev->list_mutex);
8a0427d19   David Fries   w1: optional bund...
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
  	w1_unref_block(node->block);
  }
  
  static void w1_list_count_cmds(struct w1_netlink_msg *msg, int *cmd_count,
  	u16 *slave_len)
  {
  	struct w1_netlink_cmd *cmd = (struct w1_netlink_cmd *)msg->data;
  	u16 mlen = msg->len;
  	u16 len;
  	int slave_list = 0;
  	while (mlen) {
  		if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen)
  			break;
  
  		switch (cmd->cmd) {
  		case W1_CMD_SEARCH:
  		case W1_CMD_ALARM_SEARCH:
  		case W1_CMD_LIST_SLAVES:
  			++slave_list;
  		}
  		++*cmd_count;
  		len = sizeof(*cmd) + cmd->len;
  		cmd = (struct w1_netlink_cmd *)((u8 *)cmd + len);
  		mlen -= len;
  	}
  
  	if (slave_list) {
  		struct w1_master *dev = w1_search_master_id(msg->id.mst.id);
  		if (dev) {
  			/* Bytes, and likely an overstimate, and if it isn't
  			 * the results can still be split between packets.
  			 */
  			*slave_len += sizeof(struct w1_reg_num) * slave_list *
  				(dev->slave_count + dev->max_slave_count);
  			/* search incremented it */
  			atomic_dec(&dev->refcnt);
  		}
  	}
9fcbbac5d   David Fries   w1: process w1 ne...
517
  }
8a0427d19   David Fries   w1: optional bund...
518
  static void w1_cn_callback(struct cn_msg *cn, struct netlink_skb_parms *nsp)
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
519
  {
8a0427d19   David Fries   w1: optional bund...
520
  	struct w1_netlink_msg *msg = (struct w1_netlink_msg *)(cn + 1);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
521
522
  	struct w1_slave *sl;
  	struct w1_master *dev;
9fcbbac5d   David Fries   w1: process w1 ne...
523
  	u16 msg_len;
8a0427d19   David Fries   w1: optional bund...
524
  	u16 slave_len = 0;
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
525
  	int err = 0;
9fcbbac5d   David Fries   w1: process w1 ne...
526
527
528
  	struct w1_cb_block *block = NULL;
  	struct w1_cb_node *node = NULL;
  	int node_count = 0;
8a0427d19   David Fries   w1: optional bund...
529
530
531
532
533
534
535
536
537
538
  	int cmd_count = 0;
  
  	/* If any unknown flag is set let the application know, that way
  	 * applications can detect the absence of features in kernels that
  	 * don't know about them.  http://lwn.net/Articles/587527/
  	 */
  	if (cn->flags & ~(W1_CN_BUNDLE)) {
  		w1_netlink_send_error(cn, msg, nsp->portid, -EINVAL);
  		return;
  	}
9fcbbac5d   David Fries   w1: process w1 ne...
539
540
541
542
  
  	/* Count the number of master or slave commands there are to allocate
  	 * space for one cb_node each.
  	 */
8a0427d19   David Fries   w1: optional bund...
543
  	msg_len = cn->len;
9fcbbac5d   David Fries   w1: process w1 ne...
544
  	while (msg_len && !err) {
8a0427d19   David Fries   w1: optional bund...
545
  		if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {
9fcbbac5d   David Fries   w1: process w1 ne...
546
547
548
  			err = -E2BIG;
  			break;
  		}
8a0427d19   David Fries   w1: optional bund...
549
550
551
552
  		/* count messages for nodes and allocate any additional space
  		 * required for slave lists
  		 */
  		if (msg->type == W1_MASTER_CMD || msg->type == W1_SLAVE_CMD) {
9fcbbac5d   David Fries   w1: process w1 ne...
553
  			++node_count;
8a0427d19   David Fries   w1: optional bund...
554
555
  			w1_list_count_cmds(msg, &cmd_count, &slave_len);
  		}
9fcbbac5d   David Fries   w1: process w1 ne...
556

8a0427d19   David Fries   w1: optional bund...
557
558
559
  		msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
  		msg = (struct w1_netlink_msg *)(((u8 *)msg) +
  			sizeof(struct w1_netlink_msg) + msg->len);
9fcbbac5d   David Fries   w1: process w1 ne...
560
  	}
8a0427d19   David Fries   w1: optional bund...
561
  	msg = (struct w1_netlink_msg *)(cn + 1);
9fcbbac5d   David Fries   w1: process w1 ne...
562
  	if (node_count) {
8a0427d19   David Fries   w1: optional bund...
563
  		int size;
4b97b2793   David Fries   w1: avoid potenti...
564
  		int reply_size = sizeof(*cn) + cn->len + slave_len;
8a0427d19   David Fries   w1: optional bund...
565
566
567
568
569
570
  		if (cn->flags & W1_CN_BUNDLE) {
  			/* bundling duplicats some of the messages */
  			reply_size += 2 * cmd_count * (sizeof(struct cn_msg) +
  				sizeof(struct w1_netlink_msg) +
  				sizeof(struct w1_netlink_cmd));
  		}
b04e08549   Andrew F. Davis   w1: Use kernel co...
571
  		reply_size = min(CONNECTOR_MAX_MSG_SIZE, reply_size);
8a0427d19   David Fries   w1: optional bund...
572
573
574
575
576
577
578
579
580
581
582
583
584
585
  
  		/* allocate space for the block, a copy of the original message,
  		 * one node per cmd to point into the original message,
  		 * space for replies which is the original message size plus
  		 * space for any list slave data and status messages
  		 * cn->len doesn't include itself which is part of the block
  		 * */
  		size =  /* block + original message */
  			sizeof(struct w1_cb_block) + sizeof(*cn) + cn->len +
  			/* space for nodes */
  			node_count * sizeof(struct w1_cb_node) +
  			/* replies */
  			sizeof(struct cn_msg) + reply_size;
  		block = kzalloc(size, GFP_KERNEL);
9fcbbac5d   David Fries   w1: process w1 ne...
586
  		if (!block) {
8a0427d19   David Fries   w1: optional bund...
587
588
589
590
591
  			/* if the system is already out of memory,
  			 * (A) will this work, and (B) would it be better
  			 * to not try?
  			 */
  			w1_netlink_send_error(cn, msg, nsp->portid, -ENOMEM);
9fcbbac5d   David Fries   w1: process w1 ne...
592
593
594
  			return;
  		}
  		atomic_set(&block->refcnt, 1);
5dbf5671c   David Fries   w1: reply only to...
595
  		block->portid = nsp->portid;
8a0427d19   David Fries   w1: optional bund...
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
  		memcpy(&block->request_cn, cn, sizeof(*cn) + cn->len);
  		node = (struct w1_cb_node *)(block->request_cn.data + cn->len);
  
  		/* Sneeky, when not bundling, reply_size is the allocated space
  		 * required for the reply, cn_msg isn't part of maxlen so
  		 * it should be reply_size - sizeof(struct cn_msg), however
  		 * when checking if there is enough space, w1_reply_make_space
  		 * is called with the full message size including cn_msg,
  		 * because it isn't known at that time if an additional cn_msg
  		 * will need to be allocated.  So an extra cn_msg is added
  		 * above in "size".
  		 */
  		block->maxlen = reply_size;
  		block->first_cn = (struct cn_msg *)(node + node_count);
  		memset(block->first_cn, 0, sizeof(*block->first_cn));
9fcbbac5d   David Fries   w1: process w1 ne...
611
  	}
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
612

8a0427d19   David Fries   w1: optional bund...
613
  	msg_len = cn->len;
9fcbbac5d   David Fries   w1: process w1 ne...
614
  	while (msg_len && !err) {
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
615
616
617
  
  		dev = NULL;
  		sl = NULL;
8a0427d19   David Fries   w1: optional bund...
618
  		if (msg->len + sizeof(struct w1_netlink_msg) > msg_len) {
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
619
620
621
  			err = -E2BIG;
  			break;
  		}
593ceb0c7   David Fries   w1: fix netlink r...
622
  		/* execute on this thread, no need to process later */
8a0427d19   David Fries   w1: optional bund...
623
624
  		if (msg->type == W1_LIST_MASTERS) {
  			err = w1_process_command_root(cn, nsp->portid);
593ceb0c7   David Fries   w1: fix netlink r...
625
626
627
628
629
630
  			goto out_cont;
  		}
  
  		/* All following message types require additional data,
  		 * check here before references are taken.
  		 */
8a0427d19   David Fries   w1: optional bund...
631
  		if (!msg->len) {
593ceb0c7   David Fries   w1: fix netlink r...
632
633
634
  			err = -EPROTO;
  			goto out_cont;
  		}
8a0427d19   David Fries   w1: optional bund...
635
636
637
638
639
  		/* both search calls take references */
  		if (msg->type == W1_MASTER_CMD) {
  			dev = w1_search_master_id(msg->id.mst.id);
  		} else if (msg->type == W1_SLAVE_CMD) {
  			sl = w1_search_slave((struct w1_reg_num *)msg->id.id);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
640
641
  			if (sl)
  				dev = sl->master;
610705e78   Evgeniy Polyakov   w1: add list mast...
642
  		} else {
fdc9167a7   Fjodor Schelichow   w1: use pr_* inst...
643
644
  			pr_notice("%s: cn: %x.%x, wrong type: %u, len: %u.
  ",
8a0427d19   David Fries   w1: optional bund...
645
646
  				__func__, cn->id.idx, cn->id.val,
  				msg->type, msg->len);
593ceb0c7   David Fries   w1: fix netlink r...
647
  			err = -EPROTO;
610705e78   Evgeniy Polyakov   w1: add list mast...
648
  			goto out_cont;
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
649
650
651
652
653
654
  		}
  
  		if (!dev) {
  			err = -ENODEV;
  			goto out_cont;
  		}
9be62e0b2   Evgeniy Polyakov   w1: add touch blo...
655
  		err = 0;
9be62e0b2   Evgeniy Polyakov   w1: add touch blo...
656

9fcbbac5d   David Fries   w1: process w1 ne...
657
658
659
  		atomic_inc(&block->refcnt);
  		node->async.cb = w1_process_cb;
  		node->block = block;
8a0427d19   David Fries   w1: optional bund...
660
661
  		node->msg = (struct w1_netlink_msg *)((u8 *)&block->request_cn +
  			(size_t)((u8 *)msg - (u8 *)cn));
9fcbbac5d   David Fries   w1: process w1 ne...
662
663
  		node->sl = sl;
  		node->dev = dev;
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
664

9fcbbac5d   David Fries   w1: process w1 ne...
665
666
667
668
669
  		mutex_lock(&dev->list_mutex);
  		list_add_tail(&node->async.async_entry, &dev->async_list);
  		wake_up_process(dev->thread);
  		mutex_unlock(&dev->list_mutex);
  		++node;
4037014e3   Evgeniy Polyakov   w1: send status m...
670

12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
671
  out_cont:
8a0427d19   David Fries   w1: optional bund...
672
673
674
675
  		/* Can't queue because that modifies block and another
  		 * thread could be processing the messages by now and
  		 * there isn't a lock, send directly.
  		 */
9fcbbac5d   David Fries   w1: process w1 ne...
676
  		if (err)
8a0427d19   David Fries   w1: optional bund...
677
678
679
680
  			w1_netlink_send_error(cn, msg, nsp->portid, err);
  		msg_len -= sizeof(struct w1_netlink_msg) + msg->len;
  		msg = (struct w1_netlink_msg *)(((u8 *)msg) +
  			sizeof(struct w1_netlink_msg) + msg->len);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
681
682
683
684
685
686
687
  
  		/*
  		 * Let's allow requests for nonexisting devices.
  		 */
  		if (err == -ENODEV)
  			err = 0;
  	}
8a0427d19   David Fries   w1: optional bund...
688
689
  	if (block)
  		w1_unref_block(block);
2d8331792   Evgeniy Polyakov   [PATCH] W1: w1_ne...
690
  }
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
691
  int w1_init_netlink(void)
2d8331792   Evgeniy Polyakov   [PATCH] W1: w1_ne...
692
  {
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
693
694
695
696
697
698
699
700
701
702
  	struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
  
  	return cn_add_callback(&w1_id, "w1", &w1_cn_callback);
  }
  
  void w1_fini_netlink(void)
  {
  	struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL};
  
  	cn_del_callback(&w1_id);
2d8331792   Evgeniy Polyakov   [PATCH] W1: w1_ne...
703
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
  #else
8a0427d19   David Fries   w1: optional bund...
705
  void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *cn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706
707
  {
  }
2d8331792   Evgeniy Polyakov   [PATCH] W1: w1_ne...
708

12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
709
  int w1_init_netlink(void)
2d8331792   Evgeniy Polyakov   [PATCH] W1: w1_ne...
710
711
712
  {
  	return 0;
  }
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
713
  void w1_fini_netlink(void)
2d8331792   Evgeniy Polyakov   [PATCH] W1: w1_ne...
714
715
  {
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
  #endif