/* 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */
/** library for 1-wire protocol as master
 *  @file
 *  @author King Kévin <kingkevin@cuvoodoo.info>
 *  @date 2017-2018
 *  @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio
 *  @note overdrive mode is not provided
 */
#pragma once

/** @defgroup onewire_master_gpio GPIO used for 1-wire signal
 *  @note external pull-up resistor on pin is required (< 5 kOhm)
 *  @{
 */
#define ONEWIRE_MASTER_PORT C /**< GPIO port */
#define ONEWIRE_MASTER_PIN 9  /**< GPIO pin */
/** @} */

/** setup 1-wire peripheral
 */
void onewire_master_setup(void);
/** release 1-wire peripheral
 */
void onewire_master_release(void);
/** send reset pulse
 *  @return if slaves have indicated their presence
 */
bool onewire_master_reset(void);
/** compute CRC for 1-Wire
 *  @note this CRC-8 uses normal polynomial 0x31, reverse polynomial 0x8C, start value 0x00
 *  @param[in] data bytes on which to calculate CRC checksum on
 *  @param[in] length number of bytes in data
 *  @return computed CRC checksum
 */
uint8_t onewire_master_crc(uint8_t* data, uint32_t length);
/** send READ ROM command and read ROM code response
 *  @note user needs to send reset pulse before
 *  @return ROM code read
 */
uint64_t onewire_master_rom_read(void);
/** send SEARCH ROM command
 *  @note user needs to send reset pulse before
 *  @warning undefined behaviour if a ROM code different than the last found is provided
 *  @param[in,out] code use 0 to start search ROM code from scratch, or last know value to search next; writes back next ROM code found, or 0 if error occurred
 *  @param[in] alarm search only for ROM codes for slaves with an alarm flag set
 *  @return if an additional slave has been detected
 *  @warning when the code found is 0 it very probably means that the 1-wire line is not pulled up instead of actually having found a slave with ROM code 0
 */
bool onewire_master_rom_search(uint64_t* code, bool alarm);
/** send SKIP ROM command (all slaves on the bus will be selected)
 *  @note user needs to send reset pulse before
 *  @return if operation succeeded
 */
bool onewire_master_rom_skip(void);
/** send MATCH ROM command to select a specific slave
 *  @note user needs to send reset pulse before
 *  @param[in] code ROM code of slave to select
 *  @return if operation succeeded
 */
bool onewire_master_rom_match(uint64_t code);
/** read data byte
 *  @note it is up to the user to send the reset pulse 
 *  @param[out] data buffer to save data read
 *  @return if operation succeeded
 */
bool onewire_master_read_byte(uint8_t* data);
/** write data byte
 *  @note it is up to the user to send the reset pulse 
 *  @param[in] data byte to write
 *  @return if operation succeeded
 */
bool onewire_master_write_byte(uint8_t data);
/** issue function and read data
 *  @note user needs to send a ROM command before
 *  @param[in] function function command to send
 *  @param[out] data buffer to save read bits (NULL if only the function command should be sent)
 *  @param[in] bits number of bits to read (0 if only the function command should be sent)
 *  @return if operation succeeded
 */
bool onewire_master_function_read(uint8_t function, uint8_t* data, uint32_t bits);
/** issue function and write data
 *  @note user needs to send a ROM command before
 *  @param[in] function function command to send
 *  @param[out] data data to write (NULL if only the function command should be sent)
 *  @param[in] bits number of bits to write (0 if only the function command should be sent)
 *  @return if operation succeeded
 */
bool onewire_master_function_write(uint8_t function, uint8_t* data, uint32_t bits);

/** device corresponding to a family code
 */
struct onewire_family_code_t {
	uint8_t code; /**< ROM ID code */
	const char* device; /**< device name(s) */
};

/** list of possible devices corresponding to the family code
 *  sources:
 *  - http://owfs.org/index.php?page=family-code-list
 *  - http://owfs.sourceforge.net/family.html
 *  - https://www.maximintegrated.com/en/app-notes/index.mvp/id/155
 *  - https://github.com/owfs/owfs-doc/wiki/1Wire-Device-List
 *  - IDs seen or reported
 */
