#include <afb/afb-service-itf.h>
#include "ll-can-binding.h"
+#include "obd2.h"
/*************************************************************************/
/*************************************************************************/
*
* return can_event
*/
-static can_event *get_event_of_id(uint32_t id)
+static can_event *get_event_list_of_id(uint32_t id)
{
can_event *current;
* object.
*
* return : json object
- *
- *
*/
static json_object* create_json_from_openxc_CanMessage(event *event)
{
struct json_object *json;
- openxc_CanMessage can_message;
/*
* TODO: process the openxc_CanMessage struct. Should be a call to a
* decoder function relative to that msg
- */
+
+ openxc_CanMessage can_message;
can_message = event->can_message;
+ */
json = json_object_new_object();
json_object_object_add(json, "name", json_object_new_string(event->name));
return rc;
}
+/*
+ * Parse the CAN frame data payload as a CAN packet
+ * TODO: parse as an OpenXC Can Message. Don't translate as ASCII and put bytes
+ * directly into openxc_CanMessage
+ */
+static int parse_can_frame(openxc_CanMessage *can_message, struct canfd_frame *canfd_frame, int maxdlen)
+{
+ int i, len;
+ //size_t n_msg;
+
+ len = (canfd_frame->len > maxdlen) ? maxdlen : canfd_frame->len;
+
+ can_message->has_id = true;
+ if (canfd_frame->can_id & CAN_ERR_FLAG)
+ can_message->id = canfd_frame->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG);
+ else if (canfd_frame->can_id & CAN_EFF_FLAG)
+ {
+ can_message->has_frame_format = true;
+ can_message->frame_format = openxc_CanMessage_FrameFormat_EXTENDED;
+ can_message->id = canfd_frame->can_id & CAN_EFF_MASK;
+ } else
+ {
+ can_message->has_frame_format = true;
+ can_message->frame_format = openxc_CanMessage_FrameFormat_STANDARD;
+ can_message->id = canfd_frame->can_id & CAN_SFF_MASK;
+ }
+
+ /* Don't know what to do with that for now as we haven't
+ * len fields in openxc_CanMessage struct
+
+ * standard CAN frames may have RTR enabled. There are no ERR frames with RTR
+ if (maxdlen == CAN_MAX_DLEN && canfd_frame->can_id & CAN_RTR_FLAG)
+ {
+ // print a given CAN 2.0B DLC if it's not zero
+ if (canfd_frame->len && canfd_frame->len <= CAN_MAX_DLC)
+ buf[offset++] = hex_asc_upper[canfd_frame->len & 0xF];
+
+ buf[offset] = 0;
+ return NULL;
+ }
+ */
+
+ /* Doesn't handle real canfd_frame for now
+ if (maxdlen == CANFD_MAX_DLEN)
+ {
+ // add CAN FD specific escape char and flags
+ canfd_frame->flags & 0xF;
+ } */
+
+ if (sizeof(canfd_frame->data) <= sizeof(can_message->data.bytes))
+ {
+ for (i = 0; i < len; i++)
+ can_message->data.bytes[i] = canfd_frame->data[i];
+ return 0;
+ } else if (sizeof(canfd_frame->data) <= CAN_MAX_DLEN)
+ {
+ ERROR(interface, "parse_can_frame: can_frame data too long to be stored into openxc_CanMessage data field");
+ return -1;
+ /* TODO create as many as needed openxc_CanMessage into an array to store all data from canfd_frame
+ n_msg = CAN_MAX_DLEN / sizeof(canfd_frame->data.bytes);
+ for (i = 0; i < len; i++)
+ can_message->data.bytes[i] = canfd_frame->data[i]; */
+ } else
+ {
+ 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 ?");
+ return -2;
+ }
+
+ /* You should not reach this return statement */
+ return -3;
+}
+
+
/*
* Read on CAN bus and return how much bytes has been read.
*/
int maxdlen;
/* Test that socket is really opened */
- if ( socket_test < 0)
+ if ( socket_test() < 0)
{
if (retry(open_can_dev) < 0)
{
return -3;
}
- parse_can_frame(can_message, &canfd_frame, maxdlen);
-}
-
-/*
- * Parse the CAN frame data payload as a CAN packet
- * TODO: parse as an OpenXC Can Message. Don't translate as ASCII and put bytes
- * directly into openxc_CanMessage
- */
-static void parse_can_frame(openxc_CanMessage *can_message, struct canfd_frame *canfd_frame, int maxdlen)
-{
- int i,offset;
- int len = (canfd_frame->len > maxdlen) ? maxdlen : canfd_frame->len;
-
- can_message->has_id = true;
- if (canfd_frame->can_id & CAN_ERR_FLAG)
- can_message->id = canfd_frame->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG);
- else if (canfd_frame->can_id & CAN_EFF_FLAG)
- {
- can_message->has_frame_format = true;
- can_message->frame_format = openxc_CanMessage_FrameFormat_EXTENDED;
- can_message->id = canfd_frame->can_id & CAN_EFF_MASK;
- } else
- {
- can_message->has_frame_format = true;
- can_message->frame_format = openxc_CanMessage_FrameFormat_STANDARD;
- can_message->id = canfd_frame->can_id & CAN_SFF_MASK;
- }
-
- /* standard CAN frames may have RTR enabled. There are no ERR frames with RTR */
- if (maxdlen == CAN_MAX_DLEN && canfd_frame->can_id & CAN_RTR_FLAG)
- {
- /* Don't know what to do with that for now as we haven't
- * len fields in openxc_CanMessage struct
- *
- * print a given CAN 2.0B DLC if it's not zero
- if (canfd_frame->len && canfd_frame->len <= CAN_MAX_DLC)
- buf[offset++] = hex_asc_upper[canfd_frame->len & 0xF];
-
- buf[offset] = 0;*/
- return;
- }
-
- if (maxdlen == CANFD_MAX_DLEN)
+ if (parse_can_frame(can_message, &canfd_frame, maxdlen))
{
- /* add CAN FD specific escape char and flags */
- canfd_frame->flags & 0xF;
- }
-
- for (i = 0; i < len; i++)
- {
- //put_hex_byte(buf + offset, canfd_frame->data[i]);
- //offset += 2;
+ ERROR(interface, "read_can: Can't parse the can frame. ID: %i, DLC: %i, DATA: %s",
+ canfd_frame.can_id, canfd_frame.len, canfd_frame.data);
+ return -4;
}
-// buf[offset] = 0;
- return;
+ return 0;
}
-
/*************************************************************************/
/*************************************************************************/
/** **/
/** **/
/*************************************************************************/
/*************************************************************************/
+static int on_event(sd_event_source *s, int fd, uint32_t revents, void *userdata);
+
/*
- * called on an event on the CAN bus
+ * Get the event loop running.
+ * Will trigger on_event function on EPOLLIN event on socket
+ *
+ * Return 0 or positive value on success. Else negative value for failure.
*/
-static int on_event(sd_event_source *s, int fd, uint32_t revents, void *userdata)
+static int connect_to_event_loop()
{
- openxc_CanMessage can_message;
+ sd_event *event_loop;
+ sd_event_source *source;
+ int rc;
- /* read available data */
- if ((revents & EPOLLIN) != 0)
+ if (can_handler.socket < 0)
{
- read_can(&can_message);
- send_event();
+ return can_handler.socket;
}
- /* check if error or hangup */
- if ((revents & (EPOLLERR|EPOLLRDHUP|EPOLLHUP)) != 0)
+ event_loop = afb_daemon_get_event_loop(interface->daemon);
+ rc = sd_event_add_io(event_loop, &source, can_handler.socket, EPOLLIN, on_event, NULL);
+ if (rc < 0)
{
- sd_event_source_unref(s);
- close(fd);
- connect_to_event_loop();
+ close(can_handler.socket);
+ ERROR(interface, "Can't connect CAN bus %s to the event loop", can_handler.device);
+ } else
+ {
+ NOTICE(interface, "Connected CAN bus %s to the event loop", can_handler.device);
}
- return 0;
-}
-
-/*
- * get or create an event handler for the type
- * TODO: implement function and handle retrieve or create an event as needed
- */
-static event *get_event(uint32_t id, enum type type)
-{
- event *event;
- can_event *list;
-
- /* find the can list by id */
- list = get_event_of_id(id);
-
- /* make the new event */
- event = (can_event*)calloc(1, sizeof(can_event));
- event->next = event;
- list->events = event;
- event->name = create_name(id);
- event->afb_event = afb_daemon_make_event(interface->daemon, event->name);
-
- return event;
+ return rc;
}
-
/*
* Send all events
*/
}
/*
- * Get the event loop running.
- * Will trigger on_event function on EPOLLIN event on socket
- *
- * Return 0 or positive value on success. Else negative value for failure.
+ * called on an event on the CAN bus
*/
-static int connect_to_event_loop()
+static int on_event(sd_event_source *s, int fd, uint32_t revents, void *userdata)
{
- sd_event *event_loop;
- sd_event_source *source;
- int rc;
+ openxc_CanMessage can_message;
- if (can_handler.socket < 0)
+ can_message = openxc_CanMessage_init_default;
+
+ /* read available data */
+ if ((revents & EPOLLIN) != 0)
{
- return can_handler.socket;
+ read_can(&can_message);
+ send_event();
}
- event_loop = afb_daemon_get_event_loop(interface->daemon);
- rc = sd_event_add_io(event_loop, &source, can_handler.socket, EPOLLIN, on_event, NULL);
- if (rc < 0)
- {
- close(can_handler.socket);
- ERROR(interface, "Can't connect CAN bus %s to the event loop", can_handler.device);
- } else
+ /* check if error or hangup */
+ if ((revents & (EPOLLERR|EPOLLRDHUP|EPOLLHUP)) != 0)
{
- NOTICE(interface, "Connected CAN bus %s to the event loop", can_handler.device);
+ sd_event_source_unref(s);
+ close(fd);
+ connect_to_event_loop();
}
- return rc;
+ return 0;
+}
+
+/*
+ * get or create an event handler for the type
+ */
+static event *get_event(uint32_t id, enum type type)
+{
+ event *event_elt;
+ can_event *list;
+
+ /* find the can list by id */
+ list = get_event_list_of_id(id);
+
+ /* make the new event */
+ event_elt = (event*)calloc(1, sizeof(event));
+ event_elt->next = event_elt;
+ list->events = event_elt;
+ event_elt->name = create_name(id);
+ event_elt->afb_event = afb_daemon_make_event(interface->daemon, event_elt->name);
+
+ return event_elt;
}
/*************************************************************************/
return 0;
}
-/*
- * subscribe to notification of new CAN messages
- *
- * parameters of the subscription are:
- *
- * TODO type: string: choose between CAN and OBDII messages formats.
- *
- * returns an object with 2 fields:
- *
- * name: string: the name of the event without its prefix
- * id: integer: a numeric identifier of the event to be used for unsubscribing
- */
-static void subscribe(struct afb_req req)
+static int subscribe_unsubscribe_sig(struct afb_req request, int subscribe, struct signal *sig)
{
- enum type type;
- const char *period;
- event *event;
- uint32_t id;
- struct json_object *json;
-
- if (get_type_for_req(req, &type))
- {
- id = (uint32_t)atoi(afb_req_value(req, "id"));
- event = get_event(id, type);
- if (event == NULL)
- afb_req_fail(req, "out-of-memory", NULL);
- else if (afb_req_subscribe(req, event->afb_event) != 0)
- afb_req_fail_f(req, "failed", "afb_req_subscribe returned an error: %m");
- else
- {
- /* TODO : build json openXC message to send. I guess */
- json = json_object_new_object();
- json_object_object_add(json, "name", json_object_new_string(event->name));
- afb_req_success(req, json, NULL);
+ if (!afb_event_is_valid(sig->event)) {
+ if (!subscribe)
+ return 1;
+ sig->event = afb_daemon_make_event(afbitf->daemon, sig->name);
+ if (!afb_event_is_valid(sig->event)) {
+ return 0;
}
}
+
+ if (((subscribe ? afb_req_subscribe : afb_req_unsubscribe)(request, sig->event)) < 0) {
+ return 0;
+ }
+
+ return 1;
}
-/*
- * unsubscribe a previous subscription
- *
- * parameters of the unsubscription are:
- *
- * id: integer: the numeric identifier of the event as returned when subscribing
- */
-static void unsubscribe(struct afb_req req)
+static int subscribe_unsubscribe_all(struct afb_req request, int subscribe)
{
- const char *id;
- event *event;
+ int i, n, e;
- id = afb_req_value(req, "id");
- if (id == NULL)
- afb_req_fail(req, "missing-id", NULL);
- else
- {
- event = get_event_of_id(atoi(id));
- if (event == NULL)
- afb_req_fail(req, "bad-id", NULL);
- else
- {
- afb_req_unsubscribe(req, event->afb_event);
- afb_req_success(req, NULL, NULL);
+ n = sizeof OBD2_PIDS / sizeof * OBD2_PIDS;
+ e = 0;
+ for (i = 0 ; i < n ; i++)
+ e += !subscribe_unsubscribe_sig(request, subscribe, &OBD2_PIDS[i]);
+ return e == 0;
+}
+
+static int subscribe_unsubscribe_name(struct afb_req request, int subscribe, const char *name)
+{
+ struct signal *sig;
+
+ if (0 == strcmp(name, "*"))
+ return subscribe_unsubscribe_all(request, subscribe);
+
+ sig = getsig(name);
+ if (sig == NULL) {
+ return 0;
+ }
+
+ return subscribe_unsubscribe_sig(request, subscribe, sig);
+}
+
+static void subscribe_unsubscribe(struct afb_req request, int subscribe)
+{
+ int ok, i, n;
+ struct json_object *args, *a, *x;
+
+ /* makes the subscription/unsubscription */
+ args = afb_req_json(request);
+ if (args == NULL || !json_object_object_get_ex(args, "event", &a)) {
+ ok = subscribe_unsubscribe_all(request, subscribe);
+ } else if (json_object_get_type(a) != json_type_array) {
+ ok = subscribe_unsubscribe_name(request, subscribe, json_object_get_string(a));
+ } else {
+ n = json_object_array_length(a);
+ ok = 0;
+ for (i = 0 ; i < n ; i++) {
+ x = json_object_array_get_idx(a, i);
+ if (subscribe_unsubscribe_name(request, subscribe, json_object_get_string(x)))
+ ok++;
}
+ ok = (ok == n);
}
+
+ /* send the report */
+ if (ok)
+ afb_req_success(request, NULL, NULL);
+ else
+ afb_req_fail(request, "error", NULL);
+}
+
+static void subscribe(struct afb_req request)
+{
+ subscribe_unsubscribe(request, 1);
+}
+
+static void unsubscribe(struct afb_req request)
+{
+ subscribe_unsubscribe(request, 0);
}
-// TODO: Have to change session management flag to AFB_SESSION_CHECK to use token auth
static const struct afb_verb_desc_v1 verbs[]=
{
{ .name= "subscribe", .session= AFB_SESSION_NONE, .callback= subscribe, .info= "subscribe to notification of CAN bus messages." },