Implementation

To implement the CAN driver, you can use the following process:

  • Implement the callback functions based on your hardware implementation.
  • Configure the libcan library and CAN devices.
  • Initialize the devices and interrupt handling.
  • Execute the driver.
  • Clean up the resource manager.

Implementing the required callbacks

You need to implement the following three functions to support hardware-specific interactions for the libcan library. The actual names of these functions can be different, since the library requires pointers to the functions to be passed in during initialization:

Configuring the library

To configure the libcan library, call the can_resmgr_init() function with a pointer to the can_drvr_funcs_t structure specifying hardware-specific implementations of the required functions. Also, specify the verbosity parameter corresponding to the slog2 severity as defined in the slog2_register() function.

The pointer to the can_drvr_funcs_t instance must remain valid until after the can_resmgr_fini() function is called. For example:

can_drvr_funcs_t my_driver_funcs = {
  .transmit = my_transmit,
  .devctl = my_devctl,
  .event_handler = my_event_handler
};
...
int main(int argc, char * argv[]) {
    ...
    my_options_t opts = parse_command_line(argc, argv);
    ...
    can_resmgr_init(&my_driver_funcs, opts.verbosity);
    ...
}

For each RX and TX CAN device specific to your hardware, call the can_resmgr_init_device() function, and specify the pointer to the destination device data (CANDEV structure instance) and the pointer to the request device data (CANDEV_INIT structure instance). For example:

for (size_t i=0; i<MY_DEVICE_COUNT; i++) {
  can_resmgr_init_device(&mydevs[i]->cdev, &mydevs[i]->cdev_init);
}
Note:

While the mydevs[i]->cdev instance needs to persist beyond this call, mydevs[i]->cdev_init doesn't, and can be created on the stack before calling can_resmgr_init_device().

Initializing the resource manager

To initialize the resource manager, you must create all devices using can_resmgr_create_device(). This function creates resource manager pathnames, either /dev/canX/rxY or /dev/canX/txZ, depending on the mailbox type, unit number, and device number. For example:

for (size_t i=0; i<MY_DEVICE_COUNT; i++) {
  can_resmgr_create_device(&mydevs[i]->cdev);
}

Finally, you can associate an IRQ and a pulse code with the driver using the can_resmgr_attach_intr() function. You can pass any arbitrary data to the function, which gets passed through to the event_handler() function. Make sure to save the interrupt ID. For example:

my_driver_data.iid = can_resmgr_attach_intr(&my_driver_data, MY_DRIVER_INTERRUPT, MY_DRIVER_PULSE_CODE)

Executing the driver

To start servicing requests, call the can_resmgr_start() function. The function registers libcan's signal handler, which triggers a resource manager shutdown when appropriate. The function blocks until the driver is terminated by the SIGTERM signal. For example:

can_resmgr_start();

Cleaning up the resource manager

Once can_resmgr_start() returns, you must:

  1. Detach from the IRQ using can_resmgr_detach_intr().
  2. Destroy all devices using can_resmgr_destroy_device().
  3. Clean up the resource manager using can_resmgr_fini().

For example:

can_resmgr_detach_intr(my_driver_data.iid, MY_DRIVER_PULSE_CODE);
for (size_t i=0; i<MY_DEVICE_COUNT; i++) {
  can_resmgr_destroy_device(&mydevs[i]->cdev);
}
can_resmgr_fini();
Page updated: