/*
    This file is part of MutekH.

    MutekH is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; version 2.1 of the
    License.

    MutekH is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this program.  If not, see
    <http://www.gnu.org/licenses/>.

    Copyright (c) Julien Peeters <contact@julienpeeters.net> 2016
*/

/**
   @file
   @module{Devices support library}
   @short CAN bus device class

   @section {Description}

   CAN controller class abstracts access to a CAN bus.

   A CAN message may contain from 0 to 8 bytes and is scheduled on the bus
   according to its identifier priority. The lower the identifier, the higher is
   is the priority.

   A CAN identifier may be 11 or 29 bit length. The former case is specified in
   CAN 2.0 A standard while the later is in CAN 2.0 B, which is named as
   an extended identifier.

   A CAN message that starts may encounter an arbitration lost or a
   recessive/dominant bit corruption. In such case, the message is
   considered as failed. Depending on the hardware and the driver
   implementation, the message may be automatically retried. The
   @ref dev_canbus_request_s::lifetime may be used to limit the number of
   retries. When retries stop, the request kroutine is called with the
   appropriate error code.

   Many CAN controllers offer filtering identifier to limit the bandwidth
   requirements on the device side. When supported, the controller can be
   configured using @dev dev_canbus_request_s with @tt op field set to
   @xref DEV_CANBUS_OP_FILTER.
*/

#ifndef __DEVICE_CAN_H__
#define __DEVICE_CAN_H__

#include <device/request.h>


/*********************************** Request *********************************/

enum dev_canbus_op_e
{
  /** @This schedules a message transmit on the bus. There is no guarantee that
      the message is transmitted immediately. The message will be eventually
      propagated on the bus after this one has been released by a another
      controller with a higher priority message (i.e. lower identifier).

      The following errors may occur:
      @list
        @item @tt -TIMEDOUT: if no message could have been propagated on the
          bus in the meantime of the delay provided in the request
        @item -EBUSY: if the arbitration on the bus is lost during transmission
        @item -ENOTSUP: if the controller does not support one or more requested
          parameter value (e.g. extended identifier)
        @item -EIO: in case of recessive or dominant bit error on the bus during
          the transmission of the message
      @end list
  */
  DEV_CANBUS_OP_TX = 0,

  /** @This waits for a message according to previous configuration of
      CAN identifier filtering. If no filters are configured, the controller
      will accept all messages.

      The following errors may occur:
      @list
        @item @tt -TIMEDOUT: if no message with the given identifier was transmitted
          on the bus
        @item @tt -EBUSY: if the controller cannot support an additional identifier
          filter or mask (software or hardware)
        @item @tt -ENOTSUP: if the controller does not support one or more requested
          parameter value (e.g. extended identifier)
        @item @tt -EIO: in case of recessive or dominant bit error on the bus during
          the transmission of the message
      @end list
  */
  DEV_CANBUS_OP_RX = 1,

  /** @This configure the CAN controller filter.
  */
  DEV_CANBUS_OP_FILTER = 2,
};

enum dev_canbus_format_e
{
  DEV_CANBUS_FMT_STD = 0,
  DEV_CANBUS_FMT_EXT = 1,
};

struct dev_canbus_rq_s
{
  struct dev_request_s             base;

  /** The error code of resulting of the message operation. */
  error_t                          error;

  union
    {
      struct
        {
          /** The identifier (right-aligned with the least significant bit
              first). */
          uint32_t                 ident;

          /** The data to be sent or received. In case of receive operation,
              the buffer must be at least 8-byte length. */
          uint8_t *                data;

          /** The timeout delay after the message is considered as lost (while
              transmitting). If the timeout is zero, the delay is considered as
              infinite.
          */
          dev_timer_delay_t        lifetime;

          /** Internal deadline value wrt the controller time base */
          dev_timer_value_t        deadline;

          /** @This defines the identifier format. If the controller does not
              support the given format, the message ends with @tt error set to
              @tt -ENOTSUP. */
          enum dev_canbus_format_e BITFIELD(fmt, 1);

          /** The actual size of data pointed by the @tt data pointer. The
              value is comprised in the range of 0 to 8 bytes */
          uint8_t                  BITFIELD(size, 4);
      } data;

      struct
        {
          /** A CAN identifier used with @tt mask to select a specific
              identifier group. */
          uint32_t                 ident;

          /** A CAN identifier mask that selects relevant bits in @tt ident. */
          uint32_t                 mask;

          /** @This defines the identifier format. If the controller does not
              support the given format, the message ends with @tt error set to
              @tt -ENOTSUP. */
          enum dev_canbus_format_e BITFIELD(fmt, 1);

          /** Whether ident is expected to be received or should be
              filtered out. */
          bool_t                   allowed;
        } filter;
    };

  /** The operation type of the request */
  enum dev_canbus_op_e             BITFIELD(op, 2);
};

STRUCT_INHERIT(dev_canbus_rq_s, dev_request_s, base);

/** @see dev_canbus_transmit_t */
#define DEV_CANBUS_REQUEST(n) void (n) (struct device_canbus_s * accessor, \
                                        struct dev_canbus_rq_s * rq)

/** @This schedules a request on the CAN controller. The application may cancel
    the request using @tt dev_canbus_cancel_t on the device.

    The kroutine of the message may be executed from within this function.
    Please read @xref {Nested device request completion}.
*/
typedef DEV_CANBUS_REQUEST(dev_canbus_request_t);


/************************************ Cancel *********************************/

/** @see dev_canbus_cancel_t */
#define DEV_CANBUS_CANCEL(n) error_t (n) (struct device_canbus_s * accessor, \
                                          struct dev_canbus_rq_s * rq)

/** @This cancel a request scheduled using @ref dev_canbus_request_t. This does
    not guarantee that the request if effectively canceled nor it occurs
    immediately.

    This function returns @tt -EBUSY it the request already ended or is
    going to terminate soon.
*/
typedef DEV_CANBUS_CANCEL(dev_canbus_cancel_t);


/********************************* Device class ******************************/

DRIVER_CTX_CLASS_TYPES(DRIVER_CLASS_CANBUS, canbus,
                       dev_canbus_publish_t *   f_request,
                       dev_canbus_subscribe_t * f_cancel);

#define DRIVER_CANBUS_METHODS(prefix)                                         \
  ((const struct driver_class_s *)&(const struct driver_canbus_s){            \
    .ctx_offset = offsetof(driver_pv_t, canbus_ctx),                          \
    .class_     = DRIVER_CLASS_CANBUS,                                        \
    .f_request  = prefix ## _request,                                         \
    .f_cancel   = prefix ## _cancel,                                          \
  })


#ifdef CONFIG_DEVICE_CANBUS

#define DEV_STATIC_RES_CANBUS_INFO(bitrate_, length_)                  \
    {                                                                  \
        .type = DEV_RES_CANBUS_,                                       \
            .u = { .canbus = {                                         \
                .bitrate = (bitrate_),                                 \
                .length= (length_),                                    \
            }                                                          \
        }                                                              \
    }

#else

#define DEV_STATIC_RES_CANBUS_INFO(bitrate_, length_)                  \
    {                                                                  \
        .type = DEV_RES_UNUSED,                                        \
    }

#endif


#endif
