rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
lua_can_rx.cpp
Go to the documentation of this file.
1#include "pch.h"
2
3#include "can_filter.h"
4
5#if EFI_CAN_SUPPORT
6
7#include "rusefi_lua.h"
8
9extern "C" {
10 #include "lapi.h"
11 #include "ltable.h"
12 #include "lgc.h"
13}
14
15// Stores information about one received CAN frame: which bus, plus the actual frame
16struct CanFrameData {
17 uint8_t BusIndex;
18 int Callback;
19 CANRxFrame Frame;
20};
21
22static constexpr size_t canFrameCount = 32;
23static CanFrameData canFrames[canFrameCount];
24// CAN frame buffers that are not in use
25static chibios_rt::Mailbox<CanFrameData*, canFrameCount> freeBuffers;
26// CAN frame buffers that are waiting to be processed by the lua thread
27static chibios_rt::Mailbox<CanFrameData*, canFrameCount> filledBuffers;
28
29void processLuaCan(const size_t busIndex, const CANRxFrame& frame) {
30 auto filter = getFilterForId(busIndex, CAN_ID(frame));
31
32 // Filter the frame if we aren't listening for it
33 if (!filter) {
34 return;
35 }
36
37 CanFrameData* frameBuffer;
38 msg_t msg;
39
40 {
41 // Acquire a buffer under lock
42 chibios_rt::CriticalSectionLocker csl;
43 msg = freeBuffers.fetchI(&frameBuffer);
44 }
45
46 if (msg != MSG_OK) {
47 // all buffers are already in use, this frame will be dropped!
48 // TODO: warn the user
49 return;
50 }
51
52 // Copy the frame in to the buffer
53 frameBuffer->BusIndex = busIndex;
54 frameBuffer->Frame = frame;
55 frameBuffer->Callback = filter->Callback;
56
57 {
58 // Push the frame in to the queue under lock
59 chibios_rt::CriticalSectionLocker csl;
60 filledBuffers.postI(frameBuffer);
61 }
62}
63
64// From lapi.c:762 lua_createtable, modified slightly
65static void lua_createtable_noGC(lua_State *L, int narray) {
66 Table *t;
67 lua_lock(L);
68 t = luaH_new(L);
69 sethvalue2s(L, L->top.p, t);
70 api_incr_top(L);
71 luaH_resize(L, t, narray, 0);
72
73 // This line is commented out - no need to do a GC every time in
74 // this hot path when we'll do it shortly and have plenty of memory available.
75 // luaC_checkGC(L);
76
77 lua_unlock(L);
78}
79
80static void handleCanFrame(LuaHandle& ls, CanFrameData* data) {
82 if (data->Callback == NO_CALLBACK) {
83 // No callback, use catch-all function
84 lua_getglobal(ls, "onCanRx");
85 } else {
86 // Push the specified callback on to the stack
87 lua_rawgeti(ls, LUA_REGISTRYINDEX, data->Callback);
88 }
89
90 auto frameCanId = CAN_ID(data->Frame);
91
92 if (lua_isnil(ls, -1)) {
93 // no rx function, ignore
94 efiPrintf("LUA CAN rx missing function onCanRx ID=%ld", frameCanId);
95 lua_settop(ls, 0);
96 return;
97 }
98
99 auto dlc = data->Frame.DLC;
100
101 // Push bus, ID and DLC
102 lua_pushinteger(ls, HUMAN_OFFSET + data->BusIndex);
103 lua_pushinteger(ls, frameCanId);
104 lua_pushinteger(ls, dlc);
105
107 // todo: https://github.com/rusefi/rusefi/issues/6041
108 lua_getglobal(ls, "global_can_data");
109 } else {
110 // Build table for data, custom implementation without explicit GC but still garbage
111 lua_createtable_noGC(ls, dlc);
112 }
113 for (size_t i = 0; i < dlc; i++) {
114 lua_pushinteger(ls, data->Frame.data8[i]);
115
116 // index is i+1 because Lua "arrays" (tables) are 1-indexed
117 lua_rawseti(ls, -2, i + 1);
118 }
119
120 // Perform the actual function call
121 int status = lua_pcall(ls, 4, 0, 0);
122
123 if (0 != status) {
124 // error calling CAN rx hook function
125 auto errMsg = lua_tostring(ls, -1);
126 efiPrintf("LUA CAN RX error %s", errMsg);
127 lua_pop(ls, 1);
128 }
129
130 lua_settop(ls, 0);
131}
132
133static bool doOneLuaCanRx(LuaHandle& ls) {
135 CanFrameData* data;
136
137 msg_t msg = filledBuffers.fetch(&data, TIME_IMMEDIATE);
138
139 if (msg == MSG_TIMEOUT) {
140 // No new CAN messages rx'd, nothing more to do.
141 return false;
142 }
143
144 if (msg != MSG_OK) {
145 // Message was otherwise not OK
146 // TODO: what do here?
147 return false;
148 }
149
150 // We've accepted the frame, process it in Lua.
151 handleCanFrame(ls, data);
152
153 // We're done, return this frame to the free list
154 msg = freeBuffers.post(data, TIME_IMMEDIATE);
155 efiAssert(ObdCode::OBD_PCM_Processor_Fault, msg == MSG_OK, "lua can post to free buffer fail", false);
156
157 // We processed a frame so we should check again
158 return true;
159}
160
163 int counter = 0;
164 // While it processed a frame, continue checking
165 while (doOneLuaCanRx(ls)) {
166 counter++;
167 }
168 return counter;
169}
170
172 // Push all CAN frames in to the free buffer
173 for (size_t i = 0; i < canFrameCount; i++) {
174 freeBuffers.post(&canFrames[i], TIME_INFINITE);
175 }
176}
177
178#endif // EFI_CAN_SUPPORT
CanFilter * getFilterForId(size_t busIndex, int Id)
static constexpr engine_configuration_s * engineConfiguration
int doLuaCanRx(LuaHandle &ls)
static CanFrameData canFrames[canFrameCount]
void processLuaCan(const size_t busIndex, const CANRxFrame &frame)
static chibios_rt::Mailbox< CanFrameData *, canFrameCount > filledBuffers
static chibios_rt::Mailbox< CanFrameData *, canFrameCount > freeBuffers
static void handleCanFrame(LuaHandle &ls, CanFrameData *data)
void initLuaCanRx()
static void lua_createtable_noGC(lua_State *L, int narray)
static constexpr size_t canFrameCount
static bool doOneLuaCanRx(LuaHandle &ls)
@ OBD_PCM_Processor_Fault
@ LuaOneCanRxCallback
@ LuaAllCanRxFunction
@ LuaOneCanRxFunction