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);
}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:
- Detach from the IRQ using can_resmgr_detach_intr().
- Destroy all devices using can_resmgr_destroy_device().
- 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();