serial_protocol.c (5764B)
1 // Copyright 2022 Stefan Kerkmann 2 // SPDX-License-Identifier: GPL-2.0-or-later 3 4 #include <ch.h> 5 6 #include "serial.h" 7 #include "serial_protocol.h" 8 #include "synchronization_util.h" 9 10 static inline bool initiate_transaction(uint8_t transaction_id); 11 static inline bool react_to_transaction(void); 12 13 /** 14 * @brief This thread runs on the slave and responds to transactions initiated 15 * by the master. 16 */ 17 static THD_WORKING_AREA(waSlaveThread, 1024); 18 static THD_FUNCTION(SlaveThread, arg) { 19 (void)arg; 20 chRegSetThreadName("split_protocol_tx_rx"); 21 22 while (true) { 23 if (unlikely(!react_to_transaction())) { 24 /* Clear the receive queue, to start with a clean slate. 25 * Parts of failed transactions or spurious bytes could still be in it. */ 26 serial_transport_driver_clear(); 27 } 28 } 29 } 30 31 /** 32 * @brief Slave specific initializations. 33 */ 34 void soft_serial_target_init(void) { 35 serial_transport_driver_slave_init(); 36 37 /* Start transport thread. */ 38 chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); 39 } 40 41 /** 42 * @brief Master specific initializations. 43 */ 44 void soft_serial_initiator_init(void) { 45 serial_transport_driver_master_init(); 46 } 47 48 /** 49 * @brief React to transactions started by the master. 50 */ 51 static inline bool react_to_transaction(void) { 52 uint8_t transaction_id = 0; 53 /* Wait until there is a transaction for us. */ 54 if (unlikely(!serial_transport_receive_blocking(&transaction_id, sizeof(transaction_id)))) { 55 return false; 56 } 57 58 /* Sanity check that we are actually responding to a valid transaction. */ 59 if (unlikely(transaction_id >= NUM_TOTAL_TRANSACTIONS)) { 60 return false; 61 } 62 63 split_shared_memory_lock_autounlock(); 64 65 split_transaction_desc_t* transaction = &split_transaction_table[transaction_id]; 66 67 /* Send back the handshake which is XORed as a simple checksum, 68 to signal that the slave is ready to receive possible transaction buffers */ 69 transaction_id ^= NUM_TOTAL_TRANSACTIONS; 70 if (unlikely(!serial_transport_send(&transaction_id, sizeof(transaction_id)))) { 71 return false; 72 } 73 74 /* Receive transaction buffer from the master. If this transaction requires it.*/ 75 if (transaction->initiator2target_buffer_size) { 76 if (unlikely(!serial_transport_receive(split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size))) { 77 return false; 78 } 79 } 80 81 /* Allow any slave processing to occur. */ 82 if (transaction->slave_callback) { 83 transaction->slave_callback(transaction->initiator2target_buffer_size, split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size, split_trans_target2initiator_buffer(transaction)); 84 } 85 86 /* Send transaction buffer to the master. If this transaction requires it. */ 87 if (transaction->target2initiator_buffer_size) { 88 if (unlikely(!serial_transport_send(split_trans_target2initiator_buffer(transaction), transaction->target2initiator_buffer_size))) { 89 return false; 90 } 91 } 92 93 return true; 94 } 95 96 /** 97 * @brief Start transaction from the master half to the slave half. 98 * 99 * @param index Transaction Table index of the transaction to start. 100 * @return bool Indicates success of transaction. 101 */ 102 bool soft_serial_transaction(int index) { 103 /* Clear the receive queue, to start with a clean slate. 104 * Parts of failed transactions or spurious bytes could still be in it. */ 105 serial_transport_driver_clear(); 106 107 return initiate_transaction((uint8_t)index); 108 } 109 110 /** 111 * @brief Initiate transaction to slave half. 112 */ 113 static inline bool initiate_transaction(uint8_t transaction_id) { 114 /* Sanity check that we are actually starting a valid transaction. */ 115 if (unlikely(transaction_id >= NUM_TOTAL_TRANSACTIONS)) { 116 serial_dprintf("SPLIT: illegal transaction id\n"); 117 return false; 118 } 119 120 split_shared_memory_lock_autounlock(); 121 122 split_transaction_desc_t* transaction = &split_transaction_table[transaction_id]; 123 124 /* Send transaction table index to the slave, which doubles as basic handshake token. */ 125 if (unlikely(!serial_transport_send(&transaction_id, sizeof(transaction_id)))) { 126 serial_dprintf("SPLIT: sending handshake failed\n"); 127 return false; 128 } 129 130 uint8_t transaction_id_shake = 0xFF; 131 132 /* Which we always read back first so that we can error out correctly. 133 * - due to the half duplex limitations on return codes, we always have to read *something*. 134 * - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready. 135 */ 136 if (unlikely(!serial_transport_receive(&transaction_id_shake, sizeof(transaction_id_shake)) || (transaction_id_shake != (transaction_id ^ NUM_TOTAL_TRANSACTIONS)))) { 137 serial_dprintf("SPLIT: receiving handshake failed\n"); 138 return false; 139 } 140 141 /* Send transaction buffer to the slave. If this transaction requires it. */ 142 if (transaction->initiator2target_buffer_size) { 143 if (unlikely(!serial_transport_send(split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size))) { 144 serial_dprintf("SPLIT: sending buffer failed\n"); 145 return false; 146 } 147 } 148 149 /* Receive transaction buffer from the slave. If this transaction requires it. */ 150 if (transaction->target2initiator_buffer_size) { 151 if (unlikely(!serial_transport_receive(split_trans_target2initiator_buffer(transaction), transaction->target2initiator_buffer_size))) { 152 serial_dprintf("SPLIT: receiving buffer failed\n"); 153 return false; 154 } 155 } 156 157 return true; 158 }