mwweissmann / ocaml-socketcan

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

OCaml SocketCAN

This OCaml library provides bindings to the Linux SocketCAN interface.

To start programming, check the tools:

  • candump is a simple can-sniffer using a blocking receive in a loop
  • heartbeat uses the broadcast-manager (BCM) to regularly send packages via the kernel

The full interface can be seen in the API documentation.

To compile OCaml-SocketCAN you need to have the Linux header files on your system. OCaml-SocketCAN also requires the OCaml posix-time library for timestamps on CAN frames.

The source code of time is available under the MIT license.

This library is originally written by Markus Weissmann

(** CAN bindings to the Linux SocketCAN interface *)

(** CAN identifiers of either standard (11 Bit) or extended frame format
    (29 Bit) size *)
module Id : sig
  (** A CAN identifier of up to 29 bit;
      it includes the property of it either being a CAN 2.0A (sff, 11 Bit)
      or CAN 2.0B (eff, 29 Bit) identifier. *)
  type t

  (** Create a valid CAN 2.0A {i standard frame format} identifier with 11 bit
      from an integer value.
      Masks all bits that are not in [sff_mask]. *)
  val create_sff : int -> t

  (** Create a valid CAN 2.0B {i extended frame format} identifier with 29 bit
      from an integer value.
      Masks all bits that are not in [eff_mask]. *)
  val create_eff : int -> t

  (** Get the 11 bit (sff) or 29 bit (eff) CAN identifier as integer. *)
  val to_int : t -> int

  (** [make_sff id] transforms a CAN id making it a {i standard frame format}
      identifier. If [id] already is a CAN2.0A identifier, this is a no-op. *)
  val make_sff : t -> t

  (** [is_sff id] returns whether the id [id] is a {i standard frame format}
      identifier. *)
  val is_sff : t -> bool

  (** [make_eff id] transforms a CAN id making it an {i extended frame format}
      identifier. If [id] already is a CAN2.0B identifier, this is a no-op. *)
  val make_eff : t -> t

  (** [is_eff id] returns whether the id [id] is an {i extended frame format}
      identifier.
      This is the negation of [is_sff]: [is_eff x = not is_sff x]. *)
  val is_eff : t -> bool
end

(** CAN frames representing CAN message frames including CAN identifier,
    payload and time of arrival. *)
module Frame : sig
  (** A CAN frame *)
  type t

  (** The maximum payload length in bytes of a CAN frame *)
  val max_dlen : int

  (** [create i data] creates a new CAN frame with CAN-id [i] and payload
      [data]; the payload size must not exceed [max_dlen] bytes else
      [Result.Error] is returned.
      The {i extended frame format} flag ist set, if the supplied CAN id [i] is
      an CAN 2.0B identifier ({i iff} [Id.is_eff i = true]).
      The {i remote transmission request flag} and {i error frame flag} are set
      if [rtr] and [error] are set to [true] respectively.
      The default timestamp is {i 0 sec, 0 nsec}. *)
  val create : Id.t -> ?rtr:bool -> ?error:bool
    -> ?timestamp:Posix_time.Timespec.t -> Bytes.t
    -> (t, [> `Exceeded_payload_size]) Result.result

  (** [create_exn i data] is identical to [create i data] but will raise
      [Invalid_argument] in case the payload size is exceeded. *)
  val create_exn : Id.t -> ?rtr:bool -> ?error:bool
    -> ?timestamp:Posix_time.Timespec.t -> Bytes.t -> t

  (** [create i p] creates a new CAN data frame with CAN-id [i] and payload [p];
      the payload size must not exceed [max_dlen] bytes else [Result.Error] is
      returned. *)
  val create_data : Id.t -> Bytes.t
    -> (t, [> `Exceeded_payload_size]) Result.result

  (** [create_exn i p] is identical to [create i p] but will raise
      [Invalid_argument] in case the payload size is exceeded. *)
  val create_data_exn : Id.t -> Bytes.t -> t

  (** [create_rtr i] creates a new {i remote transmission request} CAN frame
      with CAN-id [i]. The frame does not contain any payload. *)
  val create_rtr : Id.t -> t

  (** [id f] returns the CAN-id of the frame [f]. *)
  val id : t -> Id.t

  (** [data f] returns the payload of the frame [f] *)
  val data : t -> Bytes.t

  (** [set_data f d] returns a new frame with the payload set to [d]. All other
      properties of the new frame are identical to [f]. *)
  val set_data : t -> Bytes.t -> (t, [> `Exceeded_payload_size]) Result.result

  (** [timestamp frame] returns the timestamp of the frame [frame]. *)
  val timestamp : t -> Posix_time.Timespec.t

  (** [set_timestamp frame timestamp] returns a new frame with the timestamp
      set to [timestamp]. *)
  val set_timestamp : t -> Posix_time.Timespec.t -> t

  (** [is_rtr frame] returns whether the frame [frame] is a
      {i remote transmission request} frame. *)
  val is_rtr : t -> bool

  (** [set_rtr frame] returns a new frame with the
      {i remote transmission request} flag set. *)
  val set_rtr : t -> t

  (** [unset_rtr frame] returns a new frame with the
      {i remote transmission request} flag removed. *)
  val unset_rtr : t -> t

  (** [is_error frame] returns whether the frame [frame] is an error message
      frame *)
  val is_error : t -> bool

  (** [set_error frame] returns a new frame with the {i error message}
      flag set. *)
  val set_error : t -> t

  (** [unset_error frame] returns a new frame with the {i error message}
      flag removed. *)
  val unset_error : t -> t

  (** [is_sff frame] returns whether the frame [frame] is a {i standard frame
      format} frame. *)
  val is_sff : t -> bool

  (** [set_sff frame] returns a new frame with the {i standard frame format}
      flag set. The CAN identifier will be masked down to [sff_mask]. *)
  val set_sff : t -> t

  (** [is_eff frame] returns whether the frame [frame] is an {i extended frame
      format} frame.
      This is the negation of [is_sff]: [is_eff x = not is_sff x]. *)
  val is_eff : t -> bool

  (** [set_eff frame] returns a new frame with the {i extended frame format}
      flag set. *)
  val set_eff : t -> t

  (** [to_string f] returns a human-readable string representation of the frame
      [f]. *)
  val to_string : t -> string

  (** [print f] prints the frame f to [stdout] in a human-readable fashion. *)
  val print : t -> unit
end

(** CAN identifier masks for use with filters. *)
module Mask : sig
  (** A CAN identifier mask of up to 29 bit; *)
  type t

  (** Create a valid CAN 2.0A {i standard frame format} mask with 11 bit from
      an integer value.  Masks all bits that are not in [sff_mask]. *)
  val create_sff : int -> t

  (** Create a valid CAN 2.0B {i extended frame format} mask with 29 bit from
      an integer value.  Masks all bits that are not in [eff_mask]. *)
  val create_eff : int -> t

  (** Get the mask value as integer. *)
  val to_int : t -> int

  (** This is the bitmask for a {i standard frame format} CAN identifier with
      11 bit. *)
  val sff : t

  (** This is the bitmask of an {i extended frame format} CAN identifier with
      29 bit. *)
  val eff : t
end

(** Filters for incoming data that can be applied to a socket. *)
module Filter : sig
  (** A CAN filter *)
  type t

  (** [create ~mask:m id] creates a new receive filter that matches can-ids for
      which [received_id & mask = id & mask]; the default value for [can_mask]
      is [eff_mask].
      By default [extended_frames] is [`Also], [error_frames] is [`No] and
      [remote_frames] is [`No]. *)
  val create :
    ?remote_frames:[ `Also | `Exclusive | `No ] ->
    ?extended_frames:[ `Also | `Exclusive | `No ] ->
    ?mask:Mask.t -> Id.t -> t
