399 lines
9.5 KiB
C
399 lines
9.5 KiB
C
/*
|
|
* Copyright (c) 2022 Thomas Stranger
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @brief 1-Wire network related functions.
|
|
*
|
|
* The following procedures wrap basic w1 syscalls, they should be callable
|
|
* from user mode as well as supervisor mode, therefore _ZEPHYR_SUPERVISOR__
|
|
* is not defined for this file such that inline macros do not skip
|
|
* the arch_is_user_context() check.
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/drivers/w1.h>
|
|
|
|
LOG_MODULE_REGISTER(w1, CONFIG_W1_LOG_LEVEL);
|
|
|
|
#define W1_SEARCH_DISCREPANCY_INIT 0
|
|
#define W1_SEARCH_LAST_SLAVE 65
|
|
#define W1_SEARCH_NO_SLAVE 66
|
|
|
|
/* @brief Search bus for next slave.
|
|
*
|
|
* This function searches the next 1-Wire slave on the bus.
|
|
* It sets the found ROM and the last discrepancy in case more than one
|
|
* slave took part in the search.
|
|
* In case only one slave took part in the search, the discrepancy is set to
|
|
* W1_SEARCH_LAST_SLAVE, and in case no slave participated in the search,
|
|
* the discrepancy is set to W1_SEARCH_NO_SLAVE.
|
|
*
|
|
* The implementation is similar to suggested in the maxim application note 187.
|
|
* The master reads the first ROM bit and its complementary value of all slaves.
|
|
* Due to physical characteristics, the value received is a
|
|
* logical AND of all slaves' 1st bit. Slaves only continue to
|
|
* participate in the search procedure if the next bit the master sends matches
|
|
* their own addresses' bit. This allows the master to branch through 64-bit
|
|
* addresses in order to detect all slaves.
|
|
|
|
* The 1st bit received is stored in bit 1 of rom_inv_64, the 2nd in bit 2 and so
|
|
* on, until bit 64.
|
|
* As a result, each byte of the ROM has the correct bit order, but the received
|
|
* bytes (big-endian) stored in rom_inv_64 are in inverse byte order.
|
|
*
|
|
* Note: Filtering by families is currently not supported.
|
|
*
|
|
* @param dev Pointer to the device structure for the w1 instance.
|
|
* @param command Command to chose between normal and alarm search.
|
|
* @param family This parameter is currently not supported.
|
|
* @param last_discrepancy This must be set to W1_SEARCH_DISCREPANCY_INIT before
|
|
* the first call, it carries the search progress for
|
|
* further calls.
|
|
* @param rom_inv_64 The found ROM: It must be set to zero before first
|
|
* call and carries the last found ROM for furter calls.
|
|
* The ROM is stored in inverse byte order.
|
|
*
|
|
* @retval 0 If successful.
|
|
* @retval -errno Negative error code in case of 1-wire read/write error.
|
|
*/
|
|
static int search_slave(const struct device *dev, uint8_t command,
|
|
uint8_t family, size_t *last_discrepancy,
|
|
uint64_t *rom_inv_64)
|
|
{
|
|
int ret;
|
|
size_t next_discrepancy;
|
|
bool last_id_bit;
|
|
bool last_complement_id_bit;
|
|
|
|
ARG_UNUSED(family);
|
|
__ASSERT_NO_MSG(command == W1_CMD_SEARCH_ROM ||
|
|
command == W1_CMD_SEARCH_ALARM);
|
|
|
|
ret = w1_reset_bus(dev);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
if (ret == 0) {
|
|
*last_discrepancy = W1_SEARCH_NO_SLAVE;
|
|
return 0;
|
|
}
|
|
|
|
ret = w1_write_byte(dev, command);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
next_discrepancy = W1_SEARCH_LAST_SLAVE;
|
|
|
|
for (size_t id_bit_nr = 1; id_bit_nr < W1_SEARCH_LAST_SLAVE; id_bit_nr++) {
|
|
ret = w1_read_bit(dev);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
last_id_bit = (bool)ret;
|
|
ret = w1_read_bit(dev);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
last_complement_id_bit = (bool)ret;
|
|
|
|
if (last_id_bit && last_complement_id_bit) {
|
|
/*
|
|
* No slave participating:
|
|
* We can stop following the branch.
|
|
*/
|
|
LOG_DBG("No slave paricipating");
|
|
*last_discrepancy = W1_SEARCH_NO_SLAVE;
|
|
return 0;
|
|
} else if (last_id_bit != last_complement_id_bit) {
|
|
/*
|
|
* All slaves connected have same ROM bit value:
|
|
* We can directly follow last_id_bit branch.
|
|
*/
|
|
} else {
|
|
/*
|
|
* Discrepancy detected: bit value at id_bit_nr does
|
|
* not match for all slaves on the bus.
|
|
*/
|
|
if ((id_bit_nr > *last_discrepancy) ||
|
|
((id_bit_nr < *last_discrepancy) &&
|
|
(*rom_inv_64 & BIT64(id_bit_nr - 1)))) {
|
|
/*
|
|
* - id_bit_nr > last_discrepancy:
|
|
* Start always w/ branch of 1s
|
|
* - id_bit_nr < last_discrepancy:
|
|
* Follow same branch as before
|
|
*/
|
|
last_id_bit = true;
|
|
next_discrepancy = id_bit_nr;
|
|
} else {
|
|
/*
|
|
* - id_bit_nr == last_discrepancy:
|
|
* 1-path already done, therefore go 0 path
|
|
* - id_bit_nr < last_discrepancy:
|
|
* Follow same branch as before
|
|
*/
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send and store the chosen bit: all not matching slaves will
|
|
* no longer participate in this search until they are reset.
|
|
*/
|
|
ret = w1_write_bit(dev, last_id_bit);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
*rom_inv_64 &= ~BIT64(id_bit_nr - 1);
|
|
*rom_inv_64 |= last_id_bit ? BIT64(id_bit_nr - 1) : 0;
|
|
}
|
|
|
|
*last_discrepancy = next_discrepancy;
|
|
return 0;
|
|
}
|
|
|
|
int z_impl_w1_search_bus(const struct device *dev, uint8_t command,
|
|
uint8_t family, w1_search_callback_t callback,
|
|
void *user_data)
|
|
{
|
|
size_t last_discrepancy = W1_SEARCH_DISCREPANCY_INIT;
|
|
uint64_t found_rom_inv_64 = 0;
|
|
struct w1_rom found_rom = { 0 };
|
|
int found_cnt = 0;
|
|
int ret;
|
|
|
|
(void)w1_lock_bus(dev);
|
|
|
|
do {
|
|
ret = search_slave(dev, command, family, &last_discrepancy,
|
|
&found_rom_inv_64);
|
|
if (ret < 0) {
|
|
found_cnt = ret;
|
|
break;
|
|
}
|
|
if (last_discrepancy == W1_SEARCH_NO_SLAVE) {
|
|
break;
|
|
}
|
|
|
|
found_cnt++;
|
|
/*
|
|
* ROM is stored in found_rom_inv_64 in "inverse byte order" =>
|
|
* Only big-endian targets need to swap, such that struct's
|
|
* bytes are stored in big-endian byte order.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_BIG_ENDIAN)) {
|
|
sys_memcpy_swap(&found_rom, &found_rom_inv_64, 8);
|
|
} else {
|
|
*(uint64_t *)&found_rom = found_rom_inv_64;
|
|
}
|
|
LOG_DBG("ROM found: nr %u, %016llx", found_cnt,
|
|
w1_rom_to_uint64(&found_rom));
|
|
|
|
if (callback != NULL) {
|
|
callback(found_rom, user_data);
|
|
}
|
|
|
|
} while (last_discrepancy != W1_SEARCH_LAST_SLAVE);
|
|
|
|
(void)w1_unlock_bus(dev);
|
|
return found_cnt;
|
|
}
|
|
|
|
int w1_read_rom(const struct device *dev, struct w1_rom *rom)
|
|
{
|
|
int ret;
|
|
|
|
(void)w1_lock_bus(dev);
|
|
ret = w1_reset_bus(dev);
|
|
if (ret == 0) {
|
|
ret = -ENODEV;
|
|
goto out;
|
|
}
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
|
|
ret = w1_write_byte(dev, W1_CMD_READ_ROM);
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
ret = w1_read_block(dev, (uint8_t *)rom, sizeof(struct w1_rom));
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
if (w1_crc8((uint8_t *)rom, sizeof(struct w1_rom)) != 0) {
|
|
ret = -EIO;
|
|
}
|
|
|
|
out:
|
|
(void)w1_unlock_bus(dev);
|
|
return ret;
|
|
};
|
|
|
|
static int match_rom(const struct device *dev, const struct w1_slave_config *config)
|
|
{
|
|
int ret;
|
|
uint8_t cmd;
|
|
|
|
if (!config->overdrive) {
|
|
if (w1_configure(dev, W1_SETTING_SPEED, 0) < 0) {
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
ret = w1_reset_bus(dev);
|
|
if (ret == 0) {
|
|
return -ENODEV;
|
|
}
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
cmd = config->overdrive ? W1_CMD_OVERDRIVE_MATCH_ROM : W1_CMD_MATCH_ROM;
|
|
ret = w1_write_byte(dev, cmd);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
ret = w1_write_block(dev, (uint8_t *)&config->rom, 8);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (config->overdrive) {
|
|
if (w1_configure(dev, W1_SETTING_SPEED, 1) < 0) {
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
int w1_match_rom(const struct device *dev, const struct w1_slave_config *config)
|
|
{
|
|
int ret;
|
|
|
|
(void)w1_lock_bus(dev);
|
|
ret = match_rom(dev, config);
|
|
(void)w1_unlock_bus(dev);
|
|
return ret;
|
|
}
|
|
|
|
int w1_resume_command(const struct device *dev)
|
|
{
|
|
int ret;
|
|
|
|
(void)w1_lock_bus(dev);
|
|
ret = w1_reset_bus(dev);
|
|
if (ret == 0) {
|
|
ret = -ENODEV;
|
|
goto out;
|
|
}
|
|
if (ret < 0) {
|
|
goto out;
|
|
}
|
|
|
|
ret = w1_write_byte(dev, W1_CMD_RESUME);
|
|
out:
|
|
(void)w1_unlock_bus(dev);
|
|
return ret;
|
|
}
|
|
|
|
static int skip_rom(const struct device *dev, const struct w1_slave_config *config)
|
|
{
|
|
int ret;
|
|
uint8_t cmd;
|
|
|
|
if (!config->overdrive) {
|
|
if (w1_configure(dev, W1_SETTING_SPEED, 0) < 0) {
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
ret = w1_reset_bus(dev);
|
|
if (ret == 0) {
|
|
return -ENODEV;
|
|
}
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
cmd = config->overdrive ? W1_CMD_OVERDRIVE_SKIP_ROM : W1_CMD_SKIP_ROM;
|
|
ret = w1_write_byte(dev, cmd);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (config->overdrive) {
|
|
if (w1_configure(dev, W1_SETTING_SPEED, 1) < 0) {
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int w1_skip_rom(const struct device *dev, const struct w1_slave_config *config)
|
|
{
|
|
int ret;
|
|
|
|
(void)w1_lock_bus(dev);
|
|
ret = skip_rom(dev, config);
|
|
(void)w1_unlock_bus(dev);
|
|
return ret;
|
|
}
|
|
|
|
static int reset_select(const struct device *dev, const struct w1_slave_config *config)
|
|
{
|
|
if (IS_ENABLED(CONFIG_W1_NET_FORCE_MULTIDROP_ADDRESSING) || w1_get_slave_count(dev) > 1) {
|
|
return match_rom(dev, config);
|
|
}
|
|
|
|
return skip_rom(dev, config);
|
|
}
|
|
|
|
int w1_reset_select(const struct device *dev, const struct w1_slave_config *config)
|
|
{
|
|
int ret;
|
|
|
|
(void)w1_lock_bus(dev);
|
|
ret = reset_select(dev, config);
|
|
(void)w1_unlock_bus(dev);
|
|
return ret;
|
|
}
|
|
|
|
static int 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)
|
|
{
|
|
int ret;
|
|
|
|
ret = reset_select(dev, config);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = w1_write_block(dev, write_buf, write_len);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (read_buf == NULL && read_len > 0) {
|
|
return -EIO;
|
|
}
|
|
return w1_read_block(dev, read_buf, read_len);
|
|
};
|
|
|
|
int 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)
|
|
{
|
|
int ret;
|
|
|
|
(void)w1_lock_bus(dev);
|
|
ret = write_read(dev, config, write_buf, write_len, read_buf, read_len);
|
|
(void)w1_unlock_bus(dev);
|
|
return ret;
|
|
};
|