Clarify when we are using int vs float and move decoders up a level.
[apps/agl-service-can-low-level.git] / src / uds / uds.c
1 #include <uds/uds.h>
2 #include <bitfield/bitfield.h>
3 #include <canutil/read.h>
4 #include <string.h>
5 #include <limits.h>
6 #include <stddef.h>
7 #include <sys/param.h>
8
9 #define ARBITRATION_ID_OFFSET 0x8
10 #define MODE_RESPONSE_OFFSET 0x40
11 #define NEGATIVE_RESPONSE_MODE 0x7f
12 #define MAX_DIAGNOSTIC_PAYLOAD_SIZE 6
13 #define MODE_BYTE_INDEX 0
14 #define PID_BYTE_INDEX 1
15 #define NEGATIVE_RESPONSE_MODE_INDEX 1
16 #define NEGATIVE_RESPONSE_NRC_INDEX 2
17
18 #ifndef MAX
19 #define MAX(x, y) (((x) > (y)) ? (x) : (y))
20 #endif
21
22 DiagnosticShims diagnostic_init_shims(LogShim log,
23         SendCanMessageShim send_can_message,
24         SetTimerShim set_timer) {
25     DiagnosticShims shims = {
26         log: log,
27         send_can_message: send_can_message,
28         set_timer: set_timer
29     };
30     return shims;
31 }
32
33 static void setup_receive_handle(DiagnosticRequestHandle* handle) {
34     if(handle->request.arbitration_id == OBD2_FUNCTIONAL_BROADCAST_ID) {
35         uint16_t response_id;
36         for(response_id = 0;
37                 response_id < OBD2_FUNCTIONAL_RESPONSE_COUNT; ++response_id) {
38             handle->isotp_receive_handles[response_id] = isotp_receive(
39                     &handle->isotp_shims,
40                     OBD2_FUNCTIONAL_RESPONSE_START + response_id,
41                     NULL);
42         }
43         handle->isotp_receive_handle_count = OBD2_FUNCTIONAL_RESPONSE_COUNT;
44     } else {
45         handle->isotp_receive_handle_count = 1;
46         handle->isotp_receive_handles[0] = isotp_receive(&handle->isotp_shims,
47                 handle->request.arbitration_id + ARBITRATION_ID_OFFSET,
48                 NULL);
49     }
50 }
51
52 static uint16_t autoset_pid_length(uint8_t mode, uint16_t pid,
53         uint8_t pid_length) {
54     if(pid_length == 0) {
55         if(pid > 0xffff || mode > 10) {
56             pid_length = 2;
57         } else {
58             pid_length = 1;
59         }
60     }
61     return pid_length;
62 }
63
64 DiagnosticRequestHandle diagnostic_request(DiagnosticShims* shims,
65         DiagnosticRequest* request, DiagnosticResponseReceived callback) {
66     DiagnosticRequestHandle handle = {
67         request: *request,
68         callback: callback,
69         success: false,
70         completed: false
71     };
72
73     uint8_t payload[MAX_DIAGNOSTIC_PAYLOAD_SIZE] = {0};
74     payload[MODE_BYTE_INDEX] = request->mode;
75     if(request->has_pid) {
76         request->pid_length = autoset_pid_length(request->mode,
77                 request->pid, request->pid_length);
78         handle.request.pid_length = request->pid_length;
79         set_bitfield(request->pid, PID_BYTE_INDEX * CHAR_BIT,
80                 request->pid_length * CHAR_BIT, payload, sizeof(payload));
81     }
82     if(request->payload_length > 0) {
83         memcpy(&payload[PID_BYTE_INDEX + request->pid_length],
84                 request->payload, request->payload_length);
85     }
86
87     handle.isotp_shims = isotp_init_shims(shims->log,
88             shims->send_can_message,
89             shims->set_timer);
90     handle.isotp_shims.frame_padding = !request->no_frame_padding;
91
92     handle.isotp_send_handle = isotp_send(&handle.isotp_shims,
93             request->arbitration_id, payload,
94             1 + request->payload_length + request->pid_length,
95             NULL);
96     if(shims->log != NULL) {
97         shims->log("Sending diagnostic request: arb_id: 0x%02x, mode: 0x%x, pid: 0x%x, payload: 0x%02x%02x%02x%02x%02x%02x%02x, size: %d\r\n",
98                 request->arbitration_id,
99                 request->mode,
100                 request->pid,
101                 request->payload[0],
102                 request->payload[1],
103                 request->payload[2],
104                 request->payload[3],
105                 request->payload[4],
106                 request->payload[5],
107                 request->payload[6],
108                 request->payload_length);
109     }
110
111     setup_receive_handle(&handle);
112
113     // TODO notes on multi frame:
114     // TODO what are the timers for exactly?
115     //
116     // when sending multi frame, send 1 frame, wait for a response
117     // if it says send all, send all right away
118     // if it says flow control, set the time for the next send
119     // instead of creating a timer with an async callback, add a process_handle
120     // function that's called repeatedly in the main loop - if it's time to
121     // send, we do it. so there's a process_handle_send and receive_can_frame
122     // that are just called continuously from the main loop. it's a waste of a
123     // few cpu cycles but it may be more  natural than callbacks.
124     //
125     // what would a timer callback look like...it would need to pass the handle
126     // and that's all. seems like a context void* would be able to capture all
127     // of the information but arg, memory allocation. look at how it's done in
128     // the other library again
129     //
130     return handle;
131 }
132
133 DiagnosticRequestHandle diagnostic_request_pid(DiagnosticShims* shims,
134         DiagnosticPidRequestType pid_request_type, uint16_t arbitration_id,
135         uint16_t pid, DiagnosticResponseReceived callback) {
136     DiagnosticRequest request = {
137         arbitration_id: arbitration_id,
138         mode: pid_request_type == DIAGNOSTIC_STANDARD_PID ? 0x1 : 0x22,
139         has_pid: true,
140         pid: pid
141     };
142
143     return diagnostic_request(shims, &request, callback);
144 }
145
146 static bool handle_negative_response(IsoTpMessage* message,
147         DiagnosticResponse* response, DiagnosticShims* shims) {
148     bool response_was_negative = false;
149     if(response->mode == NEGATIVE_RESPONSE_MODE) {
150         response_was_negative = true;
151         if(message->size > NEGATIVE_RESPONSE_MODE_INDEX) {
152             response->mode = message->payload[NEGATIVE_RESPONSE_MODE_INDEX];
153         }
154
155         if(message->size > NEGATIVE_RESPONSE_NRC_INDEX) {
156             response->negative_response_code =
157                     message->payload[NEGATIVE_RESPONSE_NRC_INDEX];
158         }
159
160         response->success = false;
161         response->completed = true;
162     }
163     return response_was_negative;
164 }
165
166 static bool handle_positive_response(DiagnosticRequestHandle* handle,
167         IsoTpMessage* message, DiagnosticResponse* response,
168         DiagnosticShims* shims) {
169     bool response_was_positive = false;
170     if(response->mode == handle->request.mode + MODE_RESPONSE_OFFSET) {
171         response_was_positive = true;
172         // hide the "response" version of the mode from the user
173         // if it matched
174         response->mode = handle->request.mode;
175         response->has_pid = false;
176         if(handle->request.has_pid && message->size > 1) {
177             response->has_pid = true;
178             if(handle->request.pid_length == 2) {
179                 response->pid = get_bitfield(message->payload, message->size,
180                         PID_BYTE_INDEX * CHAR_BIT, sizeof(uint16_t) * CHAR_BIT);
181             } else {
182                 response->pid = message->payload[PID_BYTE_INDEX];
183             }
184
185         }
186
187         uint8_t payload_index = 1 + handle->request.pid_length;
188         response->payload_length = MAX(0, message->size - payload_index);
189         if(response->payload_length > 0) {
190             memcpy(response->payload, &message->payload[payload_index],
191                     response->payload_length);
192         }
193
194         if((!handle->request.has_pid && !response->has_pid)
195                 || response->pid == handle->request.pid) {
196             response->success = true;
197             response->completed = true;
198         } else {
199             response_was_positive = false;
200         }
201     }
202     return response_was_positive;
203 }
204
205 DiagnosticResponse diagnostic_receive_can_frame(DiagnosticShims* shims,
206         DiagnosticRequestHandle* handle, const uint16_t arbitration_id,
207         const uint8_t data[], const uint8_t size) {
208
209     DiagnosticResponse response = {
210         arbitration_id: arbitration_id,
211         success: false,
212         completed: false
213     };
214
215     if(!handle->isotp_send_handle.completed) {
216         isotp_continue_send(&handle->isotp_shims,
217                 &handle->isotp_send_handle, arbitration_id, data, size);
218     } else {
219         uint8_t i;
220         for(i = 0; i < handle->isotp_receive_handle_count; ++i) {
221             IsoTpMessage message = isotp_continue_receive(&handle->isotp_shims,
222                     &handle->isotp_receive_handles[i], arbitration_id, data,
223                     size);
224
225             if(message.completed) {
226                 if(message.size > 0) {
227                     response.mode = message.payload[0];
228                     if(handle_negative_response(&message, &response, shims)) {
229                         shims->log("Received a negative response to mode 0x%x on arb ID 0x%x",
230                                 response.mode, response.arbitration_id);
231                         handle->success = true;
232                         handle->completed = true;
233                     } else if(handle_positive_response(handle, &message,
234                                 &response, shims)) {
235                         shims->log("Received a positive mode 0x%x response on arb ID 0x%x",
236                                 response.mode, response.arbitration_id);
237                         handle->success = true;
238                         handle->completed = true;
239                     } else {
240                         shims->log("Response was for a mode 0x%x request (pid 0x%x), not our mode 0x%x request (pid 0x%x)",
241                                 MAX(0, response.mode - MODE_RESPONSE_OFFSET),
242                                 response.pid, handle->request.mode,
243                                 handle->request.pid);
244                     }
245                 } else {
246                     shims->log("Received an empty response on arb ID 0x%x",
247                             response.arbitration_id);
248                 }
249
250                 if(handle->completed && handle->callback != NULL) {
251                     handle->callback(&response);
252                 }
253
254                 break;
255             }
256         }
257     }
258     return response;
259 }
260
261 int diagnostic_payload_to_integer(const DiagnosticResponse* response) {
262     return get_bitfield(response->payload, response->payload_length, 0,
263             response->payload_length * CHAR_BIT);
264 }
265
266 float diagnostic_decode_obd2_pid(const DiagnosticResponse* response,
267         int parsed_payload) {
268     // handles on the single number values, not the bit encoded ones
269     switch(response->pid) {
270         case 0xa:
271             return response->payload[0] * 3;
272         case 0xc:
273             return (response->payload[0] * 256 + response->payload[1]) / 4.0;
274         case 0xd:
275         case 0x33:
276         case 0xb:
277             return response->payload[0];
278         case 0x10:
279             return (response->payload[0] * 256 + response->payload[1]) / 100.0;
280         case 0x11:
281         case 0x2f:
282         case 0x45:
283         case 0x4c:
284         case 0x52:
285         case 0x5a:
286         case 0x4:
287             return response->payload[0] * 100.0 / 255.0;
288         case 0x46:
289         case 0x5c:
290         case 0xf:
291         case 0x5:
292             return response->payload[0] - 40;
293         case 0x62:
294             return response->payload[0] - 125;
295         default:
296             return 0;
297     }
298 }