Blame view

drivers/w1/w1.c 25.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
2
   *	w1.c
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
   *
a80187663   Evgeniy Polyakov   MAINTAINERS: Evge...
4
   * Copyright (c) 2004 Evgeniy Polyakov <zbr@ioremap.net>
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
5
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   */
  
  #include <linux/delay.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/list.h>
  #include <linux/interrupt.h>
  #include <linux/spinlock.h>
  #include <linux/timer.h>
  #include <linux/device.h>
  #include <linux/slab.h>
  #include <linux/sched.h>
674a396c6   Evgeniy Polyakov   [PATCH] w1: use k...
33
  #include <linux/kthread.h>
7dfb71030   Nigel Cunningham   [PATCH] Add inclu...
34
  #include <linux/freezer.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35

60063497a   Arun Sharma   atomic: use <linu...
36
  #include <linux/atomic.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
  
  #include "w1.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
44
  #include "w1_log.h"
  #include "w1_int.h"
  #include "w1_family.h"
  #include "w1_netlink.h"
  
  MODULE_LICENSE("GPL");
a80187663   Evgeniy Polyakov   MAINTAINERS: Evge...
45
  MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
47
48
49
50
51
52
53
54
  MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol.");
  
  static int w1_timeout = 10;
  int w1_max_slave_count = 10;
  int w1_max_slave_ttl = 10;
  
  module_param_named(timeout, w1_timeout, int, 0);
  module_param_named(max_slave_count, w1_max_slave_count, int, 0);
  module_param_named(slave_ttl, w1_max_slave_ttl, int, 0);
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
55
  DEFINE_MUTEX(w1_mlock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
  LIST_HEAD(w1_masters);
9b4674111   David Fries   W1: be able to ma...
57
  static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
61
62
63
64
65
66
  static int w1_master_match(struct device *dev, struct device_driver *drv)
  {
  	return 1;
  }
  
  static int w1_master_probe(struct device *dev)
  {
  	return -ENODEV;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
  static void w1_master_release(struct device *dev)
  {
db2d0008d   Evgeniy Polyakov   [PATCH] w1: Added...
69
  	struct w1_master *md = dev_to_w1_master(dev);
3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
70
71
72
  
  	dev_dbg(dev, "%s: Releasing %s.
  ", __func__, md->name);
3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
73
74
  	memset(md, 0, sizeof(struct w1_master) + sizeof(struct w1_bus_master));
  	kfree(md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
  }
  
  static void w1_slave_release(struct device *dev)
  {
db2d0008d   Evgeniy Polyakov   [PATCH] w1: Added...
79
  	struct w1_slave *sl = dev_to_w1_slave(dev);
3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
80

7dc8f527e   David Fries   W1: w1.c s/printk...
81
82
  	dev_dbg(dev, "%s: Releasing %s.
  ", __func__, sl->name);
3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
83
84
  
  	while (atomic_read(&sl->refcnt)) {
7dc8f527e   David Fries   W1: w1.c s/printk...
85
86
  		dev_dbg(dev, "Waiting for %s to become free: refcnt=%d.
  ",
3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
87
88
89
90
91
92
93
94
95
  				sl->name, atomic_read(&sl->refcnt));
  		if (msleep_interruptible(1000))
  			flush_signals(current);
  	}
  
  	w1_family_put(sl->family);
  	sl->master->slave_count--;
  
  	complete(&sl->released);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  }
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
97
  static ssize_t w1_slave_read_name(struct device *dev, struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  {
3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
99
  	struct w1_slave *sl = dev_to_w1_slave(dev);
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
100

3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
101
102
  	return sprintf(buf, "%s
  ", sl->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
  }
07e003417   David Fries   W1: w1_slave_read...
104
105
  static ssize_t w1_slave_read_id(struct device *dev,
  	struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  {
07e003417   David Fries   W1: w1_slave_read...
107
108
  	struct w1_slave *sl = dev_to_w1_slave(dev);
  	ssize_t count = sizeof(sl->reg_num);
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
109

07e003417   David Fries   W1: w1_slave_read...
110
  	memcpy(buf, (u8 *)&sl->reg_num, count);
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
111
  	return count;
3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
112
  }
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
113
114
115
  
  static struct device_attribute w1_slave_attr_name =
  	__ATTR(name, S_IRUGO, w1_slave_read_name, NULL);
07e003417   David Fries   W1: w1_slave_read...
116
117
  static struct device_attribute w1_slave_attr_id =
  	__ATTR(id, S_IRUGO, w1_slave_read_id, NULL);
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
118

d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
119
  /* Default family */
f522d2396   Evgeniy Polyakov   [PATCH] w1: Added...
120

2c3c8bea6   Chris Wright   sysfs: add struct...
121
  static ssize_t w1_default_write(struct file *filp, struct kobject *kobj,
91a690295   Zhang Rui   sysfs: add parame...
122
123
  				struct bin_attribute *bin_attr,
  				char *buf, loff_t off, size_t count)
f522d2396   Evgeniy Polyakov   [PATCH] w1: Added...
124
125
  {
  	struct w1_slave *sl = kobj_to_w1_slave(kobj);
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
126
  	mutex_lock(&sl->master->mutex);
f522d2396   Evgeniy Polyakov   [PATCH] w1: Added...
127
128
129
130
131
132
133
134
  	if (w1_reset_select_slave(sl)) {
  		count = 0;
  		goto out_up;
  	}
  
  	w1_write_block(sl->master, buf, count);
  
  out_up:
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
135
  	mutex_unlock(&sl->master->mutex);
f522d2396   Evgeniy Polyakov   [PATCH] w1: Added...
136
137
  	return count;
  }
2c3c8bea6   Chris Wright   sysfs: add struct...
138
  static ssize_t w1_default_read(struct file *filp, struct kobject *kobj,
91a690295   Zhang Rui   sysfs: add parame...
139
140
  			       struct bin_attribute *bin_attr,
  			       char *buf, loff_t off, size_t count)
f522d2396   Evgeniy Polyakov   [PATCH] w1: Added...
141
142
  {
  	struct w1_slave *sl = kobj_to_w1_slave(kobj);
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
143
  	mutex_lock(&sl->master->mutex);
f522d2396   Evgeniy Polyakov   [PATCH] w1: Added...
144
  	w1_read_block(sl->master, buf, count);
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
145
  	mutex_unlock(&sl->master->mutex);
f522d2396   Evgeniy Polyakov   [PATCH] w1: Added...
146
147
148
149
150
151
152
  	return count;
  }
  
  static struct bin_attribute w1_default_attr = {
        .attr = {
                .name = "rw",
                .mode = S_IRUGO | S_IWUSR,
f522d2396   Evgeniy Polyakov   [PATCH] w1: Added...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
        },
        .size = PAGE_SIZE,
        .read = w1_default_read,
        .write = w1_default_write,
  };
  
  static int w1_default_add_slave(struct w1_slave *sl)
  {
  	return sysfs_create_bin_file(&sl->dev.kobj, &w1_default_attr);
  }
  
  static void w1_default_remove_slave(struct w1_slave *sl)
  {
  	sysfs_remove_bin_file(&sl->dev.kobj, &w1_default_attr);
  }
  
  static struct w1_family_ops w1_default_fops = {
  	.add_slave	= w1_default_add_slave,
  	.remove_slave	= w1_default_remove_slave,
  };
  
  static struct w1_family w1_default_family = {
  	.fops = &w1_default_fops,
  };
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
177

7eff2e7a8   Kay Sievers   Driver core: chan...
178
  static int w1_uevent(struct device *dev, struct kobj_uevent_env *env);
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
179

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
  static struct bus_type w1_bus_type = {
  	.name = "w1",
  	.match = w1_master_match,
312c004d3   Kay Sievers   [PATCH] driver co...
183
  	.uevent = w1_uevent,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  };
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
185
186
  struct device_driver w1_master_driver = {
  	.name = "w1_master_driver",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
  	.bus = &w1_bus_type,
  	.probe = w1_master_probe,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  };
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
190
  struct device w1_master_device = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
  	.parent = NULL,
  	.bus = &w1_bus_type,
40f91de6a   Kay Sievers   w1: struct device...
193
  	.init_name = "w1 bus master",
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
194
  	.driver = &w1_master_driver,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
  	.release = &w1_master_release
  };
2c5bfdac3   Evgeniy Polyakov   [PATCH] W1: cleanups
197
  static struct device_driver w1_slave_driver = {
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
198
199
200
  	.name = "w1_slave_driver",
  	.bus = &w1_bus_type,
  };
2c5bfdac3   Evgeniy Polyakov   [PATCH] W1: cleanups
201
  #if 0
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
202
203
204
  struct device w1_slave_device = {
  	.parent = NULL,
  	.bus = &w1_bus_type,
40f91de6a   Kay Sievers   w1: struct device...
205
  	.init_name = "w1 bus slave",
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
206
  	.driver = &w1_slave_driver,
3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
207
  	.release = &w1_slave_release
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
208
  };
2c5bfdac3   Evgeniy Polyakov   [PATCH] W1: cleanups
209
  #endif  /*  0  */
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
210

060b8845e   Yani Ioannou   [PATCH] Driver Co...
211
  static ssize_t w1_master_attribute_show_name(struct device *dev, struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
  {
db2d0008d   Evgeniy Polyakov   [PATCH] w1: Added...
213
  	struct w1_master *md = dev_to_w1_master(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  	ssize_t count;
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
215

abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
216
  	mutex_lock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
  	count = sprintf(buf, "%s
  ", md->name);
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
219
  	mutex_unlock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
  
  	return count;
  }
2a9d0c178   Evgeniy Polyakov   [PATCH] w1: Adds ...
223
224
225
226
  static ssize_t w1_master_attribute_store_search(struct device * dev,
  						struct device_attribute *attr,
  						const char * buf, size_t count)
  {
6a158c0de   David Fries   W1: feature, enab...
227
  	long tmp;
db2d0008d   Evgeniy Polyakov   [PATCH] w1: Added...
228
  	struct w1_master *md = dev_to_w1_master(dev);
2a9d0c178   Evgeniy Polyakov   [PATCH] w1: Adds ...
229

6a158c0de   David Fries   W1: feature, enab...
230
231
  	if (strict_strtol(buf, 0, &tmp) == -EINVAL)
  		return -EINVAL;
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
232
  	mutex_lock(&md->mutex);
6a158c0de   David Fries   W1: feature, enab...
233
  	md->search_count = tmp;
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
234
  	mutex_unlock(&md->mutex);
3c52e4e62   David Fries   W1: w1_process, b...
235
  	wake_up_process(md->thread);
2a9d0c178   Evgeniy Polyakov   [PATCH] w1: Adds ...
236
237
238
239
240
241
242
243
  
  	return count;
  }
  
  static ssize_t w1_master_attribute_show_search(struct device *dev,
  					       struct device_attribute *attr,
  					       char *buf)
  {
db2d0008d   Evgeniy Polyakov   [PATCH] w1: Added...
244
  	struct w1_master *md = dev_to_w1_master(dev);
2a9d0c178   Evgeniy Polyakov   [PATCH] w1: Adds ...
245
  	ssize_t count;
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
246
  	mutex_lock(&md->mutex);
2a9d0c178   Evgeniy Polyakov   [PATCH] w1: Adds ...
247
248
  	count = sprintf(buf, "%d
  ", md->search_count);
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
249
  	mutex_unlock(&md->mutex);
2a9d0c178   Evgeniy Polyakov   [PATCH] w1: Adds ...
250
251
252
  
  	return count;
  }
6a158c0de   David Fries   W1: feature, enab...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
  static ssize_t w1_master_attribute_store_pullup(struct device *dev,
  						struct device_attribute *attr,
  						const char *buf, size_t count)
  {
  	long tmp;
  	struct w1_master *md = dev_to_w1_master(dev);
  
  	if (strict_strtol(buf, 0, &tmp) == -EINVAL)
  		return -EINVAL;
  
  	mutex_lock(&md->mutex);
  	md->enable_pullup = tmp;
  	mutex_unlock(&md->mutex);
  	wake_up_process(md->thread);
  
  	return count;
  }
  
  static ssize_t w1_master_attribute_show_pullup(struct device *dev,
  					       struct device_attribute *attr,
  					       char *buf)
  {
  	struct w1_master *md = dev_to_w1_master(dev);
  	ssize_t count;
  
  	mutex_lock(&md->mutex);
  	count = sprintf(buf, "%d
  ", md->enable_pullup);
  	mutex_unlock(&md->mutex);
  
  	return count;
  }
060b8845e   Yani Ioannou   [PATCH] Driver Co...
285
  static ssize_t w1_master_attribute_show_pointer(struct device *dev, struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  {
db2d0008d   Evgeniy Polyakov   [PATCH] w1: Added...
287
  	struct w1_master *md = dev_to_w1_master(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  	ssize_t count;
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
289

abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
290
  	mutex_lock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
  	count = sprintf(buf, "0x%p
  ", md->bus_master);
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
293
  	mutex_unlock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
  	return count;
  }
060b8845e   Yani Ioannou   [PATCH] Driver Co...
296
  static ssize_t w1_master_attribute_show_timeout(struct device *dev, struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
299
300
301
302
  {
  	ssize_t count;
  	count = sprintf(buf, "%d
  ", w1_timeout);
  	return count;
  }
060b8845e   Yani Ioannou   [PATCH] Driver Co...
303
  static ssize_t w1_master_attribute_show_max_slave_count(struct device *dev, struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  {
db2d0008d   Evgeniy Polyakov   [PATCH] w1: Added...
305
  	struct w1_master *md = dev_to_w1_master(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  	ssize_t count;
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
307

abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
308
  	mutex_lock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
  	count = sprintf(buf, "%d
  ", md->max_slave_count);
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
311
  	mutex_unlock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
  	return count;
  }
060b8845e   Yani Ioannou   [PATCH] Driver Co...
314
  static ssize_t w1_master_attribute_show_attempts(struct device *dev, struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
  {
db2d0008d   Evgeniy Polyakov   [PATCH] w1: Added...
316
  	struct w1_master *md = dev_to_w1_master(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
  	ssize_t count;
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
318

abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
319
  	mutex_lock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
  	count = sprintf(buf, "%lu
  ", md->attempts);
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
322
  	mutex_unlock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
  	return count;
  }
060b8845e   Yani Ioannou   [PATCH] Driver Co...
325
  static ssize_t w1_master_attribute_show_slave_count(struct device *dev, struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
  {
db2d0008d   Evgeniy Polyakov   [PATCH] w1: Added...
327
  	struct w1_master *md = dev_to_w1_master(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
  	ssize_t count;
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
329

abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
330
  	mutex_lock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
  	count = sprintf(buf, "%d
  ", md->slave_count);
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
333
  	mutex_unlock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
  	return count;
  }
9b4674111   David Fries   W1: be able to ma...
336
337
  static ssize_t w1_master_attribute_show_slaves(struct device *dev,
  	struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
  {
db2d0008d   Evgeniy Polyakov   [PATCH] w1: Added...
339
  	struct w1_master *md = dev_to_w1_master(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  	int c = PAGE_SIZE;
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
341
  	mutex_lock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
343
344
345
346
347
348
349
350
351
  
  	if (md->slave_count == 0)
  		c -= snprintf(buf + PAGE_SIZE - c, c, "not found.
  ");
  	else {
  		struct list_head *ent, *n;
  		struct w1_slave *sl;
  
  		list_for_each_safe(ent, n, &md->slist) {
  			sl = list_entry(ent, struct w1_slave, w1_slave_entry);
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
352
353
  			c -= snprintf(buf + PAGE_SIZE - c, c, "%s
  ", sl->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
  		}
  	}
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
356
  	mutex_unlock(&md->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
359
  
  	return PAGE_SIZE - c;
  }
9b4674111   David Fries   W1: be able to ma...
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  static ssize_t w1_master_attribute_show_add(struct device *dev,
  	struct device_attribute *attr, char *buf)
  {
  	int c = PAGE_SIZE;
  	c -= snprintf(buf+PAGE_SIZE - c, c,
  		"write device id xx-xxxxxxxxxxxx to add slave
  ");
  	return PAGE_SIZE - c;
  }
  
  static int w1_atoreg_num(struct device *dev, const char *buf, size_t count,
  	struct w1_reg_num *rn)
  {
  	unsigned int family;
  	unsigned long long id;
  	int i;
  	u64 rn64_le;
  
  	/* The CRC value isn't read from the user because the sysfs directory
  	 * doesn't include it and most messages from the bus search don't
  	 * print it either.  It would be unreasonable for the user to then
  	 * provide it.
  	 */
  	const char *error_msg = "bad slave string format, expecting "
  		"ff-dddddddddddd
  ";
  
  	if (buf[2] != '-') {
  		dev_err(dev, "%s", error_msg);
  		return -EINVAL;
  	}
  	i = sscanf(buf, "%02x-%012llx", &family, &id);
  	if (i != 2) {
  		dev_err(dev, "%s", error_msg);
  		return -EINVAL;
  	}
  	rn->family = family;
  	rn->id = id;
  
  	rn64_le = cpu_to_le64(*(u64 *)rn);
  	rn->crc = w1_calc_crc8((u8 *)&rn64_le, 7);
  
  #if 0
  	dev_info(dev, "With CRC device is %02x.%012llx.%02x.
  ",
  		  rn->family, (unsigned long long)rn->id, rn->crc);
  #endif
  
  	return 0;
  }
  
  /* Searches the slaves in the w1_master and returns a pointer or NULL.
   * Note: must hold the mutex
   */
  static struct w1_slave *w1_slave_search_device(struct w1_master *dev,
  	struct w1_reg_num *rn)
  {
  	struct w1_slave *sl;
  	list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
  		if (sl->reg_num.family == rn->family &&
  				sl->reg_num.id == rn->id &&
  				sl->reg_num.crc == rn->crc) {
  			return sl;
  		}
  	}
  	return NULL;
  }
  
  static ssize_t w1_master_attribute_store_add(struct device *dev,
  						struct device_attribute *attr,
  						const char *buf, size_t count)
  {
  	struct w1_master *md = dev_to_w1_master(dev);
  	struct w1_reg_num rn;
  	struct w1_slave *sl;
  	ssize_t result = count;
  
  	if (w1_atoreg_num(dev, buf, count, &rn))
  		return -EINVAL;
  
  	mutex_lock(&md->mutex);
  	sl = w1_slave_search_device(md, &rn);
  	/* It would be nice to do a targeted search one the one-wire bus
  	 * for the new device to see if it is out there or not.  But the
  	 * current search doesn't support that.
  	 */
  	if (sl) {
  		dev_info(dev, "Device %s already exists
  ", sl->name);
  		result = -EINVAL;
  	} else {
  		w1_attach_slave_device(md, &rn);
  	}
  	mutex_unlock(&md->mutex);
  
  	return result;
  }
  
  static ssize_t w1_master_attribute_show_remove(struct device *dev,
  	struct device_attribute *attr, char *buf)
  {
  	int c = PAGE_SIZE;
  	c -= snprintf(buf+PAGE_SIZE - c, c,
  		"write device id xx-xxxxxxxxxxxx to remove slave
  ");
  	return PAGE_SIZE - c;
  }
  
  static ssize_t w1_master_attribute_store_remove(struct device *dev,
  						struct device_attribute *attr,
  						const char *buf, size_t count)
  {
  	struct w1_master *md = dev_to_w1_master(dev);
  	struct w1_reg_num rn;
  	struct w1_slave *sl;
  	ssize_t result = count;
  
  	if (w1_atoreg_num(dev, buf, count, &rn))
  		return -EINVAL;
  
  	mutex_lock(&md->mutex);
  	sl = w1_slave_search_device(md, &rn);
  	if (sl) {
  		w1_slave_detach(sl);
  	} else {
  		dev_info(dev, "Device %02x-%012llx doesn't exists
  ", rn.family,
  			(unsigned long long)rn.id);
  		result = -EINVAL;
  	}
  	mutex_unlock(&md->mutex);
  
  	return result;
  }
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
494
495
496
497
  #define W1_MASTER_ATTR_RO(_name, _mode)				\
  	struct device_attribute w1_master_attribute_##_name =	\
  		__ATTR(w1_master_##_name, _mode,		\
  		       w1_master_attribute_show_##_name, NULL)
2a9d0c178   Evgeniy Polyakov   [PATCH] w1: Adds ...
498
499
500
501
502
  #define W1_MASTER_ATTR_RW(_name, _mode)				\
  	struct device_attribute w1_master_attribute_##_name =	\
  		__ATTR(w1_master_##_name, _mode,		\
  		       w1_master_attribute_show_##_name,	\
  		       w1_master_attribute_store_##_name)
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
503
504
505
506
507
508
509
  static W1_MASTER_ATTR_RO(name, S_IRUGO);
  static W1_MASTER_ATTR_RO(slaves, S_IRUGO);
  static W1_MASTER_ATTR_RO(slave_count, S_IRUGO);
  static W1_MASTER_ATTR_RO(max_slave_count, S_IRUGO);
  static W1_MASTER_ATTR_RO(attempts, S_IRUGO);
  static W1_MASTER_ATTR_RO(timeout, S_IRUGO);
  static W1_MASTER_ATTR_RO(pointer, S_IRUGO);
12aa4c641   Brian Swetland   w1: don't allow a...
510
511
512
513
  static W1_MASTER_ATTR_RW(search, S_IRUGO | S_IWUSR | S_IWGRP);
  static W1_MASTER_ATTR_RW(pullup, S_IRUGO | S_IWUSR | S_IWGRP);
  static W1_MASTER_ATTR_RW(add, S_IRUGO | S_IWUSR | S_IWGRP);
  static W1_MASTER_ATTR_RW(remove, S_IRUGO | S_IWUSR | S_IWGRP);
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
514
515
516
517
518
519
520
521
522
  
  static struct attribute *w1_master_default_attrs[] = {
  	&w1_master_attribute_name.attr,
  	&w1_master_attribute_slaves.attr,
  	&w1_master_attribute_slave_count.attr,
  	&w1_master_attribute_max_slave_count.attr,
  	&w1_master_attribute_attempts.attr,
  	&w1_master_attribute_timeout.attr,
  	&w1_master_attribute_pointer.attr,
2a9d0c178   Evgeniy Polyakov   [PATCH] w1: Adds ...
523
  	&w1_master_attribute_search.attr,
6a158c0de   David Fries   W1: feature, enab...
524
  	&w1_master_attribute_pullup.attr,
9b4674111   David Fries   W1: be able to ma...
525
526
  	&w1_master_attribute_add.attr,
  	&w1_master_attribute_remove.attr,
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
527
  	NULL
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
  };
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
529
530
  static struct attribute_group w1_master_defattr_group = {
  	.attrs = w1_master_default_attrs,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
  };
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
532
533
534
535
  int w1_create_master_attributes(struct w1_master *master)
  {
  	return sysfs_create_group(&master->dev.kobj, &w1_master_defattr_group);
  }
c30c9b151   David Fries   W1: fix deadlocks...
536
  void w1_destroy_master_attributes(struct w1_master *master)
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
537
538
539
  {
  	sysfs_remove_group(&master->dev.kobj, &w1_master_defattr_group);
  }
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
540
  #ifdef CONFIG_HOTPLUG
7eff2e7a8   Kay Sievers   Driver core: chan...
541
  static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
542
543
544
545
  {
  	struct w1_master *md = NULL;
  	struct w1_slave *sl = NULL;
  	char *event_owner, *name;
7eff2e7a8   Kay Sievers   Driver core: chan...
546
  	int err;
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
547
548
549
550
551
552
553
554
555
556
  
  	if (dev->driver == &w1_master_driver) {
  		md = container_of(dev, struct w1_master, dev);
  		event_owner = "master";
  		name = md->name;
  	} else if (dev->driver == &w1_slave_driver) {
  		sl = container_of(dev, struct w1_slave, dev);
  		event_owner = "slave";
  		name = sl->name;
  	} else {
312c004d3   Kay Sievers   [PATCH] driver co...
557
558
  		dev_dbg(dev, "Unknown event.
  ");
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
559
560
  		return -EINVAL;
  	}
c6976a4eb   Andrew Morton   [PATCH] w1: warni...
561
562
  	dev_dbg(dev, "Hotplug event for %s %s, bus_id=%s.
  ",
40f91de6a   Kay Sievers   w1: struct device...
563
  			event_owner, name, dev_name(dev));
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
564
565
566
  
  	if (dev->driver != &w1_slave_driver || !sl)
  		return 0;
7eff2e7a8   Kay Sievers   Driver core: chan...
567
  	err = add_uevent_var(env, "W1_FID=%02X", sl->reg_num.family);
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
568
569
  	if (err)
  		return err;
7eff2e7a8   Kay Sievers   Driver core: chan...
570
571
  	err = add_uevent_var(env, "W1_SLAVE_ID=%024LX",
  			     (unsigned long long)sl->reg_num.id);
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
572
573
574
575
576
577
  	if (err)
  		return err;
  
  	return 0;
  };
  #else
7eff2e7a8   Kay Sievers   Driver core: chan...
578
  static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
579
580
581
582
  {
  	return 0;
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
584
585
586
587
  static int __w1_attach_slave_device(struct w1_slave *sl)
  {
  	int err;
  
  	sl->dev.parent = &sl->master->dev;
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
588
  	sl->dev.driver = &w1_slave_driver;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589
590
  	sl->dev.bus = &w1_bus_type;
  	sl->dev.release = &w1_slave_release;
40f91de6a   Kay Sievers   w1: struct device...
591
  	dev_set_name(&sl->dev, "%02x-%012llx",
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
592
593
594
595
596
597
  		 (unsigned int) sl->reg_num.family,
  		 (unsigned long long) sl->reg_num.id);
  	snprintf(&sl->name[0], sizeof(sl->name),
  		 "%02x-%012llx",
  		 (unsigned int) sl->reg_num.family,
  		 (unsigned long long) sl->reg_num.id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598

c6976a4eb   Andrew Morton   [PATCH] w1: warni...
599
600
  	dev_dbg(&sl->dev, "%s: registering %s as %p.
  ", __func__,
40f91de6a   Kay Sievers   w1: struct device...
601
  		dev_name(&sl->dev), sl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
603
604
605
  
  	err = device_register(&sl->dev);
  	if (err < 0) {
  		dev_err(&sl->dev,
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
606
607
  			"Device registration [%s] failed. err=%d
  ",
40f91de6a   Kay Sievers   w1: struct device...
608
  			dev_name(&sl->dev), err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
610
  		return err;
  	}
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
611
612
613
614
615
616
  	/* Create "name" entry */
  	err = device_create_file(&sl->dev, &w1_slave_attr_name);
  	if (err < 0) {
  		dev_err(&sl->dev,
  			"sysfs file creation for [%s] failed. err=%d
  ",
40f91de6a   Kay Sievers   w1: struct device...
617
  			dev_name(&sl->dev), err);
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
618
619
  		goto out_unreg;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620

d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
621
  	/* Create "id" entry */
07e003417   David Fries   W1: w1_slave_read...
622
  	err = device_create_file(&sl->dev, &w1_slave_attr_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
624
  	if (err < 0) {
  		dev_err(&sl->dev,
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
625
626
  			"sysfs file creation for [%s] failed. err=%d
  ",
40f91de6a   Kay Sievers   w1: struct device...
627
  			dev_name(&sl->dev), err);
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
628
  		goto out_rem1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
  	}
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
630
631
632
633
634
635
  	/* if the family driver needs to initialize something... */
  	if (sl->family->fops && sl->family->fops->add_slave &&
  	    ((err = sl->family->fops->add_slave(sl)) < 0)) {
  		dev_err(&sl->dev,
  			"sysfs file creation for [%s] failed. err=%d
  ",
40f91de6a   Kay Sievers   w1: struct device...
636
  			dev_name(&sl->dev), err);
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
637
  		goto out_rem2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
641
642
  	}
  
  	list_add_tail(&sl->w1_slave_entry, &sl->master->slist);
  
  	return 0;
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
643
644
  
  out_rem2:
07e003417   David Fries   W1: w1_slave_read...
645
  	device_remove_file(&sl->dev, &w1_slave_attr_id);
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
646
647
648
649
650
  out_rem1:
  	device_remove_file(&sl->dev, &w1_slave_attr_name);
  out_unreg:
  	device_unregister(&sl->dev);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
654
655
656
657
658
  }
  
  static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
  {
  	struct w1_slave *sl;
  	struct w1_family *f;
  	int err;
  	struct w1_netlink_msg msg;
dd00cc486   Yoann Padioleau   some kmalloc/mems...
659
  	sl = kzalloc(sizeof(struct w1_slave), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
662
663
664
665
666
  	if (!sl) {
  		dev_err(&dev->dev,
  			 "%s: failed to allocate new slave device.
  ",
  			 __func__);
  		return -ENOMEM;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
668
669
670
  
  	sl->owner = THIS_MODULE;
  	sl->master = dev;
  	set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
671
  	memset(&msg, 0, sizeof(msg));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
673
  	memcpy(&sl->reg_num, rn, sizeof(sl->reg_num));
  	atomic_set(&sl->refcnt, 0);
3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
674
  	init_completion(&sl->released);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
676
677
678
  
  	spin_lock(&w1_flock);
  	f = w1_family_registered(rn->family);
  	if (!f) {
99c5bfe99   Evgeniy Polyakov   [PATCH] w1: Adds ...
679
  		f= &w1_default_family;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
681
682
683
  		dev_info(&dev->dev, "Family %x for %02x.%012llx.%02x is not registered.
  ",
  			  rn->family, rn->family,
  			  (unsigned long long)rn->id, rn->crc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
  	}
  	__w1_family_get(f);
  	spin_unlock(&w1_flock);
  
  	sl->family = f;
  
  
  	err = __w1_attach_slave_device(sl);
  	if (err < 0) {
  		dev_err(&dev->dev, "%s: Attaching %s failed.
  ", __func__,
  			 sl->name);
  		w1_family_put(sl->family);
  		kfree(sl);
  		return err;
  	}
  
  	sl->ttl = dev->slave_ttl;
  	dev->slave_count++;
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
703
  	memcpy(msg.id.id, rn, sizeof(msg.id));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
705
706
707
708
  	msg.type = W1_SLAVE_ADD;
  	w1_netlink_send(dev, &msg);
  
  	return 0;
  }
c30c9b151   David Fries   W1: fix deadlocks...
709
  void w1_slave_detach(struct w1_slave *sl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
711
  {
  	struct w1_netlink_msg msg;
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
712

7c8f5703d   Evgeniy Polyakov   [PATCH] w1: Decre...
713
714
  	dev_dbg(&sl->dev, "%s: detaching %s [%p].
  ", __func__, sl->name, sl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715

3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
716
  	list_del(&sl->w1_slave_entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717

d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
718
719
  	if (sl->family->fops && sl->family->fops->remove_slave)
  		sl->family->fops->remove_slave(sl);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
720
721
  	memset(&msg, 0, sizeof(msg));
  	memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id));
3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
722
723
  	msg.type = W1_SLAVE_REMOVE;
  	w1_netlink_send(sl->master, &msg);
07e003417   David Fries   W1: w1_slave_read...
724
  	device_remove_file(&sl->dev, &w1_slave_attr_id);
d2a4ef6a0   Evgeniy Polyakov   [PATCH] w1: Added...
725
  	device_remove_file(&sl->dev, &w1_slave_attr_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
726
  	device_unregister(&sl->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
727

3aca692d3   Evgeniy Polyakov   [PATCH] w1: Detou...
728
729
  	wait_for_completion(&sl->released);
  	kfree(sl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
  }
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
731
732
733
734
  struct w1_master *w1_search_master_id(u32 id)
  {
  	struct w1_master *dev;
  	int found = 0;
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
735
  	mutex_lock(&w1_mlock);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
736
737
738
739
740
741
742
  	list_for_each_entry(dev, &w1_masters, w1_master_entry) {
  		if (dev->id == id) {
  			found = 1;
  			atomic_inc(&dev->refcnt);
  			break;
  		}
  	}
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
743
  	mutex_unlock(&w1_mlock);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
744
745
746
747
748
749
750
751
752
  
  	return (found)?dev:NULL;
  }
  
  struct w1_slave *w1_search_slave(struct w1_reg_num *id)
  {
  	struct w1_master *dev;
  	struct w1_slave *sl = NULL;
  	int found = 0;
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
753
  	mutex_lock(&w1_mlock);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
754
  	list_for_each_entry(dev, &w1_masters, w1_master_entry) {
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
755
  		mutex_lock(&dev->mutex);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
756
757
758
759
760
761
762
763
764
765
  		list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
  			if (sl->reg_num.family == id->family &&
  					sl->reg_num.id == id->id &&
  					sl->reg_num.crc == id->crc) {
  				found = 1;
  				atomic_inc(&dev->refcnt);
  				atomic_inc(&sl->refcnt);
  				break;
  			}
  		}
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
766
  		mutex_unlock(&dev->mutex);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
767
768
769
770
  
  		if (found)
  			break;
  	}
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
771
  	mutex_unlock(&w1_mlock);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
772
773
774
  
  	return (found)?sl:NULL;
  }
c30c9b151   David Fries   W1: fix deadlocks...
775
  void w1_reconnect_slaves(struct w1_family *f, int attach)
6adf87bd7   Evgeniy Polyakov   [PATCH] w1: recon...
776
  {
c30c9b151   David Fries   W1: fix deadlocks...
777
  	struct w1_slave *sl, *sln;
6adf87bd7   Evgeniy Polyakov   [PATCH] w1: recon...
778
  	struct w1_master *dev;
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
779
  	mutex_lock(&w1_mlock);
6adf87bd7   Evgeniy Polyakov   [PATCH] w1: recon...
780
  	list_for_each_entry(dev, &w1_masters, w1_master_entry) {
c30c9b151   David Fries   W1: fix deadlocks...
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
  		dev_dbg(&dev->dev, "Reconnecting slaves in device %s "
  			"for family %02x.
  ", dev->name, f->fid);
  		mutex_lock(&dev->mutex);
  		list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
  			/* If it is a new family, slaves with the default
  			 * family driver and are that family will be
  			 * connected.  If the family is going away, devices
  			 * matching that family are reconneced.
  			 */
  			if ((attach && sl->family->fid == W1_FAMILY_DEFAULT
  				&& sl->reg_num.family == f->fid) ||
  				(!attach && sl->family->fid == f->fid)) {
  				struct w1_reg_num rn;
  
  				memcpy(&rn, &sl->reg_num, sizeof(rn));
  				w1_slave_detach(sl);
  
  				w1_attach_slave_device(dev, &rn);
  			}
  		}
  		dev_dbg(&dev->dev, "Reconnecting slaves in device %s "
  			"has been finished.
  ", dev->name);
  		mutex_unlock(&dev->mutex);
6adf87bd7   Evgeniy Polyakov   [PATCH] w1: recon...
806
  	}
abd52a132   Evgeniy Polyakov   [PATCH] w1: Use m...
807
  	mutex_unlock(&w1_mlock);
6adf87bd7   Evgeniy Polyakov   [PATCH] w1: recon...
808
  }
963bb1010   David Fries   w1: have netlink ...
809
  void w1_slave_found(struct w1_master *dev, u64 rn)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
  	struct w1_slave *sl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
  	struct w1_reg_num *tmp;
0e65f8281   Evgeniy Polyakov   [PATCH] w1: fix C...
813
  	u64 rn_le = cpu_to_le64(rn);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814

c30c9b151   David Fries   W1: fix deadlocks...
815
  	atomic_inc(&dev->refcnt);
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
816

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
  	tmp = (struct w1_reg_num *) &rn;
cd7b28d33   David Fries   W1: recode w1_sla...
818
819
820
821
822
823
  	sl = w1_slave_search_device(dev, tmp);
  	if (sl) {
  		set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags);
  	} else {
  		if (rn && tmp->crc == w1_calc_crc8((u8 *)&rn_le, 7))
  			w1_attach_slave_device(dev, tmp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
  	}
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
825

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
827
  	atomic_dec(&dev->refcnt);
  }
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
828
829
830
831
832
833
834
835
836
837
838
839
840
841
  /**
   * Performs a ROM Search & registers any devices found.
   * The 1-wire search is a simple binary tree search.
   * For each bit of the address, we read two bits and write one bit.
   * The bit written will put to sleep all devies that don't match that bit.
   * When the two reads differ, the direction choice is obvious.
   * When both bits are 0, we must choose a path to take.
   * When we can scan all 64 bits without having to choose a path, we are done.
   *
   * See "Application note 187 1-wire search algorithm" at www.maxim-ic.com
   *
   * @dev        The master device to search
   * @cb         Function to call when a device is found
   */
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
842
  void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
  {
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
844
845
846
847
848
  	u64 last_rn, rn, tmp64;
  	int i, slave_count = 0;
  	int last_zero, last_device;
  	int search_bit, desc_bit;
  	u8  triplet_ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
849

6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
850
851
852
853
  	search_bit = 0;
  	rn = last_rn = 0;
  	last_device = 0;
  	last_zero = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854
855
  
  	desc_bit = 64;
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
856
857
  	while ( !last_device && (slave_count++ < dev->max_slave_count) ) {
  		last_rn = rn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
  		rn = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
859
860
861
862
863
864
865
  		/*
  		 * Reset bus and all 1-wire device state machines
  		 * so they can respond to our requests.
  		 *
  		 * Return 0 - device(s) present, 1 - no devices present.
  		 */
  		if (w1_reset_bus(dev)) {
2da5bf80f   Evgeniy Polyakov   [PATCH] w1: more ...
866
867
  			dev_dbg(&dev->dev, "No devices present on the wire.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868
869
  			break;
  		}
c9cbf558e   Evgeniy Polyakov   w1: add fast sear...
870
871
872
873
874
875
876
877
878
  		/* Do fast search on single slave bus */
  		if (dev->max_slave_count == 1) {
  			w1_write_8(dev, W1_READ_ROM);
  
  			if (w1_read_block(dev, (u8 *)&rn, 8) == 8 && rn)
  				cb(dev, rn);
  
  			break;
  		}
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
879
  		/* Start the search */
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
880
  		w1_write_8(dev, search_type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
881
  		for (i = 0; i < 64; ++i) {
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
882
883
884
885
886
  			/* Determine the direction/search bit */
  			if (i == desc_bit)
  				search_bit = 1;	  /* took the 0 path last time, so take the 1 path */
  			else if (i > desc_bit)
  				search_bit = 0;	  /* take the 0 path on the next branch */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
  			else
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
888
  				search_bit = ((last_rn >> i) & 0x1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889

6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
890
891
892
893
894
895
  			/** Read two bits and write one bit */
  			triplet_ret = w1_triplet(dev, search_bit);
  
  			/* quit if no device responded */
  			if ( (triplet_ret & 0x03) == 0x03 )
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896

6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
897
898
899
  			/* If both directions were valid, and we took the 0 path... */
  			if (triplet_ret == 0)
  				last_zero = i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
900

6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
901
902
903
  			/* extract the direction taken & update the device number */
  			tmp64 = (triplet_ret >> 2);
  			rn |= (tmp64 << i);
0d671b272   David Fries   W1: abort search ...
904

3c52e4e62   David Fries   W1: w1_process, b...
905
  			if (kthread_should_stop()) {
7dc8f527e   David Fries   W1: w1.c s/printk...
906
907
  				dev_dbg(&dev->dev, "Abort w1_search
  ");
0d671b272   David Fries   W1: abort search ...
908
909
  				return;
  			}
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
910
  		}
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
911

6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
912
913
914
915
  		if ( (triplet_ret & 0x03) != 0x03 ) {
  			if ( (desc_bit == last_zero) || (last_zero < 0))
  				last_device = 1;
  			desc_bit = last_zero;
c30c9b151   David Fries   W1: fix deadlocks...
916
  			cb(dev, rn);
6b7298618   Evgeniy Polyakov   [PATCH] w1: Added...
917
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
918
919
  	}
  }
963bb1010   David Fries   w1: have netlink ...
920
921
  void w1_search_process_cb(struct w1_master *dev, u8 search_type,
  	w1_slave_found_callback cb)
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
922
923
924
925
926
  {
  	struct w1_slave *sl, *sln;
  
  	list_for_each_entry(sl, &dev->slist, w1_slave_entry)
  		clear_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags);
963bb1010   David Fries   w1: have netlink ...
927
  	w1_search_devices(dev, search_type, cb);
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
928
929
  
  	list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
a2a6c74d3   Evgeniy Polyakov   w1: decrement sla...
930
  		if (!test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags) && !--sl->ttl)
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
931
  			w1_slave_detach(sl);
a2a6c74d3   Evgeniy Polyakov   w1: decrement sla...
932
  		else if (test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags))
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
933
934
935
936
937
938
  			sl->ttl = dev->slave_ttl;
  	}
  
  	if (dev->search_count > 0)
  		dev->search_count--;
  }
963bb1010   David Fries   w1: have netlink ...
939
940
941
942
  static void w1_search_process(struct w1_master *dev, u8 search_type)
  {
  	w1_search_process_cb(dev, search_type, w1_slave_found);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
943
944
945
  int w1_process(void *data)
  {
  	struct w1_master *dev = (struct w1_master *) data;
3c52e4e62   David Fries   W1: w1_process, b...
946
947
948
949
  	/* As long as w1_timeout is only set by a module parameter the sleep
  	 * time can be calculated in jiffies once.
  	 */
  	const unsigned long jtime = msecs_to_jiffies(w1_timeout * 1000);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950

3c52e4e62   David Fries   W1: w1_process, b...
951
  	while (!kthread_should_stop()) {
01e14d6db   David Fries   W1: don't delay s...
952
953
954
955
956
  		if (dev->search_count) {
  			mutex_lock(&dev->mutex);
  			w1_search_process(dev, W1_SEARCH);
  			mutex_unlock(&dev->mutex);
  		}
3e1d1d28d   Christoph Lameter   [PATCH] Cleanup p...
957
  		try_to_freeze();
3c52e4e62   David Fries   W1: w1_process, b...
958
959
960
961
962
963
964
965
966
967
  		__set_current_state(TASK_INTERRUPTIBLE);
  
  		if (kthread_should_stop())
  			break;
  
  		/* Only sleep when the search is active. */
  		if (dev->search_count)
  			schedule_timeout(jtime);
  		else
  			schedule();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968
969
970
  	}
  
  	atomic_dec(&dev->refcnt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
971
972
973
  
  	return 0;
  }
73a98fce8   Peter Huewe   w1: add __init/__...
974
  static int __init w1_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
975
976
977
978
979
  {
  	int retval;
  
  	printk(KERN_INFO "Driver for 1-wire Dallas network protocol.
  ");
12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
980
  	w1_init_netlink();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
982
983
984
985
986
  	retval = bus_register(&w1_bus_type);
  	if (retval) {
  		printk(KERN_ERR "Failed to register bus. err=%d.
  ", retval);
  		goto err_out_exit_init;
  	}
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
987
  	retval = driver_register(&w1_master_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988
989
990
991
992
993
994
  	if (retval) {
  		printk(KERN_ERR
  			"Failed to register master driver. err=%d.
  ",
  			retval);
  		goto err_out_bus_unregister;
  	}
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
995
996
997
998
999
1000
1001
1002
  	retval = driver_register(&w1_slave_driver);
  	if (retval) {
  		printk(KERN_ERR
  			"Failed to register master driver. err=%d.
  ",
  			retval);
  		goto err_out_master_unregister;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
  	return 0;
c30c9b151   David Fries   W1: fix deadlocks...
1004
1005
  #if 0
  /* For undoing the slave register if there was a step after it. */
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
1006
1007
  err_out_slave_unregister:
  	driver_unregister(&w1_slave_driver);
c30c9b151   David Fries   W1: fix deadlocks...
1008
  #endif
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
1009
1010
1011
  
  err_out_master_unregister:
  	driver_unregister(&w1_master_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1012
1013
1014
1015
1016
1017
1018
  
  err_out_bus_unregister:
  	bus_unregister(&w1_bus_type);
  
  err_out_exit_init:
  	return retval;
  }
73a98fce8   Peter Huewe   w1: add __init/__...
1019
  static void __exit w1_fini(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1020
1021
  {
  	struct w1_master *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022

c30c9b151   David Fries   W1: fix deadlocks...
1023
  	/* Set netlink removal messages and some cleanup */
7785925dd   Evgeniy Polyakov   [PATCH] w1: clean...
1024
  	list_for_each_entry(dev, &w1_masters, w1_master_entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1025
  		__w1_remove_master_device(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026

12003375a   Evgeniy Polyakov   [PATCH] w1: Users...
1027
  	w1_fini_netlink();
7f772ed8d   Evgeniy Polyakov   [PATCH] w1: hotpl...
1028
1029
  	driver_unregister(&w1_slave_driver);
  	driver_unregister(&w1_master_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1030
1031
1032
1033
1034
  	bus_unregister(&w1_bus_type);
  }
  
  module_init(w1_init);
  module_exit(w1_fini);