Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
RailcomImpl.hxx
1
57#ifndef _FREERTOS_DRIVERS_COMMON_RAILCOMIMPL_HXX_
58#define _FREERTOS_DRIVERS_COMMON_RAILCOMIMPL_HXX_
59
62#include "dcc/RailCom.hxx"
63
64/*
65struct RailcomHw
66{
67 static const uint32_t CHANNEL_COUNT = 4;
68 static const uint32_t UART_PERIPH[CHANNEL_COUNT];
69 static const uint32_t UART_BASE[CHANNEL_COUNT];
70 // Make sure there are enough entries here for all the channels times a few
71 // DCC packets.
72 static const uint32_t Q_SIZE = 16;
73
74 static const auto OS_INTERRUPT = INT_UART2;
75
76 GPIO_HWPIN(CH1, GpioHwPin, C, 4, U4RX);
77 GPIO_HWPIN(CH2, GpioHwPin, C, 6, U3RX);
78 GPIO_HWPIN(CH3, GpioHwPin, G, 4, U2RX);
79 GPIO_HWPIN(CH4, GpioHwPin, E, 0, U7RX);
80
81 static void hw_init() {
82 CH1_Pin::hw_init();
83 CH2_Pin::hw_init();
84 CH3_Pin::hw_init();
85 CH4_Pin::hw_init();
86 }
87
88 static void set_input() {
89 CH1_Pin::set_input();
90 CH2_Pin::set_input();
91 CH3_Pin::set_input();
92 CH4_Pin::set_input();
93 }
94
95 static void set_hw() {
96 CH1_Pin::set_hw();
97 CH2_Pin::set_hw();
98 CH3_Pin::set_hw();
99 CH4_Pin::set_hw();
100 }
101
104 static uint8_t sample() {
105 uint8_t ret = 0;
106 if (!CH1_Pin::get()) ret |= 1;
107 if (!CH2_Pin::get()) ret |= 2;
108 if (!CH3_Pin::get()) ret |= 4;
109 if (!CH4_Pin::get()) ret |= 8;
110 return ret;
111 }
112};
113
114// The weak attribute is needed if the definition is put into a header file.
115const uint32_t RailcomHw::UART_BASE[] __attribute__((weak)) = {UART4_BASE, UART3_BASE, UART2_BASE, UART7_BASE};
116const uint32_t RailcomHw::UART_PERIPH[]
117__attribute__((weak)) = {SYSCTL_PERIPH_UART4, SYSCTL_PERIPH_UART3, SYSCTL_PERIPH_UART2, SYSCTL_PERIPH_UART7};
118
119*/
120
122template <class HW>
123class RailcomDriverBase : public RailcomDriver, private Node
124{
125public:
128 : Node(name)
129 , readableNotifiable_(nullptr)
130 {
131 HW::hw_init();
132 }
133
135 {
136 }
137
138private:
141 virtual void int_set_pending(unsigned int_nr) = 0;
142
143 ssize_t write(File *, const void *, size_t) OVERRIDE
144 {
145 // This device is read-only.
146 return -ENOSYS;
147 }
148
149 ssize_t read(File *file, void *buf, size_t count) OVERRIDE
150 {
151 if (count != sizeof(dcc::Feedback))
152 {
153 return -EINVAL;
154 }
155 OSMutexLock l(&lock_);
156 if (feedbackQueue_.empty())
157 {
158 return -EAGAIN;
159 }
160 memcpy(buf, &feedbackQueue_.front(), count);
162 return count;
163 }
164
166 {
167 while (!feedbackQueue_.empty())
168 {
170 }
171 }
172
173 // Asynchronous interface
174 int ioctl(File *file, unsigned long int key, unsigned long data) override
175 {
176 if (IOC_TYPE(key) == CAN_IOC_MAGIC &&
178 {
179 Notifiable *n = reinterpret_cast<Notifiable *>(data);
180 HASSERT(n);
181 // If there is no data for reading, we put the incoming notification
182 // into the holder. Otherwise we notify it immediately.
183 if (feedbackQueue_.empty())
184 {
185 portENTER_CRITICAL();
186 if (feedbackQueue_.empty())
187 {
188 // We are in a critical section now. If we got into this
189 // branch, then the buffer was full at the beginning of the
190 // critical section. If the hardware interrupt kicks in
191 // now, and sets the os_interrupt to pending, the os
192 // interrupt will not happen until we leave the critical
193 // section, and thus the swap will be in effect by then.
194 std::swap(n, readableNotifiable_);
195 }
196 portEXIT_CRITICAL();
197 }
198 if (n)
199 {
200 n->notify();
201 }
202 return 0;
203 }
204 errno = EINVAL;
205 return -1;
206 }
207
208public:
211 void os_interrupt_handler() __attribute__((always_inline))
212 {
213 if (!feedbackQueue_.empty())
214 {
216 readableNotifiable_ = nullptr;
217 if (n)
218 {
219 n->notify_from_isr();
220 os_isr_exit_yield_test(true);
221 }
222 }
223 }
224
225private:
226 void set_feedback_key(uint32_t key) OVERRIDE
227 {
228 feedbackKey_ = key;
229 }
230
231protected:
238 {
240 {
241 return nullptr;
242 }
245 entry->reset(feedbackKey_);
246 entry->channel = channel;
247 return entry;
248 }
249
253 void add_sample(int sample) {
254 if (sample < 0) return; // skipped sampling
255 if (feedbackQueue_.full()) {
256 extern uint32_t feedback_sample_overflow_count;
257 ++feedback_sample_overflow_count;
258 return;
259 }
261 feedbackQueue_.back().channel = HW::get_feedback_channel();
263 uint32_t tick_timer = HW::get_timer_tick();
264 memcpy(feedbackQueue_.back().ch2Data, &tick_timer, 4);
266 int_set_pending(HW::OS_INTERRUPT);
267 }
268
274 uint32_t feedbackKey_;
277 dcc::Feedback *returnedPackets_[HW::CHANNEL_COUNT];
278};
279
280#endif // _FREERTOS_DRIVERS_COMMON_RAILCOMIMPL_HXX_
const char * name
device name
Definition Devtab.hxx:266
This structure is safe to use from an interrupt context and a regular context at the same time,...
bool empty()
void increment_front()
Removes the head of the FIFO from the queue.
bool has_noncommit_space()
void increment_back()
Commits the element at back() into the queue.
T & front()
Returns the head of the FIFO (next element to read).
T & back()
Returns the space to write the next element to.
void noncommit_back()
Increments the back pointer without committing the entry into the queue.
bool full()
Node information.
Definition Devtab.hxx:549
OSMutex lock_
protects internal structures.
Definition Devtab.hxx:588
An object that can schedule itself on an executor to run.
virtual void notify()=0
Generic callback.
Class to allow convenient locking and unlocking of mutexes in a C context.
Definition OS.hxx:494
Base class for railcom drivers.
Definition Railcom.hxx:133
Notifiable * readableNotifiable_
Notify this when we have data in our buffers.
Definition Railcom.hxx:278
void os_interrupt_handler()
Implementation of the interrupt handler running at the kernel interrupt priority.
RailcomDriverBase(const char *name)
Constructor.
dcc::Feedback * returnedPackets_[HW::CHANNEL_COUNT]
Stores pointers to packets we are filling right now, one for each channel.
Definition Railcom.hxx:285
uint32_t feedbackKey_
Stores the key for the next packets to read.
Definition Railcom.hxx:282
ssize_t read(File *file, void *buf, size_t count) OVERRIDE
Read from a file or device.
void flush_buffers() OVERRIDE
Instructs the device driver to drop all TX and RX queues.
dcc::Feedback * alloc_new_packet(uint8_t channel)
Takes a new empty packet at the front of the queue, fills in feedback key and channel information.
void set_feedback_key(uint32_t key) OVERRIDE
Specifies the feedback key to write into the received railcom data packets.
int ioctl(File *file, unsigned long int key, unsigned long data) override
Request an ioctl transaction.
virtual void int_set_pending(unsigned int_nr)=0
Sets a given software interrupt pending.
void add_sample(int sample)
Adds a sample for a preamble bit.
FixedQueue< dcc::Feedback, HW::Q_SIZE > feedbackQueue_
The packets we have read so far.
Definition Railcom.hxx:280
ssize_t write(File *, const void *, size_t) OVERRIDE
Write to a file or device.
Abstract base class for railcom drivers.
#define CAN_IOC_READ_ACTIVE
read active ioctl.
#define NOTIFIABLE_TYPE
ioctl minor type used for the read/write active notifiable integration.
#define CAN_IOC_MAGIC
Magic number for this driver's ioctl calls.
#define IOC_SIZE(_num)
Decode ioctl size.
#define IOC_TYPE(_num)
Decode ioctl type.
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
uint8_t channel
Used by multi-channel railcom receiver drivers.
Definition railcom.h:54
uint8_t ch2Data[6]
Payload of channel 2.
Definition railcom.h:51
File information.
Definition Devtab.hxx:52
Structure used for reading (railcom) feedback data from DCC / Railcom device drivers.
Definition RailCom.hxx:50
void reset(uint32_t feedback_key)
Clears the structure and sets the feedback key to a specific value.
Definition RailCom.hxx:52
void add_ch1_data(uint8_t data)
Appends a byte to the channel 1 payload.
Definition RailCom.hxx:61