Add support for the NCN5120 KNX bus driver from NXP. To be tested. Part of manually...
authorNathael Pajani <nathael.pajani@ed3l.fr>
Thu, 10 Sep 2015 06:57:44 +0000 (08:57 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:04 +0000 (17:03 +0100)
extdrv/ncn5120.c [new file with mode: 0644]
include/extdrv/ncn5120.h [new file with mode: 0644]
include/lib/protocols/knx.h [new file with mode: 0644]

diff --git a/extdrv/ncn5120.c b/extdrv/ncn5120.c
new file mode 100644 (file)
index 0000000..e3aec83
--- /dev/null
@@ -0,0 +1,746 @@
+/****************************************************************************
+ *  extdrv/ncn5120.c
+ *
+ * Copyright 2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include <stdint.h>
+#include "core/lpc_regs_12xx.h"
+#include "core/lpc_core_cm0.h"
+#include "core/system.h"
+#include "core/systick.h"
+#include "core/pio.h"
+#include "lib/string.h"
+#include "lib/time.h"
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+#include "extdrv/ncn5120.h"
+#include "lib/protocols/knx.h"
+
+/* Debug */
+#include "lib/stdio.h"
+
+static struct ncn5120_device ncn5120 = {
+       .serial_num = 1,
+       .current_state = NCN5120_STATE_NORMAL,
+       .save_callback = NULL,
+       .reset_callback = NULL,
+       .rx_callback = NULL,
+       .address_set = 0,
+       .rx_data_running = 0,
+};
+
+
+
+/***************************************************************************** */
+/* Interrupt handlers */
+
+/* Handle RESETB pin state change */
+static void reset_event(uint32_t gpio)
+{
+       if (ncn5120.reset_callback!= NULL) {
+               ncn5120.reset_callback(gpio);
+       }
+}
+/* Handle SAVEB pin state change */
+static void save_event(uint32_t gpio)
+{
+       if (ncn5120.save_callback != NULL) {
+               ncn5120.save_callback(gpio);
+       }
+}
+
+
+/* Handle data and replies received on NCN5120 UART */
+static void ncn5120_handle_rx(uint8_t c)
+{
+       if (ncn5120.next_data_is_cmd_reply > 0) {
+               /* Receive command reply */
+               ncn5120.rx_cmd_buff[ncn5120.rx_cmd_buff_idx++] = c;
+               ncn5120.next_data_is_cmd_reply--;
+       } else {
+               /* Receive KNX data */
+               static struct time_spec last_byte;
+               static uint32_t last_timer_tick;
+
+               /* Timer based end of frame detection */
+               if (ncn5120.rx_data_running == 0) {
+                       ncn5120.rx_data_running = 1;
+                       get_time_in_interrupt(&last_byte);
+                       last_timer_tick = systick_get_timer_val();
+               } else {
+                       struct time_spec this_byte, diff;
+                       uint32_t this_tick = systick_get_timer_val();
+                       get_time_in_interrupt(&this_byte);
+                       /* Check end of Receiving condition. Consider that the time difference will never be above a few msec. */
+                       get_time_diff(&last_byte, &this_byte, &diff);
+                       if (diff.msec >= 1) {
+                               uint32_t reload = systick_get_timer_reload_val();
+                               uint32_t tick_diff = (this_tick + (reload * (diff.msec + 1)) - last_timer_tick);
+                               if (((tick_diff * 10) / reload) > 26) {
+                                       /* More than 2.6ms elapsed since previous byte : end of frame */
+                                       ncn5120.rx_data_running = 0;
+                               }
+                       }
+                       last_byte.seconds = this_byte.seconds;
+                       last_byte.msec = this_byte.msec;
+                       last_timer_tick = this_tick;
+               }
+               if (ncn5120.rx_callback != NULL) {
+                       if (ncn5120.rx_data_running == 1) {
+                               ncn5120.rx_callback(c);
+                       } else {
+                               ncn5120.rx_callback(c | END_OF_DATA_PACKET);
+                       }
+               }
+       }
+}
+
+
+
+/***************************************************************************** */
+/* Get command replies */
+
+static void ncn5120_drop_cmd_data(void)
+{
+       lpc_disable_irq();
+       ncn5120.rx_cmd_buff_idx = 0;
+       dsb();
+       lpc_enable_irq();
+}
+/* Tell the UART Rx handler that we will receive some command reply bytes */
+static void ncn5120_set_nb_cmd_req_data(uint16_t len)
+{
+       ncn5120.next_data_is_cmd_reply += len;
+}
+/* Get the requested reply bytes */
+static void ncn5120_get_req_reply_data(uint8_t* buf, uint16_t len)
+{
+       /* Wait for data to be received (happens in ncn5120_handle_rx() interrupt routine) */
+       do {} while (ncn5120.rx_cmd_buff_idx < len);
+       /* Lock */
+       lpc_disable_irq();
+       memcpy(buf, (void*)ncn5120.rx_cmd_buff, len);
+       ncn5120.rx_cmd_buff_idx -= len;
+       /* Most of the time all data is read, but ... */
+       if (ncn5120.rx_cmd_buff_idx != 0) {
+               memcpy((void*)ncn5120.rx_cmd_buff, (void*)(ncn5120.rx_cmd_buff + len), ncn5120.rx_cmd_buff_idx);
+       }
+       dsb();
+       lpc_enable_irq();
+}
+
+/***************************************************************************** */
+/* Send requests - Services access */
+static void ncn5120_send_service_request(uint8_t* buf, uint16_t len, uint16_t reply_len)
+{
+       /* Register how many byte this request should generate as reply */
+       ncn5120_set_nb_cmd_req_data(reply_len);
+       /* And send the request */
+       serial_write(ncn5120.serial_num, (char*)buf, len);
+}
+
+/***************************************************************************** */
+/* Internal registers access */
+/* Set NCN5120 internal register by isuing U_IntRegWr.req command */
+static void ncn5120_set_internal_register(uint8_t reg_addr, uint8_t value)
+{
+       uint8_t buf[2];
+       if (reg_addr > 0x03) {
+               return;
+       }
+       buf[0] = U_IntRegWr_Request | reg_addr;
+       buf[1] = value;
+       ncn5120_send_service_request(buf, 2, 0);
+}
+/* Get NCN5120 internal register by isuing U_IntRegRd.req command */
+static uint8_t ncn5120_get_internal_register(uint8_t reg_addr)
+{
+       uint8_t reg_val = 0;
+
+       /* Register address cannot be above 0x03 */
+       reg_addr &= 0x03;
+
+       /* Drop all previously received command data */
+       ncn5120_drop_cmd_data();
+
+       /* Send request */
+       reg_addr |= U_IntRegRd_Request;
+       ncn5120_send_service_request(&reg_addr, 1, 1);
+       /* And wait for our data to come */
+       ncn5120_get_req_reply_data(&reg_val, 1);
+       return reg_val;
+}
+
+
+
+/***************************************************************************** */
+/* Set NCN5120 configuration */
+static void update_thermal_state(uint8_t thermal_warning)
+{
+       if (thermal_warning) {
+               ncn5120.thermal_state = NCN5120_STATE_THERMAL_WARNING;
+       } else {
+               ncn5120.thermal_state = NCN5120_STATE_NORMAL;
+       }
+}
+
+/* Decode config reply */
+static void ncn5120_parse_conf_reply(struct ncn5120_conf* conf, uint8_t reply)
+{
+       conf->busy = (reply & NCN5120_BUSY);
+       if (conf->busy) {
+               ncn5120.current_op_mode = NCN5120_STATE_BUSY;
+       }
+       conf->auto_acknowledge = (reply & NCN5120_AUTO_ACK);
+       conf->auto_polling = (reply & NCN5120_AUTO_POLLING);
+       conf->crc_citt = (reply & NCN5120_CRC_CITT_ACTIVE);
+       conf->frame_end_marker = (reply & NCN5120_FRAME_END_MARK_ACTIVE);
+}
+/* Communication configuration
+ * Activate communication functionnality when corresponding parameter is not nul.
+ * It is only possible to activate services and functions. De-activation requires NCN5120 reset.
+ * Note :
+ *   This driver does not support crc-citt and frame end indicators.
+ *   These parameters will be ignored.
+ */
+static int ncn5120_set_comm_config(uint8_t auto_poll, uint8_t crc_citt, uint8_t use_frame_end_indicator)
+{
+       uint8_t request = U_Configure_Request;
+       uint8_t reply = 0;
+       /* Do not change config if NCN5120 is currently receiving data */
+       if (ncn5120.rx_data_running) {
+               return -1;
+       }
+       /* Send the new config */
+       if (auto_poll) {
+               request |= NCN5120_ACTIVATE_AUTO_POLL;
+       }
+       ncn5120_send_service_request(&request, 1, 1);
+       /* And read the configure byte in reply */
+       ncn5120_get_req_reply_data(&reply, 1);
+       ncn5120_parse_conf_reply(&(ncn5120.comm_config), reply);
+       return 0;
+}
+
+
+/* Set analog configuration
+ * Activate analog functionnality when corresponding parameter is not nul.
+ */
+static void ncn5120_set_analog_config(uint8_t sleep_en, uint8_t v20v_en, uint8_t dc2_en, uint8_t xclock)
+{
+       uint8_t reg_val = 0;
+       if (sleep_en) {
+               reg_val |= NCN5120_SLEEP_ENABLE;
+       }
+       if (v20v_en) {
+               reg_val |= NCN5120_V20V_ENABLE;
+       }
+       if (dc2_en) {
+               reg_val |= NCN5120_DC2_ENABLE;
+       }
+       if (xclock) {
+               reg_val |= ((xclock & NCN5120_XCLOCK_MASK) << NCN5120_XCLOCK_SHIFT);
+       }
+       ncn5120_set_internal_register(NCN5120_ACR_0_ADDR, reg_val);
+}
+
+
+
+/***************************************************************************** */
+/* NCN5120 Configuration and initialisation
+ * rx_callback must not be NULL if you want to receive data. It will be called for
+ *   any received character which is not part of a command reply.
+ * If you did not set the device's address using ncn5120_set_address() then you are
+ *   responsible of sending the ACK, NACK or BUSY using ncn5120_packet_acknowledge().
+ * The UART will be configured in 38400, 8n1 and an intermediate callback will be
+ *   used, so do not try to configure it yourself.
+ * Analog configuration will be setup according to Techno-Innov's KNX module's
+ *   requirements and communication configuration according to this module's
+ *   requirements.
+ */
+void ncn5120_init(uint8_t serial, void (*rx_callback)(uint32_t))
+{
+       /* Serial configuration */
+       ncn5120.serial_num = serial;
+       ncn5120.rx_callback = rx_callback;
+       uart_on(ncn5120.serial_num, 38400, ncn5120_handle_rx);
+
+       /* Initialize time system. Can be called anytime, will just return if it has already been called. */
+       time_init();
+
+       /* Initial driver configuration : sleep_en, v20v_en, dc2_en, external clock */
+       ncn5120_set_analog_config(0, 0, 0, 0);
+       /* Communication config : auto_poll, crc_citt, use_frame_end_indicator
+        * Note that crc_citt and use_frame_end_indicator modes are not supported. */
+       ncn5120_set_comm_config(0, 0, 0);
+}
+
+/* Se callback on SAVEB and RESETB signal for emergency data saving.
+ * Set a callback on SAVEB signal (goes low on KNX system errors)
+ *   Possible errors : Temp warning (TW), Thermal shutdown warning (TSD)
+ *   Read the system status to get the warning source. refer to System status
+ *     service description (page 33 in ncn5120 documentation)
+ * Set a callback on RESETB signal (goes low when watchdog is not reset)
+ */
+void ncn5120_register_handlers(const struct pio* save, void (*save_callback)(uint32_t),
+                                                          const struct pio* reset, void (*reset_callback)(uint32_t))
+{   
+       /* Save pin */
+       config_pio(reset, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL));
+       pio_copy(&ncn5120.save_pin, save);
+       gpio_dir_in(ncn5120.save_pin);
+       set_gpio_callback(save_event, &ncn5120.save_pin, EDGES_BOTH);
+       ncn5120.save_callback = save_callback;
+
+       /* Reset pin */
+       config_pio(reset, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL));
+       pio_copy(&ncn5120.reset_pin, reset);
+       gpio_dir_in(ncn5120.reset_pin);
+       set_gpio_callback(reset_event, &ncn5120.reset_pin, EDGES_BOTH);
+       ncn5120.reset_callback = reset_callback;
+}
+
+
+/***************************************************************************** */
+/* Watchdog configuration */
+/* Enable and configure NCN5120 watchdog.
+ * By default the watchdog is not active upon NCN5120 reset.
+ * delay is a value in mili-seconds, with minimal value of 33ms and 33ms steps. If delay
+ *   is 0 then the watchdog is turned OFF.
+ * Refer to watchdog register description (page 50 in ncn5120 documentation).
+ * Do not forget to call this function again before then of the watchdog timer. You can
+ *   register a callback on RESETB signal (goes low when watchdog is not reset).
+ */
+void ncn5120_watchdog_config(uint32_t delay)
+{
+       uint8_t reg_val = 0;
+       if (delay == 0) {
+               reg_val = NCN5120_WDT_DISABLE;
+       } else {
+               if (delay > NCN5120_WDT_DELAY_MAX_VALUE) {
+                       delay = NCN5120_WDT_DELAY_MAX_VALUE;
+               }
+               /* Set WDEN and configure Watchdog timing WDT[3:0] (tWDTO) */
+               reg_val = NCN5120_WDT_ENABLE;
+               reg_val |= ((delay >> NCN5120_WDT_DELAY_CONV_SHIFT) & NCN5120_WDT_DELAY_MASK);
+       }
+       ncn5120_set_internal_register(NCN5120_WDT_REG_ADDR, reg_val);
+}
+
+
+/***************************************************************************** */
+/* Change the NCN5120 physical address and activate the auto-acknowledge function
+ * NCN5120 starts accepting all frames whose destination address corresponds to the stored
+ *   physical address or whose destination address is the group address by sending IACK on
+ *   the bus. In case of an error detected during such frame reception, NCN5120 sends NACK
+ *   instead of IACK.
+ * The documentation says that the Set Address Service can be issued any time and that the
+ *   new physical address and the autoacknowledge function will only get active after the
+ *   KNX bus becomes idle. Anyway, it does not provide information on when the configuration
+ *   byte will be sent, thus we do not allow the set address operation while receiving data.
+ *
+ * Returns -1 when receiving data and address did not get changed.
+ *
+ * Autoacknowledge can only be deactivated by a Reset Service (call to ncn5120_reset()). 
+ */
+int ncn5120_set_address(uint16_t address)
+{
+       uint8_t buf[4] = { U_SetAddress_Request, 0, 0, 0 };
+       uint8_t reply = 0;
+       /* Do not change address if NCN5120 is currently receiving data */
+       if (ncn5120.rx_data_running) {
+               return - 1;
+       }
+       buf[1] = ((address >> 8) & 0xFF);
+       buf[2] = (address & 0xFF);
+       /* Send the new address (three data bytes, the last one is ignored) */
+       ncn5120_send_service_request(buf, 4, 1);
+       /* And read the configure byte in reply */
+       ncn5120_get_req_reply_data(&reply, 1);
+       ncn5120_parse_conf_reply(&(ncn5120.comm_config), reply);
+       ncn5120.address_set = 1;
+       return 0;
+}
+
+
+/* Set repetition service
+ * Specifies the maximum repetition count for transmitted frames when not acknowledged with IACK.
+ * Separate counters can be set for NACK and BUSY frames.
+ * Initial value of both counters is 3.
+ * If the acknowledge from remote Data Link Layer is BUSY during frame transmission, NCN5120
+ *    tries to repeat after at least 150 bit times KNX bus idle.
+ * The BUSY counter determines the maximum amount of times the frame is repeated.
+ * If the BUSY acknowledge is still received after the last try, an L_Data.con with a negative
+ *    conformation is sent back to the host controller.
+ * For all other cases (NACK acknowledgment received, invalid/corrupted acknowledge received or
+ *    time−out after 30 bit times) NCN5120 will repeat after 50 bit times of KNX bus idle.
+ * The NACK counter determines the maximum retries.
+ * L_Data.con with a negative confirmation is send back to the host controller when the maximum
+ *    retries were reached. In worst case, the same request is transmitted (NACK + BUSY + 1) times
+ *    before NCN5120 stops retransmission.
+ */
+void ncn5120_set_number_of_retries(uint8_t busy_retries, uint8_t nack_retries)
+{
+       uint8_t buf[4] = { U_SetRepetition_Request, 0, 0, 0 };
+       buf[1] = (((busy_retries & 0x07) << 4) | (nack_retries & 0x07));
+       /* Send the new retry count */
+       ncn5120_send_service_request(buf, 4, 0);
+}
+
+
+
+/***************************************************************************** */
+/* Get analog status
+ * The operation_mode returned will be the latest known operation mode. It will not be updated
+ *    during this call.
+ */
+int ncn5120_get_analog_status(struct ncn5120_status* status)
+{
+       uint8_t reg_val = 0;
+       /* Erase buffer, so that returned status is consistent */
+       memset(status, 0, sizeof(struct ncn5120_status));
+       /* Do not request status if NCN5120 is currently receiving data */
+       if (ncn5120.rx_data_running) {
+               return -1;
+       }
+       /* Get internal status byte */
+       reg_val = ncn5120_get_internal_register(NCN5120_STATUS_REG_ADDR);
+       /* Update struct according to received byte */
+       status->out_of_sleep = (reg_val & NCN5120_STATUS_OUT_OF_SLEEP);
+       status->v20v_ok = (reg_val & NCN5120_STATUS_V20V_OK);
+       status->vdd2_ok = (reg_val & NCN5120_STATUS_VDD2_OK);
+       status->vbus_ok = (reg_val & NCN5120_STATUS_VBUS_OK);
+       status->vfilt_ok = (reg_val & NCN5120_STATUS_VFILT_OK);
+       status->xtal_running = (reg_val & NCN5120_STATUS_XTAL_OK);
+       status->thermal_warning = (reg_val & NCN5120_STATUS_THERMAL_WARN);
+       update_thermal_state(status->thermal_warning);
+       status->out_of_thermal_shutdown = (reg_val & NCN5120_STATUS_OUT_OF_TSD);
+       status->operation_mode = ncn5120.current_op_mode;
+       return 0;
+}
+
+
+/* Get commuinication status as returned by U_State.req service */
+int ncn5120_get_comm_status(struct ncn5120_comm_status* status)
+{
+       uint8_t request = U_State_Request;
+       uint8_t reply = 0;
+       /* Send communication status request */
+       ncn5120_send_service_request(&request, 1, 1);
+       /* And wait for our data to come */
+       ncn5120_get_req_reply_data(&reply, 1);
+       if ((reply & U_State_Reply) != U_State_Reply) {
+               return -1;
+       }
+       status->slave_colision = (reply & NCN5120_COMM_STATUS_SLAVE_COLISION);
+       status->host_rx_error = (reply & NCN5120_COMM_STATUS_HOST_RX_ERROR);
+       status->knx_tx_error = (reply & NCN5120_COMM_STATUS_KNX_TX_ERROR);
+       status->protocol_error = (reply & NCN5120_COMM_STATUS_PROTOCOL_ERROR);
+       status->thermal_warning = (reply & NCN5120_COMM_STATUS_THERMAL_WARNING);
+       update_thermal_state(status->thermal_warning);
+       return 0;
+}
+
+/* Get system status.
+ * The 'out_of_sleep' and 'out_of_thermal_shutdown' field have no meaning for this request
+ *    and will be '0' regardless of the real state.
+ */
+int ncn5120_get_system_status(struct ncn5120_status* status)
+{
+       uint8_t request = U_SystemStat_Request;
+       uint8_t reply[2], reg_val = 0;
+       /* Erase buffer, so that returned status is consistent */
+       memset(status, 0, sizeof(struct ncn5120_status));
+       /* Do not request system status if NCN5120 is currently receiving data */
+       if (ncn5120.rx_data_running) {
+               return -1;
+       }
+       /* Send system status request */
+       ncn5120_send_service_request(&request, 1, 2);
+       /* And wait for our data to come */
+       ncn5120_get_req_reply_data(reply, 2);
+       if (reply[0] != U_SystemStat_Reply) {
+               return -2;
+       }
+       /* Decode operation mode */
+       switch (reply[1] & NCN5120_OP_MODE_MASK) {
+               case NCN5120_OP_MODE_POWER_UP:
+                       status->operation_mode = NCN5120_STATE_POWERUP;
+                       break;
+               case NCN5120_OP_MODE_SYNC:
+                       status->operation_mode = NCN5120_STATE_SYNC;
+                       break;
+               case NCN5120_OP_MODE_STOP:
+                       status->operation_mode = NCN5120_STATE_STOP;
+                       break;
+               case NCN5120_OP_MODE_NORMAL:
+                       status->operation_mode = NCN5120_STATE_NORMAL;
+                       break;
+       }
+       ncn5120.current_op_mode = status->operation_mode;
+       /* The bit field is left shifted by compared to internal register status bitfield ... shift back */
+       reg_val = (reply[1] >> 1);
+       status->v20v_ok = (reg_val & NCN5120_STATUS_V20V_OK);
+       status->vdd2_ok = (reg_val & NCN5120_STATUS_VDD2_OK);
+       status->vbus_ok = (reg_val & NCN5120_STATUS_VBUS_OK);
+       status->vfilt_ok = (reg_val & NCN5120_STATUS_VFILT_OK);
+       status->xtal_running = (reg_val & NCN5120_STATUS_XTAL_OK);
+       status->thermal_warning = (reg_val & NCN5120_STATUS_THERMAL_WARN);
+       update_thermal_state(status->thermal_warning);
+       return 0;
+}
+
+
+/***************************************************************************** */
+/* Request NCN5120 reset
+ * Return 0 on success, -1 on error
+ */
+int ncn5120_reset(void)
+{
+       uint8_t request = U_Reset_Request;
+       uint8_t reply = 0;
+
+       /* Empty buffer before device request. Any data in it is not relevant anymore
+        * Drop all previously received command data */
+       ncn5120_drop_cmd_data();
+
+       ncn5120_send_service_request(&request, 1, 1);
+       /* Update current state and clear rx_data_running flag */
+       ncn5120.current_state = NCN5120_STATE_RESET;
+       ncn5120.rx_data_running = 0;
+       /* And wait for our data to come, which will mean that the device reset is done. */
+       ncn5120_get_req_reply_data(&reply, 1);
+       if (reply != U_Reset_Reply) {
+               return -1;
+       }
+       ncn5120.current_state = NCN5120_STATE_NORMAL;
+       return 0;
+}
+
+/* Enter sleep mode if it has been enabled by config in Analog Control register 0 (call to ncn5120_set_config()
+ *    with sleep_en = 1).
+ * Warning: when entering sleep mode, only a Wake-Up Event on the FANIN/WAKE-pin can get the
+ *    NCN5120 out of sleep. Make sure your hardware alows the generation of this event.
+ */
+void ncn5120_enter_sleep(void)
+{
+       ncn5120_set_internal_register(NCN5120_ACR_1_ADDR, NCN5120_ENTER_SLEEP);
+       ncn5120.current_state = NCN5120_STATE_SLEEP;
+}
+
+/* Set (enter or quit) busy mode.
+ * Enter busy mode/state if 'state' is not 0.
+ * In Busy mode and when autoacknowledge is active the NCN5120 rejects the frames whose
+ *   destination address corresponds to the stored physical address by sending the BUSY
+ *   acknowledge.
+ * This service has no effect if autoacknowledge is not active.
+ * Refer to p30 of NCN5120 documentation for busy state an p31 for autoacknowledge and set
+ *   address service
+ */
+void ncn5120_set_busy_state(uint8_t state)
+{
+       uint8_t request = 0;
+
+       if (state != 0) {
+               request = U_SetBusy_Request;
+               ncn5120.current_state = NCN5120_STATE_BUSY;
+       } else {
+               request = U_QuitBusy_Request;
+               ncn5120.current_state = NCN5120_STATE_NORMAL;
+       }
+       ncn5120_send_service_request(&request, 1, 0);
+}
+
+/* Set (enter or quit) stop mode.
+ * Enter stop mode/state if 'state' is not 0.
+ */
+void ncn5120_set_stop_state(uint8_t state)
+{
+       uint8_t request = 0;
+       uint8_t reply = 0;
+       
+       if (state != 0) {
+               request = U_StopMode_Request;
+       } else {
+               request = U_ExitStopMode_Request;
+       }
+       ncn5120_send_service_request(&request, 1, 1);
+       /* And wait for confirmation. */
+       ncn5120_get_req_reply_data(&reply, 1);
+       if (state != 0) {
+               if (reply == U_StopMode_Reply) {
+                       ncn5120.current_state = NCN5120_STATE_STOP;
+               }
+       } else {
+               if (reply == U_Reset_Reply) {
+                       ncn5120.current_state = NCN5120_STATE_NORMAL;
+               }
+       }
+       /* Seems that there cannot be other cases, only infinite loops waiting for an idle KNX Bus */
+}
+
+
+/* Enter Bus monitor mode
+ * In this mode all data received from the KNX bus is sent to the host controller without
+ *    performing any filtering on Data Link Layer. Acknowledge Frames are also transmitted
+ *    transparently.
+ * This state can only be exited by the Reset Service (ncn5120_reset()).
+ */
+void ncn5120_enter_monitor_mode(void)
+{
+       uint8_t request = U_Busmon_Request;
+       ncn5120_send_service_request(&request, 1, 0);
+       ncn5120.current_state = NCN5120_STATE_MONITOR;
+}
+
+
+
+/***************************************************************************** */
+/* Receive packet helper
+ * When an address got set using ncn5120_set_address() then the auto-ACK function is activated.
+ * When it is not the case, the host is responsible for sending ACK, NACK or Busy on the Bus when
+ *   it receives a packet
+ */
+void ncn5120_packet_acknowledge(uint8_t nack, uint8_t busy, uint8_t addressed)
+{
+       uint8_t request = U_Ackn_Request;
+
+       if (ncn5120.address_set == 1) {
+               return;
+       }
+
+       if (nack) {
+               request |= NCN5120_ACK_NACK;
+       }
+       if (busy) {
+               request |= NCN5120_ACK_BUSY;
+       }
+       if (addressed) {
+               request |= NCN5120_ACK_ADDRESSED;
+       }
+       ncn5120_send_service_request(&request, 1, 0);
+}
+
+
+/***************************************************************************** */
+/* Send packet
+ * When using a packet oriented communication with packet size and address included
+ *   in the packet, these must be included in the packet by the software before
+ *   calling this function.
+ * The request_builder buffer is used to build parts of the request by sending the control
+ *   and data bytes in the right order.
+ *   There is no need to send them all at once so a small buffer is OK and saves memory. A
+ *   three bytes buffer is OK for one data byte and the possible two index bytes sent at
+ *   once.
+ * The reply length depends on the communication configuration. It is at least the data
+ *   size plus two control bytes.
+ *
+ * Return values:
+ * This function returns a negativ value on errors:
+ *    -1 when size is above KNX_MAX_FRAME_SIZE
+ *    -2 when data has not been sent because the bus is busy receiving data.
+ *    -3 when there has been an error while transmitting data to the NCN5120 (or checksum error)
+ *    -4 if the NCN5120 indicates Rx errors (while we were sending, what would they mean ?)
+ *    -5 if the data does not end with L_Data confirmation byte.
+ * The function returns 1 when the packet got sent and received a positive ACK, or 0 when it
+ *   received a negative ACK.
+ */
+#define REQUEST_BUILDER_SIZE  3
+int ncn5120_send_packet(uint8_t control_byte, uint8_t* buffer, uint16_t size)
+{
+       uint8_t request_builder[REQUEST_BUILDER_SIZE];
+       uint16_t data_idx = 0;
+       uint8_t reply[2]; /* In our config there are two bytes after the end of frame silence. */
+       uint8_t checksum = 0;
+
+       if (size > KNX_MAX_FRAME_SIZE) {
+               return -1;
+       }
+       if (ncn5120.rx_data_running) {
+               return -2;
+       }
+
+       /* Build the request */
+       /* initial control part */
+       request_builder[0] = U_L_DataStart_Request;
+       request_builder[1] = control_byte;
+       ncn5120_send_service_request(request_builder, 2, 0);
+
+       /* Data part */
+       while (data_idx < size) {
+               if (!(data_idx & 0x3F)) {
+                       request_builder[0] = U_L_DataOffset_Request | ((data_idx >> 6) & 0x07);
+                       request_builder[1] = U_L_DataCont_Request | (data_idx & 0x3F);
+                       request_builder[2] = buffer[data_idx];
+                       ncn5120_send_service_request(request_builder, 3, 0);
+               } else {
+                       request_builder[0] = U_L_DataCont_Request | (data_idx & 0x3F);
+                       request_builder[1] = buffer[data_idx];
+                       ncn5120_send_service_request(request_builder, 2, 0);
+               }
+               /* Compute checksum */
+               /* FIXME : check KNX documentation */
+               checksum += buffer[data_idx++];
+       }
+
+       /* End marker and checksum */
+       request_builder[0] = U_L_DataEnd_Request | (size & 0x3F); /* Low six bits of last byte index */
+       request_builder[1] = checksum;
+       /* Send end marker and data checksum and set the right reply length.
+        * We always have these four : one for L_Data indicator (start), one for checksum, one for 
+        *   U_FrameState indicator (we are in 8bits UART mode), and one for L_Data confirmation */
+       ncn5120_send_service_request(request_builder, 2, (size + 4));
+
+
+       /* And wait for the echo and data end indicator and status information */
+       ncn5120_get_req_reply_data(reply, 1);
+       /* Is it L_Data indicator (OK) or U_State indicator (error) */
+       if ((reply[0] & U_State_Reply) == U_State_Reply) {
+               /* Error case */
+               ncn5120_drop_cmd_data();
+               /* FIXME : handle errors */
+               return -3;
+       }
+               
+       /* Read all echo data to get rid of it */
+       /* Note : we should no use ncn5120. structure here, but this trick will have the memcpy return
+        *  immediatly as destination and source are the same. Add one for the checksum, no need to
+        *  check again.*/
+       ncn5120_get_req_reply_data((uint8_t*)ncn5120.rx_cmd_buff, (size + 1));
+
+       /* Read the remaining bytes (-2 for L_Data indicator and checksum) */
+       ncn5120_get_req_reply_data(reply, 2);
+       /* And decode */
+       /* First byte is the state. We are sender, there are no receive error ... (should not be) */
+       if (reply[0] != U_FrameState_Reply) {
+               uprintf(0, "Tx but state indicates Rx errors .... (0x%02x)\n", reply[0]);
+               return -4;
+       }
+       /* And the next one is the interesting one : Positive or negative ACK */
+       if ((reply[1] & 0x7F) != L_Data) {
+               uprintf(0, "Data Tx feedback does not end with L_Data confirmation byte ! (0x%02x)\n", reply[1]);
+               return -5;
+       }
+       /* Did we get a positive or negative ACK ? */
+       if (reply[1] & 0x80) {
+               return 1; /* Positive ACK */
+       }
+       return 0; /* Negative ACK */
+}
+
+
diff --git a/include/extdrv/ncn5120.h b/include/extdrv/ncn5120.h
new file mode 100644 (file)
index 0000000..cf08e7e
--- /dev/null
@@ -0,0 +1,408 @@
+/****************************************************************************
+ *  extdrv/ncn5120.h
+ *
+ * Copyright 2014 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+#ifndef EXTDRV_NCN5120_H
+#define EXTDRV_NCN5120_H
+
+#include <stdint.h>
+
+struct ncn5120_status {
+       uint8_t operation_mode;
+       uint8_t out_of_sleep;
+       uint8_t v20v_ok;
+       uint8_t vdd2_ok;
+       uint8_t vbus_ok;
+       uint8_t vfilt_ok;
+       uint8_t xtal_running;
+       uint8_t thermal_warning;
+       uint8_t out_of_thermal_shutdown;
+};
+
+struct ncn5120_comm_status {
+       uint8_t slave_colision;
+       uint8_t host_rx_error;
+       uint8_t knx_tx_error;
+       uint8_t protocol_error;
+       uint8_t thermal_warning;
+};
+
+struct ncn5120_conf {
+       uint8_t busy;
+       uint8_t auto_acknowledge;
+       uint8_t auto_polling;
+       uint8_t crc_citt;
+       uint8_t frame_end_marker;
+};
+
+enum ncn5120_states {
+       NCN5120_STATE_NORMAL = 1,
+       NCN5120_STATE_STANDBY,
+       NCN5120_STATE_STARTUP,
+       NCN5120_STATE_POWERUP,
+       NCN5120_STATE_POWERUP_STOP,
+       NCN5120_STATE_SYNC,
+       NCN5120_STATE_STOP,
+       NCN5120_STATE_BUSY,
+       NCN5120_STATE_THERMAL_WARNING,
+       NCN5120_STATE_RESET,
+       NCN5120_STATE_MONITOR,
+       NCN5120_STATE_SLEEP,
+       NCN5120_STATE_THERMAL_SHUTDOWN,
+};
+
+/***************************************************************************** */
+/*                NCN5120                                                      */
+/***************************************************************************** */
+struct ncn5120_device {
+       uint8_t serial_num;
+       /* Last known state for these : */
+       uint8_t current_state;
+       uint8_t current_op_mode;
+       uint8_t thermal_state;
+       /* Pin used for reset and save signals, and associated calbacks */
+       struct pio reset_pin; /* RESET pin, used to know the state of the KNX chip */
+       struct pio save_pin; /* SAVE pin, signal from KNX chip used as warning for power loss. */
+       void (*save_callback)(uint32_t);
+       void (*reset_callback)(uint32_t);
+
+       /* Config */
+       struct ncn5120_conf comm_config;
+       int address_set;
+
+       /* RX */
+       /* Commands */
+       volatile uint8_t rx_cmd_buff[280];
+       volatile uint16_t rx_cmd_buff_idx;
+       volatile uint16_t next_data_is_cmd_reply;
+       /* Data */
+       volatile uint8_t rx_data_running;
+       void (*rx_callback)(uint32_t);
+};
+
+/* This value cannot be received on the KNX bus. When passed to the rx_callback it indicates that
+ *   we received the end of a data packet.
+ */
+#define END_OF_DATA_PACKET  (0x100)
+
+/***************************************************************************** */
+/* List of services (Commands and data sent from host to NCN5120) */
+enum ncn5120_services {
+       /* Internal commands, device specific */
+       U_Reset_Request = 0x01,    /* U_Reset.req - No data */
+       U_State_Request = 0x02,    /* U_State.req - No data */
+       U_SetBusy_Request = 0x03,  /* U_SetBusy.req - No data */
+       U_QuitBusy_Request = 0x04, /* U_QuitBusy.req - No data */
+       U_Busmon_Request = 0x05,   /* U_Busmon.req - No data */
+       U_SetAddress_Request = 0xF1,    /* U_SetAddress.req - 3 data bytes : AddrHigh, AddrLow, DontCare  */
+       U_SetRepetition_Request = 0xF2, /* U_SetRepetition.req - 3 data bytes : Rep_Count, DontCare, DontCare */
+       U_L_DataOffset_Request = 0x08,  /* U_L_DataOffset.req - No data - Ored with MSB byte index in three lower bits */
+       U_SystemStat_Request = 0x0D,    /* U_SystemStat.req - No data */
+       U_StopMode_Request = 0x0E,      /* U_StopMode.req - No data */
+       U_ExitStopMode_Request = 0x0F,  /* U_ExitStopMode.req - No data */
+       U_Ackn_Request = 0x10,      /* U_Ackn.req - No data - Ored with 'nack' (0x04), 'busy' (0x02) and 'addressed' (0x01) */
+       U_Configure_Request = 0x18, /* U_Configure.req - No data - Ored with 'auto-polling' (0x04), 'CRC-CITT' (0x02) and 'marker' (0x01) */
+       U_IntRegWr_Request = 0x28,  /* U_IntRegWr.req - 1 data byte : data to write - Ored with two bits of internal reg addr */
+       U_IntRegRd_Request = 0x38,  /* U_IntRegRd.req - No data - Ored with two bits of internal reg addr */
+       U_PollingState_Request = 0xE0, /* U_PollingState.req - 3 data bytes : PollAddrHigh, PollAddrLow, PollState - Ored with 4 bits of slot number */
+       /* KNX transmit data commands */
+       U_L_DataStart_Request = 0x80,  /* U_L_DataStart.req - 1 data byte : Control byte */
+       U_L_DataCont_Request = 0x80,  /* U_L_DataCont.req - Ored with 6 bits of index - 1 data byte : data */
+       U_L_DataEnd_Request = 0x40,  /* U_L_DataEnd.req - Ored with 6 bits of index - 1 data byte : check byte */
+};
+/* List of replies (Replies to commands) */
+enum ncn5120_replies {
+       /* DLL (layer 2) services : device is transparent */
+       L_Data_Standard = 0x90, /* FIXME : 3 indicator bits possible */
+       L_Data_Extended = 0x10, /* FIXME : 3 indicator bits possible */
+       L_Poll_Data = 0xF0,
+       /* Acknowledge services (device is transparent in Bus monitor mode) */
+       L_Ackn = 0x00, /* FIXME : 4 possible bit set */
+       L_Data = 0x0B, /* FIXME : 0x8B is positive confirmation, 0x0B is negative confirmation */
+       /* Control services, device specific */
+       U_Reset_Reply = 0x03,
+       U_State_Reply = 0x07, /* FIXME : upper 5 bits are state indicators */
+       U_FrameState_Reply = 0x13, /* FIXME : 4 indicator bits possible */
+       U_Configure_Reply = 0x01, /* FIXME : 5 indicator bits possible */
+       U_Configure_Reply_Mask = 0x83, /* These are the constant bits */
+       U_FrameEnd_Reply = 0xCB,
+       U_StopMode_Reply = 0x2B,
+       U_SystemStat_Reply = 0x4B, /* 1 data byte with 6 flags and 2 bits for mode */
+};
+
+
+
+/***************************************************************************** */
+/* Services bits definitions */
+
+
+/* Communication status bits */
+#define NCN5120_COMM_STATUS_SLAVE_COLISION  (0x01 << 7) /* Collision during transmission of polling state */
+#define NCN5120_COMM_STATUS_HOST_RX_ERROR   (0x01 << 6) /* Corrupted bytes sent by host controller (us) */
+#define NCN5120_COMM_STATUS_KNX_TX_ERROR    (0x01 << 5) /* Transceiver error detected during frame transmission */
+#define NCN5120_COMM_STATUS_PROTOCOL_ERROR  (0x01 << 4) /* incorrect sequence of commands sent by host */
+#define NCN5120_COMM_STATUS_THERMAL_WARNING (0x01 << 3) /* Thermal warning condition detected */
+
+/* Busy state */
+#define NCN5120_ENTER_BUSY_STATE  1
+#define NCN5120_QUIT_BUSY_STATE   0
+
+/* Configure service status bits */
+#define NCN5120_BUSY             (0x01 << 6)
+#define NCN5120_AUTO_ACK         (0x01 << 5)
+#define NCN5120_AUTO_POLLING     (0x01 << 4)
+#define NCN5120_CRC_CITT_ACTIVE  (0x01 << 3)
+#define NCN5120_FRAME_END_MARK_ACTIVE  (0x01 << 2)
+/* Configure function request bits */
+#define NCN5120_ACTIVATE_END_FRAME_MARKER  (0x01 << 0)
+#define NCN5120_ACTIVATE_CRC_CITT     (0x01 << 1)
+#define NCN5120_ACTIVATE_AUTO_POLL    (0x01 << 2)
+
+/* System State service */
+#define NCN5120_OP_MODE_MASK      (0x03)
+#define NCN5120_OP_MODE_POWER_UP  0x00
+#define NCN5120_OP_MODE_SYNC      0x01
+#define NCN5120_OP_MODE_STOP      0x02
+#define NCN5120_OP_MODE_NORMAL    0x03
+
+/* ACK request bits */
+#define NCN5120_ACK_NACK        (0x01 << 2)
+#define NCN5120_ACK_BUSY        (0x01 << 1)
+#define NCN5120_ACK_ADDRESSED   (0x01 << 0)
+
+
+/***************************************************************************** */
+/* Internal registers */
+#define NCN5120_INT_REG_ADDR_SHIFT
+#define NCN5120_INT_REG_ADDR_MASK
+
+/* Watchdog register */
+#define NCN5120_WDT_REG_ADDR  0x00
+#define NCN5120_WDT_WDEN_SHIFT        7
+#define NCN5120_WDT_ENABLE            (0x01 << NCN5120_WDT_WDEN_SHIFT)
+#define NCN5120_WDT_DISABLE           0x00
+#define NCN5120_WDT_DELAY_MASK        0x0F
+#define NCN5120_WDT_DELAY_CONV_SHIFT  6
+#define NCN5120_WDT_DELAY_MAX_VALUE   524
+
+/* Analog Control register 0 */
+#define NCN5120_ACR_0_ADDR    0x01
+/* Xclock */
+#define NCN5120_XCLOCK_SHIFT     3
+#define NCN5120_XCLOCK_MASK      0x03
+#define NCN5120_XCLOCK_DISABLE   0x00
+#define NCN5120_XCLOCK_8MHZ      0x02
+#define NCN5120_XCLOCK_16MHZ     0x03
+/* Sleep */
+#define NCN5120_SLEEP_ENABLE     0x80
+#define NCN5120_SLEEP_DISABLE    0x00
+/* V20V regulator */
+#define NCN5120_V20V_ENABLE      0x40
+/* DC2 converter */
+#define NCN5120_DC2_ENABLE       0x20
+
+/* Analog Control register 1 */
+#define NCN5120_ACR_1_ADDR    0x02
+#define NCN5120_ENTER_SLEEP      0x80
+
+/* Analog Status register */
+#define NCN5120_STATUS_REG_ADDR  0x03
+#define NCN5120_STATUS_OUT_OF_SLEEP  (0x01 << 7)
+#define NCN5120_STATUS_V20V_OK       (0x01 << 6)
+#define NCN5120_STATUS_VDD2_OK       (0x01 << 5)
+#define NCN5120_STATUS_VBUS_OK       (0x01 << 4)
+#define NCN5120_STATUS_VFILT_OK      (0x01 << 3)
+#define NCN5120_STATUS_XTAL_OK       (0x01 << 2)
+#define NCN5120_STATUS_THERMAL_WARN  (0x01 << 1)
+#define NCN5120_STATUS_OUT_OF_TSD    (0x01 << 0)
+
+
+
+/***************************************************************************** */
+/* NCN5120 Configuration and initialisation
+ * rx_callback must not be NULL if you want to receive data. It will be called for
+ *   any received character which is not part of a command reply.
+ * If you did not set the device's address using ncn5120_set_address() then you are
+ *   responsible of sending the ACK, NACK or BUSY using ncn5120_packet_acknowledge().
+ * The UART will be configured in 38400, 8n1 and an intermediate callback will be
+ *   used, so do not try to configure it yourself.
+ * Analog configuration will be setup according to Techno-Innov's KNX module's
+ *   requirements and communication configuration according to this module's
+ *   requirements.
+ */
+void ncn5120_init(uint8_t serial, void (*rx_callback)(uint32_t));
+
+/* Se callback on SAVEB and RESETB signal for emergency data saving.
+ * Set a callback on SAVEB signal (goes low on KNX system errors)
+ *   Possible errors : Temp warning (TW), Thermal shutdown warning (TSD)
+ *   Read the system status to get the warning source. refer to System status
+ *     service description (page 33 in ncn5120 documentation)
+ * Set a callback on RESETB signal (goes low when watchdog is not reset)
+ */
+void ncn5120_register_handlers(const struct pio* save, void (*save_callback)(uint32_t),
+                                                          const struct pio* reset, void (*reset_callback)(uint32_t));
+
+
+/***************************************************************************** */
+/* Watchdog configuration */
+/* Enable and configure NCN5120 watchdog.
+ * By default the watchdog is not active upon NCN5120 reset.
+ * delay is a value in mili-seconds, with minimal value of 33ms and 33ms steps. If delay
+ *   is 0 then the watchdog is turned OFF.
+ * Refer to watchdog register description (page 50 in ncn5120 documentation).
+ * Do not forget to call this function again before then of the watchdog timer. You can
+ *   register a callback on RESETB signal (goes low when watchdog is not reset).
+ */
+void ncn5120_watchdog_config(uint32_t delay);
+
+
+/***************************************************************************** */
+/* Change the NCN5120 physical address and activate the auto-acknowledge function
+ * NCN5120 starts accepting all frames whose destination address corresponds to the stored
+ *   physical address or whose destination address is the group address by sending IACK on
+ *   the bus. In case of an error detected during such frame reception, NCN5120 sends NACK
+ *   instead of IACK.
+ * The documentation says that the Set Address Service can be issued any time and that the
+ *   new physical address and the autoacknowledge function will only get active after the
+ *   KNX bus becomes idle. Anyway, it does not provide information on when the configuration
+ *   byte will be sent, thus we do not allow the set address operation while receiving data.
+ *
+ * Returns -1 when receiving data and address did not get changed.
+ *
+ * Autoacknowledge can only be deactivated by a Reset Service (call to ncn5120_reset()). 
+ */
+int ncn5120_set_address(uint16_t address);
+
+
+/***************************************************************************** */
+/* Set repetition service
+ * Specifies the maximum repetition count for transmitted frames when not acknowledged with IACK.
+ * Separate counters can be set for NACK and BUSY frames.
+ * Initial value of both counters is 3.
+ * If the acknowledge from remote Data Link Layer is BUSY during frame transmission, NCN5120
+ *    tries to repeat after at least 150 bit times KNX bus idle.
+ * The BUSY counter determines the maximum amount of times the frame is repeated.
+ * If the BUSY acknowledge is still received after the last try, an L_Data.con with a negative
+ *    conformation is sent back to the host controller.
+ * For all other cases (NACK acknowledgment received, invalid/corrupted acknowledge received or
+ *    time−out after 30 bit times) NCN5120 will repeat after 50 bit times of KNX bus idle.
+ * The NACK counter determines the maximum retries.
+ * L_Data.con with a negative confirmation is send back to the host controller when the maximum
+ *    retries were reached. In worst case, the same request is transmitted (NACK + BUSY + 1) times
+ *    before NCN5120 stops retransmission.
+ */
+void ncn5120_set_number_of_retries(uint8_t busy_retries, uint8_t nack_retries);
+
+
+/***************************************************************************** */
+/* Get analog status
+ * The operation_mode returned will be the latest known operation mode. It will not be updated
+ *    during this call.
+ */
+int ncn5120_get_analog_status(struct ncn5120_status* status);
+
+
+/* Get commuinication status as returned by U_State.req service */
+int ncn5120_get_comm_status(struct ncn5120_comm_status* status);
+
+/* Get system status.
+ * The 'out_of_sleep' and 'out_of_thermal_shutdown' field have no meaning for this request
+ *    and will be '0' regardless of the real state.
+ */
+int ncn5120_get_system_status(struct ncn5120_status* status);
+
+
+/***************************************************************************** */
+/* Request NCN5120 reset
+ * Return 0 on success, -1 on error
+ */
+int ncn5120_reset(void);
+
+/* Enter sleep mode if it has been enabled by config in Analog Control register 0 (call to ncn5120_set_config()
+ *    with sleep_en = 1).
+ * Warning: when entering sleep mode, only a Wake-Up Event on the FANIN/WAKE-pin can get the
+ *    NCN5120 out of sleep. Make sure your hardware alows the generation of this event.
+ */
+void ncn5120_enter_sleep(void);
+
+/* Set (enter or quit) busy mode.
+ * Enter busy mode/state if 'state' is not 0.
+ * In Busy mode and when autoacknowledge is active the NCN5120 rejects the frames whose
+ *   destination address corresponds to the stored physical address by sending the BUSY
+ *   acknowledge.
+ * This service has no effect if autoacknowledge is not active.
+ * Refer to p30 of NCN5120 documentation for busy state an p31 for autoacknowledge and set
+ *   address service
+ */
+void ncn5120_set_busy_state(uint8_t state);
+
+/* Set (enter or quit) stop mode.
+ * Enter stop mode/state if 'state' is not 0.
+ */
+void ncn5120_set_stop_state(uint8_t state);
+
+
+/* Enter Bus monitor mode
+ * In this mode all data received from the KNX bus is sent to the host controller without
+ *    performing any filtering on Data Link Layer. Acknowledge Frames are also transmitted
+ *    transparently.
+ * This state can only be exited by the Reset Service (ncn5120_reset()).
+ */
+void ncn5120_enter_monitor_mode(void);
+
+
+
+/***************************************************************************** */
+/* Receive packet helper
+ * When an address got set using ncn5120_set_address() then the auto-ACK function is activated.
+ * When it is not the case, the host is responsible for sending ACK, NACK or Busy on the Bus when
+ *   it receives a packet
+ */
+void ncn5120_packet_acknowledge(uint8_t nack, uint8_t busy, uint8_t addressed);
+
+
+/***************************************************************************** */
+/* Send packet
+ * When using a packet oriented communication with packet size and address included
+ *   in the packet, these must be included in the packet by the software before
+ *   calling this function.
+ * The request_builder buffer is used to build parts of the request by sending the control
+ *   and data bytes in the right order.
+ *   There is no need to send them all at once so a small buffer is OK and saves memory. A
+ *   three bytes buffer is OK for one data byte and the possible two index bytes sent at
+ *   once.
+ * The reply length depends on the communication configuration. It is at least the data
+ *   size plus two control bytes.
+ *
+ * Return values:
+ * This function returns a negativ value on errors:
+ *    -1 when size is above KNX_MAX_FRAME_SIZE
+ *    -2 when data has not been sent because the bus is busy receiving data.
+ *    -3 when there has been an error while transmitting data to the NCN5120 (or checksum error)
+ *    -4 if the NCN5120 indicates Rx errors (while we were sending, what would they mean ?)
+ *    -5 if the data does not end with L_Data confirmation byte.
+ * The function returns 1 when the packet got sent and received a positive ACK, or 0 when it
+ *   received a negative ACK.
+ */
+int ncn5120_send_packet(uint8_t control_byte, uint8_t* buffer, uint16_t size);
+
+
+#endif /* EXTDRV_NCN5120_H */
+
diff --git a/include/lib/protocols/knx.h b/include/lib/protocols/knx.h
new file mode 100644 (file)
index 0000000..7e07510
--- /dev/null
@@ -0,0 +1,2 @@
+#define KNX_MAX_FRAME_SIZE  263
+