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