From 57838351b6492acfa5305691eec68645b245d6ee Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Thu, 14 Nov 2019 20:49:00 +0100 Subject: [PATCH 1/8] Fix core panics: Don't use m_malloc and gc_malloc unintended MicroPython's "gc_alloc" and "m_malloc" functions should probably not be invoked from within C-level RTOS extensions. Otherwise, the garbage collector might want to prune memory which does not really hold MicroPython objects. This eventually leads to random core panics. --- esp32/ftp/ftp.c | 4 ++-- esp32/littlefs/lfs_util.h | 5 ++--- esp32/littlefs/vfs_littlefs.c | 29 ++++++++++++++--------------- esp32/littlefs/vfs_littlefs_file.c | 2 +- esp32/mods/machpwm.c | 2 ++ esp32/mods/machrmt.c | 4 ++-- esp32/mods/machtimer_alarm.c | 3 +-- esp32/mods/modcoap.c | 29 ++++++++++++++--------------- esp32/mods/modmdns.c | 1 + esp32/mods/modpycom.c | 21 ++++++++++----------- esp32/mods/moducrypto.c | 12 ++++++------ esp32/mptask.c | 2 +- esp32/mpthreadport.c | 1 + 13 files changed, 57 insertions(+), 58 deletions(-) diff --git a/esp32/ftp/ftp.c b/esp32/ftp/ftp.c index bb673e173b..0f7cd23c10 100644 --- a/esp32/ftp/ftp.c +++ b/esp32/ftp/ftp.c @@ -338,7 +338,7 @@ STATIC FRESULT f_readdir_helper(ftp_dir_t *dp, ftp_fileinfo_t *fno ) { if(length_of_relative_path > 1) { path_length++; } - char* file_relative_path = m_malloc(path_length); + char* file_relative_path = pvPortMalloc(path_length); // Copy the current working directory (relative path) memcpy(file_relative_path, path_relative, length_of_relative_path); @@ -359,7 +359,7 @@ STATIC FRESULT f_readdir_helper(ftp_dir_t *dp, ftp_fileinfo_t *fno ) { fno->u.fpinfo_lfs.timestamp.ftime = 0; } - m_free(file_relative_path); + vPortFree(file_relative_path); } xSemaphoreGive(littlefs->mutex); diff --git a/esp32/littlefs/lfs_util.h b/esp32/littlefs/lfs_util.h index 6c1c8ebe82..4a9fc619c1 100644 --- a/esp32/littlefs/lfs_util.h +++ b/esp32/littlefs/lfs_util.h @@ -21,7 +21,6 @@ #include "py/mpconfig.h" #include "py/misc.h" -#include "py/gc.h" // System includes #include @@ -204,7 +203,7 @@ uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); // Note, memory must be 64-bit aligned static inline void *lfs_malloc(size_t size) { #ifndef LFS_NO_MALLOC - return gc_alloc(size, false); + return pvPortMalloc(size); #else return NULL; #endif @@ -213,7 +212,7 @@ static inline void *lfs_malloc(size_t size) { // Deallocate memory, only used if buffers are not provided to littlefs static inline void lfs_free(void *p) { #ifndef LFS_NO_MALLOC - m_free(p); + vPortFree(p); #endif } diff --git a/esp32/littlefs/vfs_littlefs.c b/esp32/littlefs/vfs_littlefs.c index 1cb8c8ece2..150dd4762b 100644 --- a/esp32/littlefs/vfs_littlefs.c +++ b/esp32/littlefs/vfs_littlefs.c @@ -4,7 +4,6 @@ #include #include "py/runtime.h" #include "py/mperrno.h" -#include "py/gc.h" #include "lib/oofatfs/ff.h" #include "extmod/vfs.h" #include "vfs_littlefs.h" @@ -18,14 +17,14 @@ int lfs_statvfs_count(void *p, lfs_block_t b) return 0; } -// After this function m_free() must be called on the returned address after usage!! +// After this function vPortFree() must be called on the returned address after usage!! const char* concat_with_cwd(vfs_lfs_struct_t* littlefs, const char* path) { char* path_out = NULL; if (path[0] == '/') /* Absolute path */ { - path_out = (char*)gc_alloc(strlen(path) + 1, false); // Count the \0 too + path_out = (char*)pvPortMalloc(strlen(path) + 1); // Count the \0 too if(path_out != NULL) { strcpy(path_out, path); @@ -33,7 +32,7 @@ const char* concat_with_cwd(vfs_lfs_struct_t* littlefs, const char* path) } else { - path_out = (char*)gc_alloc(strlen(littlefs->cwd) + 1 + strlen(path) + 1, false); + path_out = (char*)pvPortMalloc(strlen(littlefs->cwd) + 1 + strlen(path) + 1); if(path_out != NULL) { strcpy(path_out, littlefs->cwd); @@ -130,7 +129,7 @@ static int change_cwd(vfs_lfs_struct_t* littlefs, const char* path_in) } else if(is_valid_directory(littlefs, new_path)) { - m_free(littlefs->cwd); + vPortFree(littlefs->cwd); littlefs->cwd = (char*)new_path; MP_STATE_PORT(lfs_cwd) = littlefs->cwd; @@ -354,12 +353,12 @@ void littlefs_prepare_attributes(struct lfs_file_config *cfg) { // Currently we only have 1 attribute cfg->attr_count = 1; - cfg->attrs = m_malloc(cfg->attr_count * sizeof(struct lfs_attr)); + cfg->attrs = pvPortMalloc(cfg->attr_count * sizeof(struct lfs_attr)); // Set attribute for storing the timestamp cfg->attrs[0].size = sizeof(lfs_timestamp_attribute_t); cfg->attrs[0].type = LFS_ATTRIBUTE_TIMESTAMP; - cfg->attrs[0].buffer = m_malloc(sizeof(lfs_timestamp_attribute_t)); + cfg->attrs[0].buffer = pvPortMalloc(sizeof(lfs_timestamp_attribute_t)); } @@ -367,8 +366,8 @@ void littlefs_free_up_attributes(struct lfs_file_config *cfg) { cfg->attr_count = 0; // Currently we only have 1 attribute for timestamp - m_free(cfg->attrs[0].buffer); - m_free(cfg->attrs); + vPortFree(cfg->attrs[0].buffer); + vPortFree(cfg->attrs); } @@ -490,7 +489,7 @@ STATIC mp_obj_t littlefs_vfs_ilistdir_func(size_t n_args, const mp_obj_t *args) } xSemaphoreGive(self->fs.littlefs.mutex); - m_free((void*)path); + vPortFree((void*)path); if (res != LFS_ERR_OK) { mp_raise_OSError(littleFsErrorToErrno(res)); @@ -521,7 +520,7 @@ STATIC mp_obj_t littlefs_vfs_mkdir(mp_obj_t vfs_in, mp_obj_t path_param) { } xSemaphoreGive(self->fs.littlefs.mutex); - m_free((void*)path); + vPortFree((void*)path); if (res != LFS_ERR_OK) { mp_raise_OSError(littleFsErrorToErrno(res)); @@ -550,7 +549,7 @@ STATIC mp_obj_t littlefs_vfs_remove(mp_obj_t vfs_in, mp_obj_t path_param) { } xSemaphoreGive(self->fs.littlefs.mutex); - m_free((void*)path); + vPortFree((void*)path); if (res != LFS_ERR_OK) { mp_raise_OSError(littleFsErrorToErrno(res)); @@ -582,8 +581,8 @@ STATIC mp_obj_t littlefs_vfs_rename(mp_obj_t vfs_in, mp_obj_t path_param_in, mp_ } xSemaphoreGive(self->fs.littlefs.mutex); - m_free((void*)old_path); - m_free((void*)new_path); + vPortFree((void*)old_path); + vPortFree((void*)new_path); if (res != LFS_ERR_OK) { mp_raise_OSError(littleFsErrorToErrno(res)); @@ -650,7 +649,7 @@ STATIC mp_obj_t littlefs_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_param) { xSemaphoreGive(self->fs.littlefs.mutex); - m_free((void*)path); + vPortFree((void*)path); if (res < LFS_ERR_OK) { mp_raise_OSError(littleFsErrorToErrno(res)); diff --git a/esp32/littlefs/vfs_littlefs_file.c b/esp32/littlefs/vfs_littlefs_file.c index e16e45df9d..2e6e503d0d 100644 --- a/esp32/littlefs/vfs_littlefs_file.c +++ b/esp32/littlefs/vfs_littlefs_file.c @@ -171,7 +171,7 @@ STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_ar int res = littlefs_open_common_helper(&vfs->fs.littlefs.lfs, fname, &o->fp, mode, &o->cfg, &o->timestamp_update); xSemaphoreGive(vfs->fs.littlefs.mutex); - m_free((void*)fname); + vPortFree((void*)fname); if (res < LFS_ERR_OK) { m_del_obj(pyb_file_obj_t, o); mp_raise_OSError(littleFsErrorToErrno(res)); diff --git a/esp32/mods/machpwm.c b/esp32/mods/machpwm.c index 323deb4f46..3f79fc9796 100644 --- a/esp32/mods/machpwm.c +++ b/esp32/mods/machpwm.c @@ -103,6 +103,7 @@ STATIC mp_obj_t mach_pwm_channel_make_new(mp_uint_t n_args, const mp_obj_t *pos_ // get the correct pwm timer instance if (pwm->mach_pwm_channel_obj_t[pwm_channel_id] == NULL) { + // FIXME: This should be reviewed. "gc_alloc" might not be appropriate here. pwm->mach_pwm_channel_obj_t[pwm_channel_id] = gc_alloc(sizeof(mach_pwm_channel_obj_t), false); } mach_pwm_channel_obj_t *self = pwm->mach_pwm_channel_obj_t[pwm_channel_id]; @@ -191,6 +192,7 @@ STATIC mp_obj_t mach_pwm_timer_make_new(const mp_obj_type_t *type, mp_uint_t n_a // get the correct pwm timer instance or create a new one if (MP_STATE_PORT(mach_pwm_timer_obj[pwm_timer_id]) == NULL) { + // FIXME: This should be reviewed. "gc_alloc" might not be appropriate here. MP_STATE_PORT(mach_pwm_timer_obj[pwm_timer_id]) = gc_alloc(sizeof(mach_pwm_timer_obj_t), false); memset(((mach_pwm_timer_obj_t *) MP_STATE_PORT(mach_pwm_timer_obj[pwm_timer_id]))->mach_pwm_channel_obj_t, 0, sizeof(((mach_pwm_timer_obj_t *) MP_STATE_PORT(mach_pwm_timer_obj[pwm_timer_id]))->mach_pwm_channel_obj_t)); diff --git a/esp32/mods/machrmt.c b/esp32/mods/machrmt.c index 20e4197016..d1a4716c82 100644 --- a/esp32/mods/machrmt.c +++ b/esp32/mods/machrmt.c @@ -365,7 +365,7 @@ STATIC mp_obj_t mach_rmt_pulses_send(mp_uint_t n_args, const mp_obj_t *pos_args, /* An rmt_item32_t can contain 2 bits, calculate the number of the necessary objects needed to store the input data */ mp_uint_t items_to_send_count = (data_length / 2) + (data_length % 2); - rmt_item32_t* items_to_send = (rmt_item32_t*)m_malloc(items_to_send_count * sizeof(rmt_item32_t)); + rmt_item32_t* items_to_send = (rmt_item32_t*)pvPortMalloc(items_to_send_count * sizeof(rmt_item32_t)); for(mp_uint_t i = 0, j = 0; i < items_to_send_count; i++, j++) { items_to_send[i].level0 = mp_obj_get_int(data_ptr[j]); @@ -396,7 +396,7 @@ STATIC mp_obj_t mach_rmt_pulses_send(mp_uint_t n_args, const mp_obj_t *pos_args, esp_err_t retval = rmt_write_items(self->config.channel, items_to_send, items_to_send_count, wait_tx_done); MP_THREAD_GIL_ENTER(); - m_free(items_to_send); + vPortFree(items_to_send); if (retval != ESP_OK) { nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Could not send data!")); diff --git a/esp32/mods/machtimer_alarm.c b/esp32/mods/machtimer_alarm.c index 1415f539be..fb1a4e5236 100644 --- a/esp32/mods/machtimer_alarm.c +++ b/esp32/mods/machtimer_alarm.c @@ -8,7 +8,6 @@ #include "py/mpconfig.h" #include "py/nlr.h" #include "py/runtime.h" -#include "py/gc.h" #include "py/mperrno.h" #include "util/mpirq.h" @@ -56,7 +55,7 @@ void mach_timer_alarm_preinit(void) { void mach_timer_alarm_init_heap(void) { alarm_heap.count = 0; - MP_STATE_PORT(mp_alarm_heap) = gc_alloc(ALARM_HEAP_MAX_ELEMENTS * sizeof(mp_obj_alarm_t *), false); + MP_STATE_PORT(mp_alarm_heap) = pvPortMalloc(ALARM_HEAP_MAX_ELEMENTS * sizeof(mp_obj_alarm_t *)); alarm_heap.data = MP_STATE_PORT(mp_alarm_heap); if (alarm_heap.data == NULL) { mp_printf(&mp_plat_print, "FATAL ERROR: not enough memory for the alarms heap\n"); diff --git a/esp32/mods/modcoap.c b/esp32/mods/modcoap.c index cdf4922568..ac29e87c5e 100644 --- a/esp32/mods/modcoap.c +++ b/esp32/mods/modcoap.c @@ -11,7 +11,6 @@ #include "py/mpconfig.h" #include "py/obj.h" #include "py/runtime.h" -#include "py/gc.h" #include "coap.h" #include "coap_list.h" @@ -209,7 +208,7 @@ STATIC mod_coap_resource_obj_t* add_resource(const char* uri, uint8_t mediatype, resource->next = NULL; // uri parameter pointer will be destroyed, pass a pointer to a permanent location - resource->uri = m_malloc(strlen(uri)); + resource->uri = pvPortMalloc(strlen(uri)); memcpy(resource->uri, uri, strlen(uri)); resource->coap_resource = coap_resource_init((const unsigned char* )resource->uri, strlen(uri), 0); if(resource->coap_resource != NULL) { @@ -239,7 +238,7 @@ STATIC mod_coap_resource_obj_t* add_resource(const char* uri, uint8_t mediatype, return resource; } else { - m_free(resource->uri); + vPortFree(resource->uri); m_del_obj(mod_coap_resource_obj_t, resource); // Resource cannot be created return NULL; @@ -280,13 +279,13 @@ STATIC void remove_resource_by_key(coap_key_t key) { } // Free the URI - m_free(current->uri); + vPortFree(current->uri); // Free the resource in coap's scope coap_delete_resource(context->context, key); // Free the element in MP scope - m_free(current->value); + vPortFree(current->value); // Free the resource itself - m_free(current); + vPortFree(current); return; } @@ -321,7 +320,7 @@ STATIC void resource_update_value(mod_coap_resource_obj_t* resource, mp_obj_t ne // Invalidate current data first resource->value_len = 0; - m_free(resource->value); + vPortFree(resource->value); if (mp_obj_is_integer(new_value)) { @@ -335,7 +334,7 @@ STATIC void resource_update_value(mod_coap_resource_obj_t* resource, mp_obj_t ne } // Allocate memory for the new data - resource->value = m_malloc(resource->value_len); + resource->value = pvPortMalloc(resource->value_len); memcpy(resource->value, &value, sizeof(value)); } else { @@ -345,7 +344,7 @@ STATIC void resource_update_value(mod_coap_resource_obj_t* resource, mp_obj_t ne resource->value_len = value_bufinfo.len; // Allocate memory for the new data - resource->value = m_malloc(resource->value_len); + resource->value = pvPortMalloc(resource->value_len); memcpy(resource->value, value_bufinfo.buf, resource->value_len); } } @@ -749,7 +748,7 @@ STATIC coap_pdu_t * modcoap_new_request // Helper function to create a new option for a request message STATIC coap_list_t * modcoap_new_option_node(unsigned short key, unsigned int length, unsigned char *data) { - coap_list_t *node = m_malloc(sizeof(coap_list_t) + sizeof(coap_option) + length); + coap_list_t *node = pvPortMalloc(sizeof(coap_list_t) + sizeof(coap_option) + length); if (node) { coap_option *option; option = (coap_option *)(node->data); @@ -757,7 +756,7 @@ STATIC coap_list_t * modcoap_new_option_node(unsigned short key, unsigned int le COAP_OPTION_LENGTH(*option) = length; memcpy(COAP_OPTION_DATA(*option), data, length); } else { - m_free(node); + vPortFree(node); node = NULL; } @@ -938,7 +937,7 @@ STATIC mp_obj_t mod_coap_init(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map // Only 1 context is supported currently if(initialized == false) { - MP_STATE_PORT(coap_ptr) = m_malloc(sizeof(mod_coap_obj_t)); + MP_STATE_PORT(coap_ptr) = pvPortMalloc(sizeof(mod_coap_obj_t)); coap_obj_ptr = MP_STATE_PORT(coap_ptr); coap_obj_ptr->context = NULL; coap_obj_ptr->resources = NULL; @@ -1235,7 +1234,7 @@ STATIC mp_obj_t mod_coap_send_request(mp_uint_t n_args, const mp_obj_t *pos_args // Split up the URI-PATH into more segments if needed //TODO: allocate the proper length size_t length = 300; - unsigned char* path = m_malloc(length); + unsigned char* path = pvPortMalloc(length); int segments = coap_split_path(coap_uri.path.s, coap_uri.path.length, path, &length); // Insert the segments as separate URI-Path options @@ -1249,7 +1248,7 @@ STATIC mp_obj_t mod_coap_send_request(mp_uint_t n_args, const mp_obj_t *pos_args } - m_free(path); + vPortFree(path); // Put Content Format option if given if(content_format != -1) { @@ -1272,7 +1271,7 @@ STATIC mp_obj_t mod_coap_send_request(mp_uint_t n_args, const mp_obj_t *pos_args while(coap_obj_ptr->optlist != NULL) { next = coap_obj_ptr->optlist->next; coap_obj_ptr->optlist->next = NULL; - m_free(coap_obj_ptr->optlist); + vPortFree(coap_obj_ptr->optlist); coap_obj_ptr->optlist = next; } diff --git a/esp32/mods/modmdns.c b/esp32/mods/modmdns.c index 5b1c8ebed6..4b36479a4d 100644 --- a/esp32/mods/modmdns.c +++ b/esp32/mods/modmdns.c @@ -168,6 +168,7 @@ STATIC mp_obj_t mod_mdns_add_service(mp_uint_t n_args, const mp_obj_t *pos_args, mp_obj_t* items; mp_obj_tuple_get(args[3].u_obj, &length_total, &items); + // FIXME: This should be reviewed. "m_malloc" might not be appropriate here. service_txt = m_malloc(length_total * sizeof(mdns_txt_item_t)); for(int i = 0; i < length_total; i++) { diff --git a/esp32/mods/modpycom.c b/esp32/mods/modpycom.c index 477abc2e17..16758ab6d9 100644 --- a/esp32/mods/modpycom.c +++ b/esp32/mods/modpycom.c @@ -302,16 +302,15 @@ STATIC mp_obj_t mod_pycom_nvs_get (mp_uint_t n_args, const mp_obj_t *args) { else { esp_err = nvs_get_str(pycom_nvs_handle, key, NULL, &value); if(esp_err == ESP_OK) { - char* value_string = (char*)m_malloc(value); + char* value_string = (char*)pvPortMalloc(value); esp_err = nvs_get_str(pycom_nvs_handle, key, value_string, &value); if(esp_err == ESP_OK) { //do not count the terminating \0 ret = mp_obj_new_str(value_string, value-1); - m_free(value_string); } - m_free(value_string); + vPortFree(value_string); } } @@ -422,7 +421,7 @@ STATIC mp_obj_t mod_pycom_wifi_ssid_sta (mp_uint_t n_args, const mp_obj_t *args) else{/*Nothing*/} } else { - uint8_t * ssid = (uint8_t *)m_malloc(33); + uint8_t * ssid = (uint8_t *)pvPortMalloc(33); mp_obj_t ssid_obj; if(config_get_wifi_sta_ssid(ssid)) { @@ -432,7 +431,7 @@ STATIC mp_obj_t mod_pycom_wifi_ssid_sta (mp_uint_t n_args, const mp_obj_t *args) { ssid_obj = mp_const_none; } - m_free(ssid); + vPortFree(ssid); return ssid_obj; } return mp_const_none; @@ -451,7 +450,7 @@ STATIC mp_obj_t mod_pycom_wifi_pwd_sta (mp_uint_t n_args, const mp_obj_t *args) } else{/*Nothing*/} } else { - uint8_t * pwd = (uint8_t *)m_malloc(65); + uint8_t * pwd = (uint8_t *)pvPortMalloc(65); mp_obj_t pwd_obj; if(config_get_wifi_sta_pwd(pwd)) { @@ -461,7 +460,7 @@ STATIC mp_obj_t mod_pycom_wifi_pwd_sta (mp_uint_t n_args, const mp_obj_t *args) { pwd_obj = mp_const_none; } - m_free(pwd); + vPortFree(pwd); return pwd_obj; } return mp_const_none; @@ -480,7 +479,7 @@ STATIC mp_obj_t mod_pycom_wifi_ssid_ap (mp_uint_t n_args, const mp_obj_t *args) } else{/*Nothing*/} } else { - uint8_t * ssid = (uint8_t *)m_malloc(33); + uint8_t * ssid = (uint8_t *)pvPortMalloc(33); mp_obj_t ssid_obj; if(config_get_wifi_ap_ssid(ssid)) { @@ -490,7 +489,7 @@ STATIC mp_obj_t mod_pycom_wifi_ssid_ap (mp_uint_t n_args, const mp_obj_t *args) { ssid_obj = mp_const_none; } - m_free(ssid); + vPortFree(ssid); return ssid_obj; } return mp_const_none; @@ -509,7 +508,7 @@ STATIC mp_obj_t mod_pycom_wifi_pwd_ap (mp_uint_t n_args, const mp_obj_t *args) { } else{/*Nothing*/} } else { - uint8_t * pwd = (uint8_t *)m_malloc(65); + uint8_t * pwd = (uint8_t *)pvPortMalloc(65); mp_obj_t pwd_obj; if(config_get_wifi_ap_pwd(pwd)) { @@ -519,7 +518,7 @@ STATIC mp_obj_t mod_pycom_wifi_pwd_ap (mp_uint_t n_args, const mp_obj_t *args) { { pwd_obj = mp_const_none; } - m_free(pwd); + vPortFree(pwd); return pwd_obj; } return mp_const_none; diff --git a/esp32/mods/moducrypto.c b/esp32/mods/moducrypto.c index bdd7704f9c..b6ef460ca2 100644 --- a/esp32/mods/moducrypto.c +++ b/esp32/mods/moducrypto.c @@ -324,7 +324,7 @@ STATIC mp_obj_t mod_crypt_generate_rsa_signature(mp_uint_t n_args, const mp_obj_ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_RuntimeError, "Message Digest operation failed, error code: %d", rc)); } - unsigned char *signature = m_malloc(5000); + unsigned char *signature = pvPortMalloc(5000); size_t signature_length; rc = mbedtls_pk_sign(&pk_context, MBEDTLS_MD_SHA256, digest, sizeof(digest), signature, &signature_length, mbedtls_ctr_drbg_random, &ctr_drbg); @@ -335,7 +335,7 @@ STATIC mp_obj_t mod_crypt_generate_rsa_signature(mp_uint_t n_args, const mp_obj_ mp_obj_t ret_signature = mp_obj_new_bytes((const byte*)signature, signature_length); mbedtls_pk_free(&pk_context); - m_free((char*)signature); + vPortFree((char*)signature); return ret_signature; } @@ -382,7 +382,7 @@ STATIC mp_obj_t mod_crypt_rsa_encrypt(mp_uint_t n_args, const mp_obj_t *pos_args strlen(pers)); size_t output_len = message.len + 256; - unsigned char *output = m_malloc(output_len); + unsigned char *output = pvPortMalloc(output_len); size_t output_actual_length = 0; rc = mbedtls_pk_encrypt(&pk_context, @@ -401,7 +401,7 @@ STATIC mp_obj_t mod_crypt_rsa_encrypt(mp_uint_t n_args, const mp_obj_t *pos_args mp_obj_t ret_output = mp_obj_new_bytes((const byte*)output, output_actual_length); mbedtls_pk_free(&pk_context); - m_free((char*)output); + vPortFree((char*)output); return ret_output; } @@ -448,7 +448,7 @@ STATIC mp_obj_t mod_crypt_rsa_decrypt(mp_uint_t n_args, const mp_obj_t *pos_args strlen(pers)); size_t output_len = message.len + 256; - unsigned char *output = m_malloc(output_len); + unsigned char *output = pvPortMalloc(output_len); size_t output_actual_length = 0; rc = mbedtls_pk_decrypt(&pk_context, @@ -467,7 +467,7 @@ STATIC mp_obj_t mod_crypt_rsa_decrypt(mp_uint_t n_args, const mp_obj_t *pos_args mp_obj_t ret_output = mp_obj_new_bytes((const byte*)output, output_actual_length); mbedtls_pk_free(&pk_context); - m_free((char*)output); + vPortFree((char*)output); return ret_output; } diff --git a/esp32/mptask.c b/esp32/mptask.c index 9ae359dc7d..37194dfebe 100644 --- a/esp32/mptask.c +++ b/esp32/mptask.c @@ -534,7 +534,7 @@ STATIC void mptask_init_sflash_filesystem_littlefs(void) { MP_STATE_PORT(vfs_cur) = vfs; //Initialize the current working directory (cwd) - vfs_littlefs->fs.littlefs.cwd = (char*)m_malloc(2); + vfs_littlefs->fs.littlefs.cwd = (char*)pvPortMalloc(2); vfs_littlefs->fs.littlefs.cwd[0] = '/'; vfs_littlefs->fs.littlefs.cwd[1] = '\0'; diff --git a/esp32/mpthreadport.c b/esp32/mpthreadport.c index 276c2cd5aa..e88e0f3106 100644 --- a/esp32/mpthreadport.c +++ b/esp32/mpthreadport.c @@ -280,6 +280,7 @@ void vPortCleanUpTCB (void *tcb) { mp_obj_thread_lock_t *mp_thread_new_thread_lock(void) { mp_obj_thread_lock_t *self = m_new_obj(mp_obj_thread_lock_t); + // FIXME: This should be reviewed. "gc_alloc" might not be appropriate here. self->mutex = gc_alloc(sizeof(mp_thread_mutex_t), false); if (NULL == self->mutex) { nlr_raise(mp_obj_new_exception_msg(&mp_type_MemoryError, "can't create lock")); From 3b6a73874d9514c259ca59beba71bb52922132c4 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Wed, 26 Feb 2020 03:34:12 +0100 Subject: [PATCH 2/8] Fix integer overflow in filesystem stat implementation. Using small ints for storing st_atime, st_mtime and st_ctime values is prone to integer overflows, effectively yielding wrong timestamps. This resolves #409. Contributed by @robert-hh. --- esp32/littlefs/vfs_littlefs.c | 6 +++--- extmod/vfs_fat.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esp32/littlefs/vfs_littlefs.c b/esp32/littlefs/vfs_littlefs.c index 150dd4762b..dd4fd3dee7 100644 --- a/esp32/littlefs/vfs_littlefs.c +++ b/esp32/littlefs/vfs_littlefs.c @@ -685,9 +685,9 @@ STATIC mp_obj_t littlefs_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_param) { else { t->items[6] = mp_obj_new_int_from_uint(0); // st_size } - t->items[7] = MP_OBJ_NEW_SMALL_INT(seconds); // st_atime - t->items[8] = MP_OBJ_NEW_SMALL_INT(seconds); // st_mtime - t->items[9] = MP_OBJ_NEW_SMALL_INT(seconds); // st_ctime + t->items[7] = mp_obj_new_int_from_uint(seconds); // st_atime + t->items[8] = mp_obj_new_int_from_uint(seconds); // st_mtime + t->items[9] = mp_obj_new_int_from_uint(seconds); // st_ctime return MP_OBJ_FROM_PTR(t); } diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 9776f60aa9..f7cb095cbd 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -365,9 +365,9 @@ STATIC mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) { t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid t->items[6] = mp_obj_new_int_from_uint(fno.fsize); // st_size - t->items[7] = MP_OBJ_NEW_SMALL_INT(seconds); // st_atime - t->items[8] = MP_OBJ_NEW_SMALL_INT(seconds); // st_mtime - t->items[9] = MP_OBJ_NEW_SMALL_INT(seconds); // st_ctime + t->items[7] = mp_obj_new_int_from_uint(seconds); // st_atime + t->items[8] = mp_obj_new_int_from_uint(seconds); // st_mtime + t->items[9] = mp_obj_new_int_from_uint(seconds); // st_ctime return MP_OBJ_FROM_PTR(t); } From 2cb8cd469abcf9abccafccf3b235e67251cbe6aa Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Mon, 4 Nov 2019 20:33:42 +0100 Subject: [PATCH 3/8] extmod/modonewire.c: Add "modonewire" from Genuine MicroPython This tremendously improves robustness of 1-Wire support. While the original Pycom implementation is written in pure-Python, this adds the C-level "modonewire" module from Genuine MicroPython. It will also perform crc checks when reading the DS18B20 sensors. Driver support by @robert-hh: https://github.com/robert-hh/Onewire_DS18X20 --- esp32/hal/esp32_mphal.h | 17 +++++++++++++++++ esp32/mpconfigport.h | 2 ++ py/py.mk | 1 + 3 files changed, 20 insertions(+) diff --git a/esp32/hal/esp32_mphal.h b/esp32/hal/esp32_mphal.h index 8958e391ed..5deb90eb76 100644 --- a/esp32/hal/esp32_mphal.h +++ b/esp32/hal/esp32_mphal.h @@ -30,4 +30,21 @@ void mp_hal_set_interrupt_char(int c); void mp_hal_set_reset_char(int c); void mp_hal_reset_safe_and_boot(bool reset); +// https://github.com/micropython/micropython/blob/master/ports/esp32/mphalport.h +#define mp_hal_quiet_timing_enter() MICROPY_BEGIN_ATOMIC_SECTION() +#define mp_hal_quiet_timing_exit(irq_state) MICROPY_END_ATOMIC_SECTION(irq_state) +#define mp_hal_delay_us_fast(us) ets_delay_us(us) + +// C-level pin HAL +#include "py/obj.h" +#include "machpin.h" +#define mp_hal_pin_obj_t mp_obj_t +#define mp_hal_get_pin_obj(pin) (pin) +#define mp_hal_pin_write(p, v) do { \ + pin_obj_t *npin = pin_find(p); \ + npin->value = v; \ + pin_set_value(npin); \ + } while (0) +#define mp_hal_pin_read(p) pin_get_value(pin_find(p)) + #endif // _INCLUDED_MPHAL_H_ diff --git a/esp32/mpconfigport.h b/esp32/mpconfigport.h index 51a30b2e05..357dff752a 100644 --- a/esp32/mpconfigport.h +++ b/esp32/mpconfigport.h @@ -191,6 +191,7 @@ extern const struct _mp_obj_module_t mp_module_uhashlib; extern const struct _mp_obj_module_t module_ucrypto; extern const struct _mp_obj_module_t mp_module_ussl; extern const struct _mp_obj_module_t mp_module_uqueue; +extern const struct _mp_obj_module_t mp_module_onewire; #define MICROPY_PORT_BUILTIN_MODULES \ { MP_OBJ_NEW_QSTR(MP_QSTR_umachine), (mp_obj_t)&machine_module }, \ @@ -206,6 +207,7 @@ extern const struct _mp_obj_module_t mp_module_uqueue; { MP_OBJ_NEW_QSTR(MP_QSTR_ussl), (mp_obj_t)&mp_module_ussl }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_uerrno), (mp_obj_t)&mp_module_uerrno }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_uqueue), (mp_obj_t)&mp_module_uqueue }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \ #define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \ { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&machine_module }, \ diff --git a/py/py.mk b/py/py.mk index 3d8d849e2b..3a882d2dea 100644 --- a/py/py.mk +++ b/py/py.mk @@ -287,6 +287,7 @@ PY_EXTMOD_O_BASENAME = \ extmod/moduwebsocket.o \ extmod/modwebrepl.o \ extmod/modframebuf.o \ + extmod/modonewire.o \ extmod/vfs.o \ extmod/vfs_reader.o \ extmod/vfs_posix.o \ From 2536ff88db45120a0387cc3628bfe367d786b192 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Tue, 19 Nov 2019 02:31:37 +0100 Subject: [PATCH 4/8] Add I2S support This commit adds I2S support from Genuine MicroPython by Mike Teachman. See also https://github.com/micropython/micropython/pull/4471 --- esp32/application.mk | 1 + esp32/mods/machine_i2s.c | 447 +++++++++++++++++++++++++++++++++++++++ esp32/mods/machine_i2s.h | 34 +++ esp32/mods/modmachine.c | 2 + 4 files changed, 484 insertions(+) create mode 100644 esp32/mods/machine_i2s.c create mode 100644 esp32/mods/machine_i2s.h diff --git a/esp32/application.mk b/esp32/application.mk index 70c0f7d96d..f830e5532e 100644 --- a/esp32/application.mk +++ b/esp32/application.mk @@ -134,6 +134,7 @@ APP_MODS_SRC_C = $(addprefix mods/,\ pybflash.c \ machspi.c \ machine_i2c.c \ + machine_i2s.c \ machpwm.c \ machcan.c \ modmachine.c \ diff --git a/esp32/mods/machine_i2s.c b/esp32/mods/machine_i2s.c new file mode 100644 index 0000000000..0c1da772ed --- /dev/null +++ b/esp32/mods/machine_i2s.c @@ -0,0 +1,447 @@ +/* + * This file is part of the MicroPython ESP32 project + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Mike Teachman + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "py/mpconfig.h" +#include "py/obj.h" +#include "py/runtime.h" + +#include "machine_i2s.h" + +// Adapt calls to "machine_pin_get_id" +#include "machpin.h" +#include "pins.h" +#define machine_pin_get_id(pin) pin_find(pin)->pin_number + +#include "driver/i2s.h" + +// Notes on naming conventions: +// 1. "id" versus "port" +// The MicroPython API identifies instances of a peripheral using "id", while the ESP-IDF uses "port". +// - for example, the first I2S peripheral on the ESP32 would be indicated by id=0 in MicroPython +// and port=0 in ESP-IDF +// 2. any C type, macro, or function prefaced by "i2s" is associated with an ESP-IDF I2S interface definition +// 3. any C type, macro, or function prefaced by "machine_i2s" is associated with the MicroPython implementation of I2S + +typedef struct _machine_i2s_obj_t { + mp_obj_base_t base; + i2s_port_t id; + i2s_comm_format_t standard; + uint8_t mode; + i2s_bits_per_sample_t dataformat; + i2s_channel_fmt_t channelformat; + int32_t samplerate; + int16_t dmacount; + int16_t dmalen; + int32_t apllrate; + int8_t bck; + int8_t ws; + int8_t sdout; + int8_t sdin; + bool used; +} machine_i2s_obj_t; + +// Static object mapping to I2S peripherals +// note: I2S implementation makes use of the following mapping between I2S peripheral and I2S object +// I2S peripheral 1: machine_i2s_obj[0] +// I2S peripheral 2: machine_i2s_obj[1] +STATIC machine_i2s_obj_t machine_i2s_obj[I2S_NUM_MAX] = { + [0].used = false, + [1].used = false }; + +STATIC void machine_i2s_init_helper(machine_i2s_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + + enum { + ARG_bck, + ARG_ws, + ARG_sdout, + ARG_sdin, + ARG_standard, + ARG_mode, + ARG_dataformat, + ARG_channelformat, + ARG_samplerate, + ARG_dmacount, + ARG_dmalen, + ARG_apllrate, + }; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bck, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_ws, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_sdout, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_sdin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_standard, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = I2S_COMM_FORMAT_I2S} }, + { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_dataformat, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_channelformat, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_samplerate, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_dmacount, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16} }, + { MP_QSTR_dmalen, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} }, + { MP_QSTR_apllrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_pos_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // + // ---- Check validity of arguments ---- + // + + // are I2S pin assignments valid? + int8_t bck = args[ARG_bck].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_bck].u_obj); + int8_t ws = args[ARG_ws].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_ws].u_obj); + int8_t sdin = args[ARG_sdin].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_sdin].u_obj); + int8_t sdout = args[ARG_sdout].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_sdout].u_obj); + + if ((sdin == -1) && (args[ARG_mode].u_int == (I2S_MODE_MASTER | I2S_MODE_RX))) { + mp_raise_ValueError("sdin must be specified for RX mode"); + } + + if ((sdout == -1) && (args[ARG_mode].u_int == (I2S_MODE_MASTER | I2S_MODE_TX))) { + mp_raise_ValueError("sdout must be specified for TX mode"); + } + + if ((sdin != -1) && (sdout != -1)) { + mp_raise_ValueError("only one of sdin or sdout can be specified"); + } + + // is Standard valid? + i2s_comm_format_t i2s_commformat = args[ARG_standard].u_int; + if ((i2s_commformat != I2S_COMM_FORMAT_I2S) && + (i2s_commformat != (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB))) { + mp_raise_ValueError("Standard is not valid"); + } + + // is Mode valid? + i2s_mode_t i2s_mode = args[ARG_mode].u_int; + if ((i2s_mode != (I2S_MODE_MASTER | I2S_MODE_RX)) && + (i2s_mode != (I2S_MODE_MASTER | I2S_MODE_TX))) { + mp_raise_ValueError("Only Master Rx or Master Tx Modes are supported"); + } + + // is Data Format valid? + i2s_bits_per_sample_t i2s_bits_per_sample = args[ARG_dataformat].u_int; + if ((i2s_bits_per_sample != I2S_BITS_PER_SAMPLE_16BIT) && + (i2s_bits_per_sample != I2S_BITS_PER_SAMPLE_24BIT) && + (i2s_bits_per_sample != I2S_BITS_PER_SAMPLE_32BIT)) { + mp_raise_ValueError("Data Format is not valid"); + } + + // is Channel Format valid? + i2s_channel_fmt_t i2s_channelformat = args[ARG_channelformat].u_int; + if ((i2s_channelformat != I2S_CHANNEL_FMT_RIGHT_LEFT) && + (i2s_channelformat != I2S_CHANNEL_FMT_ALL_RIGHT) && + (i2s_channelformat != I2S_CHANNEL_FMT_ALL_LEFT) && + (i2s_channelformat != I2S_CHANNEL_FMT_ONLY_RIGHT) && + (i2s_channelformat != I2S_CHANNEL_FMT_ONLY_LEFT)) { + mp_raise_ValueError("Channel Format is not valid"); + } + + // is Sample Rate valid? + // No validation done: ESP-IDF API does not indicate a valid range for sample rate + + // is DMA Buffer Count valid? + // ESP-IDF API code checks for buffer count in this range: [2, 128] + int16_t i2s_dmacount = args[ARG_dmacount].u_int; + if ((i2s_dmacount < 2) || (i2s_dmacount > 128)) { + mp_raise_ValueError("DMA Buffer Count is not valid. Allowed range is [2, 128]"); + } + + // is DMA Buffer Length valid? + // ESP-IDF API code checks for buffer length in this range: [8, 1024] + int16_t i2s_dmalen = args[ARG_dmalen].u_int; + if ((i2s_dmalen < 8) || (i2s_dmalen > 1024)) { + mp_raise_ValueError("DMA Buffer Length is not valid. Allowed range is [8, 1024]"); + } + + // is APLL Rate valid? + // No validation done: ESP-IDF API does not indicate a valid range for APLL rate + + self->bck = bck; + self->ws = ws; + self->sdout = sdout; + self->sdin = sdin; + self->standard = args[ARG_standard].u_int; + self->mode = args[ARG_mode].u_int; + self->dataformat = args[ARG_dataformat].u_int; + self->channelformat = args[ARG_channelformat].u_int; + self->samplerate = args[ARG_samplerate].u_int; + self->dmacount = args[ARG_dmacount].u_int; + self->dmalen = args[ARG_dmalen].u_int; + self->apllrate = args[ARG_apllrate].u_int; + + i2s_config_t i2s_config; + i2s_config.communication_format = self->standard; + i2s_config.mode = self->mode; + i2s_config.bits_per_sample = self->dataformat; + i2s_config.channel_format = self->channelformat; + i2s_config.sample_rate = self->samplerate; + i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LOWMED; // allows simultaneous use of both I2S channels + i2s_config.dma_buf_count = self->dmacount; + i2s_config.dma_buf_len = self->dmalen; + if (self->apllrate != 0) { + i2s_config.use_apll = true; + } else { + i2s_config.use_apll = false; + } + i2s_config.fixed_mclk = self->apllrate; + + // uninstall I2S driver when changes are being made to an active I2S peripheral + if (self->used) { + i2s_driver_uninstall(self->id); + } + + esp_err_t ret = i2s_driver_install(self->id, &i2s_config, 0, NULL); + switch (ret) { + case ESP_OK: + break; + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "I2S driver install: Parameter error"); + break; + case ESP_ERR_NO_MEM: + mp_raise_msg(&mp_type_OSError, "I2S driver install: Out of memory"); + break; + default: + // this error not documented in ESP-IDF + mp_raise_msg(&mp_type_OSError, "I2S driver install: Undocumented error"); + break; + } + + i2s_pin_config_t pin_config; + pin_config.bck_io_num = self->bck; + pin_config.ws_io_num = self->ws; + pin_config.data_out_num = self->sdout; + pin_config.data_in_num = self->sdin; + + ret = i2s_set_pin(self->id, &pin_config); + switch (ret) { + case ESP_OK: + break; + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "I2S set pin: Parameter error"); + break; + case ESP_FAIL: + mp_raise_msg(&mp_type_OSError, "I2S set pin: IO error"); + break; + default: + // this error not documented in ESP-IDF + mp_raise_msg(&mp_type_OSError, "I2S set pin: Undocumented error"); + break; + } + + self->used = true; +} + +/******************************************************************************/ +// MicroPython bindings for I2S +STATIC void machine_i2s_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_i2s_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "I2S(id=%u, bck=%d, ws=%d, sdout=%d, sdin=%d\n" + "standard=%u, mode=%u,\n" + "dataformat=%u, channelformat=%u,\n" + "samplerate=%d,\n" + "dmacount=%d, dmalen=%d,\n" + "apllrate=%d)", + self->id, self->bck, self->ws, self->sdout, self->sdin, + self->standard, self->mode, + self->dataformat, self->channelformat, + self->samplerate, + self->dmacount, self->dmalen, + self->apllrate + ); +} + +STATIC mp_obj_t machine_i2s_make_new(const mp_obj_type_t *type, size_t n_pos_args, size_t n_kw_args, const mp_obj_t *args) { + mp_arg_check_num(n_pos_args, n_kw_args, 1, MP_OBJ_FUN_ARGS_MAX, true); + + machine_i2s_obj_t *self; + + // note: it is safe to assume that the arg pointer below references a positional argument because the arg check above + // guarantees that at least one positional argument has been provided + i2s_port_t i2s_id = mp_obj_get_int(args[0]); + if (i2s_id == I2S_NUM_0) { + self = &machine_i2s_obj[0]; + } else if (i2s_id == I2S_NUM_1) { + self = &machine_i2s_obj[1]; + } else { + mp_raise_ValueError("I2S ID is not valid"); + } + + self->base.type = &machine_i2s_type; + self->id = i2s_id; + + // is I2S peripheral already in use? + if (self->used) { + mp_raise_ValueError("I2S port is already in use"); + } + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args); + // note: "args + 1" below has the effect of skipping over the ID argument + machine_i2s_init_helper(self, n_pos_args - 1, args + 1, &kw_args); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t machine_i2s_init(mp_uint_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // note: "pos_args + 1" below has the effect of skipping over "self" + machine_i2s_init_helper(pos_args[0], n_pos_args - 1, pos_args + 1, kw_args); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2s_init_obj, 1, machine_i2s_init); + +STATIC mp_obj_t machine_i2s_readinto(mp_uint_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buf, ARG_timeout }; + STATIC const mp_arg_t allowed_args[] = { + { MP_QSTR_buf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_pos_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(args), allowed_args, args); + + machine_i2s_obj_t *self = pos_args[0]; + + if (!self->used) { + mp_raise_ValueError("I2S port is not initialized"); + } + + if (self->mode != (I2S_MODE_MASTER | I2S_MODE_RX)) { + mp_raise_ValueError("I2S not configured for read method"); + } + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_WRITE); + + TickType_t timeout_in_ticks = portMAX_DELAY; + if (args[ARG_timeout].u_int != -1) { + timeout_in_ticks = pdMS_TO_TICKS(args[ARG_timeout].u_int); + } + + uint32_t num_bytes_read = 0; + esp_err_t ret = i2s_read(self->id, bufinfo.buf, bufinfo.len, &num_bytes_read, timeout_in_ticks); + switch (ret) { + case ESP_OK: + break; + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "I2S read: Parameter error"); + break; + default: + // this error not documented in ESP-IDF + mp_raise_msg(&mp_type_OSError, "I2S read: Undocumented error"); + break; + } + + return mp_obj_new_int(num_bytes_read); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2s_readinto_obj, 2, machine_i2s_readinto); + +STATIC mp_obj_t machine_i2s_write(mp_uint_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_buf, ARG_timeout }; + STATIC const mp_arg_t allowed_args[] = { + { MP_QSTR_buf, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_pos_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(args), allowed_args, args); + + machine_i2s_obj_t *self = pos_args[0]; + + if (!self->used) { + mp_raise_ValueError("I2S port is not initialized"); + } + + if (self->mode != (I2S_MODE_MASTER | I2S_MODE_TX)) { + mp_raise_ValueError("I2S not configured for write method"); + } + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buf].u_obj, &bufinfo, MP_BUFFER_WRITE); + + TickType_t timeout_in_ticks = portMAX_DELAY; + if (args[ARG_timeout].u_int != -1) { + timeout_in_ticks = pdMS_TO_TICKS(args[ARG_timeout].u_int); + } + + uint32_t num_bytes_written = 0; + esp_err_t ret = i2s_write(self->id, bufinfo.buf, bufinfo.len, &num_bytes_written, timeout_in_ticks); + switch (ret) { + case ESP_OK: + break; + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "I2S write: Parameter error"); + break; + default: + // this error not documented in ESP-IDF + mp_raise_msg(&mp_type_OSError, "I2S write: Undocumented error"); + break; + } + + return mp_obj_new_int(num_bytes_written); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_i2s_write_obj, 2, machine_i2s_write); + +STATIC mp_obj_t machine_i2s_deinit(mp_obj_t self_in) { + machine_i2s_obj_t *self = self_in; + i2s_driver_uninstall(self->id); + self->used = false; + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_i2s_deinit_obj, machine_i2s_deinit); + +STATIC const mp_rom_map_elem_t machine_i2s_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_i2s_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&machine_i2s_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&machine_i2s_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_i2s_deinit_obj) }, + + // Constants + { MP_ROM_QSTR(MP_QSTR_NUM0), MP_ROM_INT(I2S_NUM_0) }, + { MP_ROM_QSTR(MP_QSTR_NUM1), MP_ROM_INT(I2S_NUM_1) }, + { MP_ROM_QSTR(MP_QSTR_PHILIPS), MP_ROM_INT(I2S_COMM_FORMAT_I2S) }, + { MP_ROM_QSTR(MP_QSTR_LSB), MP_ROM_INT(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB) }, + // note: ESP-IDF does not implement the MSB standard (even though the Macro I2S_COMM_FORMAT_I2S_MSB is defined) + { MP_ROM_QSTR(MP_QSTR_MASTER_RX), MP_ROM_INT(I2S_MODE_MASTER | I2S_MODE_RX) }, + { MP_ROM_QSTR(MP_QSTR_MASTER_TX), MP_ROM_INT(I2S_MODE_MASTER | I2S_MODE_TX) }, + { MP_ROM_QSTR(MP_QSTR_B16), MP_ROM_INT(I2S_BITS_PER_SAMPLE_16BIT) }, + { MP_ROM_QSTR(MP_QSTR_B24), MP_ROM_INT(I2S_BITS_PER_SAMPLE_24BIT) }, + { MP_ROM_QSTR(MP_QSTR_B32), MP_ROM_INT(I2S_BITS_PER_SAMPLE_32BIT) }, + { MP_ROM_QSTR(MP_QSTR_RIGHT_LEFT), MP_ROM_INT(I2S_CHANNEL_FMT_RIGHT_LEFT) }, + { MP_ROM_QSTR(MP_QSTR_ALL_RIGHT), MP_ROM_INT(I2S_CHANNEL_FMT_ALL_RIGHT) }, + { MP_ROM_QSTR(MP_QSTR_ALL_LEFT), MP_ROM_INT(I2S_CHANNEL_FMT_ALL_LEFT) }, + { MP_ROM_QSTR(MP_QSTR_ONLY_RIGHT), MP_ROM_INT(I2S_CHANNEL_FMT_ONLY_RIGHT) }, + { MP_ROM_QSTR(MP_QSTR_ONLY_LEFT), MP_ROM_INT(I2S_CHANNEL_FMT_ONLY_LEFT) }, +}; +MP_DEFINE_CONST_DICT(machine_i2s_locals_dict, machine_i2s_locals_dict_table); + +const mp_obj_type_t machine_i2s_type = { + { &mp_type_type }, + .name = MP_QSTR_I2S, + .print = machine_i2s_print, + .make_new = machine_i2s_make_new, + .locals_dict = (mp_obj_dict_t *) &machine_i2s_locals_dict, +}; diff --git a/esp32/mods/machine_i2s.h b/esp32/mods/machine_i2s.h new file mode 100644 index 0000000000..ecf128597a --- /dev/null +++ b/esp32/mods/machine_i2s.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython ESP32 project + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Mike Teachman + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __MICROPY_INCLUDED_EXTMOD_MACHINE_I2S_H__ +#define __MICROPY_INCLUDED_EXTMOD_MACHINE_I2S_H__ + +#include "py/obj.h" + +extern const mp_obj_type_t machine_i2s_type; + +#endif // __MICROPY_INCLUDED_EXTMOD_MACHINE_I2S_H__ diff --git a/esp32/mods/modmachine.c b/esp32/mods/modmachine.c index 7bbaa76338..9cc198c84f 100644 --- a/esp32/mods/modmachine.c +++ b/esp32/mods/modmachine.c @@ -61,6 +61,7 @@ #include "machuart.h" #include "machtimer.h" #include "machine_i2c.h" +#include "machine_i2s.h" #include "machspi.h" #include "machpwm.h" #include "machrtc.h" @@ -410,6 +411,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_UART), (mp_obj_t)&mach_uart_type }, { MP_OBJ_NEW_QSTR(MP_QSTR_SPI), (mp_obj_t)&mach_spi_type }, { MP_OBJ_NEW_QSTR(MP_QSTR_I2C), (mp_obj_t)&machine_i2c_type }, + { MP_OBJ_NEW_QSTR(MP_QSTR_I2S), (mp_obj_t)&machine_i2s_type }, { MP_OBJ_NEW_QSTR(MP_QSTR_PWM), (mp_obj_t)&mach_pwm_timer_type }, { MP_OBJ_NEW_QSTR(MP_QSTR_ADC), (mp_obj_t)&pyb_adc_type }, { MP_OBJ_NEW_QSTR(MP_QSTR_DAC), (mp_obj_t)&pyb_dac_type }, From 78671345065173f4345dbbb7295ac11497bb8421 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Sat, 22 Feb 2020 06:04:50 +0100 Subject: [PATCH 5/8] Add PPP networking This adds the PPP networking module from Genuine MicroPython. --- esp32/application.mk | 1 + esp32/mods/modnetwork.c | 1 + esp32/mods/modnetwork.h | 2 + esp32/mods/network_ppp.c | 229 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 esp32/mods/network_ppp.c diff --git a/esp32/application.mk b/esp32/application.mk index f830e5532e..ae27a3f2ff 100644 --- a/esp32/application.mk +++ b/esp32/application.mk @@ -141,6 +141,7 @@ APP_MODS_SRC_C = $(addprefix mods/,\ moduos.c \ modusocket.c \ modnetwork.c \ + network_ppp.c \ modwlan.c \ modutime.c \ modpycom.c \ diff --git a/esp32/mods/modnetwork.c b/esp32/mods/modnetwork.c index 55a8594d56..50c6fe97c6 100644 --- a/esp32/mods/modnetwork.c +++ b/esp32/mods/modnetwork.c @@ -230,6 +230,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(network_server_deinit_obj, network_server_deini STATIC const mp_map_elem_t mp_module_network_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_network) }, { MP_OBJ_NEW_QSTR(MP_QSTR_WLAN), (mp_obj_t)&mod_network_nic_type_wlan }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PPP), (mp_obj_t)&ppp_make_new_obj }, #if defined (LOPY) || defined(LOPY4) || defined (FIPY) { MP_OBJ_NEW_QSTR(MP_QSTR_LoRa), (mp_obj_t)&mod_network_nic_type_lora }, #endif diff --git a/esp32/mods/modnetwork.h b/esp32/mods/modnetwork.h index 97aee48580..d78a91d8b7 100644 --- a/esp32/mods/modnetwork.h +++ b/esp32/mods/modnetwork.h @@ -131,4 +131,6 @@ void mod_network_register_nic(mp_obj_t nic); void mod_network_deregister_nic(mp_obj_t nic); mp_obj_t mod_network_find_nic(const mod_network_socket_obj_t *s, const uint8_t *ip); +MP_DECLARE_CONST_FUN_OBJ_1(ppp_make_new_obj); + #endif // MODNETWORK_H_ diff --git a/esp32/mods/network_ppp.c b/esp32/mods/network_ppp.c new file mode 100644 index 0000000000..96d9ead305 --- /dev/null +++ b/esp32/mods/network_ppp.c @@ -0,0 +1,229 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 "Eric Poulsen" + * + * Based on the ESP IDF example code which is Public Domain / CC0 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/objtype.h" +#include "py/stream.h" +#include "netutils.h" +#include "modmachine.h" + +#include "netif/ppp/ppp.h" +#include "netif/ppp/pppos.h" +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/netdb.h" +#include "lwip/dns.h" +#include "netif/ppp/pppapi.h" + +#define PPP_CLOSE_TIMEOUT_MS (4000) + +typedef struct _ppp_if_obj_t { + mp_obj_base_t base; + bool active; + bool connected; + volatile bool clean_close; + ppp_pcb *pcb; + mp_obj_t stream; + SemaphoreHandle_t inactiveWaitSem; + volatile TaskHandle_t client_task_handle; + struct netif pppif; +} ppp_if_obj_t; + +const mp_obj_type_t ppp_if_type; + +static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { + ppp_if_obj_t* self = ctx; + struct netif *pppif = ppp_netif(self->pcb); + + switch (err_code) { + case PPPERR_NONE: + self->connected = (pppif->ip_addr.u_addr.ip4.addr != 0); + break; + case PPPERR_USER: + self->clean_close = true; + break; + case PPPERR_CONNECT: + self->connected = false; + break; + default: + break; + } +} + +STATIC mp_obj_t ppp_make_new(mp_obj_t stream) { + mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + + ppp_if_obj_t *self = m_new_obj_with_finaliser(ppp_if_obj_t); + + self->base.type = &ppp_if_type; + self->stream = stream; + self->active = false; + self->connected = false; + self->clean_close = false; + self->client_task_handle = NULL; + + return MP_OBJ_FROM_PTR(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(ppp_make_new_obj, ppp_make_new); + +static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) { + ppp_if_obj_t *self = ctx; + int err; + return mp_stream_rw(self->stream, data, len, &err, MP_STREAM_RW_WRITE); +} + +static void pppos_client_task(void *self_in) { + ppp_if_obj_t *self = (ppp_if_obj_t*)self_in; + uint8_t buf[256]; + + while (ulTaskNotifyTake(pdTRUE, 0) == 0) { + int err; + int len = mp_stream_rw(self->stream, buf, sizeof(buf), &err, 0); + if (len > 0) { + pppos_input_tcpip(self->pcb, (u8_t*)buf, len); + } + } + + self->client_task_handle = NULL; + vTaskDelete(NULL); +} + +STATIC mp_obj_t ppp_active(size_t n_args, const mp_obj_t *args) { + ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (n_args > 1) { + if (mp_obj_is_true(args[1])) { + if (self->active) { + return mp_const_true; + } + + self->pcb = pppapi_pppos_create(&self->pppif, ppp_output_callback, ppp_status_cb, self); + + if (self->pcb == NULL) { + mp_raise_msg(&mp_type_RuntimeError, "init failed"); + } + pppapi_set_default(self->pcb); + pppapi_connect(self->pcb, 0); + + xTaskCreate(pppos_client_task, "ppp", 2048, self, 1, (TaskHandle_t*)&self->client_task_handle); + self->active = true; + } else { + if (!self->active) { + return mp_const_false; + } + + // Wait for PPPERR_USER, with timeout + pppapi_close(self->pcb, 0); + uint32_t t0 = mp_hal_ticks_ms(); + while (!self->clean_close && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { + mp_hal_delay_ms(10); + } + + // Shutdown task + xTaskNotifyGive(self->client_task_handle); + t0 = mp_hal_ticks_ms(); + while (self->client_task_handle != NULL && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { + mp_hal_delay_ms(10); + } + + // Release PPP + pppapi_free(self->pcb); + self->pcb = NULL; + self->active = false; + self->connected = false; + self->clean_close = false; + } + } + return mp_obj_new_bool(self->active); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_active_obj, 1, 2, ppp_active); + +STATIC mp_obj_t ppp_delete(mp_obj_t self_in) { + ppp_if_obj_t* self = MP_OBJ_TO_PTR(self_in); + mp_obj_t args[] = {self, mp_const_false}; + ppp_active(2, args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(ppp_delete_obj, ppp_delete); + +STATIC mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) { + ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + ip_addr_t dns; + if (n_args == 1) { + // get + if (self->pcb != NULL) { + dns = dns_getserver(0); + struct netif *pppif = ppp_netif(self->pcb); + mp_obj_t tuple[4] = { + netutils_format_ipv4_addr((uint8_t*)&pppif->ip_addr, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t*)&pppif->gw, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t*)&pppif->netmask, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t*)&dns, NETUTILS_BIG), + }; + return mp_obj_new_tuple(4, tuple); + } else { + mp_obj_t tuple[4] = { mp_const_none, mp_const_none, mp_const_none, mp_const_none }; + return mp_obj_new_tuple(4, tuple); + } + } else { + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[1], 4, &items); + netutils_parse_ipv4_addr(items[3], (uint8_t*)&dns.u_addr.ip4, NETUTILS_BIG); + dns_setserver(0, &dns); + return mp_const_none; + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_ifconfig_obj, 1, 2, ppp_ifconfig); + +STATIC mp_obj_t ppp_status(mp_obj_t self_in) { + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ppp_status_obj, ppp_status); + +STATIC mp_obj_t ppp_isconnected(mp_obj_t self_in) { + ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(self->connected); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(ppp_isconnected_obj, ppp_isconnected); + +STATIC const mp_rom_map_elem_t ppp_if_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&ppp_active_obj) }, + { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&ppp_isconnected_obj) }, + { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&ppp_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&ppp_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ppp_delete_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(ppp_if_locals_dict, ppp_if_locals_dict_table); + +const mp_obj_type_t ppp_if_type = { + { &mp_type_type }, + .name = MP_QSTR_PPP, + .locals_dict = (mp_obj_dict_t*)&ppp_if_locals_dict, +}; From 9f1d8bb6a64d78e47b1c6bb48be967f7fa969da5 Mon Sep 17 00:00:00 2001 From: robert Date: Fri, 15 Nov 2019 13:24:55 +0100 Subject: [PATCH 6/8] mods/machuart.c: Support the UART inverted mode Inverted mode can be set by a keyword argument to init or the constructor, using invert= options are: INV_RX, INV_TX, INV_RTS and INV_CTS Example: from machine import UART uart=UART(1, 9600, invert=UART.INV_TX|UART.INV_TX) --- esp32/mods/machuart.c | 52 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/esp32/mods/machuart.c b/esp32/mods/machuart.c index 6e515485c1..a411c446f5 100644 --- a/esp32/mods/machuart.c +++ b/esp32/mods/machuart.c @@ -25,6 +25,7 @@ #include "esp_system.h" #include "esp_spi_flash.h" #include "nvs_flash.h" +#include "esp_event.h" #include "esp_types.h" #include "esp_attr.h" @@ -92,6 +93,7 @@ struct _mach_uart_obj_t { uint8_t rx_timeout; uint8_t n_pins; bool init; + uint32_t invert; // lines to invert }; /****************************************************************************** @@ -293,10 +295,39 @@ STATIC void mach_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ mp_printf(print, ", parity=UART.%q", (self->config.parity == UART_PARITY_EVEN) ? MP_QSTR_EVEN : MP_QSTR_ODD); } if (self->config.stop_bits == UART_STOP_BITS_1_5) { - mp_printf(print, ", stop=1.5)"); + mp_printf(print, ", stop=1.5"); } else { - mp_printf(print, ", stop=%u)", (self->config.stop_bits == UART_STOP_BITS_1) ? 1 : 2); + mp_printf(print, ", stop=%u", (self->config.stop_bits == UART_STOP_BITS_1) ? 1 : 2); } + if (self->invert) { + mp_printf(print, ", invert="); + uint32_t invert_mask = self->invert; + if (invert_mask & UART_INVERSE_TXD) { + mp_print_str(print, "INV_TX"); + invert_mask &= ~UART_INVERSE_TXD; + if (invert_mask) { + mp_print_str(print, "|"); + } + } + if (invert_mask & UART_INVERSE_RXD) { + mp_print_str(print, "INV_RX"); + invert_mask &= ~UART_INVERSE_RXD; + if (invert_mask) { + mp_print_str(print, "|"); + } + } + if (invert_mask & UART_INVERSE_RTS) { + mp_print_str(print, "INV_RTS"); + invert_mask &= ~UART_INVERSE_RTS; + if (invert_mask) { + mp_print_str(print, "|"); + } + } + if (invert_mask & UART_INVERSE_CTS) { + mp_print_str(print, "INV_CTS"); + } + } + mp_printf(print, ")"); } else { mp_printf(print, "UART(%u)", self->uart_id); } @@ -432,6 +463,11 @@ STATIC mp_obj_t mach_uart_init_helper(mach_uart_obj_t *self, const mp_arg_val_t self->config.rx_flow_ctrl_thresh = 64; uart_param_config(self->uart_id, &self->config); + self->invert = args[7].u_int; + if (self->invert != 0) { + uart_set_line_inverse(self->uart_id, self->invert); + } + // install the UART driver uart_driver_install(self->uart_id, rx_buffer_size, 0, 0, NULL, 0, UARTRxCallback); @@ -458,7 +494,8 @@ STATIC const mp_arg_t mach_uart_init_args[] = { { MP_QSTR_stop, MP_ARG_OBJ, {.u_obj = mp_const_none} }, { MP_QSTR_pins, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_timeout_chars, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 2} }, - { MP_QSTR_rx_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MACHUART_RX_BUFFER_LEN} } + { MP_QSTR_rx_buffer_size, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MACHUART_RX_BUFFER_LEN} }, + { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} } }; STATIC mp_obj_t mach_uart_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *all_args) { // parse args @@ -560,9 +597,9 @@ STATIC mp_obj_t mach_uart_sendbreak(mp_uint_t n_args, const mp_obj_t *pos_args, gpio_matrix_out(pin->pin_number, pin->af_out, false, false); } - WRITE_PERI_REG(UART_CONF0_REG(self->uart_id), READ_PERI_REG(UART_CONF0_REG(self->uart_id)) | UART_TXD_INV); + WRITE_PERI_REG(UART_CONF0_REG(self->uart_id), READ_PERI_REG(UART_CONF0_REG(self->uart_id)) ^ UART_TXD_INV); ets_delay_us((delay > 0) ? delay : 1); - WRITE_PERI_REG(UART_CONF0_REG(self->uart_id), READ_PERI_REG(UART_CONF0_REG(self->uart_id)) & (~UART_TXD_INV)); + WRITE_PERI_REG(UART_CONF0_REG(self->uart_id), READ_PERI_REG(UART_CONF0_REG(self->uart_id)) ^ UART_TXD_INV); if (self->n_pins == 1) { // make it an input again @@ -595,6 +632,11 @@ STATIC const mp_map_elem_t mach_uart_locals_dict_table[] = { // class constants { MP_OBJ_NEW_QSTR(MP_QSTR_EVEN), MP_OBJ_NEW_SMALL_INT(UART_PARITY_EVEN) }, { MP_OBJ_NEW_QSTR(MP_QSTR_ODD), MP_OBJ_NEW_SMALL_INT(UART_PARITY_ODD) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_INV_RX), MP_OBJ_NEW_SMALL_INT(UART_INVERSE_RXD) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_INV_CTS), MP_OBJ_NEW_SMALL_INT(UART_INVERSE_CTS) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_INV_TX), MP_OBJ_NEW_SMALL_INT(UART_INVERSE_TXD) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_INV_RTS), MP_OBJ_NEW_SMALL_INT(UART_INVERSE_RTS) }, + // { MP_OBJ_NEW_QSTR(MP_QSTR_RX_ANY), MP_OBJ_NEW_SMALL_INT(2) }, }; STATIC MP_DEFINE_CONST_DICT(mach_uart_locals_dict, mach_uart_locals_dict_table); From 3349256e229027077fc76ddf982f32d6eb6aecee Mon Sep 17 00:00:00 2001 From: robert Date: Mon, 24 Feb 2020 22:27:10 +0100 Subject: [PATCH 7/8] Diverse changes to uos.stat() and UART modules - Use long int for time stamps in uos.stat() because of short int overflow - Simplify display of the UART config --- esp32/mods/machuart.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/esp32/mods/machuart.c b/esp32/mods/machuart.c index a411c446f5..e08ed05cf9 100644 --- a/esp32/mods/machuart.c +++ b/esp32/mods/machuart.c @@ -301,29 +301,23 @@ STATIC void mach_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ } if (self->invert) { mp_printf(print, ", invert="); - uint32_t invert_mask = self->invert; - if (invert_mask & UART_INVERSE_TXD) { + char *prequel = ""; + if (self->invert & UART_INVERSE_TXD) { mp_print_str(print, "INV_TX"); - invert_mask &= ~UART_INVERSE_TXD; - if (invert_mask) { - mp_print_str(print, "|"); - } + prequel = "|"; } - if (invert_mask & UART_INVERSE_RXD) { + if (self->invert & UART_INVERSE_RXD) { + mp_print_str(print, prequel); mp_print_str(print, "INV_RX"); - invert_mask &= ~UART_INVERSE_RXD; - if (invert_mask) { - mp_print_str(print, "|"); - } + prequel = "|"; } - if (invert_mask & UART_INVERSE_RTS) { + if (self->invert & UART_INVERSE_RTS) { + mp_print_str(print, prequel); mp_print_str(print, "INV_RTS"); - invert_mask &= ~UART_INVERSE_RTS; - if (invert_mask) { - mp_print_str(print, "|"); - } + prequel = "|"; } - if (invert_mask & UART_INVERSE_CTS) { + if (self->invert & UART_INVERSE_CTS) { + mp_print_str(print, prequel); mp_print_str(print, "INV_CTS"); } } From d82a6f43e99ba1365b7ab6d05fdc7e1bb1d08ed2 Mon Sep 17 00:00:00 2001 From: robert Date: Tue, 25 Feb 2020 13:00:58 +0100 Subject: [PATCH 8/8] modmachine.c: Check machine.sleep() for arg <= 0 This resolves #410. --- esp32/mods/modmachine.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esp32/mods/modmachine.c b/esp32/mods/modmachine.c index 9cc198c84f..4235747a42 100644 --- a/esp32/mods/modmachine.c +++ b/esp32/mods/modmachine.c @@ -185,6 +185,14 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); STATIC mp_obj_t machine_sleep (uint n_args, const mp_obj_t *arg) { bool reconnect = false; + int64_t sleep_time = 1000; // Safe default value, just to silence compiler warnings + + if (n_args > 0) { + sleep_time = (int64_t)mp_obj_get_int_truncated(arg[0]) * 1000; + if (sleep_time <= 0) { + return mp_const_none; + } + } #if defined(FIPY) || defined(GPY) if (lteppp_modem_state() < E_LTE_MODEM_DISCONNECTED) { @@ -208,7 +216,6 @@ STATIC mp_obj_t machine_sleep (uint n_args, const mp_obj_t *arg) { } else { - int64_t sleep_time = (int64_t)mp_obj_get_int_truncated(arg[0]) * 1000; struct timeval tv; gettimeofday(&tv, NULL); mach_expected_wakeup_time = (int64_t)((tv.tv_sec * 1000000ull) + tv.tv_usec) + sleep_time;