├── License ├── Readme.md ├── arch ├── pc │ ├── arch_wrapper.c │ └── arch_wrapper.h └── stm32 │ ├── arch_wrapper.c │ └── arch_wrapper.h ├── core ├── cdnet_core.c └── cdnet_core.h ├── dev ├── cdbus.h ├── cdbus_uart.c ├── cdbus_uart.h ├── cdctl.c ├── cdctl.h ├── cdctl_it.c ├── cdctl_it.h ├── cdctl_pll_cal.c ├── cdctl_pll_cal.h └── cdctl_regs.h ├── parser ├── cdnet.c ├── cdnet.h ├── cdnet_l0.c └── cdnet_l1.c └── utils ├── cd_debug.h ├── cd_list.c ├── cd_list.h ├── cd_utils.h ├── hex_dump.c ├── modbus_crc.c └── modbus_crc.h /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 DUKELEC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | CDNET: An Optional High-Layer Protocol for CDBUS 2 | ======================================= 3 | 4 | Protocol status: Stable [Version 2.0] 5 | 6 | A CDBUS frame carrying a CDNET packet is structured as follows: 7 | `[src, dst, len] + [CDNET package] + [crc_l, crc_h]` 8 | or equivalently: 9 | `[src, dst, len] + [CDNET header, payload] + [crc_l, crc_h]` 10 | 11 | 12 | ## CDNET Levels 13 | 14 | CDNET defines two protocol levels, determined by bit 7 of the first header byte: 15 | 16 | | Bit7 | DESCRIPTION | 17 | |------|---------------------------------------------------------------| 18 | | 0 | Level 0: Simplified communication | 19 | | 1 | Level 1: Standard communication | 20 | 21 | Applications may support either or both levels, depending on requirements. 22 | 23 | CDNET uses little-endian byte order. 24 | 25 | 26 | ## Level 0 Format 27 | 28 | The header is always 2 bytes. 29 | 30 | First byte: 31 | 32 | | FIELD | DESCRIPTION | 33 | |-------- |---------------------------------------------------| 34 | | [7] | Always 0 (indicates level 0) | 35 | | [6:0] | src_port, range 0 ~ 127 | 36 | 37 | Second byte: 38 | 39 | | FIELD | DESCRIPTION | 40 | |-------- |---------------------------------------------------| 41 | | [7] | _Reserved as 0_ | 42 | | [6:0] | dst_port, range 0 ~ 127 | 43 | 44 | 45 | Note: The port number is similar to the UDP port number. 46 | 47 | 48 | ## Level 1 Format 49 | The first byte of the header: 50 | 51 | | FIELD | DESCRIPTION | 52 | |-------- |---------------------------------| 53 | | [7] | Always 1 (indicates level 1) | 54 | | [6] | _Reserved as 0_ | 55 | | [5] | MULTI_NET | 56 | | [4] | MULTICAST | 57 | | [3:2] | _Reserved as 0_ | 58 | | [1] | SRC_PORT_SIZE | 59 | | [0] | DST_PORT_SIZE | 60 | 61 | ### MULTI_NET & MULTICAST 62 | 63 | | MULTI_NET | MULTICAST | DESCRIPTION | 64 | |-----------|-----------|---------------------------------------------------------------------------| 65 | | 0 | 0 | Local net: append 0 byte | 66 | | 0 | 1 | Local net multicast: append 2 bytes `[mh, ml]` | 67 | | 1 | 0 | Cross net: append 4 bytes: `[src_net, src_mac, dst_net, dst_mac]` | 68 | | 1 | 1 | Cross net multicast: append 4 bytes: `[src_net, src_mac, mh, ml]` | 69 | 70 | Notes: 71 | - mh/ml: multicast_id, ml is mapped to mac layer multicast address (h: high byte, l: low byte); 72 | - Could simply use MULTI_NET = 0 and MULTICAST = 0 for local net multicast and broadcast. 73 | - Implementations of MULTI_NET and MULTICAST are optional. 74 | 75 | ### PORT_SIZE: 76 | 77 | | Bit1 | SRC_PORT | 78 | |------|---------------| 79 | | 0 | 1-byte | 80 | | 1 | 2-byte | 81 | 82 | | Bit0 | DST_PORT | 83 | |------|---------------| 84 | | 0 | 1-byte | 85 | | 1 | 2-byte | 86 | 87 | Notes: 88 | - Append bytes for `src_port` before `dst_port`. 89 | - Implementation of 2-byte port is optional. 90 | 91 | 92 | ## Port Allocation Recommendation 93 | 94 | Ports 0 to 15 are recommended for general-purpose use. 95 | Specifically, port 1 is designated for device information queries. 96 | While the port assignments mentioned in this section are optional, it is recommended to implement the basic function of port 1 (`read_device_info`). 97 | 98 | Port numbers ≥ 64 are typically used as ephemeral ports. 99 | 100 | 101 | ### Port 1 102 | 103 | Provide device info. 104 | ``` 105 | Types: 106 | - mac_start and mac_end: uint8_t 107 | - max_time: uint16_t (unit: ms) 108 | - "string": variable-length string, including empty string 109 | (excluding the terminating null byte '\0') 110 | 111 | 112 | Read device_info string: 113 | Write None 114 | Return ["device_info"] 115 | 116 | Search devices by filters (for resolving mac conflicts): 117 | Write [0x10, max_time, mac_start, mac_end, "string"] 118 | Return ["device_info"] after a random time in the range [0, max_time] 119 | only if "device_info" contains "string" (always true for an empty string) 120 | and the current mac address is in the range [mac_start, mac_end] (inclusive) 121 | Not return otherwise 122 | and reject any subsequent modify mac (or save config) commands 123 | ``` 124 | Example of `device_info`: 125 | `M: model; S: serial id; HW: hardware version; SW: software version` ... 126 | Field order is not important; it is recommended to include the model field at least. 127 | 128 | 129 | 130 | ## CDNET Address String Formats 131 | 132 | ``` 133 | localhost local link unique local multicast 134 | 10:00:00 135 | level0: 00:NN:MM 136 | level1: 80:NN:MM a0:NN:MM f0:MH:ML 137 | ``` 138 | 139 | Notes: 140 | - NN: net_id, MM: mac_addr, MH/ML: multicast_id (H: high byte, L: low byte). 141 | - The string address is analogous to IPv6 address. 142 | 143 | 144 | ## Examples 145 | 146 | Request device info: 147 | (Local network, requester's mac address: `0x0c`, target's mac address: `0x0d`) 148 | 149 | Reply the device info string: `"M: c1; S: 1234"`, 150 | expressed in hexadecimal: `[0x4d, 0x3a, 0x20, 0x63, 0x31, 0x3b, 0x20, 0x53, 0x3a, 0x20, 0x31, 0x32, 0x33, 0x34]`. 151 | 152 | The Level 0 Format: 153 | * Request: 154 | - CDNET socket: `[00:00:0c]:64` -> `[00:00:0d]:1`: `None` 155 | - CDNET packet: `[0x40, 0x01]` (`src_port`: `0x40`, `dst_port`: `0x01`) 156 | - CDBUS frame: `[0x0c, 0x0d, 0x02, 0x40, 0x01, crc_l, crc_h]` 157 | * Reply: 158 | - CDNET socket: `[00:00:0d]:1` -> `[00:00:0c]:64`: `"M: c1; S: 1234"` 159 | - CDNET packet: `[0x01, 0x40, 0x4d, 0x3a, 0x20 ... 0x34]` 160 | - CDBUS frame: `[0x0d, 0x0c, 0x10, 0x01, 0x40, 0x4d, 0x3a, 0x20 ... 0x34, crc_l, crc_h]` 161 | 162 | The Level 1 Format: 163 | * Request: 164 | - CDNET socket: `[80:00:0c]:64` -> `[80:00:0d]:1`: `None` 165 | - CDNET packet: `[0x80, 0x40, 0x01]` (`src_port`: `0x40`, `dst_port`: `0x01`) 166 | - CDBUS frame: `[0x0c, 0x0d, 0x03, 0x80, 0x40, 0x01, crc_l, crc_h]` 167 | * Reply: 168 | - CDNET socket: `[80:00:0d]:1` -> `[80:00:0c]:64`: `"M: c1; S: 1234"` 169 | - CDNET packet: `[0x80, 0x01, 0x40, 0x4d, 0x3a, 0x20 ... 0x34]` (`src_port`: `0x01`, `dst_port`: `0x40`) 170 | - CDBUS frame: `[0x0d, 0x0c, 0x11, 0x80, 0x01, 0x40, 0x4d, 0x3a, 0x20 ... 0x34, crc_l, crc_h]` 171 | 172 | 173 | ### Sequence & Flow Control 174 | 175 | For different commands, `SRC_PORT` can increment sequentially within a defined range. This allows the receiver to avoid re-executing commands during retransmissions and ensures correct matching between requests and responses. 176 | 177 | For large data transfers, a high bit in `SRC_PORT` can be used as a `not_reply` flag. When set, the receiver does not send a reply. 178 | The lower bits of `SRC_PORT` increment to indicate the sequence. 179 | 180 | We can begin by sending two groups of packets, each requiring a reply only for the last packet. Upon receiving a reply, the next group can be sent. Each group contains multiple packets. 181 | -------------------------------------------------------------------------------- /arch/pc/arch_wrapper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "cd_utils.h" 14 | #include "cd_list.h" 15 | #include "arch_wrapper.h" 16 | 17 | 18 | uint32_t get_systick(void) 19 | { 20 | struct timespec t; 21 | clock_gettime(CLOCK_MONOTONIC, &t); 22 | return t.tv_sec * 1000 + t.tv_nsec / 1000000; 23 | } 24 | 25 | void _dprintf(char* format, ...) 26 | { 27 | uint32_t flags; 28 | list_node_t *node; 29 | 30 | va_list args; 31 | va_start (args, format); 32 | vprintf (format, args); 33 | va_end (args); 34 | } 35 | 36 | void _dputs(char *str) 37 | { 38 | fputs(str, stdout); 39 | } 40 | -------------------------------------------------------------------------------- /arch/pc/arch_wrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __ARCH_WRAPPER_H__ 11 | #define __ARCH_WRAPPER_H__ 12 | 13 | #define local_irq_save(flags) \ 14 | do { } while (0) 15 | #define local_irq_restore(flags) \ 16 | do { } while (0) 17 | #define local_irq_enable() \ 18 | do { } while (0) 19 | #define local_irq_disable() \ 20 | do { } while (0) 21 | 22 | 23 | uint32_t get_systick(void); 24 | 25 | #ifndef CD_SYSTICK_US_DIV 26 | #define CD_SYSTICK_US_DIV 1000 27 | #endif 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /arch/stm32/arch_wrapper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "cd_utils.h" 11 | #include "arch_wrapper.h" 12 | 13 | 14 | #ifdef CD_ARCH_SPI_DMA 15 | 16 | void spi_wr(spi_t *dev, const uint8_t *w_buf, uint8_t *r_buf, int len) 17 | { 18 | DMA_TypeDef *dma = dev->dma_rx; 19 | DMA_Channel_TypeDef *dma_r = dev->dma_ch_rx; 20 | DMA_Channel_TypeDef *dma_t = dev->dma_ch_tx; 21 | uint32_t mask = dev->dma_mask; 22 | 23 | dma_r->CCR &= ~DMA_CCR_EN; 24 | dma_r->CCR &= ~(DMA_CCR_MINC | DMA_CCR_TCIE); 25 | dma_r->CNDTR = len; 26 | dma_r->CMAR = (uint32_t)(r_buf ? r_buf : &dev->dummy_rx); 27 | dma_r->CCR |= r_buf ? (DMA_CCR_EN | DMA_CCR_MINC) : DMA_CCR_EN; 28 | 29 | dma_t->CCR &= ~DMA_CCR_EN; 30 | dma_t->CCR &= ~DMA_CCR_MINC; 31 | dma_t->CNDTR = len; 32 | dma_t->CMAR = (uint32_t)(w_buf ? w_buf : &dev->dummy_tx); 33 | dma_t->CCR |= w_buf ? (DMA_CCR_EN | DMA_CCR_MINC) : DMA_CCR_EN; 34 | 35 | while (!(dma->ISR & mask)); 36 | dma->IFCR = mask; 37 | } 38 | 39 | void spi_wr_it(spi_t *dev, const uint8_t *w_buf, uint8_t *r_buf, int len) 40 | { 41 | DMA_Channel_TypeDef *dma_r = dev->dma_ch_rx; 42 | DMA_Channel_TypeDef *dma_t = dev->dma_ch_tx; 43 | 44 | dma_r->CCR &= ~DMA_CCR_EN; 45 | dma_r->CCR &= ~DMA_CCR_MINC; 46 | dma_r->CNDTR = len; 47 | dma_r->CMAR = (uint32_t)(r_buf ? r_buf : &dev->dummy_rx); 48 | dma_r->CCR |= r_buf ? (DMA_CCR_EN | DMA_CCR_MINC | DMA_CCR_TCIE) : (DMA_CCR_EN | DMA_CCR_TCIE); 49 | 50 | dma_t->CCR &= ~DMA_CCR_EN; 51 | dma_t->CCR &= ~DMA_CCR_MINC; 52 | dma_t->CNDTR = len; 53 | dma_t->CMAR = (uint32_t)(w_buf ? w_buf : &dev->dummy_tx); 54 | dma_t->CCR |= w_buf ? (DMA_CCR_EN | DMA_CCR_MINC) : DMA_CCR_EN; 55 | } 56 | 57 | 58 | /* isr template: 59 | void spi_wr_isr(void) 60 | { 61 | uint32_t flag_it = spi_dev.dma_rx->ISR; 62 | if (flag_it & spi_dev.dma_mask) { 63 | spi_dev.dma_rx->IFCR = spi_dev.dma_mask; 64 | user_callback(); 65 | } 66 | } 67 | */ 68 | 69 | void spi_wr_init(spi_t *dev) 70 | { 71 | SET_BIT(dev->spi->CR1, SPI_CR1_SPE); // enable spi 72 | SET_BIT(dev->spi->CR2, SPI_CR2_RXDMAEN); 73 | SET_BIT(dev->spi->CR2, SPI_CR2_TXDMAEN); 74 | dev->dma_ch_rx->CCR &= ~DMA_CCR_EN; 75 | dev->dma_ch_tx->CCR &= ~DMA_CCR_EN; 76 | dev->dma_ch_rx->CPAR = (uint32_t)&dev->spi->DR; 77 | dev->dma_ch_tx->CPAR = (uint32_t)&dev->spi->DR; 78 | } 79 | 80 | #endif 81 | 82 | 83 | #ifdef CD_ARCH_CRC_HW 84 | 85 | uint16_t crc16_hw_sub(const uint8_t *data, uint32_t length, uint16_t crc_val) 86 | { 87 | uint16_t ret_val; 88 | #ifdef CD_CRC_HW_IRQ_SAFE // not recommended, avoid large critical sections 89 | uint32_t flags; 90 | local_irq_save(flags); 91 | #endif 92 | CRC->INIT = crc_val; 93 | CRC->CR = 0xe9; 94 | CRC->INIT = CRC->DR; // bit-reverse crc_val 95 | 96 | while (((unsigned)data & 3) && length) { 97 | *(volatile uint8_t *)&CRC->DR = *data++; 98 | length--; 99 | } 100 | 101 | unsigned cnt = length >> 2; 102 | while (cnt--) { 103 | CRC->DR = *(uint32_t *)data; 104 | data += 4; 105 | } 106 | 107 | length &= 3; 108 | while (length--) 109 | *(volatile uint8_t *)&CRC->DR = *data++; 110 | 111 | ret_val = CRC->DR; 112 | #ifdef CD_CRC_HW_IRQ_SAFE 113 | local_irq_restore(flags); 114 | #endif 115 | return ret_val; 116 | } 117 | 118 | #endif 119 | 120 | 121 | void delay_us(uint32_t us) 122 | { 123 | uint32_t cnt_1ms = SysTick->LOAD + 1; 124 | uint32_t last = SysTick->VAL; 125 | uint32_t total = 0; 126 | uint32_t target = cnt_1ms / 1000 * us; 127 | 128 | while (total < target) { 129 | uint32_t cur = SysTick->VAL; 130 | int32_t diff = last - cur; 131 | if (diff < 0) 132 | diff += cnt_1ms; 133 | total += diff; 134 | last = cur; 135 | } 136 | } 137 | 138 | -------------------------------------------------------------------------------- /arch/stm32/arch_wrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __ARCH_WRAPPER_H__ 11 | #define __ARCH_WRAPPER_H__ 12 | 13 | #define local_irq_save(flags) \ 14 | do { \ 15 | flags = _local_irq_save(); \ 16 | } while (0) 17 | 18 | static inline uint32_t _local_irq_save(void) 19 | { 20 | uint32_t flags; 21 | __asm__ volatile( 22 | " mrs %0, primask\n" 23 | " cpsid i" 24 | : "=r" (flags) : : "memory", "cc"); 25 | return flags; 26 | } 27 | 28 | static inline void local_irq_restore(uint32_t flags) 29 | { 30 | __asm__ volatile( 31 | " msr primask, %0" 32 | : : "r" (flags) : "memory", "cc"); 33 | } 34 | 35 | static inline void local_irq_enable(void) 36 | { 37 | __asm__ volatile(" cpsie i" : : : "memory", "cc"); 38 | } 39 | 40 | static inline void local_irq_disable(void) 41 | { 42 | __asm__ volatile(" cpsid i" : : : "memory", "cc"); 43 | } 44 | 45 | 46 | #define irq_t IRQn_Type 47 | 48 | static inline void irq_enable(irq_t irq) 49 | { 50 | NVIC_EnableIRQ(irq); 51 | } 52 | 53 | static inline void irq_disable(irq_t irq) 54 | { 55 | NVIC_DisableIRQ(irq); 56 | } 57 | 58 | 59 | // gpio wrapper 60 | 61 | typedef struct { 62 | GPIO_TypeDef *group; 63 | uint16_t num; 64 | } gpio_t; 65 | 66 | static inline bool gpio_get_val(gpio_t *gpio) 67 | { 68 | return gpio->group->IDR & gpio->num; 69 | } 70 | 71 | static inline void gpio_set_val(gpio_t *gpio, bool value) 72 | { 73 | if (value) 74 | gpio->group->BSRR = gpio->num; 75 | else 76 | gpio->group->BRR = gpio->num; 77 | } 78 | 79 | static inline void gpio_set_high(gpio_t *gpio) 80 | { 81 | gpio->group->BSRR = gpio->num; 82 | } 83 | 84 | static inline void gpio_set_low(gpio_t *gpio) 85 | { 86 | gpio->group->BRR = gpio->num; 87 | } 88 | 89 | 90 | // uart wrapper 91 | 92 | typedef struct { 93 | UART_HandleTypeDef *huart; 94 | } uart_t; 95 | 96 | 97 | #ifdef CD_ARCH_SPI 98 | // spi wrapper 99 | 100 | typedef struct { 101 | SPI_HandleTypeDef *hspi; 102 | gpio_t *ns_pin; 103 | } spi_t; 104 | 105 | static inline int spi_mem_write(spi_t *spi, uint8_t mem_addr, const uint8_t *buf, int len) 106 | { 107 | int ret = 0; 108 | gpio_set_low(spi->ns_pin); 109 | ret = HAL_SPI_Transmit(spi->hspi, &mem_addr, 1, HAL_MAX_DELAY); 110 | ret = HAL_SPI_Transmit(spi->hspi, (uint8_t *)buf, len, HAL_MAX_DELAY); 111 | gpio_set_high(spi->ns_pin); 112 | return ret; 113 | } 114 | 115 | static inline int spi_mem_read(spi_t *spi, uint8_t mem_addr, uint8_t *buf, int len) 116 | { 117 | int ret = 0; 118 | gpio_set_low(spi->ns_pin); 119 | ret = HAL_SPI_Transmit(spi->hspi, &mem_addr, 1, HAL_MAX_DELAY); 120 | ret = HAL_SPI_Receive(spi->hspi, buf, len, HAL_MAX_DELAY); 121 | gpio_set_high(spi->ns_pin); 122 | return ret; 123 | } 124 | #endif 125 | 126 | 127 | #ifdef CD_ARCH_SPI_DMA 128 | // spi wrapper 129 | 130 | typedef struct { 131 | SPI_TypeDef *spi; 132 | gpio_t *ns_pin; 133 | 134 | DMA_TypeDef *dma_rx; 135 | DMA_Channel_TypeDef *dma_ch_rx; 136 | DMA_Channel_TypeDef *dma_ch_tx; 137 | uint32_t dma_mask; // DMA_ISR.TCIFx 138 | uint8_t dummy_tx; 139 | uint8_t dummy_rx; 140 | } spi_t; 141 | 142 | 143 | void spi_wr(spi_t *dev, const uint8_t *w_buf, uint8_t *r_buf, int len); 144 | void spi_wr_it(spi_t *dev, const uint8_t *w_buf, uint8_t *r_buf, int len); 145 | void spi_wr_init(spi_t *dev); 146 | 147 | static inline int spi_mem_write(spi_t *spi, uint8_t mem_addr, const uint8_t *buf, int len) 148 | { 149 | gpio_set_low(spi->ns_pin); 150 | spi_wr(spi, &mem_addr, NULL, 1); 151 | spi_wr(spi, buf, NULL, len); 152 | gpio_set_high(spi->ns_pin); 153 | return 0; 154 | } 155 | 156 | static inline int spi_mem_read(spi_t *spi, uint8_t mem_addr, uint8_t *buf, int len) 157 | { 158 | gpio_set_low(spi->ns_pin); 159 | spi_wr(spi, &mem_addr, NULL, 1); 160 | spi_wr(spi, NULL, buf, len); 161 | gpio_set_high(spi->ns_pin); 162 | return 0; 163 | } 164 | #endif 165 | 166 | 167 | #ifdef CD_ARCH_I2C 168 | // i2c wrapper 169 | 170 | typedef struct { 171 | I2C_HandleTypeDef *hi2c; 172 | uint8_t dev_addr; // 8 bit, equal to i2c write address 173 | } i2c_t; 174 | 175 | static inline int i2c_mem_write(i2c_t *i2c, uint8_t mem_addr, const uint8_t *buf, int len) 176 | { 177 | return HAL_I2C_Mem_Write(i2c->hi2c, i2c->dev_addr, mem_addr, 1, (uint8_t *)buf, len, HAL_MAX_DELAY); 178 | } 179 | 180 | static inline int i2c_mem_read(i2c_t *i2c, uint8_t mem_addr, uint8_t *buf, int len) 181 | { 182 | return HAL_I2C_Mem_Read(i2c->hi2c, i2c->dev_addr, mem_addr, 1, buf, len, HAL_MAX_DELAY); 183 | } 184 | #endif 185 | 186 | 187 | #ifdef CD_ARCH_CRC_HW 188 | uint16_t crc16_hw_sub(const uint8_t *data, uint32_t length, uint16_t crc_val); 189 | 190 | static inline uint16_t crc16_hw(const uint8_t *data, uint32_t length) 191 | { 192 | return crc16_hw_sub(data, length, 0xffff); 193 | } 194 | #endif 195 | 196 | 197 | #ifndef CD_SYSTICK_US_DIV 198 | #define CD_SYSTICK_US_DIV 1000 199 | #endif 200 | 201 | static inline uint32_t get_systick(void) 202 | { 203 | return HAL_GetTick(); 204 | } 205 | 206 | static inline void delay_systick(uint32_t val) 207 | { 208 | HAL_Delay(val); 209 | } 210 | 211 | void delay_us(uint32_t us); 212 | 213 | #endif 214 | -------------------------------------------------------------------------------- /core/cdnet_core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "cdnet_core.h" 11 | #include "cd_debug.h" 12 | 13 | 14 | // simplified for basic use, override for full processing 15 | __weak cdn_intf_t *cdn_route(cdn_ns_t *ns, cdn_pkt_t *pkt) 16 | { 17 | cdn_intf_t *intf = &ns->intfs[0]; 18 | pkt->src.addr[0] = pkt->dst.addr[0] != 0xf0 ? pkt->dst.addr[0] : 0x80; // or 0xa0 19 | pkt->src.addr[1] = intf->net; 20 | pkt->src.addr[2] = intf->mac; 21 | pkt->_s_mac = intf->mac; 22 | pkt->_d_mac = pkt->dst.addr[2]; // or default route for unique local 23 | return intf; 24 | } 25 | 26 | static cdn_sock_t *cdn_sock_search(cdn_ns_t *ns, uint16_t port) 27 | { 28 | list_node_t *pos; 29 | list_for_each_ro(&ns->socks, pos) { 30 | cdn_sock_t *sock = list_entry(pos, cdn_sock_t); 31 | if (sock->port == port) { 32 | return sock; 33 | } 34 | } 35 | return NULL; 36 | } 37 | 38 | static int cdn_sock_insert(cdn_sock_t *sock) 39 | { 40 | if (cdn_sock_search(sock->ns, sock->port)) 41 | return -1; 42 | list_put(&sock->ns->socks, &sock->node); 43 | return 0; 44 | } 45 | 46 | 47 | void cdn_routine(cdn_ns_t *ns) 48 | { 49 | // rx 50 | for (int i = 0; i < CDN_INTF_MAX; i++) { 51 | cdn_intf_t *intf = &ns->intfs[i]; 52 | cd_dev_t *dev = intf->dev; 53 | 54 | while (true) { 55 | if (!ns->rx_tmp) 56 | ns->rx_tmp = cdn_list_get(ns->free_pkt); 57 | if (!ns->rx_tmp) { 58 | d_warn("rx: no free pkt\n"); 59 | break; 60 | } 61 | 62 | cd_frame_t *frame = dev->get_rx_frame(dev); 63 | if (!frame) 64 | break; 65 | cdn_pkt_t *pkt = ns->rx_tmp; 66 | memset(pkt, 0, sizeof(cdn_pkt_t)); 67 | pkt->frm = frame; 68 | pkt->_l_net = intf->net; 69 | 70 | int ret = cdn_frame_r(pkt); 71 | if (!ret) { 72 | cdn_sock_t *sock = cdn_sock_search(ns, pkt->dst.port); 73 | if (!ret && sock && !sock->tx_only) { 74 | cdn_list_put(&sock->rx_head, pkt); 75 | } else { 76 | d_verbose("cdn rx: no sock\n"); 77 | cdn_pkt_free(ns, pkt); 78 | } 79 | } else { 80 | d_verbose("cdn rx: frame err: %d\n", ret); 81 | cdn_pkt_free(ns, pkt); 82 | } 83 | ns->rx_tmp = NULL; 84 | } 85 | } 86 | } 87 | 88 | 89 | int cdn_send_frame(cdn_ns_t *ns, cdn_pkt_t *pkt) 90 | { 91 | int ret; 92 | cdn_intf_t *intf = cdn_route(ns, pkt); 93 | if (!intf) { 94 | d_verbose("tx: no intf found\n"); 95 | return CDN_RET_ROUTE_ERR; 96 | } 97 | 98 | ret = cdn_frame_w(pkt); 99 | if (ret) 100 | return CDN_RET_FMT_ERR; 101 | intf->dev->put_tx_frame(intf->dev, pkt->frm); 102 | pkt->frm = NULL; 103 | pkt->dat = NULL; 104 | return 0; 105 | } 106 | 107 | 108 | int cdn_send_pkt(cdn_ns_t *ns, cdn_pkt_t *pkt) 109 | { 110 | if (pkt->dst.addr[0] == 0x10) { // localhost 111 | cdn_sock_t *sock = cdn_sock_search(ns, pkt->dst.port); 112 | if (sock && !sock->tx_only) { 113 | memcpy(pkt->src.addr, pkt->dst.addr, 3); 114 | cdn_list_put(&sock->rx_head, pkt); 115 | } else { 116 | d_verbose("tx: localhost no sock\n"); 117 | cdn_pkt_free(ns, pkt); 118 | } 119 | return 0; 120 | } 121 | 122 | int ret = cdn_send_frame(ns, pkt); 123 | pkt->ret = 0x80 | ret; 124 | if (!(pkt->conf & CDN_CONF_NOT_FREE)) 125 | cdn_pkt_free(ns, pkt); 126 | return ret; 127 | } 128 | 129 | 130 | int cdn_sock_sendto(cdn_sock_t *sock, cdn_pkt_t *pkt) 131 | { 132 | pkt->src.port = sock->port; 133 | return cdn_send_pkt(sock->ns, pkt); 134 | } 135 | 136 | cdn_pkt_t *cdn_sock_recvfrom(cdn_sock_t *sock) 137 | { 138 | if (!sock->rx_head.len) 139 | return NULL; 140 | return cdn_list_get(&sock->rx_head); 141 | } 142 | 143 | int cdn_sock_bind(cdn_sock_t *sock) 144 | { 145 | return cdn_sock_insert(sock); 146 | } 147 | 148 | int cdn_add_intf(cdn_ns_t *ns, cd_dev_t *dev, uint8_t net, uint8_t mac) 149 | { 150 | for (int i = 0; i < CDN_INTF_MAX; i++) { 151 | if (!ns->intfs[i].dev) { 152 | ns->intfs[i].dev = dev; 153 | ns->intfs[i].net = net; 154 | ns->intfs[i].mac = mac; 155 | return 0; 156 | } 157 | } 158 | return -1; 159 | } 160 | 161 | void cdn_init_ns(cdn_ns_t *ns, list_head_t *free_pkt, list_head_t *free_frm) 162 | { 163 | memset(ns, 0, sizeof(cdn_ns_t)); 164 | ns->free_pkt = free_pkt; 165 | ns->free_frm = free_frm; 166 | } 167 | -------------------------------------------------------------------------------- /core/cdnet_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CDNET_CORE_H__ 11 | #define __CDNET_CORE_H__ 12 | 13 | #include "cdbus.h" 14 | #include "cdnet.h" 15 | 16 | #ifndef CDN_INTF_MAX 17 | #define CDN_INTF_MAX 1 18 | #endif 19 | 20 | struct _cdn_ns; 21 | 22 | typedef struct { 23 | list_node_t node; 24 | struct _cdn_ns *ns; // cdn_ns_t 25 | uint16_t port; 26 | list_head_t rx_head; 27 | bool tx_only; 28 | } cdn_sock_t; 29 | 30 | typedef struct { 31 | cd_dev_t *dev; 32 | 33 | // interface address 34 | uint8_t net; 35 | uint8_t mac; 36 | } cdn_intf_t; 37 | 38 | typedef struct _cdn_ns { 39 | list_head_t *free_pkt; 40 | list_head_t *free_frm; 41 | list_head_t socks; 42 | cdn_intf_t intfs[CDN_INTF_MAX]; 43 | cdn_pkt_t *rx_tmp; 44 | } cdn_ns_t; // name space 45 | 46 | 47 | cdn_intf_t *cdn_route(cdn_ns_t *ns, cdn_pkt_t *pkt); // set _s_mac, _d_mac 48 | int cdn_send_frame(cdn_ns_t *ns, cdn_pkt_t *pkt); 49 | int cdn_send_pkt(cdn_ns_t *ns, cdn_pkt_t *pkt); 50 | 51 | int cdn_sock_bind(cdn_sock_t *sock); 52 | int cdn_sock_sendto(cdn_sock_t *sock, cdn_pkt_t *pkt); 53 | cdn_pkt_t *cdn_sock_recvfrom(cdn_sock_t *sock); 54 | 55 | void cdn_routine(cdn_ns_t *ns); 56 | void cdn_init_ns(cdn_ns_t *ns, list_head_t *free_pkt, list_head_t *free_frm); 57 | int cdn_add_intf(cdn_ns_t *ns, cd_dev_t *dev, uint8_t net, uint8_t mac); 58 | 59 | 60 | static inline void cdn_pkt_prepare(cdn_sock_t *sock, cdn_pkt_t *pkt) 61 | { 62 | pkt->src.port = sock->port; 63 | if (pkt->dst.addr[0] == 0x10) { // localhost 64 | pkt->dat = pkt->frm->dat + 3; 65 | } else { 66 | cdn_route(sock->ns, pkt); 67 | pkt->dat = pkt->frm->dat + 3 + cdn_hdr_size_pkt(pkt); 68 | } 69 | } 70 | 71 | static inline cdn_pkt_t *cdn_pkt_alloc(cdn_ns_t *ns) 72 | { 73 | cd_frame_t *frame = cd_list_get(ns->free_frm); 74 | if (!frame) 75 | return NULL; 76 | cdn_pkt_t *pkt = cdn_list_get(ns->free_pkt); 77 | if (!pkt) { 78 | cd_list_put(ns->free_frm, frame); 79 | return NULL; 80 | } 81 | memset(pkt, 0, sizeof(cdn_pkt_t)); 82 | pkt->frm = frame; 83 | return pkt; 84 | } 85 | 86 | static inline void cdn_pkt_free(cdn_ns_t *ns, cdn_pkt_t *pkt) 87 | { 88 | if (pkt->frm) { 89 | cd_list_put(ns->free_frm, pkt->frm); 90 | pkt->frm = NULL; 91 | } 92 | cdn_list_put(ns->free_pkt, pkt); 93 | } 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /dev/cdbus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CDBUS_H__ 11 | #define __CDBUS_H__ 12 | 13 | #include "cd_utils.h" 14 | #include "cd_list.h" 15 | 16 | // 256 bytes are enough for the CDCTL controller (without CRC) 17 | // 258 bytes are enough for the UART controller (with CRC) 18 | // allow smaller sizes to save memory 19 | #ifndef CD_FRAME_SIZE 20 | #define CD_FRAME_SIZE 256 21 | #endif 22 | 23 | typedef struct { 24 | list_node_t node; 25 | // uint8_t _pad; 26 | uint8_t dat[CD_FRAME_SIZE]; 27 | } cd_frame_t; 28 | 29 | #ifdef CD_IRQ_SAFE 30 | #define cd_list_get(head) list_get_entry_it(head, cd_frame_t) 31 | #define cd_list_put(head, frm) list_put_it(head, &(frm)->node) 32 | #elif !defined(CD_USER_LIST) 33 | #define cd_list_get(head) list_get_entry(head, cd_frame_t) 34 | #define cd_list_put(head, frm) list_put(head, &(frm)->node) 35 | #endif 36 | 37 | typedef struct cd_dev { 38 | cd_frame_t *(* get_rx_frame)(struct cd_dev *cd_dev); 39 | void (* put_tx_frame)(struct cd_dev *cd_dev, cd_frame_t *frame); 40 | } cd_dev_t; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /dev/cdbus_uart.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "cdbus_uart.h" 11 | #include "cd_debug.h" 12 | 13 | 14 | static cd_frame_t *cduart_get_rx_frame(cd_dev_t *cd_dev) 15 | { 16 | cduart_dev_t *dev = container_of(cd_dev, cduart_dev_t, cd_dev); 17 | return cd_list_get(&dev->rx_head); 18 | } 19 | 20 | static void cduart_put_tx_frame(cd_dev_t *cd_dev, cd_frame_t *frame) 21 | { 22 | cduart_dev_t *dev = container_of(cd_dev, cduart_dev_t, cd_dev); 23 | cd_list_put(&dev->tx_head, frame); 24 | } 25 | 26 | 27 | void cduart_dev_init(cduart_dev_t *dev, list_head_t *free_head) 28 | { 29 | if (!dev->name) 30 | dev->name = "cduart"; 31 | dev->rx_frame = cd_list_get(free_head); 32 | dev->free_head = free_head; 33 | dev->cd_dev.get_rx_frame = cduart_get_rx_frame; 34 | dev->cd_dev.put_tx_frame = cduart_put_tx_frame; 35 | 36 | dev->t_last = get_systick(); 37 | dev->rx_crc = 0xffff; 38 | dev->local_mac = 0xff; // local_mac should update by caller 39 | 40 | #ifdef CD_USE_DYNAMIC_INIT 41 | list_head_init(&dev->rx_head); 42 | list_head_init(&dev->tx_head); 43 | dev->rx_byte_cnt = 0; 44 | dev->rx_drop = false; 45 | #endif 46 | } 47 | 48 | 49 | void cduart_rx_handle(cduart_dev_t *dev, const uint8_t *buf, unsigned len) 50 | { 51 | unsigned max_len; 52 | unsigned cpy_len; 53 | const uint8_t *rd = buf; 54 | 55 | while (true) { 56 | cd_frame_t *frame = dev->rx_frame; 57 | 58 | if (dev->rx_byte_cnt != 0 && get_systick() - dev->t_last > CDUART_IDLE_TIME) { 59 | dn_warn(dev->name, "drop timeout, cnt: %d, hdr: %02x %02x %02x\n", 60 | dev->rx_byte_cnt, frame->dat[0], frame->dat[1], frame->dat[2]); 61 | dev->rx_byte_cnt = 0; 62 | dev->rx_crc = 0xffff; 63 | dev->rx_drop = false; 64 | } 65 | 66 | if (!len || rd == buf + len) 67 | return; 68 | max_len = buf + len - rd; 69 | dev->t_last = get_systick(); 70 | 71 | if (dev->rx_byte_cnt < 3) 72 | cpy_len = min(3 - dev->rx_byte_cnt, max_len); 73 | else 74 | cpy_len = min(frame->dat[2] + 5 - dev->rx_byte_cnt, max_len); 75 | 76 | if (!dev->rx_drop) 77 | memcpy(frame->dat + dev->rx_byte_cnt, rd, cpy_len); 78 | dev->rx_byte_cnt += cpy_len; 79 | 80 | if (dev->rx_byte_cnt == 3 && (frame->dat[2] > CD_FRAME_SIZE - 5 || 81 | (dev->local_mac != 0xff && frame->dat[1] != 0xff && frame->dat[1] != dev->local_mac))) { 82 | dn_warn(dev->name, "drop, hdr: %02x %02x %02x\n", frame->dat[0], frame->dat[1], frame->dat[2]); 83 | dev->rx_drop = true; 84 | } 85 | 86 | if (!dev->rx_drop) 87 | dev->rx_crc = CDUART_CRC_SUB(rd, cpy_len, dev->rx_crc); 88 | rd += cpy_len; 89 | 90 | if (dev->rx_byte_cnt == frame->dat[2] + 5) { 91 | if (!dev->rx_drop) { 92 | if (dev->rx_crc != 0) { 93 | dn_error(dev->name, "crc error, hdr: %02x %02x %02x\n", 94 | frame->dat[0], frame->dat[1], frame->dat[2]); 95 | } else { 96 | cd_frame_t *frm = cd_list_get(dev->free_head); 97 | if (frm) { 98 | #ifdef CD_VERBOSE 99 | char pbuf[52]; 100 | hex_dump_small(pbuf, frame->dat, frame->dat[2] + 3, 16); 101 | dn_verbose(dev->name, "-> [%s]\n", pbuf); 102 | #endif 103 | cd_list_put(&dev->rx_head, dev->rx_frame); 104 | dev->rx_frame = frm; 105 | } else { 106 | // set rx_lost flag 107 | dn_error(dev->name, "rx_lost\n"); 108 | } 109 | } 110 | } 111 | dev->rx_byte_cnt = 0; 112 | dev->rx_crc = 0xffff; 113 | dev->rx_drop = false; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /dev/cdbus_uart.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CDBUS_UART_H__ 11 | #define __CDBUS_UART_H__ 12 | 13 | #include "cdbus.h" 14 | #include "modbus_crc.h" 15 | 16 | #ifndef CDUART_IDLE_TIME 17 | #define CDUART_IDLE_TIME (5000 / SYSTICK_US_DIV) // 5 ms 18 | #endif 19 | #ifndef CDUART_CRC 20 | #define CDUART_CRC crc16 21 | #endif 22 | #ifndef CDUART_CRC_SUB 23 | #define CDUART_CRC_SUB crc16_sub 24 | #endif 25 | 26 | typedef struct cduart_dev { 27 | cd_dev_t cd_dev; 28 | const char *name; 29 | 30 | list_head_t *free_head; 31 | list_head_t rx_head; 32 | list_head_t tx_head; 33 | 34 | cd_frame_t *rx_frame; // init: != NULL 35 | uint16_t rx_byte_cnt; 36 | uint16_t rx_crc; 37 | bool rx_drop; 38 | uint32_t t_last; // last receive time 39 | 40 | uint8_t local_mac; 41 | } cduart_dev_t; 42 | 43 | 44 | void cduart_dev_init(cduart_dev_t *dev, list_head_t *free_head); 45 | void cduart_rx_handle(cduart_dev_t *dev, const uint8_t *buf, unsigned len); 46 | 47 | static inline void cduart_fill_crc(uint8_t *dat) 48 | { 49 | uint16_t crc_val = CDUART_CRC(dat, dat[2] + 3); 50 | dat[dat[2] + 3] = crc_val & 0xff; 51 | dat[dat[2] + 4] = crc_val >> 8; 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /dev/cdctl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "cdctl.h" 11 | #include "cd_debug.h" 12 | #include "cdctl_pll_cal.h" 13 | 14 | 15 | uint8_t cdctl_reg_r(cdctl_dev_t *dev, uint8_t reg) 16 | { 17 | uint8_t dat = 0xff; 18 | spi_mem_read(dev->spi, reg, &dat, 1); 19 | return dat; 20 | } 21 | 22 | void cdctl_reg_w(cdctl_dev_t *dev, uint8_t reg, uint8_t val) 23 | { 24 | spi_mem_write(dev->spi, reg | 0x80, &val, 1); 25 | } 26 | 27 | static int cdctl_read_frame(cdctl_dev_t *dev, cd_frame_t *frame) 28 | { 29 | spi_mem_read(dev->spi, CDREG_RX, frame->dat, 3); 30 | if (frame->dat[2] > min(CD_FRAME_SIZE - 3, 253)) 31 | return -1; 32 | spi_mem_read(dev->spi, CDREG_RX, frame->dat + 3, frame->dat[2]); 33 | return 0; 34 | } 35 | 36 | static void cdctl_write_frame(cdctl_dev_t *dev, const cd_frame_t *frame) 37 | { 38 | spi_mem_write(dev->spi, CDREG_TX | 0x80, frame->dat, frame->dat[2] + 3); 39 | } 40 | 41 | 42 | cd_frame_t *cdctl_get_rx_frame(cd_dev_t *cd_dev) 43 | { 44 | cdctl_dev_t *dev = container_of(cd_dev, cdctl_dev_t, cd_dev); 45 | return cd_list_get(&dev->rx_head); 46 | } 47 | 48 | void cdctl_put_tx_frame(cd_dev_t *cd_dev, cd_frame_t *frame) 49 | { 50 | cdctl_dev_t *dev = container_of(cd_dev, cdctl_dev_t, cd_dev); 51 | cd_list_put(&dev->tx_head, frame); 52 | } 53 | 54 | 55 | void cdctl_set_baud_rate(cdctl_dev_t *dev, uint32_t low, uint32_t high) 56 | { 57 | uint16_t l, h; 58 | l = min(65535, max(2, DIV_ROUND_CLOSEST(dev->sysclk, low) - 1)); 59 | h = min(65535, max(2, DIV_ROUND_CLOSEST(dev->sysclk, high) - 1)); 60 | cdctl_reg_w(dev, CDREG_DIV_LS_L, l & 0xff); 61 | cdctl_reg_w(dev, CDREG_DIV_LS_H, l >> 8); 62 | cdctl_reg_w(dev, CDREG_DIV_HS_L, h & 0xff); 63 | cdctl_reg_w(dev, CDREG_DIV_HS_H, h >> 8); 64 | dn_debug(dev->name, "set baud rate: %"PRIu32" %"PRIu32" (%u %u)\n", low, high, l, h); 65 | } 66 | 67 | void cdctl_get_baud_rate(cdctl_dev_t *dev, uint32_t *low, uint32_t *high) 68 | { 69 | uint16_t l, h; 70 | l = cdctl_reg_r(dev, CDREG_DIV_LS_L) | cdctl_reg_r(dev, CDREG_DIV_LS_H) << 8; 71 | h = cdctl_reg_r(dev, CDREG_DIV_HS_L) | cdctl_reg_r(dev, CDREG_DIV_HS_H) << 8; 72 | *low = DIV_ROUND_CLOSEST(dev->sysclk, l + 1); 73 | *high = DIV_ROUND_CLOSEST(dev->sysclk, h + 1); 74 | } 75 | 76 | void cdctl_set_clk(cdctl_dev_t *dev, uint32_t target_baud) 77 | { 78 | cdctl_reg_w(dev, CDREG_CLK_CTRL, 0x00); // select osc 79 | dn_info(dev->name, "version (clk: osc): %02x\n", cdctl_reg_r(dev, CDREG_VERSION)); 80 | 81 | dev->sysclk = cdctl_sys_cal(target_baud); 82 | pllcfg_t pll = cdctl_pll_cal(CDCTL_OSC_CLK, dev->sysclk); 83 | uint32_t actual_freq = cdctl_pll_get(CDCTL_OSC_CLK, pll); 84 | dn_info(dev->name, "sysclk %"PRIu32", actual: %"PRIu32"\n", dev->sysclk, actual_freq); 85 | dev->sysclk = actual_freq; 86 | cdctl_reg_w(dev, CDREG_PLL_N, pll.n); 87 | cdctl_reg_w(dev, CDREG_PLL_ML, pll.m & 0xff); 88 | cdctl_reg_w(dev, CDREG_PLL_OD_MH, (pll.d << 4) | (pll.m >> 8)); 89 | dn_info(dev->name, "pll_n: %02x, ml: %02x, od_mh: %02x\n", 90 | cdctl_reg_r(dev, CDREG_PLL_N), cdctl_reg_r(dev, CDREG_PLL_ML), cdctl_reg_r(dev, CDREG_PLL_OD_MH)); 91 | dn_info(dev->name, "pll_ctrl: %02x\n", cdctl_reg_r(dev, CDREG_PLL_CTRL)); 92 | cdctl_reg_w(dev, CDREG_PLL_CTRL, 0x10); // enable pll 93 | dn_info(dev->name, "clk_status: %02x\n", cdctl_reg_r(dev, CDREG_CLK_STATUS)); 94 | cdctl_reg_w(dev, CDREG_CLK_CTRL, 0x01); // select pll 95 | dn_info(dev->name, "clk_status (clk: pll): %02x\n", cdctl_reg_r(dev, CDREG_CLK_STATUS)); 96 | dn_info(dev->name, "version (clk: pll): %02x\n", cdctl_reg_r(dev, CDREG_VERSION)); 97 | } 98 | 99 | 100 | int cdctl_dev_init(cdctl_dev_t *dev, list_head_t *free_head, cdctl_cfg_t *init, spi_t *spi) 101 | { 102 | if (!dev->name) 103 | dev->name = "cdctl"; 104 | dev->free_head = free_head; 105 | dev->cd_dev.get_rx_frame = cdctl_get_rx_frame; 106 | dev->cd_dev.put_tx_frame = cdctl_put_tx_frame; 107 | 108 | #ifdef CD_USE_DYNAMIC_INIT 109 | list_head_init(&dev->rx_head); 110 | list_head_init(&dev->tx_head); 111 | dev->is_pending = NULL; 112 | dev->rx_cnt = 0; 113 | dev->tx_cnt = 0; 114 | dev->rx_lost_cnt = 0; 115 | dev->rx_error_cnt = 0; 116 | dev->rx_break_cnt = 0; 117 | dev->tx_cd_cnt = 0; 118 | dev->tx_error_cnt = 0; 119 | dev->rx_no_free_node_cnt = 0; 120 | dev->rx_len_err_cnt = 0; 121 | #endif 122 | dev->spi = spi; 123 | 124 | dn_info(dev->name, "init...\n"); 125 | uint8_t ver = cdctl_reg_r(dev, CDREG_VERSION); 126 | dn_info(dev->name, "chip version: %02x\n", ver); 127 | if (ver != 0x10) { 128 | dn_error(dev->name, "version error\n"); 129 | return -1; 130 | } 131 | 132 | cdctl_reg_w(dev, CDREG_CLK_CTRL, 0x80); // soft reset 133 | cdctl_set_clk(dev, init->baud_h); 134 | #ifndef CDCTL_AVOID_PIN_RE 135 | cdctl_reg_w(dev, CDREG_PIN_RE_CTRL, 0x10); // enable phy rx 136 | #endif 137 | 138 | uint8_t setting = (cdctl_reg_r(dev, CDREG_SETTING) & 0xf) | CDBIT_SETTING_TX_PUSH_PULL; 139 | setting |= init->mode == 1 ? CDBIT_SETTING_BREAK_SYNC : CDBIT_SETTING_ARBITRATE; 140 | cdctl_reg_w(dev, CDREG_SETTING, setting); 141 | cdctl_reg_w(dev, CDREG_FILTER, init->mac); 142 | cdctl_reg_w(dev, CDREG_FILTER_M0, init->filter_m[0]); 143 | cdctl_reg_w(dev, CDREG_FILTER_M1, init->filter_m[1]); 144 | cdctl_reg_w(dev, CDREG_TX_PERMIT_LEN_L, init->tx_permit_len & 0xff); 145 | cdctl_reg_w(dev, CDREG_TX_PERMIT_LEN_H, init->tx_permit_len >> 8); 146 | cdctl_reg_w(dev, CDREG_MAX_IDLE_LEN_L, init->max_idle_len & 0xff); 147 | cdctl_reg_w(dev, CDREG_MAX_IDLE_LEN_H, init->max_idle_len >> 8); 148 | cdctl_reg_w(dev, CDREG_TX_PRE_LEN, init->tx_pre_len); 149 | cdctl_set_baud_rate(dev, init->baud_l, init->baud_h); 150 | cdctl_flush(dev); 151 | 152 | cdctl_get_baud_rate(dev, &init->baud_l, &init->baud_h); 153 | dn_debug(dev->name, "get baud rate: %"PRIu32" %"PRIu32"\n", init->baud_l, init->baud_h); 154 | dn_debug(dev->name, "get filter(m): %02x (%02x %02x)\n", 155 | cdctl_reg_r(dev, CDREG_FILTER), cdctl_reg_r(dev, CDREG_FILTER_M0), cdctl_reg_r(dev, CDREG_FILTER_M1)); 156 | dn_debug(dev->name, "flags: %02x\n", cdctl_reg_r(dev, CDREG_INT_FLAG)); 157 | return 0; 158 | } 159 | 160 | 161 | void cdctl_routine(cdctl_dev_t *dev) 162 | { 163 | uint8_t flags = cdctl_reg_r(dev, CDREG_INT_FLAG); 164 | 165 | if (flags & (CDBIT_FLAG_RX_LOST | CDBIT_FLAG_RX_ERROR | CDBIT_FLAG_RX_BREAK | \ 166 | CDBIT_FLAG_TX_CD | CDBIT_FLAG_TX_ERROR)) { 167 | if (flags & CDBIT_FLAG_RX_LOST) 168 | dev->rx_lost_cnt++; 169 | if (flags & CDBIT_FLAG_RX_ERROR) 170 | dev->rx_error_cnt++; 171 | if (flags & CDBIT_FLAG_RX_BREAK) 172 | dev->rx_break_cnt++; 173 | if (flags & CDBIT_FLAG_TX_CD) 174 | dev->tx_cd_cnt++; 175 | if (flags & CDBIT_FLAG_TX_ERROR) 176 | dev->tx_error_cnt++; 177 | } 178 | 179 | if (flags & CDBIT_FLAG_RX_PENDING) { 180 | cd_frame_t *frame = cd_list_get(dev->free_head); 181 | if (frame) { 182 | int ret = cdctl_read_frame(dev, frame); 183 | cdctl_reg_w(dev, CDREG_RX_CTRL, CDBIT_RX_CLR_PENDING | CDBIT_RX_RST_POINTER); 184 | #ifdef CD_VERBOSE 185 | char pbuf[52]; 186 | hex_dump_small(pbuf, frame->dat, frame->dat[2] + 3, 16); 187 | dn_verbose(dev->name, "-> [%s]\n", pbuf); 188 | #endif 189 | if (ret) { 190 | dn_error(dev->name, "rx frame len err\n"); 191 | cd_list_put(dev->free_head, frame); 192 | dev->rx_len_err_cnt++; 193 | } else { 194 | cd_list_put(&dev->rx_head, frame); 195 | dev->rx_cnt++; 196 | } 197 | } else { 198 | dn_error(dev->name, "get rx, no free frame\n"); 199 | dev->rx_no_free_node_cnt++; 200 | } 201 | } 202 | 203 | if (!dev->is_pending) { 204 | if (dev->tx_head.first) { 205 | cd_frame_t *frame = cd_list_get(&dev->tx_head); 206 | cdctl_write_frame(dev, frame); 207 | dev->tx_cnt++; 208 | 209 | if (flags & CDBIT_FLAG_TX_BUF_CLEAN) { 210 | cdctl_reg_w(dev, CDREG_TX_CTRL, CDBIT_TX_START | CDBIT_TX_RST_POINTER); 211 | cdctl_tx_cb(dev, frame); 212 | } else { 213 | dev->is_pending = frame; 214 | } 215 | #ifdef CD_VERBOSE 216 | char pbuf[52]; 217 | hex_dump_small(pbuf, frame->dat, frame->dat[2] + 3, 16); 218 | dn_verbose(dev->name, "<- [%s]%s\n", pbuf, dev->is_pending ? " (p)" : ""); 219 | #endif 220 | #ifndef CDCTL_TX_NOT_FREE 221 | cd_list_put(dev->free_head, frame); 222 | #endif 223 | } 224 | } else { 225 | if (flags & CDBIT_FLAG_TX_BUF_CLEAN) { 226 | dn_verbose(dev->name, "trigger pending tx\n"); 227 | cdctl_reg_w(dev, CDREG_TX_CTRL, CDBIT_TX_START | CDBIT_TX_RST_POINTER); 228 | cdctl_tx_cb(dev, dev->is_pending); 229 | dev->is_pending = NULL; 230 | } 231 | } 232 | } 233 | 234 | 235 | __weak void cdctl_tx_cb(cdctl_dev_t *dev, cd_frame_t *frame) {} 236 | -------------------------------------------------------------------------------- /dev/cdctl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CDCTL_H__ 11 | #define __CDCTL_H__ 12 | 13 | #include "cdbus.h" 14 | #include "cdctl_regs.h" 15 | 16 | typedef struct { 17 | cd_dev_t cd_dev; 18 | const char *name; 19 | uint32_t sysclk; 20 | 21 | list_head_t *free_head; 22 | list_head_t rx_head; 23 | list_head_t tx_head; 24 | 25 | cd_frame_t *is_pending; 26 | 27 | uint32_t rx_cnt; 28 | uint32_t tx_cnt; 29 | uint32_t rx_lost_cnt; 30 | uint32_t rx_error_cnt; 31 | uint32_t rx_break_cnt; 32 | uint32_t tx_cd_cnt; 33 | uint32_t tx_error_cnt; 34 | uint32_t rx_no_free_node_cnt; 35 | uint32_t rx_len_err_cnt; 36 | 37 | spi_t *spi; 38 | gpio_t *rst_n; 39 | } cdctl_dev_t; 40 | 41 | typedef struct { 42 | uint8_t mac; 43 | uint32_t baud_l; 44 | uint32_t baud_h; 45 | uint8_t filter_m[2]; 46 | 47 | uint8_t mode; // 0: Arbitration, 1: Break Sync 48 | uint16_t tx_permit_len; 49 | uint16_t max_idle_len; 50 | uint8_t tx_pre_len; 51 | } cdctl_cfg_t; 52 | 53 | #define CDCTL_CFG_DFT(_mac) { \ 54 | .mac = _mac, \ 55 | .baud_l = 115200, \ 56 | .baud_h = 115200, \ 57 | .filter_m = { 0xff, 0xff }, \ 58 | .mode = 0, \ 59 | .tx_permit_len = 0x14, \ 60 | .max_idle_len = 0xc8, \ 61 | .tx_pre_len = 0x01 \ 62 | } 63 | 64 | 65 | int cdctl_dev_init(cdctl_dev_t *dev, list_head_t *free_head, cdctl_cfg_t *init, spi_t *spi); 66 | 67 | uint8_t cdctl_reg_r(cdctl_dev_t *dev, uint8_t reg); 68 | void cdctl_reg_w(cdctl_dev_t *dev, uint8_t reg, uint8_t val); 69 | void cdctl_set_clk(cdctl_dev_t *dev, uint32_t target_baud); 70 | void cdctl_set_baud_rate(cdctl_dev_t *dev, uint32_t low, uint32_t high); 71 | void cdctl_get_baud_rate(cdctl_dev_t *dev, uint32_t *low, uint32_t *high); 72 | 73 | cd_frame_t *cdctl_get_rx_frame(cd_dev_t *cd_dev); 74 | void cdctl_put_tx_frame(cd_dev_t *cd_dev, cd_frame_t *frame); 75 | 76 | static inline void cdctl_flush(cdctl_dev_t *dev) 77 | { 78 | cdctl_reg_w(dev, CDREG_RX_CTRL, CDBIT_RX_RST_ALL); 79 | } 80 | 81 | void cdctl_routine(cdctl_dev_t *dev); 82 | 83 | void cdctl_tx_cb(cdctl_dev_t *dev, cd_frame_t *frame); 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /dev/cdctl_it.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "cdctl_it.h" 11 | #include "cd_debug.h" 12 | #include "cdctl_pll_cal.h" 13 | 14 | #define CDCTL_MASK (CDBIT_FLAG_RX_PENDING | CDBIT_FLAG_RX_LOST | CDBIT_FLAG_RX_ERROR | \ 15 | CDBIT_FLAG_TX_CD | CDBIT_FLAG_TX_ERROR) 16 | 17 | 18 | uint8_t cdctl_reg_r(cdctl_dev_t *dev, uint8_t reg) 19 | { 20 | uint8_t dat = 0xff; 21 | irq_disable(dev->int_irq); 22 | while (dev->state > CDCTL_WAIT_TX_CLEAN); 23 | spi_mem_read(dev->spi, reg, &dat, 1); 24 | irq_enable(dev->int_irq); 25 | return dat; 26 | } 27 | 28 | void cdctl_reg_w(cdctl_dev_t *dev, uint8_t reg, uint8_t val) 29 | { 30 | irq_disable(dev->int_irq); 31 | while (dev->state > CDCTL_WAIT_TX_CLEAN); 32 | spi_mem_write(dev->spi, reg | 0x80, &val, 1); 33 | irq_enable(dev->int_irq); 34 | } 35 | 36 | 37 | cd_frame_t *cdctl_get_rx_frame(cd_dev_t *cd_dev) 38 | { 39 | cdctl_dev_t *dev = container_of(cd_dev, cdctl_dev_t, cd_dev); 40 | return cd_list_get(&dev->rx_head); 41 | } 42 | 43 | void cdctl_put_tx_frame(cd_dev_t *cd_dev, cd_frame_t *frame) 44 | { 45 | cdctl_dev_t *dev = container_of(cd_dev, cdctl_dev_t, cd_dev); 46 | 47 | cd_list_put(&dev->tx_head, frame); 48 | irq_disable(dev->int_irq); 49 | if (dev->state == CDCTL_IDLE) 50 | cdctl_int_isr(dev); 51 | irq_enable(dev->int_irq); 52 | } 53 | 54 | 55 | void cdctl_set_baud_rate(cdctl_dev_t *dev, uint32_t low, uint32_t high) 56 | { 57 | uint16_t l, h; 58 | l = min(65535, max(2, DIV_ROUND_CLOSEST(dev->sysclk, low) - 1)); 59 | h = min(65535, max(2, DIV_ROUND_CLOSEST(dev->sysclk, high) - 1)); 60 | cdctl_reg_w(dev, CDREG_DIV_LS_L, l & 0xff); 61 | cdctl_reg_w(dev, CDREG_DIV_LS_H, l >> 8); 62 | cdctl_reg_w(dev, CDREG_DIV_HS_L, h & 0xff); 63 | cdctl_reg_w(dev, CDREG_DIV_HS_H, h >> 8); 64 | dn_debug(dev->name, "set baud rate: %"PRIu32" %"PRIu32" (%u %u)\n", low, high, l, h); 65 | } 66 | 67 | void cdctl_get_baud_rate(cdctl_dev_t *dev, uint32_t *low, uint32_t *high) 68 | { 69 | uint16_t l, h; 70 | l = cdctl_reg_r(dev, CDREG_DIV_LS_L) | cdctl_reg_r(dev, CDREG_DIV_LS_H) << 8; 71 | h = cdctl_reg_r(dev, CDREG_DIV_HS_L) | cdctl_reg_r(dev, CDREG_DIV_HS_H) << 8; 72 | *low = DIV_ROUND_CLOSEST(dev->sysclk, l + 1); 73 | *high = DIV_ROUND_CLOSEST(dev->sysclk, h + 1); 74 | } 75 | 76 | void cdctl_set_clk(cdctl_dev_t *dev, uint32_t target_baud) 77 | { 78 | cdctl_reg_w(dev, CDREG_CLK_CTRL, 0x00); // select osc 79 | dn_info(dev->name, "version (clk: osc): %02x\n", cdctl_reg_r(dev, CDREG_VERSION)); 80 | 81 | dev->sysclk = cdctl_sys_cal(target_baud); 82 | pllcfg_t pll = cdctl_pll_cal(CDCTL_OSC_CLK, dev->sysclk); 83 | uint32_t actual_freq = cdctl_pll_get(CDCTL_OSC_CLK, pll); 84 | dn_info(dev->name, "sysclk %"PRIu32", actual: %"PRIu32"\n", dev->sysclk, actual_freq); 85 | dev->sysclk = actual_freq; 86 | cdctl_reg_w(dev, CDREG_PLL_N, pll.n); 87 | cdctl_reg_w(dev, CDREG_PLL_ML, pll.m & 0xff); 88 | cdctl_reg_w(dev, CDREG_PLL_OD_MH, (pll.d << 4) | (pll.m >> 8)); 89 | dn_info(dev->name, "pll_n: %02x, ml: %02x, od_mh: %02x\n", 90 | cdctl_reg_r(dev, CDREG_PLL_N), cdctl_reg_r(dev, CDREG_PLL_ML), cdctl_reg_r(dev, CDREG_PLL_OD_MH)); 91 | dn_info(dev->name, "pll_ctrl: %02x\n", cdctl_reg_r(dev, CDREG_PLL_CTRL)); 92 | cdctl_reg_w(dev, CDREG_PLL_CTRL, 0x10); // enable pll 93 | dn_info(dev->name, "clk_status: %02x\n", cdctl_reg_r(dev, CDREG_CLK_STATUS)); 94 | cdctl_reg_w(dev, CDREG_CLK_CTRL, 0x01); // select pll 95 | dn_info(dev->name, "clk_status (clk: pll): %02x\n", cdctl_reg_r(dev, CDREG_CLK_STATUS)); 96 | dn_info(dev->name, "version (clk: pll): %02x\n", cdctl_reg_r(dev, CDREG_VERSION)); 97 | } 98 | 99 | 100 | int cdctl_dev_init(cdctl_dev_t *dev, list_head_t *free_head, cdctl_cfg_t *init, 101 | spi_t *spi, gpio_t *int_n, irq_t int_irq) 102 | { 103 | if (!dev->name) 104 | dev->name = "cdctl"; 105 | dev->rx_frame = cd_list_get(free_head); 106 | dev->free_head = free_head; 107 | dev->cd_dev.get_rx_frame = cdctl_get_rx_frame; 108 | dev->cd_dev.put_tx_frame = cdctl_put_tx_frame; 109 | 110 | #ifdef CD_USE_DYNAMIC_INIT 111 | dev->state = CDCTL_RST; 112 | list_head_init(&dev->rx_head); 113 | list_head_init(&dev->tx_head); 114 | dev->rx_frame = NULL; 115 | dev->tx_wait_trigger = NULL; 116 | dev->tx_buf_clean_mask = false; 117 | dev->rx_cnt = 0; 118 | dev->tx_cnt = 0; 119 | dev->rx_lost_cnt = 0; 120 | dev->rx_error_cnt = 0; 121 | dev->rx_break_cnt = 0; 122 | dev->tx_cd_cnt = 0; 123 | dev->tx_error_cnt = 0; 124 | dev->rx_no_free_node_cnt = 0; 125 | dev->rx_len_err_cnt = 0; 126 | #endif 127 | 128 | dev->spi = spi; 129 | dev->int_n = int_n; 130 | dev->int_irq = int_irq; 131 | 132 | dn_info(dev->name, "init...\n"); 133 | uint8_t ver = cdctl_reg_r(dev, CDREG_VERSION); 134 | dn_info(dev->name, "chip version: %02x\n", ver); 135 | if (ver != 0x10) { 136 | dn_error(dev->name, "version error\n"); 137 | return -1; 138 | } 139 | 140 | cdctl_reg_w(dev, CDREG_CLK_CTRL, 0x80); // soft reset 141 | cdctl_set_clk(dev, init->baud_h); 142 | #ifndef CDCTL_AVOID_PIN_RE 143 | cdctl_reg_w(dev, CDREG_PIN_RE_CTRL, 0x10); // enable phy rx 144 | #endif 145 | 146 | uint8_t setting = (cdctl_reg_r(dev, CDREG_SETTING) & 0xf) | CDBIT_SETTING_TX_PUSH_PULL; 147 | setting |= init->mode == 1 ? CDBIT_SETTING_BREAK_SYNC : CDBIT_SETTING_ARBITRATE; 148 | cdctl_reg_w(dev, CDREG_SETTING, setting); 149 | cdctl_reg_w(dev, CDREG_FILTER, init->mac); 150 | cdctl_reg_w(dev, CDREG_FILTER_M0, init->filter_m[0]); 151 | cdctl_reg_w(dev, CDREG_FILTER_M1, init->filter_m[1]); 152 | cdctl_reg_w(dev, CDREG_TX_PERMIT_LEN_L, init->tx_permit_len & 0xff); 153 | cdctl_reg_w(dev, CDREG_TX_PERMIT_LEN_H, init->tx_permit_len >> 8); 154 | cdctl_reg_w(dev, CDREG_MAX_IDLE_LEN_L, init->max_idle_len & 0xff); 155 | cdctl_reg_w(dev, CDREG_MAX_IDLE_LEN_H, init->max_idle_len >> 8); 156 | cdctl_reg_w(dev, CDREG_TX_PRE_LEN, init->tx_pre_len); 157 | cdctl_set_baud_rate(dev, init->baud_l, init->baud_h); 158 | cdctl_flush(dev); 159 | 160 | cdctl_get_baud_rate(dev, &init->baud_l, &init->baud_h); 161 | dn_debug(dev->name, "get baud rate: %"PRIu32" %"PRIu32"\n", init->baud_l, init->baud_h); 162 | dn_debug(dev->name, "get filter(m): %02x (%02x %02x)\n", 163 | cdctl_reg_r(dev, CDREG_FILTER), cdctl_reg_r(dev, CDREG_FILTER_M0), cdctl_reg_r(dev, CDREG_FILTER_M1)); 164 | dn_debug(dev->name, "flags: %02x\n", cdctl_reg_r(dev, CDREG_INT_FLAG)); 165 | dev->state = CDCTL_IDLE; 166 | cdctl_reg_w(dev, CDREG_INT_MASK, CDCTL_MASK); 167 | return 0; 168 | } 169 | 170 | 171 | static inline void cdctl_reg_r_it(cdctl_dev_t *dev, uint8_t reg) 172 | { 173 | dev->buf[0] = reg; 174 | gpio_set_low(dev->spi->ns_pin); 175 | spi_wr_it(dev->spi, dev->buf, dev->buf, 2); 176 | } 177 | 178 | static inline void cdctl_reg_w_it(cdctl_dev_t *dev, uint8_t reg, uint8_t val) 179 | { 180 | dev->buf[0] = reg | 0x80; 181 | dev->buf[1] = val; 182 | gpio_set_low(dev->spi->ns_pin); 183 | spi_wr_it(dev->spi, dev->buf, NULL, 2); 184 | } 185 | 186 | 187 | // int_n pin interrupt isr 188 | void cdctl_int_isr(cdctl_dev_t *dev) 189 | { 190 | if (dev->state == CDCTL_IDLE || dev->state == CDCTL_WAIT_TX_CLEAN) { 191 | dev->state = CDCTL_RD_FLAG; 192 | cdctl_reg_r_it(dev, CDREG_INT_FLAG); 193 | } 194 | } 195 | 196 | // dma finish callback 197 | void cdctl_spi_isr(cdctl_dev_t *dev) 198 | { 199 | // end of CDCTL_RD_FLAG 200 | if (dev->state == CDCTL_RD_FLAG) { 201 | uint8_t val = dev->buf[1]; 202 | gpio_set_high(dev->spi->ns_pin); 203 | 204 | if (val & (CDBIT_FLAG_RX_LOST | CDBIT_FLAG_RX_ERROR | CDBIT_FLAG_RX_BREAK | \ 205 | CDBIT_FLAG_TX_CD | CDBIT_FLAG_TX_ERROR)) { 206 | if (val & CDBIT_FLAG_RX_LOST) 207 | dev->rx_lost_cnt++; 208 | if (val & CDBIT_FLAG_RX_ERROR) 209 | dev->rx_error_cnt++; 210 | if (val & CDBIT_FLAG_RX_BREAK) 211 | dev->rx_break_cnt++; 212 | if (val & CDBIT_FLAG_TX_CD) 213 | dev->tx_cd_cnt++; 214 | if (val & CDBIT_FLAG_TX_ERROR) 215 | dev->tx_error_cnt++; 216 | } 217 | 218 | // check for new frame 219 | if (val & CDBIT_FLAG_RX_PENDING) { 220 | dev->state = CDCTL_RX_HEADER; 221 | uint8_t *buf = dev->rx_frame->dat - 1; 222 | *buf = CDREG_RX; // borrow space from the "node" item 223 | gpio_set_low(dev->spi->ns_pin); 224 | spi_wr_it(dev->spi, buf, buf, 4); 225 | return; 226 | } 227 | 228 | // check for tx 229 | if (dev->tx_wait_trigger) { 230 | if (val & CDBIT_FLAG_TX_BUF_CLEAN) { 231 | dev->state = CDCTL_REG_W; 232 | cdctl_reg_w_it(dev, CDREG_TX_CTRL, CDBIT_TX_START | CDBIT_TX_RST_POINTER); 233 | cdctl_tx_cb(dev, dev->tx_wait_trigger); 234 | dev->tx_wait_trigger = NULL; 235 | return; 236 | } else if (!dev->tx_buf_clean_mask) { 237 | // enable tx_buf_clean irq 238 | dev->tx_buf_clean_mask = true; 239 | dev->state = CDCTL_REG_W; 240 | cdctl_reg_w_it(dev, CDREG_INT_MASK, CDCTL_MASK | CDBIT_FLAG_TX_BUF_CLEAN); 241 | return; 242 | } 243 | } else if (dev->tx_head.first) { 244 | dev->tx_frame = cd_list_get(&dev->tx_head); 245 | uint8_t *buf = dev->tx_frame->dat - 1; 246 | *buf = CDREG_TX | 0x80; // borrow space from the "node" item 247 | dev->state = CDCTL_TX_FRAME; 248 | gpio_set_low(dev->spi->ns_pin); 249 | spi_wr_it(dev->spi, buf, NULL, 4 + buf[3]); 250 | return; 251 | } else if (dev->tx_buf_clean_mask) { 252 | dev->tx_buf_clean_mask = false; 253 | dev->state = CDCTL_REG_W; 254 | cdctl_reg_w_it(dev, CDREG_INT_MASK, CDCTL_MASK); 255 | return; 256 | } 257 | 258 | dev->state = dev->tx_wait_trigger ? CDCTL_WAIT_TX_CLEAN : CDCTL_IDLE; 259 | if (!gpio_get_val(dev->int_n)) 260 | cdctl_int_isr(dev); 261 | return; 262 | } 263 | 264 | // end of write RX_CTRL, TX_CTRL, INT_MASK 265 | if (dev->state == CDCTL_REG_W) { 266 | gpio_set_high(dev->spi->ns_pin); 267 | dev->state = CDCTL_RD_FLAG; 268 | cdctl_reg_r_it(dev, CDREG_INT_FLAG); 269 | return; 270 | } 271 | 272 | // end of CDCTL_RX_HEADER 273 | if (dev->state == CDCTL_RX_HEADER) { 274 | dev->state = CDCTL_RX_BODY; 275 | if (dev->rx_frame->dat[2] > min(CD_FRAME_SIZE - 3, 253)) { 276 | dev->rx_len_err_cnt++; 277 | dev->state = CDCTL_REG_W; 278 | cdctl_reg_w_it(dev, CDREG_RX_CTRL, CDBIT_RX_CLR_PENDING | CDBIT_RX_RST_POINTER); 279 | return; 280 | } 281 | if (dev->rx_frame->dat[2] != 0) { 282 | spi_wr_it(dev->spi, NULL, dev->rx_frame->dat + 3, dev->rx_frame->dat[2]); 283 | return; 284 | } // no return 285 | } 286 | 287 | // end of CDCTL_RX_BODY 288 | if (dev->state == CDCTL_RX_BODY) { 289 | gpio_set_high(dev->spi->ns_pin); 290 | dev->state = CDCTL_REG_W; 291 | cdctl_reg_w_it(dev, CDREG_RX_CTRL, CDBIT_RX_CLR_PENDING | CDBIT_RX_RST_POINTER); 292 | cd_frame_t *frame = cd_list_get(dev->free_head); 293 | if (frame) { 294 | cd_list_put(&dev->rx_head, dev->rx_frame); 295 | dev->rx_frame = frame; 296 | dev->rx_cnt++; 297 | cdctl_rx_cb(dev, frame); 298 | } else { 299 | dev->rx_no_free_node_cnt++; 300 | } 301 | return; 302 | } 303 | 304 | // end of CDCTL_TX_FRAME 305 | if (dev->state == CDCTL_TX_FRAME) { 306 | gpio_set_high(dev->spi->ns_pin); 307 | #ifndef CDCTL_TX_NOT_FREE 308 | cd_list_put(dev->free_head, dev->tx_frame); 309 | #endif 310 | dev->tx_wait_trigger = dev->tx_frame; 311 | dev->tx_frame = NULL; 312 | dev->tx_cnt++; 313 | 314 | dev->state = CDCTL_RD_FLAG; 315 | cdctl_reg_r_it(dev, CDREG_INT_FLAG); 316 | return; 317 | } 318 | 319 | dn_warn(dev->name, "unexpected spi dma cb\n"); 320 | } 321 | 322 | 323 | __weak void cdctl_rx_cb(cdctl_dev_t *dev, cd_frame_t *frame) {} 324 | __weak void cdctl_tx_cb(cdctl_dev_t *dev, cd_frame_t *frame) {} 325 | -------------------------------------------------------------------------------- /dev/cdctl_it.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CDCTL_IT_H__ 11 | #define __CDCTL_IT_H__ 12 | 13 | #include "cdbus.h" 14 | #include "cdctl_regs.h" 15 | 16 | typedef enum { 17 | CDCTL_RST = 0, 18 | 19 | CDCTL_IDLE, 20 | CDCTL_WAIT_TX_CLEAN, 21 | CDCTL_RD_FLAG, 22 | CDCTL_REG_W, 23 | 24 | CDCTL_RX_HEADER, 25 | CDCTL_RX_BODY, 26 | CDCTL_TX_FRAME 27 | } cdctl_state_t; 28 | 29 | typedef struct { 30 | cd_dev_t cd_dev; 31 | const char *name; 32 | uint32_t sysclk; 33 | 34 | volatile cdctl_state_t state; 35 | 36 | list_head_t *free_head; 37 | list_head_t rx_head; 38 | list_head_t tx_head; 39 | 40 | cd_frame_t *rx_frame; 41 | cd_frame_t *tx_frame; 42 | cd_frame_t *tx_wait_trigger; 43 | bool tx_buf_clean_mask; 44 | 45 | uint8_t buf[2]; 46 | 47 | volatile uint32_t rx_cnt; 48 | volatile uint32_t tx_cnt; 49 | volatile uint32_t rx_lost_cnt; 50 | volatile uint32_t rx_error_cnt; 51 | volatile uint32_t rx_break_cnt; 52 | volatile uint32_t tx_cd_cnt; 53 | volatile uint32_t tx_error_cnt; 54 | volatile uint32_t rx_no_free_node_cnt; 55 | volatile uint32_t rx_len_err_cnt; 56 | 57 | spi_t *spi; 58 | gpio_t *int_n; 59 | irq_t int_irq; 60 | } cdctl_dev_t; 61 | 62 | typedef struct { 63 | uint8_t mac; 64 | uint32_t baud_l; 65 | uint32_t baud_h; 66 | uint8_t filter_m[2]; 67 | 68 | uint8_t mode; // 0: Arbitration, 1: Break Sync 69 | uint16_t tx_permit_len; 70 | uint16_t max_idle_len; 71 | uint8_t tx_pre_len; 72 | } cdctl_cfg_t; 73 | 74 | #define CDCTL_CFG_DFT(_mac) { \ 75 | .mac = _mac, \ 76 | .baud_l = 115200, \ 77 | .baud_h = 115200, \ 78 | .filter_m = { 0xff, 0xff }, \ 79 | .mode = 0, \ 80 | .tx_permit_len = 0x14, \ 81 | .max_idle_len = 0xc8, \ 82 | .tx_pre_len = 0x01 \ 83 | } 84 | 85 | int cdctl_dev_init(cdctl_dev_t *dev, list_head_t *free_head, cdctl_cfg_t *init, 86 | spi_t *spi, gpio_t *int_n, irq_t int_irq); 87 | 88 | uint8_t cdctl_reg_r(cdctl_dev_t *dev, uint8_t reg); 89 | void cdctl_reg_w(cdctl_dev_t *dev, uint8_t reg, uint8_t val); 90 | void cdctl_set_clk(cdctl_dev_t *dev, uint32_t target_baud); 91 | void cdctl_set_baud_rate(cdctl_dev_t *dev, uint32_t low, uint32_t high); 92 | void cdctl_get_baud_rate(cdctl_dev_t *dev, uint32_t *low, uint32_t *high); 93 | 94 | cd_frame_t *cdctl_get_rx_frame(cd_dev_t *cd_dev); 95 | void cdctl_put_tx_frame(cd_dev_t *cd_dev, cd_frame_t *frame); 96 | 97 | static inline void cdctl_flush(cdctl_dev_t *dev) 98 | { 99 | cdctl_reg_w(dev, CDREG_RX_CTRL, CDBIT_RX_RST_ALL); 100 | } 101 | 102 | void cdctl_int_isr(cdctl_dev_t *dev); 103 | void cdctl_spi_isr(cdctl_dev_t *dev); 104 | 105 | void cdctl_rx_cb(cdctl_dev_t *dev, cd_frame_t *frame); 106 | void cdctl_tx_cb(cdctl_dev_t *dev, cd_frame_t *frame); 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /dev/cdctl_pll_cal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "cd_utils.h" 11 | #include "cdctl_pll_cal.h" 12 | 13 | 14 | pllcfg_t cdctl_pll_cal(uint32_t input, uint32_t output) { 15 | pllcfg_t best = {0, 0, 0, 0xffffffff, 0xffffffff}; 16 | uint32_t min_vco = 100e6L, max_vco = 500e6L, target_vco = 300e6L; 17 | uint32_t min_div_freq = 1e6L, max_div_freq = 15e6L, target_div_freq = 8e6L; 18 | 19 | for (int d = 0; d <= 2; d++) { 20 | uint32_t factor_d = 1 << d; // pow(2, d) 21 | 22 | for (int n = 31; n >= 0; n--) { 23 | uint32_t div_freq = DIV_ROUND_CLOSEST(input, n + 2); 24 | if (div_freq < min_div_freq) 25 | continue; 26 | if (div_freq > max_div_freq) 27 | break; 28 | 29 | for (int m = 0; m < 512; m++) { 30 | uint32_t vco_freq = div_freq * (m + 2); 31 | if (vco_freq < min_vco) 32 | continue; 33 | if (vco_freq > max_vco) 34 | break; 35 | 36 | uint32_t computed_output = DIV_ROUND_CLOSEST(vco_freq, factor_d); 37 | uint32_t error = abs((int32_t)(computed_output - output)); 38 | 39 | // optimize div_freq and vco_freq 40 | uint32_t div_freq_deviation = abs((int32_t)(div_freq - target_div_freq)); 41 | uint32_t vco_freq_deviation = abs((int32_t)(vco_freq - target_vco)); 42 | uint32_t total_deviation = div_freq_deviation * 10 + vco_freq_deviation; 43 | 44 | if (error < best.error || (error == best.error && total_deviation < best.deviation)) { 45 | best.n = n; 46 | best.m = m; 47 | best.d = d; 48 | best.error = error; 49 | best.deviation = total_deviation; 50 | } 51 | } 52 | } 53 | } 54 | 55 | if (best.d == 2) 56 | best.d = 3; 57 | return best; 58 | } 59 | 60 | 61 | uint32_t cdctl_pll_get(uint32_t input, pllcfg_t cfg) 62 | { 63 | if (cfg.d == 3) 64 | cfg.d = 2; 65 | uint32_t div_freq = DIV_ROUND_CLOSEST(input, cfg.n + 2); 66 | uint32_t vco_freq = div_freq * (cfg.m + 2); 67 | return vco_freq / (1 << cfg.d); 68 | } 69 | 70 | 71 | uint32_t cdctl_sys_cal(uint32_t baud) { 72 | uint32_t best[2] = {0, 0xffffffff}; 73 | uint32_t clk_max = 150e6L; 74 | uint32_t clk_min = 100e6L; // higher sysclk for higher spi clk 75 | uint32_t clk_step = 2e5L; 76 | 77 | for (uint32_t c = clk_max; c >= clk_min; c -= clk_step) { 78 | uint32_t div = min(65535, DIV_ROUND_CLOSEST(c, baud)); 79 | uint32_t error = abs((int32_t)(DIV_ROUND_CLOSEST(c, div) - baud)); 80 | 81 | if (error < best[1]) { 82 | best[0] = c; 83 | best[1] = error; 84 | if (error == 0) 85 | break; 86 | } 87 | } 88 | 89 | return best[0]; 90 | } 91 | -------------------------------------------------------------------------------- /dev/cdctl_pll_cal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CDCTL_PLL_CAL_H__ 11 | #define __CDCTL_PLL_CAL_H__ 12 | 13 | typedef struct { 14 | uint8_t n; 15 | uint16_t m; 16 | uint8_t d; 17 | uint32_t error; 18 | uint32_t deviation; 19 | } pllcfg_t; 20 | 21 | pllcfg_t cdctl_pll_cal(uint32_t input, uint32_t output); 22 | uint32_t cdctl_pll_get(uint32_t input, pllcfg_t cfg); 23 | 24 | uint32_t cdctl_sys_cal(uint32_t baud); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /dev/cdctl_regs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CDCTL_REGS_H__ 11 | #define __CDCTL_REGS_H__ 12 | 13 | #define CDREG_VERSION 0x00 14 | #define CDREG_CLK_CTRL 0x01 15 | #define CDREG_SETTING 0x02 16 | #define CDREG_IDLE_WAIT_LEN 0x04 17 | #define CDREG_TX_PERMIT_LEN_L 0x05 18 | #define CDREG_TX_PERMIT_LEN_H 0x06 19 | #define CDREG_MAX_IDLE_LEN_L 0x07 20 | #define CDREG_MAX_IDLE_LEN_H 0x08 21 | #define CDREG_TX_PRE_LEN 0x09 22 | #define CDREG_FILTER 0x0b 23 | #define CDREG_DIV_LS_L 0x0c 24 | #define CDREG_DIV_LS_H 0x0d 25 | #define CDREG_DIV_HS_L 0x0e 26 | #define CDREG_DIV_HS_H 0x0f 27 | #define CDREG_INT_FLAG 0x10 28 | #define CDREG_INT_MASK 0x11 29 | #define CDREG_RX 0x14 30 | #define CDREG_TX 0x15 31 | #define CDREG_RX_CTRL 0x16 32 | #define CDREG_TX_CTRL 0x17 33 | #define CDREG_RX_ADDR 0x18 34 | #define CDREG_RX_PAGE_FLAG 0x19 35 | #define CDREG_FILTER_M0 0x1a 36 | #define CDREG_FILTER_M1 0x1b 37 | #define CDREG_PLL_ML 0x30 38 | #define CDREG_PLL_OD_MH 0x31 39 | #define CDREG_PLL_N 0x32 40 | #define CDREG_PLL_CTRL 0x33 41 | #define CDREG_PIN_INT_CTRL 0x34 42 | #define CDREG_PIN_RE_CTRL 0x35 43 | #define CDREG_CLK_STATUS 0x36 44 | 45 | #define CDBIT_SETTING_TX_PUSH_PULL (1 << 0) 46 | #define CDBIT_SETTING_TX_INVERT (1 << 1) 47 | #define CDBIT_SETTING_USER_CRC (1 << 2) 48 | #define CDBIT_SETTING_NO_DROP (1 << 3) 49 | #define CDBIT_SETTING_ARBITRATE (1 << 4) 50 | #define CDBIT_SETTING_BREAK_SYNC (1 << 5) 51 | #define CDBIT_SETTING_FULL_DUPLEX (1 << 6) 52 | 53 | #define CDBIT_FLAG_BUS_IDLE (1 << 0) 54 | #define CDBIT_FLAG_RX_PENDING (1 << 1) 55 | #define CDBIT_FLAG_RX_BREAK (1 << 2) 56 | #define CDBIT_FLAG_RX_LOST (1 << 3) 57 | #define CDBIT_FLAG_RX_ERROR (1 << 4) 58 | #define CDBIT_FLAG_TX_BUF_CLEAN (1 << 5) 59 | #define CDBIT_FLAG_TX_CD (1 << 6) 60 | #define CDBIT_FLAG_TX_ERROR (1 << 7) 61 | 62 | #define CDBIT_RX_RST_POINTER (1 << 0) 63 | #define CDBIT_RX_CLR_PENDING (1 << 1) 64 | #define CDBIT_RX_RST (1 << 4) 65 | #define CDBIT_RX_RST_ALL (CDBIT_RX_RST | CDBIT_RX_RST_POINTER) 66 | 67 | #define CDBIT_TX_RST_POINTER (1 << 0) 68 | #define CDBIT_TX_START (1 << 1) 69 | #define CDBIT_TX_ABORT (1 << 4) 70 | #define CDBIT_TX_SEND_BREAK (1 << 5) 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /parser/cdnet.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "cdbus.h" 11 | #include "cdnet.h" 12 | 13 | 14 | int cdn_hdr_size_pkt(const cdn_pkt_t *pkt) 15 | { 16 | if (pkt->src.addr[0] == 0x10) // localhost 17 | return 0; 18 | if (pkt->src.addr[0] == 0x00) // level 0 19 | return 2; 20 | 21 | int hdr_size = 1; 22 | if (pkt->src.addr[0] == 0xa0) 23 | hdr_size += 4; 24 | else if (pkt->dst.addr[0] == 0xf0) 25 | hdr_size += 2; 26 | 27 | hdr_size += pkt->src.port <= 0xff ? 1 : 2; 28 | hdr_size += pkt->dst.port <= 0xff ? 1 : 2; 29 | return hdr_size; 30 | } 31 | 32 | int cdn_hdr_size_frm(const cd_frame_t *frm) 33 | { 34 | uint8_t hdr = frm->dat[3]; 35 | 36 | if ((hdr & 0x80) == 0) // level 0 37 | return 2; 38 | 39 | int hdr_size = 3; 40 | if (hdr & 0x20) 41 | hdr_size += 4; 42 | else if (hdr & 0x10) 43 | hdr_size += 2; 44 | 45 | if (hdr & 2) 46 | hdr_size ++; 47 | if (hdr & 1) 48 | hdr_size ++; 49 | return hdr_size; 50 | } 51 | -------------------------------------------------------------------------------- /parser/cdnet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CDNET_H__ 11 | #define __CDNET_H__ 12 | 13 | #include "cd_utils.h" 14 | #include "cd_list.h" 15 | 16 | #ifndef cdn_assert 17 | #define CDN_ERR_ASSERT -1 18 | #define cdn_assert(expr) { if (!(expr)) return CDN_ERR_ASSERT; } 19 | #endif 20 | 21 | #if __BYTE_ORDER != __LITTLE_ENDIAN 22 | #error "This library currently only supports little-endian" 23 | #endif 24 | 25 | typedef enum { 26 | CDN_MULTI_NONE = 0, 27 | CDN_MULTI_CAST, 28 | CDN_MULTI_NET, 29 | CDN_MULTI_CAST_NET 30 | } cdn_multi_t; 31 | 32 | 33 | typedef struct { 34 | uint8_t addr[3]; // [type, net, mac] or [type, mh, ml] 35 | uint16_t port; 36 | } cdn_sockaddr_t; 37 | 38 | 39 | #ifndef CDN_MAX_DAT 40 | #define CDN_MAX_DAT 252 // allow smaller sizes to save memory 41 | #endif 42 | 43 | #define CDN_CONF_NOT_FREE (1 << 0) // not free packet after transmit 44 | 45 | #define CDN_RET_FMT_ERR (1 << 1) 46 | #define CDN_RET_ROUTE_ERR (1 << 2) 47 | 48 | typedef struct { 49 | list_node_t node; 50 | uint8_t _s_mac; 51 | uint8_t _d_mac; 52 | uint8_t _l_net; // local net 53 | 54 | uint8_t conf; 55 | uint8_t ret; // bit7: 0: init, 1: finished (bit6~0: error code) 56 | 57 | cdn_sockaddr_t src; 58 | cdn_sockaddr_t dst; 59 | 60 | cd_frame_t *frm; 61 | uint8_t *dat; 62 | uint8_t len; 63 | } cdn_pkt_t; 64 | 65 | #ifdef CDN_IRQ_SAFE 66 | #define cdn_list_get(head) list_get_entry_it(head, cdn_pkt_t) 67 | #define cdn_list_put(head, frm) list_put_it(head, &(frm)->node) 68 | #elif !defined(CDN_USER_LIST) 69 | #define cdn_list_get(head) list_get_entry(head, cdn_pkt_t) 70 | #define cdn_list_put(head, frm) list_put(head, &(frm)->node) 71 | #endif 72 | 73 | int cdn_hdr_size_pkt(const cdn_pkt_t *pkt); 74 | int cdn_hdr_size_frm(const cd_frame_t *frm); 75 | 76 | int cdn0_hdr_w(const cdn_pkt_t *pkt, uint8_t *hdr); 77 | int cdn0_hdr_r(cdn_pkt_t *pkt, const uint8_t *hdr); 78 | int cdn1_hdr_w(const cdn_pkt_t *pkt, uint8_t *hdr); 79 | int cdn1_hdr_r(cdn_pkt_t *pkt, const uint8_t *hdr); 80 | 81 | int cdn0_frame_w(cdn_pkt_t *pkt); 82 | int cdn0_frame_r(cdn_pkt_t *pkt); 83 | int cdn1_frame_w(cdn_pkt_t *pkt); 84 | int cdn1_frame_r(cdn_pkt_t *pkt); 85 | 86 | static inline void cdn_set_addr(uint8_t *addr, uint8_t a0, uint8_t a1, uint8_t a2) 87 | { 88 | addr[0] = a0; 89 | addr[1] = a1; 90 | addr[2] = a2; 91 | } 92 | 93 | static inline int cdn_frame_w(cdn_pkt_t *pkt) 94 | { 95 | if (pkt->src.addr[0] == 0x10) // localhost 96 | return -1; 97 | if (pkt->src.addr[0] == 0x00) 98 | return cdn0_frame_w(pkt); 99 | return cdn1_frame_w(pkt); 100 | } 101 | 102 | static inline int cdn_frame_r(cdn_pkt_t *pkt) 103 | { 104 | if (pkt->frm->dat[3] & 0x80) 105 | return cdn1_frame_r(pkt); 106 | return cdn0_frame_r(pkt); 107 | } 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /parser/cdnet_l0.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "cdbus.h" 11 | #include "cdnet.h" 12 | 13 | 14 | int cdn0_hdr_w(const cdn_pkt_t *pkt, uint8_t *hdr) 15 | { 16 | const cdn_sockaddr_t *src = &pkt->src; 17 | const cdn_sockaddr_t *dst = &pkt->dst; 18 | 19 | cdn_assert(dst->addr[0] == 0x00); 20 | cdn_assert((src->port & 0xff80) == 0); 21 | cdn_assert((dst->port & 0xff80) == 0); 22 | 23 | *hdr = src->port; 24 | *(hdr + 1) = dst->port; 25 | return 2; 26 | } 27 | 28 | int cdn0_frame_w(cdn_pkt_t *pkt) 29 | { 30 | uint8_t *frame = pkt->frm->dat; 31 | frame[0] = pkt->src.addr[2]; 32 | frame[1] = pkt->dst.addr[2]; 33 | int len = cdn0_hdr_w(pkt, frame + 3); 34 | if (len < 0) 35 | return len; 36 | frame[2] = pkt->len + len; 37 | return 0; 38 | } 39 | 40 | 41 | int cdn0_hdr_r(cdn_pkt_t *pkt, const uint8_t *hdr) 42 | { 43 | cdn_sockaddr_t *src = &pkt->src; 44 | cdn_sockaddr_t *dst = &pkt->dst; 45 | 46 | cdn_assert((*hdr & 0x80) == 0); 47 | src->addr[0] = 0; 48 | src->addr[1] = pkt->_l_net; 49 | dst->addr[0] = 0; 50 | dst->addr[1] = pkt->_l_net; 51 | src->addr[2] = pkt->_s_mac; 52 | dst->addr[2] = pkt->_d_mac; 53 | 54 | src->port = *hdr; 55 | dst->port = *(hdr + 1); 56 | cdn_assert((dst->port & 0x80) == 0); 57 | return 2; 58 | } 59 | 60 | // addition in: _l_net 61 | int cdn0_frame_r(cdn_pkt_t *pkt) 62 | { 63 | uint8_t *frame = pkt->frm->dat; 64 | pkt->_s_mac = frame[0]; 65 | pkt->_d_mac = frame[1]; 66 | int len = cdn0_hdr_r(pkt, frame + 3); 67 | if (len < 0) 68 | return len; 69 | pkt->dat = frame + 3 + len; 70 | pkt->len = frame[2] - len; 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /parser/cdnet_l1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "cdbus.h" 11 | #include "cdnet.h" 12 | 13 | 14 | int cdn1_hdr_w(const cdn_pkt_t *pkt, uint8_t *hdr) 15 | { 16 | const cdn_sockaddr_t *src = &pkt->src; 17 | const cdn_sockaddr_t *dst = &pkt->dst; 18 | uint8_t *buf = hdr + 1; 19 | cdn_multi_t multi = CDN_MULTI_NONE; 20 | 21 | if (src->addr[0] == 0xa0) 22 | multi |= CDN_MULTI_NET; 23 | if (dst->addr[0] == 0xf0) 24 | multi |= CDN_MULTI_CAST; 25 | 26 | *hdr = 0x80 | (multi << 4); // hdr 27 | 28 | if (multi & CDN_MULTI_NET) { 29 | *buf++ = src->addr[1]; 30 | *buf++ = src->addr[2]; 31 | } 32 | if (multi != CDN_MULTI_NONE) { 33 | *buf++ = dst->addr[1]; 34 | *buf++ = dst->addr[2]; 35 | } 36 | 37 | *buf++ = src->port & 0xff; 38 | if (src->port & 0xff00) { 39 | *buf++ = src->port >> 8; 40 | *hdr |= 2; 41 | } 42 | 43 | *buf++ = dst->port & 0xff; 44 | if (dst->port & 0xff00) { 45 | *buf++ = dst->port >> 8; 46 | *hdr |= 1; 47 | } 48 | 49 | return buf - hdr; 50 | } 51 | 52 | // addition in: _s_mac, _d_mac 53 | int cdn1_frame_w(cdn_pkt_t *pkt) 54 | { 55 | uint8_t *frame = pkt->frm->dat; 56 | frame[0] = pkt->_s_mac; 57 | frame[1] = pkt->_d_mac; 58 | int len = cdn1_hdr_w(pkt, frame + 3); 59 | if (len < 0) 60 | return len; 61 | frame[2] = pkt->len + len; 62 | return 0; 63 | } 64 | 65 | 66 | int cdn1_hdr_r(cdn_pkt_t *pkt, const uint8_t *hdr) 67 | { 68 | cdn_sockaddr_t *src = &pkt->src; 69 | cdn_sockaddr_t *dst = &pkt->dst; 70 | 71 | const uint8_t *buf = hdr + 1; 72 | cdn_assert((*hdr & 0xc8) == 0x80); 73 | cdn_multi_t multi = (*hdr >> 4) & 3; 74 | 75 | if (multi & CDN_MULTI_NET) { 76 | src->addr[0] = 0xa0; 77 | src->addr[1] = *buf++; 78 | src->addr[2] = *buf++; 79 | } else { 80 | src->addr[0] = 0x80; 81 | src->addr[1] = pkt->_l_net; 82 | src->addr[2] = pkt->_s_mac; 83 | } 84 | if (multi != CDN_MULTI_NONE) { 85 | dst->addr[0] = (multi & CDN_MULTI_CAST) ? 0xf0 : 0xa0; 86 | dst->addr[1] = *buf++; 87 | dst->addr[2] = *buf++; 88 | } else { 89 | dst->addr[0] = 0x80; 90 | dst->addr[1] = pkt->_l_net; 91 | dst->addr[2] = pkt->_d_mac; 92 | } 93 | 94 | src->port = *buf++; 95 | if (*hdr & 2) 96 | src->port |= *buf++ << 8; 97 | 98 | dst->port = *buf++; 99 | if (*hdr & 1) 100 | dst->port |= *buf++ << 8; 101 | 102 | return buf - hdr; 103 | } 104 | 105 | // addition in: _l_net 106 | int cdn1_frame_r(cdn_pkt_t *pkt) 107 | { 108 | uint8_t *frame = pkt->frm->dat; 109 | pkt->_s_mac = frame[0]; 110 | pkt->_d_mac = frame[1]; 111 | int len = cdn1_hdr_r(pkt, frame + 3); 112 | if (len < 0) 113 | return len; 114 | pkt->dat = frame + 3 + len; 115 | pkt->len = frame[2] - len; 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /utils/cd_debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CD_DEBUG_H__ 11 | #define __CD_DEBUG_H__ 12 | 13 | #include "cd_utils.h" 14 | 15 | #ifndef d_printf 16 | #define d_printf(fmt, ...) printf(fmt, ## __VA_ARGS__) 17 | #endif 18 | #ifndef d_puts 19 | #define d_puts(str) d_printf("%s", str) 20 | #endif 21 | 22 | #define d_info(fmt, ...) d_printf("I: " fmt, ## __VA_ARGS__) 23 | #ifndef d_warn 24 | #define d_warn(fmt, ...) d_printf("W: " fmt, ## __VA_ARGS__) 25 | #endif 26 | #ifndef d_error 27 | #define d_error(fmt, ...) d_printf("E: " fmt, ## __VA_ARGS__) 28 | #endif 29 | 30 | #define dn_info(name, fmt, ...) d_info("%s: " fmt, name, ## __VA_ARGS__) 31 | #define dn_warn(name, fmt, ...) d_warn("%s: " fmt, name, ## __VA_ARGS__) 32 | #define dn_error(name, fmt, ...) d_error("%s: " fmt, name, ## __VA_ARGS__) 33 | 34 | #define df_info(fmt, ...) dn_info(__FUNCTION__, fmt, ## __VA_ARGS__) 35 | #define df_warn(fmt, ...) dn_warn(__FUNCTION__, fmt, ## __VA_ARGS__) 36 | #define df_error(fmt, ...) dn_error(__FUNCTION__, fmt, ## __VA_ARGS__) 37 | 38 | #define dnf_info(name, fmt, ...) d_info("%s: %s: " fmt, name, __FUNCTION__, ## __VA_ARGS__) 39 | #define dnf_warn(name, fmt, ...) d_warn("%s: %s: " fmt, name, __FUNCTION__, ## __VA_ARGS__) 40 | #define dnf_error(name, fmt, ...) d_error("%s: %s: " fmt, name, __FUNCTION__, ## __VA_ARGS__) 41 | 42 | #ifdef CD_VERBOSE 43 | #define d_verbose_c(fmt, ...) d_printf(fmt, ## __VA_ARGS__) 44 | #define d_verbose(fmt, ...) d_printf("V: " fmt, ## __VA_ARGS__) 45 | #define dn_verbose(name, fmt, ...) d_verbose("%s: " fmt, name, ## __VA_ARGS__) 46 | #define df_verbose(fmt, ...) dn_verbose(__FUNCTION__, fmt, ## __VA_ARGS__) 47 | #define dnf_verbose(name, fmt, ...) d_verbose("%s: %s: " fmt, name, __FUNCTION__, ## __VA_ARGS__) 48 | #ifndef CD_DEBUG 49 | #define CD_DEBUG 50 | #endif // CD_DEBUG 51 | #else 52 | #define d_verbose_c(fmt, ...) do {} while (0) 53 | #define d_verbose(fmt, ...) do {} while (0) 54 | #define dn_verbose(name, ...) do {} while (0) 55 | #define df_verbose(name, ...) do {} while (0) 56 | #define dnf_verbose(name, ...) do {} while (0) 57 | #endif 58 | 59 | #ifdef CD_DEBUG 60 | #define d_debug_c(fmt, ...) d_printf(fmt, ## __VA_ARGS__) 61 | #define d_debug(fmt, ...) d_printf("D: " fmt, ## __VA_ARGS__) 62 | #define dn_debug(name, fmt, ...) d_debug("%s: " fmt, name, ## __VA_ARGS__) 63 | #define df_debug(fmt, ...) dn_debug(__FUNCTION__, fmt, ## __VA_ARGS__) 64 | #define dnf_debug(name, fmt, ...) d_debug("%s: %s: " fmt, name, __FUNCTION__, ## __VA_ARGS__) 65 | #else 66 | #define d_debug_c(fmt, ...) do {} while (0) 67 | #define d_debug(fmt, ...) do {} while (0) 68 | #define dn_debug(name, ...) do {} while (0) 69 | #define df_debug(name, ...) do {} while (0) 70 | #define dnf_debug(name, ...) do {} while (0) 71 | #endif 72 | 73 | 74 | void hex_dump_small(char *pbuf, const void *addr, int len, int max); 75 | void hex_dump(const void *addr, int len); 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /utils/cd_list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "cd_utils.h" 11 | #include "cd_list.h" 12 | 13 | #ifdef CD_LIST_DEBUG 14 | #include 15 | static void list_check(list_head_t *head); 16 | #endif 17 | 18 | // pick first item 19 | list_node_t *list_get(list_head_t *head) 20 | { 21 | list_node_t *node = NULL; 22 | if (head->len) { 23 | node = head->first; 24 | head->first = node->next; 25 | if (--head->len == 0) 26 | head->last = NULL; 27 | } 28 | #ifdef CD_LIST_DEBUG 29 | list_check(head); 30 | #endif 31 | return node; 32 | } 33 | 34 | // append item at end 35 | void list_put(list_head_t *head, list_node_t *node) 36 | { 37 | if (head->len++) 38 | head->last->next = node; 39 | else 40 | head->first = node; 41 | head->last = node; 42 | node->next = NULL; 43 | #ifdef CD_LIST_DEBUG 44 | list_check(head); 45 | #endif 46 | } 47 | 48 | list_node_t *list_get_last(list_head_t *head) 49 | { 50 | list_node_t *pre = NULL; 51 | list_node_t *node = head->first; 52 | 53 | if (!node) 54 | return NULL; 55 | 56 | while (node->next) { 57 | pre = node; 58 | node = node->next; 59 | } 60 | 61 | if (pre) { 62 | pre->next = NULL; 63 | head->last = pre; 64 | } else { 65 | head->first = head->last = NULL; 66 | } 67 | head->len--; 68 | 69 | #ifdef CD_LIST_DEBUG 70 | list_check(head); 71 | #endif 72 | return node; 73 | } 74 | 75 | void list_put_begin(list_head_t *head, list_node_t *node) 76 | { 77 | node->next = head->first; 78 | head->first = node; 79 | if (!head->len++) 80 | head->last = node; 81 | #ifdef CD_LIST_DEBUG 82 | list_check(head); 83 | #endif 84 | } 85 | 86 | void list_pick(list_head_t *head, list_node_t *pre, list_node_t *node) 87 | { 88 | if (pre) 89 | pre->next = node->next; 90 | else 91 | head->first = node->next; 92 | if (--head->len == 0) 93 | head->last = NULL; 94 | #ifdef CD_LIST_DEBUG 95 | list_check(head); 96 | #endif 97 | } 98 | 99 | void list_move_begin(list_head_t *head, list_node_t *pre, list_node_t *node) 100 | { 101 | if (!pre) 102 | return; 103 | 104 | pre->next = node->next; 105 | node->next = head->first; 106 | head->first = node; 107 | 108 | if (head->last == node) 109 | head->last = pre; 110 | #ifdef CD_LIST_DEBUG 111 | list_check(head); 112 | #endif 113 | } 114 | 115 | 116 | #ifdef CD_LIST_DEBUG 117 | static _Unwind_Reason_Code trace_fcn(_Unwind_Context *ctx, void *_) 118 | { 119 | printf("bt: [%08x]\n", _Unwind_GetIP(ctx)); 120 | return _URC_NO_REASON; 121 | } 122 | 123 | static void list_check(list_head_t *head) 124 | { 125 | int len = 0; 126 | list_node_t *node = head->first; 127 | list_node_t *pre = NULL; 128 | 129 | while (node) { 130 | pre = node; 131 | node = node->next; 132 | len++; 133 | } 134 | 135 | if (head->len != len) { 136 | printf("PANIC: list %p, wrong len: %"PRIu32", %d\n", head, head->len, len); 137 | _Unwind_Backtrace(&trace_fcn, NULL); 138 | while (true); 139 | } 140 | if (head->last != pre) { 141 | printf("PANIC: list %p, wrong head->last: %p, %p, len: %"PRIu32", %d\n", 142 | head, head->last, node, head->len, len); 143 | _Unwind_Backtrace(&trace_fcn, NULL); 144 | while (true); 145 | } 146 | } 147 | #endif 148 | -------------------------------------------------------------------------------- /utils/cd_list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CD_LIST_H__ 11 | #define __CD_LIST_H__ 12 | 13 | typedef struct list_node { 14 | struct list_node *next; 15 | } list_node_t; 16 | 17 | typedef struct { 18 | list_node_t *first; 19 | list_node_t *last; 20 | uint32_t len; 21 | } list_head_t; 22 | 23 | 24 | list_node_t *list_get(list_head_t *head); 25 | void list_put(list_head_t *head, list_node_t *node); 26 | 27 | list_node_t *list_get_last(list_head_t *head); 28 | void list_put_begin(list_head_t *head, list_node_t *node); 29 | void list_pick(list_head_t *head, list_node_t *pre, list_node_t *node); 30 | void list_move_begin(list_head_t *head, list_node_t *pre, list_node_t *node); 31 | 32 | 33 | #define list_entry(ptr, type) \ 34 | container_of(ptr, type, node) 35 | 36 | #define list_entry_safe(ptr, type) ({ \ 37 | list_node_t *__ptr = (ptr); \ 38 | __ptr ? container_of(__ptr, type, node) : NULL; \ 39 | }) 40 | 41 | #define list_get_entry(head, type) \ 42 | list_entry_safe(list_get(head), type) 43 | 44 | #ifdef CD_LIST_IT 45 | #define list_get_entry_it(head, type) \ 46 | list_entry_safe(list_get_it(head), type) 47 | #endif 48 | 49 | #define list_for_each(head, pre, pos) \ 50 | for (pre = NULL, pos = (head)->first; pos != NULL; \ 51 | pre = pos, pos = (pos ? (pos)->next : (head)->first)) 52 | // you can remove a node during the loop: 53 | // list_pick(head, pre, pos); 54 | // pos = pre; 55 | 56 | // read only version: 57 | #define list_for_each_ro(head, pos) \ 58 | for (pos = (head)->first; pos != NULL; \ 59 | pos = (pos ? (pos)->next : (head)->first)) 60 | 61 | #define list_head_init(head) \ 62 | memset(head, 0, sizeof(list_head_t)) 63 | 64 | 65 | #ifdef CD_LIST_IT 66 | 67 | static inline list_node_t *list_get_it(list_head_t *head) 68 | { 69 | uint32_t flags; 70 | list_node_t *node; 71 | local_irq_save(flags); 72 | node = list_get(head); 73 | local_irq_restore(flags); 74 | return node; 75 | } 76 | 77 | static inline void list_put_it(list_head_t *head, list_node_t *node) 78 | { 79 | uint32_t flags; 80 | local_irq_save(flags); 81 | list_put(head, node); 82 | local_irq_restore(flags); 83 | } 84 | 85 | static inline void list_put_begin_it(list_head_t *head, list_node_t *node) 86 | { 87 | uint32_t flags; 88 | local_irq_save(flags); 89 | list_put_begin(head, node); 90 | local_irq_restore(flags); 91 | } 92 | 93 | #endif // CD_LIST_IT 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /utils/cd_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __CD_UTILS_H__ 11 | #define __CD_UTILS_H__ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include // provide offsetof, NULL 20 | #include 21 | #include 22 | 23 | #ifdef __has_include 24 | #if __has_include("cd_config.h") 25 | #include "cd_config.h" 26 | #endif 27 | #if __has_include("arch_wrapper.h") 28 | #include "arch_wrapper.h" 29 | #endif 30 | #else 31 | #include "cd_config.h" 32 | #include "arch_wrapper.h" 33 | #endif 34 | 35 | //#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 36 | //#define NULL 0 37 | 38 | #ifndef container_of 39 | #define container_of(ptr, type, member) \ 40 | ((type *)((char *)(ptr) - offsetof(type, member))) 41 | #endif 42 | 43 | #if !__STRICT_ANSI__ && __GNUC__ >= 3 44 | 45 | #ifndef sign 46 | #define sign(a) ({ \ 47 | typeof(a) __a = (a); \ 48 | __a < 0 ? -1 : (__a > 0); \ 49 | }) 50 | #endif 51 | #ifndef max 52 | #define max(a, b) ({ \ 53 | typeof(a) __a = (a); \ 54 | typeof(b) __b = (b); \ 55 | __a > __b ? __a : __b; \ 56 | }) 57 | #endif 58 | #ifndef min 59 | #define min(a, b) ({ \ 60 | typeof(a) __a = (a); \ 61 | typeof(b) __b = (b); \ 62 | __a < __b ? __a : __b; \ 63 | }) 64 | #endif 65 | #ifndef clip 66 | #define clip(a, b, c) ({ \ 67 | typeof(a) __a = (a); \ 68 | typeof(b) __b = (b); \ 69 | typeof(c) __c = (c); \ 70 | __a < __b ? __b : (__a > __c ? __c : __a); \ 71 | }) 72 | #endif 73 | #ifndef swap 74 | #define swap(a, b) \ 75 | do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) 76 | #endif 77 | 78 | /* 79 | * Divide positive or negative dividend by positive divisor and round 80 | * to closest integer. Result is undefined for negative divisors and 81 | * for negative dividends if the divisor variable type is unsigned. 82 | */ 83 | #define DIV_ROUND_CLOSEST(x, divisor)({ \ 84 | typeof(x) __x = x; \ 85 | typeof(divisor) __d = divisor; \ 86 | (((typeof(x))-1) > 0 || ((typeof(divisor))-1) > 0 || (__x) > 0) ? \ 87 | (((__x) + ((__d) / 2)) / (__d)) : \ 88 | (((__x) - ((__d) / 2)) / (__d)); \ 89 | }) 90 | 91 | #ifndef __weak 92 | #define __weak __attribute__((weak)) 93 | #endif 94 | 95 | #else 96 | 97 | // simple version without GCC's statement expression ({...}) 98 | #ifndef sign 99 | #define sign(a) (((a) < 0) ? -1 : ((a) > 0)) 100 | #endif 101 | #ifndef max 102 | #define max(a, b) ((a) > (b) ? (a) : (b)) 103 | #endif 104 | #ifndef min 105 | #define min(a, b) ((a) < (b) ? (a) : (b)) 106 | #endif 107 | #ifndef clip 108 | #define clip(a, b, c) ((a) < (b) ? (b) : ((a) > (c) ? (c) : (a))) 109 | #endif 110 | #define DIV_ROUND_CLOSEST(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) 111 | 112 | #endif 113 | 114 | 115 | static inline uint16_t get_unaligned16(const uint8_t *p) 116 | { 117 | return p[0] | p[1] << 8; 118 | } 119 | 120 | static inline uint32_t get_unaligned32(const uint8_t *p) 121 | { 122 | return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; 123 | } 124 | 125 | static inline void put_unaligned16(uint16_t val, uint8_t *p) 126 | { 127 | *p++ = val; 128 | *p++ = val >> 8; 129 | } 130 | 131 | static inline void put_unaligned32(uint32_t val, uint8_t *p) 132 | { 133 | put_unaligned16(val >> 16, p + 2); 134 | put_unaligned16(val, p); 135 | } 136 | 137 | 138 | static inline uint16_t get_unaligned_be16(const uint8_t *p) 139 | { 140 | return p[1] | p[0] << 8; 141 | } 142 | 143 | static inline uint32_t get_unaligned_be32(const uint8_t *p) 144 | { 145 | return p[3] | p[2] << 8 | p[1] << 16 | p[0] << 24; 146 | } 147 | 148 | static inline void put_unaligned_be16(uint16_t val, uint8_t *p) 149 | { 150 | *p++ = val >> 8; 151 | *p++ = val; 152 | } 153 | 154 | static inline void put_unaligned_be32(uint32_t val, uint8_t *p) 155 | { 156 | put_unaligned_be16(val >> 16, p); 157 | put_unaligned_be16(val, p + 2); 158 | } 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /utils/hex_dump.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: 8 | * Dominik Liebler 9 | * Duke Fong 10 | */ 11 | 12 | #include "cd_utils.h" 13 | #include "cd_debug.h" 14 | 15 | 16 | void hex_dump_small(char *pbuf, const void *addr, int len, int limit) 17 | { 18 | int i; 19 | int dump_len = min(len, limit); 20 | int pos = 0; 21 | const uint8_t *pc = (const uint8_t *)addr; 22 | 23 | if (len == 0 || len < 0) 24 | pbuf[0] = '\0'; 25 | for (i = 0; i < dump_len; i++) 26 | pos += sprintf(pbuf + pos, i ? " %02x" : "%02x", pc[i]); 27 | if (dump_len != len) 28 | sprintf(pbuf + pos, " ..."); 29 | } 30 | 31 | void hex_dump(const void *addr, int len) 32 | { 33 | int i; 34 | int pos = 0; 35 | char pbuf[75]; 36 | char sbuf[17]; 37 | const uint8_t *pc = (const uint8_t *)addr; 38 | 39 | if (len == 0 || len < 0) 40 | return; 41 | 42 | for (i = 0; i < len; i++) { 43 | if ((i % 16) == 0) { 44 | if (i != 0) { 45 | pos += sprintf(pbuf + pos, " %s\n", sbuf); 46 | d_puts(pbuf); 47 | pos = 0; 48 | } 49 | pos += sprintf(pbuf + pos, " %04x ", i); // offset 50 | } 51 | pos += sprintf(pbuf + pos, " %02x", pc[i]); 52 | 53 | // printable ascii character 54 | if (pc[i] < 0x20 || pc[i] > 0x7e) 55 | sbuf[i % 16] = '.'; 56 | else 57 | sbuf[i % 16] = pc[i]; 58 | sbuf[(i % 16) + 1] = '\0'; 59 | } 60 | 61 | // pad out last line 62 | while ((i % 16) != 0) { 63 | pos += sprintf(pbuf + pos, " "); 64 | i++; 65 | } 66 | // print the final ascii field 67 | pos += sprintf(pbuf + pos, " %s\n", sbuf); 68 | d_puts(pbuf); 69 | } 70 | -------------------------------------------------------------------------------- /utils/modbus_crc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #include "modbus_crc.h" 11 | 12 | #if defined(CD_CRC_NO_TBL) 13 | 14 | static inline uint16_t crc16_byte(uint8_t data, uint16_t crc_val) 15 | { 16 | crc_val ^= data; 17 | for (int i = 0; i < 8; i++) 18 | crc_val = (crc_val & 1) ? (crc_val >> 1) ^ 0xa001 : crc_val >> 1; 19 | return crc_val; 20 | } 21 | 22 | uint16_t crc16_sub(const uint8_t *data, uint32_t length, uint16_t crc_val) 23 | { 24 | while (length--) 25 | crc_val = crc16_byte(*data++, crc_val); 26 | return crc_val; 27 | } 28 | 29 | #elif defined(CD_CRC_SM_TBL) 30 | 31 | #ifdef CD_CRC_GEN_TBL 32 | static uint16_t crc16_table[16]; 33 | void crc16_table_init(void) 34 | { 35 | for (uint8_t i = 0; i < 16; i++) { 36 | uint16_t crc = i; 37 | for (uint8_t j = 0; j < 4; j++) 38 | crc = (crc & 1) ? (crc >> 1) ^ 0xa001 : crc >> 1; 39 | crc16_table[i] = crc; 40 | } 41 | } 42 | #else 43 | static const uint16_t crc16_table[] = { 44 | 0x0000, 0xcc01, 0xd801, 0x1400, 0xf001, 0x3c00, 0x2800, 0xe401, 45 | 0xa001, 0x6c00, 0x7800, 0xb401, 0x5000, 0x9c01, 0x8801, 0x4400 46 | }; 47 | #endif 48 | 49 | uint16_t crc16_sub(const uint8_t *data, uint32_t length, uint16_t crc_val) 50 | { 51 | while (length--) { 52 | uint8_t d = *data++; 53 | crc_val = (crc_val >> 4) ^ crc16_table[(crc_val ^ d) & 0xf]; 54 | crc_val = (crc_val >> 4) ^ crc16_table[(crc_val ^ (d >> 4)) & 0xf]; 55 | } 56 | return crc_val; 57 | } 58 | 59 | 60 | #else 61 | 62 | #ifdef CD_CRC_GEN_TBL 63 | static uint16_t crc16_table[256]; 64 | void crc16_table_init(void) 65 | { 66 | for (int i = 0; i < 256; i++) { 67 | uint16_t crc = i; 68 | for (uint8_t j = 0; j < 8; j++) 69 | crc = (crc & 1) ? (crc >> 1) ^ 0xa001 : crc >> 1; 70 | crc16_table[i] = crc; 71 | } 72 | } 73 | #else 74 | static const uint16_t crc16_table[] = { 75 | 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 76 | 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 77 | 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 78 | 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 79 | 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 80 | 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 81 | 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 82 | 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 83 | 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, 84 | 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 85 | 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 86 | 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 87 | 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 88 | 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, 89 | 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 90 | 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, 91 | 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 92 | 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, 93 | 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 94 | 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 95 | 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 96 | 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 97 | 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 98 | 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 99 | 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 100 | 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 101 | 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 102 | 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 103 | 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 104 | 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 105 | 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 106 | 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 107 | }; 108 | #endif 109 | 110 | uint16_t crc16_sub(const uint8_t *data, uint32_t length, uint16_t crc_val) 111 | { 112 | while (length--) { 113 | uint8_t tmp = *data++ ^ crc_val; 114 | crc_val >>= 8; 115 | crc_val ^= crc16_table[tmp]; 116 | } 117 | return crc_val; 118 | } 119 | #endif 120 | -------------------------------------------------------------------------------- /utils/modbus_crc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Software License Agreement (MIT License) 3 | * 4 | * Copyright (c) 2017, DUKELEC, Inc. 5 | * All rights reserved. 6 | * 7 | * Author: Duke Fong 8 | */ 9 | 10 | #ifndef __MODBUS_CRC_H__ 11 | #define __MODBUS_CRC_H__ 12 | 13 | #include "cd_utils.h" 14 | 15 | #ifdef CD_CRC_GEN_TBL 16 | void crc16_table_init(void); 17 | #endif 18 | 19 | uint16_t crc16_sub(const uint8_t *data, uint32_t length, uint16_t crc_val); 20 | 21 | static inline uint16_t crc16(const uint8_t *data, uint32_t length) 22 | { 23 | return crc16_sub(data, length, 0xffff); 24 | } 25 | 26 | #endif 27 | --------------------------------------------------------------------------------