static const struct onewire_family_code_t onewire_family_codes[] = {
	{
		.code = 0x01,
		.device = "DS1990R/DS2401/DS2411/DS2490A",
	},
	{
		.code = 0x02,
		.device = "DS1991/DS1425",
	},
	{
		.code = 0x04,
		.device = "DS1994/DS2404",
	},
	{
		.code = 0x05,
		.device = "DS2405",
	},
	{
		.code = 0x06,
		.device = "DS1993",
	},
	{
		.code = 0x08,
		.device = "DS1992",
	},
	{
		.code = 0x09,
		.device = "DS1982/DS2502/DS2703/DS2704",
	},
	{
		.code = 0x0a,
		.device = "DS1995",
	},
	{
		.code = 0x0b,
		.device = "DS1985/DS2505",
	},
	{
		.code = 0x0c,
		.device = "DS1996",
	},
	{
		.code = 0x0f,
		.device = "DS1986/DS2506",
	},
	{
		.code = 0x10,
		.device = "DS1920/DS18S20",
	},
	{
		.code = 0x12,
		.device = "DS2406/DS2407",
	},
	{
		.code = 0x14,
		.device = "DS1971/DS2430A",
	},
	{
		.code = 0x16,
		.device = "DS1954/DS1957",
	},
	{
		.code = 0x18,
		.device = "DS1963S/DS1962",
	},
	{
		.code = 0x1a,
		.device = "DS1963L",
	},
	{
		.code = 0x1b,
		.device = "DS2436",
	},
	{
		.code = 0x1c,
		.device = "DS28E04-100",
	},
	{
		.code = 0x1d,
		.device = "DS2423",
	},
	{
		.code = 0x1e,
		.device = "DS2437",
	},
	{
		.code = 0x1f,
		.device = "DS2409",
	},
	{
		.code = 0x20,
		.device = "DS2450",
	},
	{
		.code = 0x21,
		.device = "DS1921",
	},
	{
		.code = 0x22,
		.device = "DS1922",
	},
	{
		.code = 0x23,
		.device = "DS1973/DS2433",
	},
	{
		.code = 0x24,
		.device = "DS1904/DS2415",
	},
	{
		.code = 0x26,
		.device = "DS2438",
	},
	{
		.code = 0x27,
		.device = "DS2417",
	},
	{
		.code = 0x28,
		.device = "DS18B20",
	},
	{
		.code = 0x29,
		.device = "DS2408",
	},
	{
		.code = 0x2c,
		.device = "DS2890",
	},
	{
		.code = 0x2d,
		.device = "DS1972/DS2431",
	},
	{
		.code = 0x2e,
		.device = "DS2770",
	},
	{
		.code = 0x2f,
		.device = "DS28E01-100",
	},
	{
		.code = 0x30,
		.device = "DS2760/DS2761/DS2762",
	},
	{
		.code = 0x31,
		.device = "DS2720",
	},
	{
		.code = 0x32,
		.device = "DS2780",
	},
	{
		.code = 0x33,
		.device = "DS1961S/DS2432",
	},
	{
		.code = 0x34,
		.device = "DS2703",
	},
	{
		.code = 0x35,
		.device = "DS2755",
	},
	{
		.code = 0x36,
		.device = "DS2740",
	},
	{
		.code = 0x37,
		.device = "DS1977",
	},
	{
		.code = 0x3a,
		.device = "DS2413",
	},
	{
		.code = 0x3b,
		.device = "DS1825/MAX31826/MAX31850",
	},
	{
		.code = 0x3d,
		.device = "DS2781",
	},
	{
		.code = 0x41,
		.device = "DS1922/DS1923/DS2422",
	},
	{
		.code = 0x42,
		.device = "DS28EA00",
	},
	{
		.code = 0x43,
		.device = "DS28EC20",
	},
	{
		.code = 0x44,
		.device = "DS28E10",
	},
	{
		.code = 0x51,
		.device = "DS2751",
	},
	{
		.code = 0x7e,
		.device = "EDS00xx",
	},
	{
		.code = 0x81,
		.device = "DS1420/DS2490R/DS2490B",
	},
	{
		.code = 0x82,
		.device = "DS1425",
	},
	{
		.code = 0x84,
		.device = "DS2404S",
	},
	{
		.code = 0x89,
		.device = "DS1982U/DS2502",
	},
	{
		.code = 0x8b,
		.device = "DS1985U/DS2505",
	},
	{
		.code = 0x8f,
		.device = "DS1986U/DS2506",
	},
	{
		.code = 0xa0,
		.device = "mRS001",
	},
	{
		.code = 0xa1,
		.device = "mVM001",
	},
	{
		.code = 0xa2,
		.device = "mCM001",
	},
	{
		.code = 0xa6,
		.device = "mTS017",
	},
	{
		.code = 0xb1,
		.device = "mTC001",
	},
	{
		.code = 0xb2,
		.device = "mAM001",
	},
	{
		.code = 0xb3,
		.device = "DS2432/mTC002",
	},
	{
		.code = 0xfc,
		.device = "BAE0910/BAE0911",
	},
	{
		.code = 0xff,
		.device = "Swart LCD",
	}
};
