2 * Copyright (C) 2015, 2016 "IoT.bzh"
3 * Author "Romain Forlot" <romain.forlot@iot.bzh>
4 * Author "Loic Collignon" <loic.collignon@iot.bzh>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/ioctl.h>
25 #include <linux/can.h>
26 #include <linux/can/raw.h>
28 #include <systemd/sd-event.h>
38 #include <json-c/json.h>
39 #include <openxc.pb.h>
41 #include <afb/afb-binding.h>
42 #include <afb/afb-service-itf.h>
44 #include "ll-can-binding.h"
47 /*************************************************************************/
48 /*************************************************************************/
51 /** SECTION: UTILITY FUNCTIONS **/
54 /*************************************************************************/
55 /*************************************************************************/
58 * Browse chained list and return the one with specified id
60 * param uint32_t id : can arbitration identifier
64 static can_event *get_event_list_of_id(uint32_t id)
68 /* create and return if lists not exists */
71 can_events_list = (can_event*)calloc(1, sizeof(can_event));
72 can_events_list->id = id;
73 return can_events_list;
77 current = can_events_list;
80 if (current->id == id)
84 current->next = (can_event*)calloc(1, sizeof(can_event));
85 current->next->id = id;
88 current = current->next;
95 * Take an id and return it into a char array
97 static char* create_name(uint32_t id)
102 nchar = (size_t)sprintf(name, "can_%u", id);
105 char *result = (char*)malloc(nchar + 1);
106 memcpy(result, name, nchar);
115 * Create json object that will be pushed through event_loop to any subscriber
117 * param : openxc_CanMessage structure complete with data to put into json
120 * return : json object
122 static json_object* create_json_from_openxc_CanMessage(event *event)
124 struct json_object *json;
127 * TODO: process the openxc_CanMessage struct. Should be a call to a
128 * decoder function relative to that msg
130 openxc_CanMessage can_message;
131 can_message = event->can_message;
134 json = json_object_new_object();
135 json_object_object_add(json, "name", json_object_new_string(event->name));
140 /*************************************************************************/
141 /*************************************************************************/
144 /** SECTION: HANDLE CAN DEVICE **/
147 /*************************************************************************/
148 /*************************************************************************/
151 * open the can socket
155 const int canfd_on = 1;
157 struct timeval timeout = {1, 0};
159 DEBUG(interface, "open_can_dev: CAN Handler socket : %d", socket);
163 socket = socket(PF_CAN, SOCK_RAW, CAN_RAW);
166 ERROR(interface, "open_can_dev: socket could not be created");
170 /* Set timeout for read */
171 setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
172 /* try to switch the socket into CAN_FD mode */
173 if (setsockopt(socket, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on)) < 0)
175 NOTICE(interface, "open_can_dev: Can not switch into CAN Extended frame format.");
176 is_fdmode_on = false;
181 /* Attempts to open a socket to CAN bus */
182 strcpy(ifr.ifr_name, device);
183 if(ioctl(socket, SIOCGIFINDEX, &ifr) < 0)
184 ERROR(interface, "open_can_dev: ioctl failed");
187 txAddress.can_family = AF_CAN;
188 txAddress.can_ifindex = ifr.ifr_ifindex;
190 /* And bind it to txAddress */
191 if (bind(socket, (struct sockaddr *)&txAddress, sizeof(txAddress)) < 0)
193 ERROR(interface, "open_can_dev: bind failed");
197 fcntl(socket, F_SETFL, O_NONBLOCK);
208 * TODO : test that socket is really opened
210 static int write_can()
219 * TODO change old hvac write can frame to generic on_event
221 nbytes = sendto(socket, &canfd_frame, sizeof(struct canfd_frame), 0,
222 (struct sockaddr*)&txAddress, sizeof(txAddress));
225 ERROR(interface, "write_can: Sending CAN frame failed.");
230 ERROR(interface, "write_can: socket not initialized. Attempt to reopen can device socket.");
237 * Parse the CAN frame data payload as a CAN packet
238 * TODO: parse as an OpenXC Can Message. Don't translate as ASCII and put bytes
239 * directly into openxc_CanMessage
241 static int parse_can_frame(openxc_CanMessage *can_message, struct canfd_frame *canfd_frame, int maxdlen)
246 len = (canfd_frame->len > maxdlen) ? maxdlen : canfd_frame->len;
248 can_message->has_id = true;
249 if (canfd_frame->can_id & CAN_ERR_FLAG)
250 can_message->id = canfd_frame->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG);
251 else if (canfd_frame->can_id & CAN_EFF_FLAG)
253 can_message->has_frame_format = true;
254 can_message->frame_format = openxc_CanMessage_FrameFormat_EXTENDED;
255 can_message->id = canfd_frame->can_id & CAN_EFF_MASK;
258 can_message->has_frame_format = true;
259 can_message->frame_format = openxc_CanMessage_FrameFormat_STANDARD;
260 can_message->id = canfd_frame->can_id & CAN_SFF_MASK;
263 /* Don't know what to do with that for now as we haven't
264 * len fields in openxc_CanMessage struct
266 * standard CAN frames may have RTR enabled. There are no ERR frames with RTR
267 if (maxdlen == CAN_MAX_DLEN && canfd_frame->can_id & CAN_RTR_FLAG)
269 // print a given CAN 2.0B DLC if it's not zero
270 if (canfd_frame->len && canfd_frame->len <= CAN_MAX_DLC)
271 buf[offset++] = hex_asc_upper[canfd_frame->len & 0xF];
278 /* Doesn't handle real canfd_frame for now
279 if (maxdlen == CANFD_MAX_DLEN)
281 // add CAN FD specific escape char and flags
282 canfd_frame->flags & 0xF;
285 if (sizeof(canfd_frame->data) <= sizeof(can_message->data.bytes))
287 for (i = 0; i < len; i++)
288 can_message->data.bytes[i] = canfd_frame->data[i];
290 } else if (sizeof(canfd_frame->data) <= CAN_MAX_DLEN)
292 ERROR(interface, "parse_can_frame: can_frame data too long to be stored into openxc_CanMessage data field");
294 /* TODO create as many as needed openxc_CanMessage into an array to store all data from canfd_frame
295 n_msg = CAN_MAX_DLEN / sizeof(canfd_frame->data.bytes);
296 for (i = 0; i < len; i++)
297 can_message->data.bytes[i] = canfd_frame->data[i]; */
300 ERROR(interface, "parse_can_frame: can_frame is really too long here. Size of data greater than canfd maximum 64bytes size. Is it a CAN message ?");
304 /* You should not reach this return statement */
310 * Read on CAN bus and return how much bytes has been read.
312 static int read_can(openxc_CanMessage *can_message)
317 /* Test that socket is really opened */
318 if ( socket_test() < 0)
320 if (open_can_dev() < 0)
322 ERROR(interface, "read_can: Socket unavailable");
327 nbytes = read(socket, &canfd_frame, CANFD_MTU);
329 if (nbytes == CANFD_MTU)
331 DEBUG(interface, "read_can: Got an CAN FD frame with length %d", canfd_frame.len);
333 else if (nbytes == CAN_MTU)
335 DEBUG(interface, "read_can: Got a legacy CAN frame with length %d", canfd_frame.len);
339 if (errno == ENETDOWN)
340 ERROR(interface, "read_can: %s interface down", device);
341 ERROR(interface, "read_can: Error reading CAN bus");
345 /* CAN frame integrity check */
346 if ((size_t)nbytes == CAN_MTU)
347 maxdlen = CAN_MAX_DLEN;
348 else if ((size_t)nbytes == CANFD_MTU)
349 maxdlen = CANFD_MAX_DLEN;
352 ERROR(interface, "read_can: CAN frame incomplete");
356 if (parse_can_frame(can_message, &canfd_frame, maxdlen))
358 ERROR(interface, "read_can: Can't parse the can frame. ID: %i, DLC: %i, DATA: %s",
359 canfd_frame.can_id, canfd_frame.len, canfd_frame.data);
365 /*************************************************************************/
366 /*************************************************************************/
369 /** SECTION: MANAGING EVENTS **/
372 /*************************************************************************/
373 /*************************************************************************/
374 static int on_event(sd_event_source *s, int fd, uint32_t revents, void *userdata);
377 * Get the event loop running.
378 * Will trigger on_event function on EPOLLIN event on socket
380 * Return 0 or positive value on success. Else negative value for failure.
382 static int connect_to_event_loop(CanBus &CanBus_handler)
384 sd_event *event_loop;
385 sd_event_source *source;
388 if (CanBus_handler.socket < 0)
390 return CanBus_handler.socket;
393 event_loop = afb_daemon_get_event_loop(interface->daemon);
394 rc = sd_event_add_io(event_loop, &source, CanBus_handler.socket, EPOLLIN, on_event, NULL);
397 CanBus_handler.close();
398 ERROR(interface, "Can't connect CAN bus %s to the event loop", CanBus_handler.device);
401 NOTICE(interface, "Connected CAN bus %s to the event loop", CanBus_handler.device);
409 static void send_event()
415 /* Browse can_events */
416 current = can_events_list;
419 /* Browse event for each can_events no matter what the id */
420 events = current->events;
423 object = create_json_from_openxc_CanMessage(events);
424 afb_event_push(events->afb_event, object);
425 events = events->next;
427 current = current->next;
432 * called on event on the CAN bus
434 static int on_event(sd_event_source *s, int fd, uint32_t revents, void *userdata)
436 openxc_CanMessage can_message;
438 can_message = openxc_CanMessage_init_default;
440 /* read available data */
441 if ((revents & EPOLLIN) != 0)
443 read_can(&can_message);
447 /* check if error or hangup */
448 if ((revents & (EPOLLERR|EPOLLRDHUP|EPOLLHUP)) != 0)
450 sd_event_source_unref(s);
452 connect_to_event_loop();
459 * get or create an event handler for the type
461 static event *get_event(uint32_t id, enum type type)
466 /* find the can list by id */
467 list = get_event_list_of_id(id);
469 /* make the new event */
470 event_elt = (event*)calloc(1, sizeof(event));
471 event_elt->next = event_elt;
472 list->events = event_elt;
473 event_elt->name = create_name(id);
474 event_elt->afb_event = afb_daemon_make_event(interface->daemon, event_elt->name);
479 /*************************************************************************/
480 /*************************************************************************/
483 /** SECTION: BINDING VERBS IMPLEMENTATION **/
486 /*************************************************************************/
487 /*************************************************************************/
489 * Returns the type corresponding to the given name
491 static enum type type_of_name(const char *name)
496 for (result = 0 ; (size_t)result < type_size; result++)
497 if (strcmp(type_NAMES[result], name) == 0)
503 * extract a valid type from the request
505 static int get_type_for_req(struct afb_req req, enum type *type)
507 if ((*type = type_of_name(afb_req_value(req, "type"))) != type_INVALID)
509 afb_req_fail(req, "unknown-type", NULL);
513 static int subscribe_unsubscribe_sig(struct afb_req request, int subscribe, struct signal *sig)
515 if (!afb_event_is_valid(sig->event)) {
518 sig->event = afb_daemon_make_event(afbitf->daemon, sig->name);
519 if (!afb_event_is_valid(sig->event)) {
524 if (((subscribe ? afb_req_subscribe : afb_req_unsubscribe)(request, sig->event)) < 0) {
531 static int subscribe_unsubscribe_all(struct afb_req request, int subscribe)
535 n = sizeof OBD2_PIDS / sizeof * OBD2_PIDS;
537 for (i = 0 ; i < n ; i++)
538 e += !subscribe_unsubscribe_sig(request, subscribe, &OBD2_PIDS[i]);
542 static int subscribe_unsubscribe_name(struct afb_req request, int subscribe, const char *name)
546 if (0 == strcmp(name, "*"))
547 return subscribe_unsubscribe_all(request, subscribe);
549 sig = get_signals(name);
554 return subscribe_unsubscribe_sig(request, subscribe, sig);
557 static void subscribe_unsubscribe(struct afb_req request, int subscribe)
560 struct json_object *args, *a, *x;
562 /* makes the subscription/unsubscription */
563 args = afb_req_json(request);
564 if (args == NULL || !json_object_object_get_ex(args, "event", &a)) {
565 ok = subscribe_unsubscribe_all(request, subscribe);
566 } else if (json_object_get_type(a) != json_type_array) {
567 ok = subscribe_unsubscribe_name(request, subscribe, json_object_get_string(a));
569 n = json_object_array_length(a);
571 for (i = 0 ; i < n ; i++) {
572 x = json_object_array_get_idx(a, i);
573 if (subscribe_unsubscribe_name(request, subscribe, json_object_get_string(x)))
579 /* send the report */
581 afb_req_success(request, NULL, NULL);
583 afb_req_fail(request, "error", NULL);
586 static void subscribe(struct afb_req request)
588 subscribe_unsubscribe(request, 1);
591 static void unsubscribe(struct afb_req request)
593 subscribe_unsubscribe(request, 0);
596 static const struct afb_verb_desc_v1 verbs[]=
598 { .name= "subscribe", .session= AFB_SESSION_NONE, .callback= subscribe, .info= "subscribe to notification of CAN bus messages." },
599 { .name= "unsubscribe", .session= AFB_SESSION_NONE, .callback= unsubscribe, .info= "unsubscribe a previous subscription." },
603 static const struct afb_binding binding_desc = {
604 .type = AFB_BINDING_VERSION_1,
606 .info = "CAN bus service",
612 const struct afb_binding *afbBindingV1Register (const struct afb_binding_interface *itf)
616 return &binding_desc;
619 int afbBindingV1ServiceInit(struct afb_service service)
621 std::ifstream fd_conf;
622 std::string fd_conf_content;
623 json_object jo_canbus;
625 /* Open JSON conf file */
626 /* TODO: can't use this method in c++ for now. Need to reimplement it ? */
627 jo_canbus = json_object_new_object();
628 fd_conf = afb_daemon_rootdir_open_locale(interface->daemon, "canbus.json", O_RDONLY, NULL);
631 fd_conf.seekg(0, std::ios::end);
632 fd_conf_content.resize(fd_conf.tellg());
633 fd_conf.seekg(0, std::ios::beg);
634 fd_conf.read(&fd_conf_content[0], fd_conf_content.size());
638 jo_canbus = json_tokener_parse(&fd_conf_content);
640 CanBus CanBus_handler;
641 CanBus_handler.deviceName = json_object_get_string("deviceName")
643 /* Open CAN socket */
644 CanBus_handler.open();
646 return connect_to_event_loop(CanBus_handler);