Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Pic32mxCan.cxx
Go to the documentation of this file.
1
37
38#include "DeviceBuffer.hxx"
39#include "nmranet_config.h"
40#include "can_frame.h"
41#include <fcntl.h>
42
43Pic32mxCan::Pic32mxCan(CAN_MODULE module, const char *dev, unsigned irq_vector)
44 : Node(dev)
45 , hw_(module)
46 , overrunCount_(0)
47 , irqVector_(irq_vector)
48{
49 messageFifoArea_ = malloc(
50 (config_can_tx_buffer_size() + config_can_rx_buffer_size()) * 16);
51}
52
53Pic32mxCan::~Pic32mxCan()
54{
55 disable();
56 free(messageFifoArea_);
57}
58
64static void pic_buffer_to_frame(const CANRxMessageBuffer *message,
65 struct can_frame *can_frame)
66{
67 uint32_t id = message->msgSID.SID;
68 if (message->msgEID.IDE)
69 {
70 SET_CAN_FRAME_EFF(*can_frame);
71 id <<= 18;
72 id |= message->msgEID.EID;
73 SET_CAN_FRAME_ID_EFF(*can_frame, id);
74 }
75 else
76 {
77 CLR_CAN_FRAME_EFF(*can_frame);
78 SET_CAN_FRAME_ID(*can_frame, id);
79 }
80 if (message->msgEID.RTR)
81 {
82 SET_CAN_FRAME_RTR(*can_frame);
83 }
84 else
85 {
86 CLR_CAN_FRAME_RTR(*can_frame);
87 }
88 CLR_CAN_FRAME_ERR(*can_frame);
89
90 can_frame->can_dlc = message->msgEID.DLC;
91 memcpy(can_frame->data, message->data, can_frame->can_dlc);
92}
93
99static void frame_to_pic_buffer(const struct can_frame *can_frame,
100 CANTxMessageBuffer *message)
101{
102 message->messageWord[0] = 0;
103 message->messageWord[1] = 0;
104 if (IS_CAN_FRAME_EFF(*can_frame))
105 {
106 uint32_t id = GET_CAN_FRAME_ID_EFF(*can_frame);
107 message->msgEID.IDE = 1;
108 message->msgSID.SID = id >> 18;
109 message->msgEID.EID = id & ((1 << 19) - 1);
110
111 // message->msgSID.SID = id & 0x7ff;
112 // message->msgEID.EID = id >> 11;
113 }
114 else
115 {
116 message->msgSID.SID = GET_CAN_FRAME_ID(*can_frame);
117 message->msgEID.IDE = 0;
118 }
119 if (IS_CAN_FRAME_RTR(*can_frame))
120 {
121 message->msgEID.RTR = 1;
122 }
123 else
124 {
125 message->msgEID.RTR = 0;
126 }
127 message->msgEID.DLC = can_frame->can_dlc;
128 memcpy(message->data, can_frame->data, can_frame->can_dlc);
129}
130
131ssize_t Pic32mxCan::read(File *file, void *buf, size_t count)
132{
133 struct can_frame *can_frame = static_cast<struct can_frame *>(buf);
134 ssize_t result = 0;
135
136 int flags = -1;
137
138 while (count >= sizeof(struct can_frame))
139 {
140 /*
141 At the beginning of the iteration we are in a critical section.
142
143 We need this critical section because the GetRxBuffer call will
144 return the same buffer until the Update call is successful. We want
145 to prevent multiple threads from receiving the same CAN frame
146 however. */
147
148 taskENTER_CRITICAL();
149 CANRxMessageBuffer *message =
150 (CANRxMessageBuffer *)CANGetRxMessage(hw_, CAN_CHANNEL1);
151 if (message != NULL)
152 {
153 CANUpdateChannel(hw_, CAN_CHANNEL1);
154 }
155 if (flags == -1)
156 {
157 flags = file->flags;
158 }
159 taskEXIT_CRITICAL();
160
161 /* Now let's take a look if we have actually found a message. */
162 if (message != NULL)
163 {
164 pic_buffer_to_frame(message, can_frame);
165
166 count -= sizeof(struct can_frame);
167 result += sizeof(struct can_frame);
168 can_frame++;
169 continue;
170 }
171
172 /* We do not have a message. Return a short read or zero if we have a
173 * non-blocking filedes. */
174
175 if (result || (flags & O_NONBLOCK))
176 {
177 break;
178 }
179
180 /* Blocking read. Enable the receive interrupt and pend on the rx
181 semaphore. Spurious interrupts and extra tokens in the semaphore are
182 not a problem, they will just drop back here with no messages
183 found.
184
185 There is no race condition between checking the queue above and
186 enabling the interrupt here. The interrupt pending flag is
187 persistent, thus if there is already a message in the queue it will
188 trigger the interrupt immediately.
189 */
190
191 CANEnableChannelEvent(hw_, CAN_CHANNEL1, CAN_RX_CHANNEL_NOT_EMPTY,
192 TRUE);
193 DeviceBufferBase::block_until_condition(file, true);
194 }
195
196 /* As a good-bye we wake up the interrupt handler once more to post on the
197 * semaphore in case there is another thread waiting. */
198 CANEnableChannelEvent(hw_, CAN_CHANNEL1, CAN_RX_CHANNEL_NOT_EMPTY,
199 TRUE);
200
201 if (!result && (file->flags & O_NONBLOCK))
202 {
203 return -EAGAIN;
204 }
205
206 return result;
207}
208
209// static
210ssize_t Pic32mxCan::write(File *file, const void *buf, size_t count)
211{
212 const struct can_frame *can_frame =
213 static_cast<const struct can_frame *>(buf);
214 ssize_t result = 0;
215
216 int flags = -1;
217
218 while (count >= sizeof(struct can_frame))
219 {
220 taskENTER_CRITICAL();
221 CANTxMessageBuffer *message =
222 CANGetTxMessageBuffer(hw_, CAN_CHANNEL0);
223 if (message != NULL)
224 {
225 /* Unfortunately we have to fill the buffer in the critical section
226 * or else we risk that another thread will call the FlushTxChannel
227 * while our buffer is not fully completed. */
228 frame_to_pic_buffer(can_frame, message);
229 CANUpdateChannel(hw_, CAN_CHANNEL0);
230 }
231 if (flags == -1)
232 {
233 flags = file->flags;
234 }
235 taskEXIT_CRITICAL();
236
237 /* Did we actually find a slot to transmit? */
238 if (message != NULL)
239 {
240 CANFlushTxChannel(hw_, CAN_CHANNEL0);
241
242 count -= sizeof(struct can_frame);
243 result += sizeof(struct can_frame);
244 can_frame++;
245 continue;
246 }
247
248 /* We did not find a transmit slot. We purposefully do not execute a
249 * short write here, although that would be an option. */
250 if (flags & O_NONBLOCK)
251 {
252 break;
253 }
254 /* Blocking read. We enable the interrupt and wait for the
255 * semaphore. There is no race condition here, because the TX buffer
256 * not full interrupt is persistent, so if a buffer got free between
257 * our check and now, the interrupt will trigger immediately and wake
258 * us up. */
259 CANEnableChannelEvent(hw_, CAN_CHANNEL0, CAN_TX_CHANNEL_NOT_FULL,
260 TRUE);
261 DeviceBufferBase::block_until_condition(file, false);
262 }
263
264 /* As a good-bye we wake up the interrupt handler once more to post on the
265 * semaphore in case there is another thread waiting. */
266 CANEnableChannelEvent(hw_, CAN_CHANNEL0, CAN_TX_CHANNEL_NOT_FULL,
267 TRUE);
268
269 if (!result && (file->flags & O_NONBLOCK))
270 {
271 return -EAGAIN;
272 }
273
274 return result;
275}
276
283bool Pic32mxCan::select(File* file, int mode)
284{
285 portENTER_CRITICAL();
286 bool retval = false;
287 switch (mode)
288 {
289 case FREAD:
290 if (CANGetChannelEvent(hw_, CAN_CHANNEL1) &
291 CAN_RX_CHANNEL_NOT_EMPTY)
292 {
293 retval = true;
294 }
295 else
296 {
297 Device::select_insert(&rxSelect_);
298 }
299 break;
300 case FWRITE:
301 if (CANGetChannelEvent(hw_, CAN_CHANNEL0) & CAN_TX_CHANNEL_NOT_FULL)
302 {
303 retval = true;
304 }
305 else
306 {
307 Device::select_insert(&txSelect_);
308 }
309 break;
310 default:
311 case 0:
312 /* we don't support any exceptions */
313 break;
314 }
315 portEXIT_CRITICAL();
316
317 return retval;
318}
319
321{
322 CANEnableModule(hw_, TRUE);
323 /* Step 1: Switch the CAN module
324 * ON and switch it to Configuration
325 * mode. Wait till the mode switch is
326 * complete. */
327 CANSetOperatingMode(hw_, CAN_CONFIGURATION);
328 while (CANGetOperatingMode(hw_) != CAN_CONFIGURATION)
329 ;
330
331 /* Step 2: Configure the Clock.The
332 * CAN_BIT_CONFIG data structure is used
333 * for this purpose. The propagation segment,
334 * phase segment 1 and phase segment 2
335 * are configured to have 3TQ. SYSTEM_FREQ
336 * and CAN_BUS_SPEED are defined in */
337
338 CAN_BIT_CONFIG canBitConfig;
339 canBitConfig.phaseSeg2Tq = CAN_BIT_3TQ;
340 canBitConfig.phaseSeg1Tq = CAN_BIT_3TQ;
341 canBitConfig.propagationSegTq = CAN_BIT_3TQ;
342 canBitConfig.phaseSeg2TimeSelect = TRUE;
343 canBitConfig.sample3Time = TRUE;
344 canBitConfig.syncJumpWidth = CAN_BIT_2TQ;
345
346 CANSetSpeed(hw_, &canBitConfig, configCPU_CLOCK_HZ,
347 config_nmranet_can_bitrate());
348
349 /* Step 3: Assign the buffer area to the
350 * CAN module.
351 */
352
353 CANAssignMemoryBuffer(
355 16 * (config_can_tx_buffer_size() + config_can_rx_buffer_size()));
356
357 /* Step 4: Configure channel 0 for TX and size of tx_queue_len message
358 * buffers with RTR disabled and low medium priority. Configure channel 1
359 * for RX and size of rx_queue_len message buffers and receive the full
360 * message.
361 */
362
364 CANConfigureChannelForTx(hw_, CAN_CHANNEL0, 1, CAN_TX_RTR_DISABLED,
365 CAN_LOW_MEDIUM_PRIORITY);
366 CANConfigureChannelForRx(hw_, CAN_CHANNEL1, config_can_rx_buffer_size(),
367 CAN_RX_FULL_RECEIVE);
368
369 // We create a catch-all filter for channel 1.
370 CANConfigureFilterMask(hw_, CAN_FILTER_MASK0, 0, CAN_EID, CAN_FILTER_MASK_ANY_TYPE);
371 CANEnableFilter(hw_, CAN_FILTER0, FALSE);
372 while(CANIsFilterDisabled(hw_, CAN_FILTER0) == FALSE);
373 CANConfigureFilter(hw_, CAN_FILTER0, 0, CAN_EID);
374 CANLinkFilterToChannel(hw_, CAN_FILTER0, CAN_FILTER_MASK0, CAN_CHANNEL1);
375 CANEnableFilter(hw_, CAN_FILTER0, TRUE);
376
377 CANEnableModuleEvent(hw_, CAN_RX_EVENT, TRUE);
378 CANEnableModuleEvent(hw_, CAN_TX_EVENT, TRUE);
379
380 /* Step 6: Enable interrupt and events.
381 * The interrrupt peripheral library is used to enable
382 * the CAN interrupt to the CPU. */
383
384 INTSetVectorPriority(can_vector(), INT_PRIORITY_LEVEL_2);
385 INTSetVectorSubPriority(can_vector(), INT_SUB_PRIORITY_LEVEL_0);
386 INTEnable(can_int(), INT_ENABLED);
387 /* Step 7: Switch the CAN mode
388 * to normal mode. */
389
390 CANSetOperatingMode(hw_, CAN_NORMAL_OPERATION);
391 while (CANGetOperatingMode(hw_) != CAN_NORMAL_OPERATION)
392 ;
393}
394
396{
397 /* If the transmit buffer is not empty, we should crash here. Otherweise it
398 * is possible that the user sends some frames, then closes the device and
399 * the frames never get sent really. */
400 INTEnable(can_int(), INT_DISABLED);
401 CANEnableModule(hw_, FALSE);
402}
403
static void frame_to_pic_buffer(const struct can_frame *can_frame, CANTxMessageBuffer *message)
Translates a struct can_frame to a hardware buffer.
static void pic_buffer_to_frame(const CANRxMessageBuffer *message, struct can_frame *can_frame)
Translates a hardware buffer to a struct can_frame.
Node information.
Definition Devtab.hxx:549
void * messageFifoArea_
Points to the shared RAM area between the hardware and the driver.
ssize_t read(File *file, void *buf, size_t count)
Read from a file or device.
CAN_MODULE hw_
Hardware (enumeration value).
bool select(File *file, int mode) OVERRIDE
Device select method.
Pic32mxCan(CAN_MODULE module, const char *dev, unsigned irq_vector)
Constructor.
INT_VECTOR can_vector()
void disable()
function to disable device
INT_SOURCE can_int()
ssize_t write(File *file, const void *buf, size_t count)
Write to a file or device.
void enable()
function to enable device
#define FWRITE
Workaround for missing header defines on some newlib versions.
Definition fcntl.h:58
#define FREAD
Workaround for missing header defines on some newlib versions.
Definition fcntl.h:53
static void select_insert(SelectInfo *info)
Add client to list of clients needing woken.
Definition Select.cxx:197
File information.
Definition Devtab.hxx:52
int flags
open flags
Definition Devtab.hxx:63