1 /*
2 * Copyright (c) 2022 Thomas Stranger
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @brief 1-Wire network related functions.
9 *
10 * The following procedures wrap basic w1 syscalls, they should be callable
11 * from user mode as well as supervisor mode, therefore _ZEPHYR_SUPERVISOR__
12 * is not defined for this file such that inline macros do not skip
13 * the arch_is_user_context() check.
14 */
15
16 #include <zephyr/logging/log.h>
17 #include <zephyr/drivers/w1.h>
18
19 LOG_MODULE_REGISTER(w1, CONFIG_W1_LOG_LEVEL);
20
21 #define W1_SEARCH_DISCREPANCY_INIT 0
22 #define W1_SEARCH_LAST_SLAVE 65
23 #define W1_SEARCH_NO_SLAVE 66
24
25 /* @brief Search bus for next slave.
26 *
27 * This function searches the next 1-Wire slave on the bus.
28 * It sets the found ROM and the last discrepancy in case more than one
29 * slave took part in the search.
30 * In case only one slave took part in the search, the discrepancy is set to
31 * W1_SEARCH_LAST_SLAVE, and in case no slave participated in the search,
32 * the discrepancy is set to W1_SEARCH_NO_SLAVE.
33 *
34 * The implementation is similar to suggested in the maxim application note 187.
35 * The master reads the first ROM bit and its complementary value of all slaves.
36 * Due to physical characteristics, the value received is a
37 * logical AND of all slaves' 1st bit. Slaves only continue to
38 * participate in the search procedure if the next bit the master sends matches
39 * their own addresses' bit. This allows the master to branch through 64-bit
40 * addresses in order to detect all slaves.
41
42 * The 1st bit received is stored in bit 1 of rom_inv_64, the 2nd in bit 2 and so
43 * on, until bit 64.
44 * As a result, each byte of the ROM has the correct bit order, but the received
45 * bytes (big-endian) stored in rom_inv_64 are in inverse byte order.
46 *
47 * Note: Filtering by families is currently not supported.
48 *
49 * @param dev Pointer to the device structure for the w1 instance.
50 * @param command Command to chose between normal and alarm search.
51 * @param family This parameter is currently not supported.
52 * @param last_discrepancy This must be set to W1_SEARCH_DISCREPANCY_INIT before
53 * the first call, it carries the search progress for
54 * further calls.
55 * @param rom_inv_64 The found ROM: It must be set to zero before first
56 * call and carries the last found ROM for furter calls.
57 * The ROM is stored in inverse byte order.
58 *
59 * @retval 0 If successful.
60 * @retval -errno Negative error code in case of 1-wire read/write error.
61 */
search_slave(const struct device * dev,uint8_t command,uint8_t family,size_t * last_discrepancy,uint64_t * rom_inv_64)62 static int search_slave(const struct device *dev, uint8_t command,
63 uint8_t family, size_t *last_discrepancy,
64 uint64_t *rom_inv_64)
65 {
66 int ret;
67 size_t next_discrepancy;
68 bool last_id_bit;
69 bool last_complement_id_bit;
70
71 ARG_UNUSED(family);
72 __ASSERT_NO_MSG(command == W1_CMD_SEARCH_ROM ||
73 command == W1_CMD_SEARCH_ALARM);
74
75 ret = w1_reset_bus(dev);
76 if (ret < 0) {
77 return ret;
78 }
79 if (ret == 0) {
80 *last_discrepancy = W1_SEARCH_NO_SLAVE;
81 return 0;
82 }
83
84 ret = w1_write_byte(dev, command);
85 if (ret < 0) {
86 return ret;
87 }
88 next_discrepancy = W1_SEARCH_LAST_SLAVE;
89
90 for (size_t id_bit_nr = 1; id_bit_nr < W1_SEARCH_LAST_SLAVE; id_bit_nr++) {
91 ret = w1_read_bit(dev);
92 if (ret < 0) {
93 return ret;
94 }
95 last_id_bit = (bool)ret;
96 ret = w1_read_bit(dev);
97 if (ret < 0) {
98 return ret;
99 }
100 last_complement_id_bit = (bool)ret;
101
102 if (last_id_bit && last_complement_id_bit) {
103 /*
104 * No slave participating:
105 * We can stop following the branch.
106 */
107 LOG_DBG("No slave paricipating");
108 *last_discrepancy = W1_SEARCH_NO_SLAVE;
109 return 0;
110 } else if (last_id_bit != last_complement_id_bit) {
111 /*
112 * All slaves connected have same ROM bit value:
113 * We can directly follow last_id_bit branch.
114 */
115 } else {
116 /*
117 * Discrepancy detected: bit value at id_bit_nr does
118 * not match for all slaves on the bus.
119 */
120 if ((id_bit_nr > *last_discrepancy) ||
121 ((id_bit_nr < *last_discrepancy) &&
122 (*rom_inv_64 & BIT64(id_bit_nr - 1)))) {
123 /*
124 * - id_bit_nr > last_discrepancy:
125 * Start always w/ branch of 1s
126 * - id_bit_nr < last_discrepancy:
127 * Follow same branch as before
128 */
129 last_id_bit = true;
130 next_discrepancy = id_bit_nr;
131 } else {
132 /*
133 * - id_bit_nr == last_discrepancy:
134 * 1-path already done, therefore go 0 path
135 * - id_bit_nr < last_discrepancy:
136 * Follow same branch as before
137 */
138 }
139 }
140
141 /*
142 * Send and store the chosen bit: all not matching slaves will
143 * no longer participate in this search until they are reset.
144 */
145 ret = w1_write_bit(dev, last_id_bit);
146 if (ret < 0) {
147 return ret;
148 }
149 *rom_inv_64 &= ~BIT64(id_bit_nr - 1);
150 *rom_inv_64 |= last_id_bit ? BIT64(id_bit_nr - 1) : 0;
151 }
152
153 *last_discrepancy = next_discrepancy;
154 return 0;
155 }
156
z_impl_w1_search_bus(const struct device * dev,uint8_t command,uint8_t family,w1_search_callback_t callback,void * user_data)157 int z_impl_w1_search_bus(const struct device *dev, uint8_t command,
158 uint8_t family, w1_search_callback_t callback,
159 void *user_data)
160 {
161 size_t last_discrepancy = W1_SEARCH_DISCREPANCY_INIT;
162 uint64_t found_rom_inv_64 = 0;
163 struct w1_rom found_rom = { 0 };
164 int found_cnt = 0;
165 int ret;
166
167 (void)w1_lock_bus(dev);
168
169 do {
170 ret = search_slave(dev, command, family, &last_discrepancy,
171 &found_rom_inv_64);
172 if (ret < 0) {
173 found_cnt = ret;
174 break;
175 }
176 if (last_discrepancy == W1_SEARCH_NO_SLAVE) {
177 break;
178 }
179
180 found_cnt++;
181 /*
182 * ROM is stored in found_rom_inv_64 in "inverse byte order" =>
183 * Only big-endian targets need to swap, such that struct's
184 * bytes are stored in big-endian byte order.
185 */
186 if (IS_ENABLED(CONFIG_BIG_ENDIAN)) {
187 sys_memcpy_swap(&found_rom, &found_rom_inv_64, 8);
188 } else {
189 *(uint64_t *)&found_rom = found_rom_inv_64;
190 }
191 LOG_DBG("ROM found: nr %u, %016llx", found_cnt,
192 w1_rom_to_uint64(&found_rom));
193
194 if (callback != NULL) {
195 callback(found_rom, user_data);
196 }
197
198 } while (last_discrepancy != W1_SEARCH_LAST_SLAVE);
199
200 (void)w1_unlock_bus(dev);
201 return found_cnt;
202 }
203
w1_read_rom(const struct device * dev,struct w1_rom * rom)204 int w1_read_rom(const struct device *dev, struct w1_rom *rom)
205 {
206 int ret;
207
208 (void)w1_lock_bus(dev);
209 ret = w1_reset_bus(dev);
210 if (ret == 0) {
211 ret = -ENODEV;
212 goto out;
213 }
214 if (ret < 0) {
215 goto out;
216 }
217
218 ret = w1_write_byte(dev, W1_CMD_READ_ROM);
219 if (ret < 0) {
220 goto out;
221 }
222 ret = w1_read_block(dev, (uint8_t *)rom, sizeof(struct w1_rom));
223 if (ret < 0) {
224 goto out;
225 }
226 if (w1_crc8((uint8_t *)rom, sizeof(struct w1_rom)) != 0) {
227 ret = -EIO;
228 }
229
230 out:
231 (void)w1_unlock_bus(dev);
232 return ret;
233 };
234
match_rom(const struct device * dev,const struct w1_slave_config * config)235 static int match_rom(const struct device *dev, const struct w1_slave_config *config)
236 {
237 int ret;
238 uint8_t cmd;
239
240 if (!config->overdrive) {
241 if (w1_configure(dev, W1_SETTING_SPEED, 0) < 0) {
242 return -EIO;
243 }
244 }
245
246 ret = w1_reset_bus(dev);
247 if (ret == 0) {
248 return -ENODEV;
249 }
250 if (ret < 0) {
251 return ret;
252 }
253
254 cmd = config->overdrive ? W1_CMD_OVERDRIVE_MATCH_ROM : W1_CMD_MATCH_ROM;
255 ret = w1_write_byte(dev, cmd);
256 if (ret < 0) {
257 return ret;
258 }
259 ret = w1_write_block(dev, (uint8_t *)&config->rom, 8);
260 if (ret < 0) {
261 return ret;
262 }
263
264 if (config->overdrive) {
265 if (w1_configure(dev, W1_SETTING_SPEED, 1) < 0) {
266 return -EIO;
267 }
268 }
269
270 return 0;
271 };
272
w1_match_rom(const struct device * dev,const struct w1_slave_config * config)273 int w1_match_rom(const struct device *dev, const struct w1_slave_config *config)
274 {
275 int ret;
276
277 (void)w1_lock_bus(dev);
278 ret = match_rom(dev, config);
279 (void)w1_unlock_bus(dev);
280 return ret;
281 }
282
w1_resume_command(const struct device * dev)283 int w1_resume_command(const struct device *dev)
284 {
285 int ret;
286
287 (void)w1_lock_bus(dev);
288 ret = w1_reset_bus(dev);
289 if (ret == 0) {
290 ret = -ENODEV;
291 goto out;
292 }
293 if (ret < 0) {
294 goto out;
295 }
296
297 ret = w1_write_byte(dev, W1_CMD_RESUME);
298 out:
299 (void)w1_unlock_bus(dev);
300 return ret;
301 }
302
skip_rom(const struct device * dev,const struct w1_slave_config * config)303 static int skip_rom(const struct device *dev, const struct w1_slave_config *config)
304 {
305 int ret;
306 uint8_t cmd;
307
308 if (!config->overdrive) {
309 if (w1_configure(dev, W1_SETTING_SPEED, 0) < 0) {
310 return -EIO;
311 }
312 }
313
314 ret = w1_reset_bus(dev);
315 if (ret == 0) {
316 return -ENODEV;
317 }
318 if (ret < 0) {
319 return ret;
320 }
321
322 cmd = config->overdrive ? W1_CMD_OVERDRIVE_SKIP_ROM : W1_CMD_SKIP_ROM;
323 ret = w1_write_byte(dev, cmd);
324 if (ret < 0) {
325 return ret;
326 }
327
328 if (config->overdrive) {
329 if (w1_configure(dev, W1_SETTING_SPEED, 1) < 0) {
330 return -EIO;
331 }
332 }
333
334 return 0;
335 }
336
w1_skip_rom(const struct device * dev,const struct w1_slave_config * config)337 int w1_skip_rom(const struct device *dev, const struct w1_slave_config *config)
338 {
339 int ret;
340
341 (void)w1_lock_bus(dev);
342 ret = skip_rom(dev, config);
343 (void)w1_unlock_bus(dev);
344 return ret;
345 }
346
reset_select(const struct device * dev,const struct w1_slave_config * config)347 static int reset_select(const struct device *dev, const struct w1_slave_config *config)
348 {
349 if (IS_ENABLED(CONFIG_W1_NET_FORCE_MULTIDROP_ADDRESSING) || w1_get_slave_count(dev) > 1) {
350 return match_rom(dev, config);
351 }
352
353 return skip_rom(dev, config);
354 }
355
w1_reset_select(const struct device * dev,const struct w1_slave_config * config)356 int w1_reset_select(const struct device *dev, const struct w1_slave_config *config)
357 {
358 int ret;
359
360 (void)w1_lock_bus(dev);
361 ret = reset_select(dev, config);
362 (void)w1_unlock_bus(dev);
363 return ret;
364 }
365
write_read(const struct device * dev,const struct w1_slave_config * config,const uint8_t * write_buf,size_t write_len,uint8_t * read_buf,size_t read_len)366 static int write_read(const struct device *dev, const struct w1_slave_config *config,
367 const uint8_t *write_buf, size_t write_len,
368 uint8_t *read_buf, size_t read_len)
369 {
370 int ret;
371
372 ret = reset_select(dev, config);
373 if (ret != 0) {
374 return ret;
375 }
376
377 ret = w1_write_block(dev, write_buf, write_len);
378 if (ret < 0) {
379 return ret;
380 }
381
382 if (read_buf == NULL && read_len > 0) {
383 return -EIO;
384 }
385 return w1_read_block(dev, read_buf, read_len);
386 };
387
w1_write_read(const struct device * dev,const struct w1_slave_config * config,const uint8_t * write_buf,size_t write_len,uint8_t * read_buf,size_t read_len)388 int w1_write_read(const struct device *dev, const struct w1_slave_config *config,
389 const uint8_t *write_buf, size_t write_len,
390 uint8_t *read_buf, size_t read_len)
391 {
392 int ret;
393
394 (void)w1_lock_bus(dev);
395 ret = write_read(dev, config, write_buf, write_len, read_buf, read_len);
396 (void)w1_unlock_bus(dev);
397 return ret;
398 };
399