end

(** Errors of the CAN interface *)
module Error : sig
  (** error class which gets encoded in the can-id of error-frames *)
  type t =
    | CAN_ERR_TX_TIMEOUT
    | CAN_ERR_LOSTARB
    | CAN_ERR_CRTL
    | CAN_ERR_PROT
    | CAN_ERR_TRX
    | CAN_ERR_ACK
    | CAN_ERR_BUSOFF
    | CAN_ERR_BUSERROR
    | CAN_ERR_RESTARTED

  (** error status that is encoded in the payload of error-frames *)
  type status =
    | CAN_ERR_CRTL_RX_OVERFLOW
    | CAN_ERR_CRTL_TX_OVERFLOW
    | CAN_ERR_CRTL_RX_WARNING
    | CAN_ERR_CRTL_TX_WARNING
    | CAN_ERR_CRTL_RX_PASSIVE
    | CAN_ERR_CRTL_TX_PASSIVE
    | CAN_ERR_PROT_BIT
    | CAN_ERR_PROT_FORM
    | CAN_ERR_PROT_STUFF
    | CAN_ERR_PROT_BIT0
    | CAN_ERR_PROT_BIT1
    | CAN_ERR_PROT_OVERLOAD
    | CAN_ERR_PROT_ACTIVE
    | CAN_ERR_PROT_TX
    | CAN_ERR_PROT_LOC_SOF
    | CAN_ERR_PROT_LOC_ID28_21
    | CAN_ERR_PROT_LOC_ID20_18
    | CAN_ERR_PROT_LOC_SRTR
    | CAN_ERR_PROT_LOC_IDE
    | CAN_ERR_PROT_LOC_ID17_13
    | CAN_ERR_PROT_LOC_ID12_05
    | CAN_ERR_PROT_LOC_ID04_00
    | CAN_ERR_PROT_LOC_RTR
    | CAN_ERR_PROT_LOC_RES1
    | CAN_ERR_PROT_LOC_RES0
    | CAN_ERR_PROT_LOC_DLC
    | CAN_ERR_PROT_LOC_DATA
    | CAN_ERR_PROT_LOC_CRC_SEQ
    | CAN_ERR_PROT_LOC_CRC_DEL
    | CAN_ERR_PROT_LOC_ACK
    | CAN_ERR_PROT_LOC_ACK_DEL
    | CAN_ERR_PROT_LOC_EOF
    | CAN_ERR_PROT_LOC_INTERM
    | CAN_ERR_TRX_CANH_NO_WIRE
    | CAN_ERR_TRX_CANH_SHORT_TO_BAT
    | CAN_ERR_TRX_CANH_SHORT_TO_VCC
    | CAN_ERR_TRX_CANH_SHORT_TO_GND
    | CAN_ERR_TRX_CANL_NO_WIRE
    | CAN_ERR_TRX_CANL_SHORT_TO_BAT
    | CAN_ERR_TRX_CANL_SHORT_TO_VCC
    | CAN_ERR_TRX_CANL_SHORT_TO_GND
    | CAN_ERR_TRX_CANL_SHORT_TO_CANH

  (** [of_frame f] parses the given frame [f] for it's error class and error
    status. If [f] is not an error frame, empty lists are returned. *)
  val of_frame : Frame.t -> t list * status list

  val string_of : t -> string

  val string_of_status : status -> string
end

(** A CAN socket: This is a file descriptor on which CAN message frames can be
    sent and received. *)
module Socket : sig
  (** A CAN socket for sending and receiving CAN message frames. *)
  type t

  (** [create s] opens the can-interface named [s] (e.g. "can0") *)
  val create : string -> (t, [> `EUnix of Unix.error]) Result.result

  (** [create_exn s] is identical to [create s] but will raise a
    [Unix_error] exception in case of an error. *)
  val create_exn : string -> t

  (** [close s] closes the socket [s]. *)
  val close : t -> unit

  (** [set_receive_filter s fs] adds the list of input filters [fs] to the
      socket [s]; the socket will then only receive frames the can-id of which
      matches any of the given filters;
      Calling [set_receive_filter s []] -- with an empty filter list -- will
      filter out all incoming messages!
      The original socket is modified and returned for convenience. *)
  val set_receive_filter : t -> Filter.t list
    -> (t, [> `EUnix of Unix.error ]) Result.result

  (** [set_error_flags s es] will alter the socket [s] such that all errors as
      selected in [es] will now be sent as CAN frames to the socket.
      The original socket is modified and returned for convenience. *)
  val set_error_flags : t -> Error.t list
    -> (t, [> `EUnix of Unix.error ]) Result.result

  (** [receive s] will blocking wait for the next frame on the socket [s];
      the returned frame is a copy and will not be altered by subsequent calls
      to [receive].
      The timestamp of the frame is set to the (not necessarily monotonic)
      system time indicating the time of arrival of the frame. *)
  val receive : t -> (Frame.t, [>`EUnix of Unix.error]) Result.result

  (** [receive_exn s] works exactly like [receive s] but will raise a
      [Unix_error] exception in case of an error. *)
  val receive_exn : t -> Frame.t

  (** [send s f] will send the frame [f] on the socket [s]; this call will block
      if the interface is not ready. *)
  val send : t -> Frame.t -> (int, [>`EUnix of Unix.error]) Result.result

  (** [send_exn s f] works exactly like [send s f] but will raise a
      [Unix_error] exception in case of an error. *)
  val send_exn : t -> Frame.t -> int

  (** [fs s] will return a Unix-file-descriptor of the socket [s];
      this file-descriptor can then be used with e.g. [Unix.select]. *)
  val fd : t -> Unix.file_descr
end

(** The SocketCAN BroadCast Manager (BCM) allows to send CAN message frames
    from kernel space; the kernel e.g. can then send periodic messages without
    having to be triggered by user space. *)
module BCM : sig
  (** CAN broadcast manager *)
  type t

  type opcode =
    | TX_SETUP
    | TX_DELETE
    | TX_READ
    | TX_SEND
    | TX_STATUS
    | TX_EXPIRED
    | RX_SETUP
    | RX_DELETE
    | RX_READ
    | RX_STATUS
    | RX_TIMEOUT
    | RX_CHANGED

  type flag =
    | SETTIMER
    | STARTTIMER
    | TX_COUNTEVT
    | TX_ANNOUNCE
    | TX_CP_CAN_ID
    | RX_FILTER_ID
    | RX_CHECK_DLC
    | RX_NO_AUTOTIMER
    | RX_ANNOUNCE_RESUME
    | TX_RESET_MULTI_IDX
    | RX_RTR_FRAME

  (** [create s] opens the can-interface named [s] (e.g. "can0") *)
  val create : string -> (t, [>`EUnix of Unix.error]) Result.result

  (** [close s] closes the socket [s]. *)
  val close : t -> unit

  (** time value with seconds [tv_sec] and microseconds [tv_usec] *)
  type timer = Posix_time.Timeval.t

  (** [write socket ops flags (x, t1, t2, id) frames] writes the
      configuration to the broadcast socket *)
  val write : t -> opcode list -> flag list -> (int * timer * timer * Id.t) ->
    Frame.t list -> (unit, [>`EUnix of Unix.error]) Result.result

  (** [fs s] will return a Unix-file-descriptor of the socket [s];
      this file-descriptor can then be used with e.g. [Unix.select]. *)
  val fd : t -> Unix.file_descr
end

About

License:MIT License


Languages

Language:OCaml 95.7%Language:C 4.3%