- PortOwner is a C++ class in the following use cases. - In the case of C: can->port_owner->xxx(can, ...) instead of this->port_owner->xxx(...) - oneway calls: handled by erl_drv_entry->output() (sort of gen_server:cast) - twoway calls: handled by erl_drv_entry->control() (sort of gen_server:call) - 'magic' is here something that the programmer should not need to care about erlang -> driver Port = can_driver:init(...) - CAN and PortOwner instances created on C/C++ side CAN::init(...) { /* Start a thread for incoming messages */ /* call driver_select to register */ } {ok, {CAN_Ref}} = can_driver:open(Port, "CAN0") -> some erlang code and 'magic' in erl_drv_entry->control() -> CAN::open(const char * ifc, CAN_Ref_out ifc_ref) { CAN_Interface * interface = new CAN_Interface(ifc); CAN_Ref ifc_ref_(ifc); this->register(ifc_ref_, interface); ifc_ref = ifc_ref_; } /* Raising exception: port_owner->error() or throw */ -> 'magic' -> {ok, {CAN_Ref}} ok = can_driver:send_message(Port, CAN_Ref, 1, false, [1, 2, 3]) -> 'magic' -> CAN::send_message(CAN_Ref ifc_ref, uint32_t arbitration_id, bool remote, const OctetSeq & bytes) { /* C++ code */ } -> 'magic' -> ok (if no errors) driver -> erlang void ready_input(ErlDrvData drv_data, ErlDrvEvent event) { CAN_ptr can(CAN_OBJECT(drv_data)); /* obtain CAN_Ref_out ifc_ref and Message_out msg somehow * (e.g. from a queue) */ can->port_owner->on_message(ifc_ref, msg); } -> 'magic' -> can_driver:on_message(MyData, {CAN_Ref, Message}) ... user defined erlang code