Add 'CAN-binder/libs/uds-c/' from commit 'ca20db3dd978871bbb9f01f3c862b510c03d1dc4'
[apps/agl-service-can-low-level.git] / CAN-binder / libs / uds-c / 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 #include <inttypes.h>
9
10 #define ARBITRATION_ID_OFFSET 0x8
11 #define MODE_RESPONSE_OFFSET 0x40
12 #define NEGATIVE_RESPONSE_MODE 0x7f
13 #define MAX_DIAGNOSTIC_PAYLOAD_SIZE 6
14 #define MODE_BYTE_INDEX 0
15 #define PID_BYTE_INDEX 1
16 #define NEGATIVE_RESPONSE_MODE_INDEX 1
17 #define NEGATIVE_RESPONSE_NRC_INDEX 2
18
19 #ifndef MAX
20 #define MAX(x, y) (((x) > (y)) ? (x) : (y))
21 #endif
22
23 DiagnosticShims diagnostic_init_shims(LogShim log,
24         SendCanMessageShim send_can_message,
25         SetTimerShim set_timer) {
26     DiagnosticShims shims = {
27         log: log,
28         send_can_message: send_can_message,
29         set_timer: set_timer
30     };
31     return shims;
32 }
33
34 static void setup_receive_handle(DiagnosticRequestHandle* handle) {
35     if(handle->request.arbitration_id == OBD2_FUNCTIONAL_BROADCAST_ID) {
36         uint32_t response_id;
37         for(response_id = 0;
38                 response_id < OBD2_FUNCTIONAL_RESPONSE_COUNT; ++response_id) {
39             handle->isotp_receive_handles[response_id] = isotp_receive(
40                     &handle->isotp_shims,
41                     OBD2_FUNCTIONAL_RESPONSE_START + response_id,
42                     NULL);
43         }
44         handle->isotp_receive_handle_count = OBD2_FUNCTIONAL_RESPONSE_COUNT;
45     } else {
46         handle->isotp_receive_handle_count = 1;
47         handle->isotp_receive_handles[0] = isotp_receive(&handle->isotp_shims,
48                 handle->request.arbitration_id + ARBITRATION_ID_OFFSET,
49                 NULL);
50     }
51 }
52
53 static uint16_t autoset_pid_length(uint8_t mode, uint16_t pid,
54         uint8_t pid_length) {
55     if(pid_length == 0) {
56         if(mode <= 0xa || mode == 0x3e ) {
57             pid_length = 1;
58         } else if(pid > 0xffff || ((pid & 0xFF00) > 0x0)) {
59             pid_length = 2;
60         } else {
61             pid_length = 1;
62         }
63     }
64     return pid_length;
65 }
66
67 static void send_diagnostic_request(DiagnosticShims* shims,
68         DiagnosticRequestHandle* handle) {
69     uint8_t payload[MAX_DIAGNOSTIC_PAYLOAD_SIZE] = {0};
70     payload[MODE_BYTE_INDEX] = handle->request.mode;
71     if(handle->request.has_pid) {
72         handle->request.pid_length = autoset_pid_length(handle->request.mode,
73                 handle->request.pid, handle->request.pid_length);
74         set_bitfield(handle->request.pid, PID_BYTE_INDEX * CHAR_BIT,
75                 handle->request.pid_length * CHAR_BIT, payload,
76                 sizeof(payload));
77     }
78
79     if(handle->request.payload_length > 0) {
80         memcpy(&payload[PID_BYTE_INDEX + handle->request.pid_length],
81                 handle->request.payload, handle->request.payload_length);
82     }
83
84     handle->isotp_send_handle = isotp_send(&handle->isotp_shims,
85             handle->request.arbitration_id, payload,
86             1 + handle->request.payload_length + handle->request.pid_length,
87             NULL);
88     if(handle->isotp_send_handle.completed &&
89             !handle->isotp_send_handle.success) {
90         handle->completed = true;
91         handle->success = false;
92         if(shims->log != NULL) {
93             shims->log("%s", "Diagnostic request not sent");
94         }
95     } else if(shims->log != NULL) {
96         char request_string[128] = {0};
97         diagnostic_request_to_string(&handle->request, request_string,
98                 sizeof(request_string));
99         shims->log("Sending diagnostic request: %s", request_string);
100     }
101 }
102
103 bool diagnostic_request_sent(DiagnosticRequestHandle* handle) {
104     return handle->isotp_send_handle.completed;
105 }
106
107 void start_diagnostic_request(DiagnosticShims* shims,
108         DiagnosticRequestHandle* handle) {
109     handle->success = false;
110     handle->completed = false;
111     send_diagnostic_request(shims, handle);
112     if(!handle->completed) {
113         setup_receive_handle(handle);
114     }
115 }
116
117 DiagnosticRequestHandle generate_diagnostic_request(DiagnosticShims* shims,
118         DiagnosticRequest* request, DiagnosticResponseReceived callback) {
119     DiagnosticRequestHandle handle = {
120         request: *request,
121         callback: callback,
122         success: false,
123         completed: false
124     };
125
126     handle.isotp_shims = isotp_init_shims(shims->log,
127             shims->send_can_message,
128             shims->set_timer);
129     handle.isotp_shims.frame_padding = !request->no_frame_padding;
130
131     return handle;
132     // TODO notes on multi frame:
133     // TODO what are the timers for exactly?
134     //
135     // when sending multi frame, send 1 frame, wait for a response
136     // if it says send all, send all right away
137     // if it says flow control, set the time for the next send
138     // instead of creating a timer with an async callback, add a process_handle
139     // function that's called repeatedly in the main loop - if it's time to
140     // send, we do it. so there's a process_handle_send and receive_can_frame
141     // that are just called continuously from the main loop. it's a waste of a
142     // few cpu cycles but it may be more  natural than callbacks.
143     //
144     // what would a timer callback look like...it would need to pass the handle
145     // and that's all. seems like a context void* would be able to capture all
146     // of the information but arg, memory allocation. look at how it's done in
147     // the other library again
148     //
149 }
150
151 DiagnosticRequestHandle diagnostic_request(DiagnosticShims* shims,
152         DiagnosticRequest* request, DiagnosticResponseReceived callback) {
153     DiagnosticRequestHandle handle = generate_diagnostic_request(
154             shims, request, callback);
155     start_diagnostic_request(shims, &handle);
156     return handle;
157 }
158
159 DiagnosticRequestHandle diagnostic_request_pid(DiagnosticShims* shims,
160         DiagnosticPidRequestType pid_request_type, uint32_t arbitration_id,
161         uint16_t pid, DiagnosticResponseReceived callback) {
162     DiagnosticRequest request = {
163         arbitration_id: arbitration_id,
164         mode: pid_request_type == DIAGNOSTIC_STANDARD_PID ? 0x1 : 0x22,
165         has_pid: true,
166         pid: pid
167     };
168
169     return diagnostic_request(shims, &request, callback);
170 }
171
172 static bool handle_negative_response(IsoTpMessage* message,
173         DiagnosticResponse* response, DiagnosticShims* shims) {
174     bool response_was_negative = false;
175     if(response->mode == NEGATIVE_RESPONSE_MODE) {
176         response_was_negative = true;
177         if(message->size > NEGATIVE_RESPONSE_MODE_INDEX) {
178             response->mode = message->payload[NEGATIVE_RESPONSE_MODE_INDEX];
179         }
180
181         if(message->size > NEGATIVE_RESPONSE_NRC_INDEX) {
182             response->negative_response_code =
183                     message->payload[NEGATIVE_RESPONSE_NRC_INDEX];
184         }
185
186         response->success = false;
187         response->completed = true;
188     }
189     return response_was_negative;
190 }
191
192 static bool handle_positive_response(DiagnosticRequestHandle* handle,
193         IsoTpMessage* message, DiagnosticResponse* response,
194         DiagnosticShims* shims) {
195     bool response_was_positive = false;
196     if(response->mode == handle->request.mode + MODE_RESPONSE_OFFSET) {
197         response_was_positive = true;
198         // hide the "response" version of the mode from the user
199         // if it matched
200         response->mode = handle->request.mode;
201         response->has_pid = false;
202         if(handle->request.has_pid && message->size > 1) {
203             response->has_pid = true;
204             if(handle->request.pid_length == 2) {
205                 response->pid = get_bitfield(message->payload, message->size,
206                         PID_BYTE_INDEX * CHAR_BIT, sizeof(uint16_t) * CHAR_BIT);
207             } else {
208                 response->pid = message->payload[PID_BYTE_INDEX];
209             }
210
211         }
212
213         if((!handle->request.has_pid && !response->has_pid)
214                 || response->pid == handle->request.pid) {
215             response->success = true;
216             response->completed = true;
217
218             uint8_t payload_index = 1 + handle->request.pid_length;
219             response->payload_length = MAX(0, message->size - payload_index);
220             if(response->payload_length > 0) {
221                 memcpy(response->payload, &message->payload[payload_index],
222                         response->payload_length);
223             }
224         } else {
225             response_was_positive = false;
226         }
227     }
228     return response_was_positive;
229 }
230
231 DiagnosticResponse diagnostic_receive_can_frame(DiagnosticShims* shims,
232         DiagnosticRequestHandle* handle, const uint32_t arbitration_id,
233         const uint8_t data[], const uint8_t size) {
234
235     DiagnosticResponse response = {
236         arbitration_id: arbitration_id,
237         multi_frame: false,
238         success: false,
239         completed: false
240     };
241
242     if(!handle->isotp_send_handle.completed) {
243         isotp_continue_send(&handle->isotp_shims,
244                 &handle->isotp_send_handle, arbitration_id, data, size);
245     } else {
246         uint8_t i;
247         for(i = 0; i < handle->isotp_receive_handle_count; ++i) {
248             IsoTpMessage message = isotp_continue_receive(&handle->isotp_shims,
249                     &handle->isotp_receive_handles[i], arbitration_id, data,
250                     size);
251             response.multi_frame = message.multi_frame;
252
253             if(message.completed) {
254                 if(message.size > 0) {
255                     response.mode = message.payload[0];
256                     if(handle_negative_response(&message, &response, shims) ||
257                             handle_positive_response(handle, &message,
258                                 &response, shims)) {
259                         if(shims->log != NULL) {
260                             char response_string[128] = {0};
261                             diagnostic_response_to_string(&response,
262                                     response_string, sizeof(response_string));
263                             shims->log("Diagnostic response received: %s",
264                                     response_string);
265                         }
266
267                         handle->success = true;
268                         handle->completed = true;
269                     }
270                 } else {
271                     if(shims->log != NULL) {
272                         shims->log("Received an empty response on arb ID 0x%x",
273                                 response.arbitration_id);
274                     }
275                 }
276
277                 if(handle->completed && handle->callback != NULL) {
278                     handle->callback(&response);
279                 }
280
281                 break;
282             }
283         }
284     }
285     return response;
286 }
287
288 int diagnostic_payload_to_integer(const DiagnosticResponse* response) {
289     return get_bitfield(response->payload, response->payload_length, 0,
290             response->payload_length * CHAR_BIT);
291 }
292
293 float diagnostic_decode_obd2_pid(const DiagnosticResponse* response) {
294     // handles on the single number values, not the bit encoded ones
295     switch(response->pid) {
296         case 0xa:
297             return response->payload[0] * 3;
298         case 0xc:
299             return (response->payload[0] * 256 + response->payload[1]) / 4.0;
300         case 0xd:
301         case 0x33:
302         case 0xb:
303             return response->payload[0];
304         case 0x10:
305             return (response->payload[0] * 256 + response->payload[1]) / 100.0;
306         case 0x11:
307         case 0x2f:
308         case 0x45:
309         case 0x4c:
310         case 0x52:
311         case 0x5a:
312         case 0x4:
313             return response->payload[0] * 100.0 / 255.0;
314         case 0x46:
315         case 0x5c:
316         case 0xf:
317         case 0x5:
318             return response->payload[0] - 40;
319         case 0x62:
320             return response->payload[0] - 125;
321         default:
322             return diagnostic_payload_to_integer(response);
323     }
324 }
325
326 void diagnostic_response_to_string(const DiagnosticResponse* response,
327         char* destination, size_t destination_length) {
328     int bytes_used = snprintf(destination, destination_length,
329             "arb_id: 0x%lx, mode: 0x%x, ",
330             (unsigned long) response->arbitration_id,
331             response->mode);
332
333     if(response->has_pid) {
334         bytes_used += snprintf(destination + bytes_used,
335                 destination_length - bytes_used,
336                 "pid: 0x%x, ",
337                 response->pid);
338     }
339
340     if(!response->success) {
341         bytes_used += snprintf(destination + bytes_used,
342                 destination_length - bytes_used,
343                 "nrc: 0x%x, ",
344                 response->negative_response_code);
345     }
346
347     if(response->payload_length > 0) {
348         snprintf(destination + bytes_used, destination_length - bytes_used,
349                 "payload: 0x%02x%02x%02x%02x%02x%02x%02x",
350                 response->payload[0],
351                 response->payload[1],
352                 response->payload[2],
353                 response->payload[3],
354                 response->payload[4],
355                 response->payload[5],
356                 response->payload[6]);
357     } else {
358         snprintf(destination + bytes_used, destination_length - bytes_used,
359                 "no payload");
360     }
361 }
362
363 void diagnostic_request_to_string(const DiagnosticRequest* request,
364         char* destination, size_t destination_length) {
365     int bytes_used = snprintf(destination, destination_length,
366             "arb_id: 0x%lx, mode: 0x%x, ",
367             (unsigned long) request->arbitration_id,
368             request->mode);
369
370     if(request->has_pid) {
371         bytes_used += snprintf(destination + bytes_used,
372                 destination_length - bytes_used,
373                 "pid: 0x%x, ",
374                 request->pid);
375     }
376
377     int remaining_space = destination_length - bytes_used;
378     if(request->payload_length > 0) {
379         snprintf(destination + bytes_used, remaining_space,
380                 "payload: 0x%02x%02x%02x%02x%02x%02x%02x",
381                 request->payload[0],
382                 request->payload[1],
383                 request->payload[2],
384                 request->payload[3],
385                 request->payload[4],
386                 request->payload[5],
387                 request->payload[6]);
388     } else {
389         snprintf(destination + bytes_used, remaining_space, "no payload");
390     }
391 }
392
393 bool diagnostic_request_equals(const DiagnosticRequest* ours,
394         const DiagnosticRequest* theirs) {
395     bool equals = ours->arbitration_id == theirs->arbitration_id &&
396         ours->mode == theirs->mode;
397     equals &= ours->has_pid == theirs->has_pid;
398     equals &= ours->pid == theirs->pid;
399     return equals;
400 }