4949#include "esp32_sys.h"
5050#include "sys.h"
5151
52+ #include "include/i2c_driver.h"
53+
5254#define TAG "i2c_driver"
5355
5456static void i2c_driver_init (GlobalContext * global );
@@ -89,6 +91,9 @@ struct I2CData
8991 i2c_cmd_handle_t cmd ;
9092 term transmitting_pid ;
9193 i2c_port_t i2c_num ;
94+
95+ // no need to make it atomic, we use it only when the process table is locked
96+ int ref_count ;
9297};
9398
9499#define I2C_VALIDATE_NOT_INVALID (moniker ) \
@@ -106,6 +111,7 @@ void i2c_driver_init(GlobalContext *global)
106111Context * i2c_driver_create_port (GlobalContext * global , term opts )
107112{
108113 struct I2CData * i2c_data = calloc (1 , sizeof (struct I2CData ));
114+ i2c_data -> ref_count = 1 ;
109115 i2c_data -> transmitting_pid = term_invalid_term ();
110116
111117 term scl_io_num_term = interop_kv_get_value (opts , ATOM_STR ("\x3" , "scl" ), global );
@@ -165,16 +171,23 @@ Context *i2c_driver_create_port(GlobalContext *global, term opts)
165171 return NULL ;
166172}
167173
168- static void i2c_driver_close (Context * ctx )
174+ static NativeHandlerResult i2c_driver_maybe_close (Context * ctx )
169175{
170176 struct I2CData * i2c_data = ctx -> platform_data ;
177+ if (-- i2c_data -> ref_count != 0 ) {
178+ return NativeContinue ;
179+ }
180+
181+ ctx -> platform_data = NULL ;
171182
172183 esp_err_t err = i2c_driver_delete (i2c_data -> i2c_num );
173184 if (UNLIKELY (err != ESP_OK )) {
174185 ESP_LOGW (TAG , "Failed to delete I2C driver. err=%i" , err );
175186 }
176- free (ctx -> platform_data );
177- ctx -> platform_data = NULL ;
187+
188+ free (i2c_data );
189+
190+ return NativeTerminate ;
178191}
179192
180193static term i2cdriver_begin_transmission (Context * ctx , term pid , term req )
@@ -564,6 +577,7 @@ static NativeHandlerResult i2cdriver_consume_mailbox(Context *ctx)
564577 int local_process_id = term_to_local_process_id (gen_message .pid );
565578
566579 term ret ;
580+ NativeHandlerResult handler_result = NativeContinue ;
567581
568582 enum i2c_cmd cmd = interop_atom_term_select_int (cmd_table , cmd_term , ctx -> global );
569583 switch (cmd ) {
@@ -591,7 +605,11 @@ static NativeHandlerResult i2cdriver_consume_mailbox(Context *ctx)
591605 }
592606 break ;
593607 case I2CCloseCmd :
594- i2c_driver_close (ctx );
608+ // ugly hack: we lock before closing so _release and _acquire can assume
609+ // ctx->platform is not changed.
610+ globalcontext_get_process_lock (ctx -> global , ctx -> process_id );
611+ handler_result = i2c_driver_maybe_close (ctx );
612+ globalcontext_get_process_unlock (ctx -> global , ctx );
595613 ret = OK_ATOM ;
596614 break ;
597615
@@ -610,7 +628,60 @@ static NativeHandlerResult i2cdriver_consume_mailbox(Context *ctx)
610628 globalcontext_send_message (ctx -> global , local_process_id , ret_msg );
611629 mailbox_remove_message (& ctx -> mailbox , & ctx -> heap );
612630
613- return cmd == I2CCloseCmd ? NativeTerminate : NativeContinue ;
631+ return handler_result ;
632+ }
633+
634+ I2CAcquireResult i2c_driver_acquire (term i2c_port , i2c_port_t * i2c_num , GlobalContext * global )
635+ {
636+ if (UNLIKELY (!term_is_pid (i2c_port ))) {
637+ ESP_LOGW (TAG , "Given term is not a I2C port driver." );
638+ return I2CAcquireInvalidPeripheral ;
639+ }
640+
641+ int local_process_id = term_to_local_process_id (i2c_port );
642+ Context * ctx = globalcontext_get_process_lock (global , local_process_id );
643+
644+ if ((ctx == NULL ) || (ctx -> native_handler != i2cdriver_consume_mailbox )
645+ || (ctx -> platform_data == NULL )) {
646+ ESP_LOGW (TAG , "Given term is not a I2C port driver." );
647+ globalcontext_get_process_unlock (global , ctx );
648+ return I2CAcquireInvalidPeripheral ;
649+ }
650+
651+ struct I2CData * i2c_data = ctx -> platform_data ;
652+ i2c_data -> ref_count ++ ;
653+
654+ * i2c_num = i2c_data -> i2c_num ;
655+
656+ globalcontext_get_process_unlock (global , ctx );
657+
658+ return I2CAcquireOk ;
659+ }
660+
661+ void i2c_driver_release (term i2c_port , GlobalContext * global )
662+ {
663+ if (UNLIKELY (!term_is_pid (i2c_port ))) {
664+ ESP_LOGW (TAG , "Given term is not a I2C port driver." );
665+ return ;
666+ }
667+
668+ int local_process_id = term_to_local_process_id (i2c_port );
669+ Context * ctx = globalcontext_get_process_lock (global , local_process_id );
670+
671+ if ((ctx == NULL ) || (ctx -> native_handler != i2cdriver_consume_mailbox )
672+ || (ctx -> platform_data == NULL )) {
673+ ESP_LOGW (TAG , "Given term is not a I2C port driver." );
674+ globalcontext_get_process_unlock (global , ctx );
675+ return ;
676+ }
677+
678+ struct I2CData * i2c_data = ctx -> platform_data ;
679+ i2c_data -> ref_count -- ;
680+ NativeHandlerResult close_result = i2c_driver_maybe_close (ctx );
681+ if (close_result == NativeTerminate ) {
682+ mailbox_send_term_signal (ctx , KillSignal , NORMAL_ATOM );
683+ }
684+ globalcontext_get_process_unlock (global , ctx );
614685}
615686
616687//
0 commit comments